/// <summary> /// Compile the trunc rule into binary writer. /// </summary> /// <param name="truncRuleFileName">File path of trunc rule.</param> /// <param name="phoneSet">Phone set.</param> /// <param name="bw">Binary writer.</param> /// <returns>Error.</returns> private static ErrorSet CompTruncRuleData(string truncRuleFileName, TtsPhoneSet phoneSet, BinaryWriter bw) { // maximum truncate rule length is 5 phonmes currently const int MaxTruncRuleLength = 5; ErrorSet errorSet = new ErrorSet(); List<TruncateNucleusRule> rules = new List<TruncateNucleusRule>(); XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(truncRuleFileName); XmlNamespaceManager nm = new XmlNamespaceManager(xmldoc.NameTable); nm.AddNamespace("tts", "http://schemas.microsoft.com/tts/toolsuite"); XmlNodeList nodeList = xmldoc.DocumentElement.SelectNodes( "/tts:offline/tts:truncateRules/tts:truncateRule", nm); if (nodeList != null) { foreach (XmlNode node in nodeList) { XmlNodeList phoneNodeList; XmlElement xmlNode = node as XmlElement; string side = xmlNode.GetAttribute("side"); int direction = 0; if (side.Equals("Right", StringComparison.OrdinalIgnoreCase)) { direction = 2; // TruncFromRight } else if (side.Equals("Left", StringComparison.OrdinalIgnoreCase)) { direction = 1; // TruncFromLeft } else { errorSet.Add(UnitGeneratorDataCompilerError.WrongRuleSide, side, xmlNode.InnerXml); } phoneNodeList = xmlNode.SelectNodes("tts:phone", nm); if (phoneNodeList.Count > MaxTruncRuleLength) { errorSet.Add(UnitGeneratorDataCompilerError.RuleLengthExceeded, MaxTruncRuleLength.ToString(CultureInfo.InvariantCulture), xmlNode.InnerXml); } else { int idx = 0; short[] ids = new short[MaxTruncRuleLength + 1]; foreach (XmlNode phoneNode in phoneNodeList) { XmlElement xmlPhoneNode = phoneNode as XmlElement; string phoneValue = xmlPhoneNode.GetAttribute("value"); Phone phone = phoneSet.GetPhone(phoneValue); if (phone != null) { ids[idx++] = (short)phone.Id; } else { errorSet.Add(UnitGeneratorDataCompilerError.InvalidPhone, phoneValue); } } ids[idx] = 0; TruncateNucleusRule rule = new TruncateNucleusRule(); rule.Ids = ids; rule.Direction = direction; rules.Add(rule); } } } // write the data bw.Write(rules.Count); foreach (TruncateNucleusRule ci in rules) { bw.Write(ci.Direction); for (int i = 0; i < ci.Ids.Length; i++) { bw.Write(BitConverter.GetBytes(ci.Ids[i])); } } return errorSet; }
/// <summary> /// Build the syllable's pronunciation. /// </summary> /// <param name="phoneSet">Phone set.</param> /// <returns>The built pronunciation string.</returns> public string BuildTextFromPhones(TtsPhoneSet phoneSet) { if (phoneSet == null) { throw new ArgumentNullException("phoneSet"); } StringBuilder sb = new StringBuilder(); foreach (ScriptPhone phone in _phones) { sb.AppendFormat("{0}{1}", phone.Name, " "); // append stress if (_stress != TtsStress.None) { Phone ttsPhone = phoneSet.GetPhone(phone.Name); if (ttsPhone != null && ttsPhone.IsVowel) { switch (_stress) { case TtsStress.Primary: sb.Append("1 "); break; case TtsStress.Secondary: sb.Append("2 "); break; case TtsStress.Tertiary: sb.Append("3 "); break; } } } // append tone if (!string.IsNullOrEmpty(phone.Tone)) { sb.AppendFormat("{0}{1}", phone.Tone, " "); } } return sb.ToString().Trim(); }
/// <summary> /// Compiler. /// </summary> /// <param name="syllabifyRuleFileName">Path of syllabify rule.</param> /// <param name="phoneSet">Phone set.</param> /// <param name="outputStream">Output Stream.</param> /// <returns>ErrorSet.</returns> public static ErrorSet Compile(string syllabifyRuleFileName, TtsPhoneSet phoneSet, Stream outputStream) { if (string.IsNullOrEmpty(syllabifyRuleFileName)) { throw new ArgumentNullException("syllabifyRuleFileName"); } if (phoneSet == null) { throw new ArgumentNullException("phoneSet"); } if (outputStream == null) { throw new ArgumentNullException("outputStream"); } // maximum rule length is 3 phonmes currently const int MaxRuleLength = 3; ErrorSet errorSet = new ErrorSet(); phoneSet.Validate(); if (phoneSet.ErrorSet.Contains(ErrorSeverity.MustFix)) { errorSet.Add(SyllabifyRuleCompilerError.InvalidPhoneSet); } else { BinaryWriter bw = new BinaryWriter(outputStream); { List<ushort[]> rules = new List<ushort[]>(); XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(syllabifyRuleFileName); XmlNamespaceManager nm = new XmlNamespaceManager(xmldoc.NameTable); nm.AddNamespace("tts", _ttsSchemaUri); XmlNodeList nodeList = xmldoc.DocumentElement.SelectNodes( "/tts:syllabifyRules/tts:initialConsonants", nm); if (nodeList != null) { foreach (XmlNode node in nodeList) { XmlNodeList phoneNodeList; XmlElement xmlNode = node as XmlElement; phoneNodeList = xmlNode.SelectNodes("tts:phone", nm); if (phoneNodeList.Count > MaxRuleLength) { errorSet.Add(SyllabifyRuleCompilerError.RuleLengthExceeded, MaxRuleLength.ToString(CultureInfo.InvariantCulture), xmlNode.InnerXml); } else { ushort[] rule = new ushort[MaxRuleLength + 1]; int idx = 0; foreach (XmlNode phoneNode in phoneNodeList) { XmlElement xmlPhoneNode = phoneNode as XmlElement; string phoneValue = xmlPhoneNode.GetAttribute("value"); Phone phone = phoneSet.GetPhone(phoneValue); if (phone != null) { rule[idx++] = (ushort)phone.Id; } else { errorSet.Add(SyllabifyRuleCompilerError.InvalidPhone, phoneValue); } } rule[idx] = 0; rules.Add(rule); } } bw.Write(rules.Count); foreach (ushort[] ci in rules) { for (int i = 0; i < ci.Length; i++) { bw.Write(BitConverter.GetBytes(ci[i])); } } } } } return errorSet; }
/// <summary> /// Check if the phone in phone question is in tts phone set. /// </summary> /// <param name="phoneSet">TTS Phone set.</param> /// <returns>Errors.</returns> public ErrorSet Validate(TtsPhoneSet phoneSet) { ErrorSet errors = new ErrorSet(); foreach (string phone in _phones) { if (phoneSet.GetPhone(phone) == null) { errors.Add(PhoneQuestionErrorType.UnrecognizedPhone, phone, _name); } } return errors; }