using static SAMSharp.Renderer; namespace SAMSharp { class Sam { //tab40672 static byte[] stressInputTable = { (byte)'*',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7',(byte)'8' }; //tab40682 static byte[] signInputTable1 = { (byte)' ',(byte)'.',(byte)'?',(byte)',',(byte)'-',(byte)'I',(byte)'I',(byte)'E', (byte)'A',(byte)'A',(byte)'A',(byte)'A',(byte)'U',(byte)'A',(byte)'I',(byte)'E', (byte)'U',(byte)'O',(byte)'R',(byte)'L',(byte)'W',(byte)'Y',(byte)'W',(byte)'R', (byte)'L',(byte)'W',(byte)'Y',(byte)'M',(byte)'N',(byte)'N',(byte)'D',(byte)'Q', (byte)'S',(byte)'S',(byte)'F',(byte)'T',(byte)'/',(byte)'/',(byte)'Z',(byte)'Z', (byte)'V',(byte)'D',(byte)'C',(byte)'*',(byte)'J',(byte)'*',(byte)'*',(byte)'*', (byte)'E',(byte)'A',(byte)'O',(byte)'A',(byte)'O',(byte)'U',(byte)'B',(byte)'*', (byte)'*',(byte)'D',(byte)'*',(byte)'*',(byte)'G',(byte)'*',(byte)'*',(byte)'G', (byte)'*',(byte)'*',(byte)'P',(byte)'*',(byte)'*',(byte)'T',(byte)'*',(byte)'*', (byte)'K',(byte)'*',(byte)'*',(byte)'K',(byte)'*',(byte)'*',(byte)'U',(byte)'U', (byte)'U' }; //tab40763 static byte[] signInputTable2 = { (byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'Y',(byte)'H',(byte)'H', (byte)'E',(byte)'A',(byte)'H',(byte)'O',(byte)'H',(byte)'X',(byte)'X',(byte)'R', (byte)'X',(byte)'H',(byte)'X',(byte)'X',(byte)'X',(byte)'X',(byte)'H',(byte)'*', (byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'X',(byte)'X',(byte)'*', (byte)'*',(byte)'H',(byte)'*',(byte)'H',(byte)'H',(byte)'X',(byte)'*',(byte)'H', (byte)'*',(byte)'H',(byte)'H',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*', (byte)'Y',(byte)'Y',(byte)'Y',(byte)'W',(byte)'W',(byte)'W',(byte)'*',(byte)'*', (byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'X', (byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*',(byte)'*', (byte)'*',(byte)'*',(byte)'*',(byte)'X',(byte)'*',(byte)'*',(byte)'L',(byte)'M', (byte)'N' }; //loc_9F8C enum Flags { FLAG_PLOSIVE = 0x0001, FLAG_STOPCONS = 0x0002, /* stop consonant */ FLAG_VOICED = 0x0004, /* 0x08 */ FLAG_DIPTHONG = 0x0010, FLAG_DIP_YX = 0x0020, /* dipthong ending with YX */ FLAG_CONSONANT = 0x0040, FLAG_VOWEL = 0x0080, FLAG_PUNCT = 0x0100, /* 0x200 */ FLAG_ALVEOLAR = 0x0400, FLAG_NASAL = 0x0800, FLAG_LIQUIC = 0x1000, /* liquic consonant */ FLAG_FRICATIVE = 0x2000 }; static ushort[] flags = { 0x8000 , 0xC100 , 0xC100 , 0xC100 , 0xC100 , 0x00A4 , 0x00A4 , 0x00A4 , 0x00A4 , 0x00A4 , 0x00A4 , 0x0084 , 0x0084 , 0x00A4 , 0x00A4 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0084 , 0x0044 , 0x1044 , 0x1044 , 0x1044 , 0x1044 , 0x084C , 0x0C4C , 0x084C , 0x0448 , 0x404C , 0x2440 , 0x2040 , 0x2040 , 0x2440 , 0x0040 , 0x0040 , 0x2444 , 0x2044 , 0x2044 , 0x2444 , 0x2048 , 0x2040 , 0x004C , 0x2044 , 0x0000 , 0x0000 , 0x00B4 , 0x00B4 , 0x00B4 , 0x0094 , 0x0094 , 0x0094 , 0x004E , 0x004E , 0x004E , 0x044E , 0x044E , 0x044E , 0x004E , 0x004E , 0x004E , 0x004E , 0x004E , 0x004E , 0x004B , 0x004B , 0x004B , 0x044B , 0x044B , 0x044B , 0x004B , 0x004B , 0x004B , 0x004B , 0x004B , 0x004B , 0x0080 , 0x00C1 , 0x00C1 }; //tab45616??? static byte[] phonemeStressedLengthTable = { 0x00 , 0x12 , 0x12 , 0x12 , 8 ,0xB , 9 ,0xB , 0xE ,0xF ,0xB , 0x10 ,0xC , 6 , 6 ,0xE , 0xC ,0xE ,0xC ,0xB , 8 , 8 ,0xB ,0xA , 9 , 8 , 8 , 8 , 8 , 8 , 3 , 5 , 2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 , 8 , 6 , 6 , 2 , 9 , 4 , 2 , 1 , 0xE ,0xF ,0xF ,0xF ,0xE ,0xE , 8 , 2 , 2 , 7 , 2 , 1 , 7 , 2 , 2 , 7 , 2 , 2 , 8 , 2 , 2 , 6 , 2 , 2 , 7 , 2 , 4 , 7 , 1 , 4 , 5 , 5 }; //tab45536??? static byte[] phonemeLengthTable = { 0 , 0x12 , 0x12 , 0x12 , 8 , 8 , 8 , 8 , 8 ,0xB , 6 ,0xC ,0xA , 5 , 5 ,0xB , 0xA ,0xA ,0xA , 9 , 8 , 7 , 9 , 7 , 6 , 8 , 6 , 7 , 7 , 7 , 2 , 5 , 2 , 2 , 2 , 2 , 2 , 2 , 6 , 6 , 7 , 6 , 6 , 2 , 8 , 3 , 1 , 0x1E , 0xD ,0xC ,0xC ,0xC ,0xE , 9 , 6 , 1 , 2 , 5 , 1 , 1 , 6 , 1 , 2 , 6 , 1 , 2 , 8 , 2 , 2 , 4 , 2 , 2 , 6 , 1 , 4 , 6 , 1 , 4 , 0xC7 , 0xFF }; enum Handler : byte { pR = 23, pD = 57, pT = 69, BREAK = 254, END = 255 }; static byte[] input = new byte[256]; //tab39445 //standard sam sound public static byte speed = 72; public static byte pitch = 64; public static byte mouth = 128; public static byte throat = 128; public static int singmode = 0; public static byte[] stress = new byte[256]; //numbers from 0 to 8 public static byte[] phonemeLength = new byte[256]; //tab40160 public static byte[] phonemeindex = new byte[256]; public static byte[] phonemeIndexOutput = new byte[60]; //tab47296 public static byte[] stressOutput = new byte[60]; //tab47365 public static byte[] phonemeLengthOutput = new byte[60]; //tab47416 // contains the final soundbuffer public static int bufferpos = 0; public static byte[] buffer = null; public static void SetInput(byte[] _input) { int i, l; l = input.Length; if (l > 254) l = 254; for (i = 0; i < l; i++) input[i] = _input[i]; input[l] = 0; } void SetSpeed(byte _speed) => speed = _speed; void SetPitch(byte _pitch) => pitch = _pitch; void SetMouth(byte _mouth) => mouth = _mouth; void SetThroat(byte _throat) => throat = _throat; void EnableSingmode() => singmode = 1; public static byte[] GetBuffer() => buffer; public static int GetBufferLength() => bufferpos; static void Init() { int i; SetMouthThroat(mouth, throat); bufferpos = 0; // TODO, check for free the memory, 10 seconds of output should be more than enough buffer = new byte[22050 * 10]; for (i = 0; i < 256; i++) { stress[i] = 0; phonemeLength[i] = 0; } for (i = 0; i < 60; i++) { phonemeIndexOutput[i] = 0; stressOutput[i] = 0; phonemeLengthOutput[i] = 0; } phonemeindex[255] = (byte)Handler.END; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs } public static int SAMMain() { byte X = 0; //!! is this intended like this? Init(); /* FIXME: At odds with assignment in Init() */ phonemeindex[255] = 32; //to prevent buffer overflow if (Parser1() == 0) return 0; Parser2(); CopyStress(); SetPhonemeLength(); AdjustLengths(); Code41240(); do { if (phonemeindex[X] > 80) { phonemeindex[X] = (byte)Handler.END; break; // error: delete all behind it } } while (++X != 0); InsertBreath(); PrepareOutput(); return 1; } static void PrepareOutput() { byte srcpos = 0; // Position in source byte destpos = 0; // Position in output while (true) { byte A = phonemeindex[srcpos]; phonemeIndexOutput[destpos] = A; switch (A) { case (byte)Handler.END: Render(); return; case (byte)Handler.BREAK: phonemeIndexOutput[destpos] = (byte)Handler.END; Render(); destpos = 0; break; case 0: break; default: phonemeLengthOutput[destpos] = phonemeLength[srcpos]; stressOutput[destpos] = stress[srcpos]; ++destpos; break; } ++srcpos; } } static void InsertBreath() { byte mem54 = 255; byte len = 0; byte index; //variable Y byte pos = 0; while ((index = phonemeindex[pos]) != (byte)Handler.END) { len += phonemeLength[pos]; if (len < 232) { if (index == (byte)Handler.BREAK) { } else if ((flags[index] & (ushort)Flags.FLAG_PUNCT) == 0) { if (index == 0) mem54 = pos; } else { len = 0; Insert(++pos, (byte)Handler.BREAK, 0, 0); } } else { pos = mem54; phonemeindex[pos] = 31; // 'Q*' glottal stop phonemeLength[pos] = 4; stress[pos] = 0; len = 0; Insert(++pos, (byte)Handler.BREAK, 0, 0); } ++pos; } } // Iterates through the phoneme buffer, copying the stress value from // the following phoneme under the following circumstance: // 1. The current phoneme is voiced, excluding plosives and fricatives // 2. The following phoneme is voiced, excluding plosives and fricatives, and // 3. The following phoneme is stressed // // In those cases, the stress value+1 from the following phoneme is copied. // // For example, the word LOITER is represented as LOY5TER, with as stress // of 5 on the dipthong OY. This routine will copy the stress value of 6 (5+1) // to the L that precedes it. static void CopyStress() { // loop thought all the phonemes to be output byte pos = 0; //mem66 byte Y; while ((Y = phonemeindex[pos]) != (byte)Handler.END) { // if CONSONANT_FLAG set, skip - only vowels get stress if ((flags[Y] & 64) != 0) { Y = phonemeindex[pos + 1]; // if the following phoneme is the end, or a vowel, skip if (Y != (byte)Handler.END && (flags[Y] & 128) != 0) { // get the stress value at the next position Y = stress[pos + 1]; if (Y != 0 && (Y & 128) == 0) { // if next phoneme is stressed, and a VOWEL OR ER // copy stress from next phoneme to this one stress[pos] = (byte)(Y + 1); } } } ++pos; } } static void Insert(byte position/*var57*/, byte mem60, byte mem59, byte mem58) { int i; for (i = 253; i >= position; i--) // ML : always keep last safe-guarding 255 { phonemeindex[i + 1] = phonemeindex[i]; phonemeLength[i + 1] = phonemeLength[i]; stress[i + 1] = stress[i]; } phonemeindex[position] = mem60; phonemeLength[position] = mem59; stress[position] = mem58; } static int full_match(byte sign1, byte sign2) { byte Y = 0; do { // GET FIRST CHARACTER AT POSITION Y IN signInputTable // --> should change name to PhonemeNameTable1 byte A = signInputTable1[Y]; if (A == sign1) { A = signInputTable2[Y]; // NOT A SPECIAL AND MATCHES SECOND CHARACTER? if ((A != '*') && (A == sign2)) return Y; } } while (++Y != 81); return -1; } static int wild_match(byte sign1) { int Y = 0; do { if (signInputTable2[Y] == '*') { if (signInputTable1[Y] == sign1) return Y; } } while (++Y != 81); return -1; } // The input[] buffer contains a string of phonemes and stress markers along // the lines of: // // DHAX KAET IHZ AH5GLIY. <0x9B> // // The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes // long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". // There are also stress markers, such as "5" and ".". // // The first character of the phonemes are stored in the table signInputTable1[]. // The second character of the phonemes are stored in the table signInputTable2[]. // The stress characters are arranged in low to high stress order in stressInputTable[]. // // The following process is used to parse the input[] buffer: // // Repeat until the <0x9B> character is reached: // // First, a search is made for a 2 character match for phonemes that do not // end with the '*' (wildcard) character. On a match, the index of the phoneme // is added to phonemeIndex[] and the buffer position is advanced 2 bytes. // // If this fails, a search is made for a 1 character match against all // phoneme names ending with a '*' (wildcard). If this succeeds, the // phoneme is added to phonemeIndex[] and the buffer position is advanced // 1 byte. // // If this fails, search for a 1 character match in the stressInputTable[]. // If this succeeds, the stress value is placed in the last stress[] table // at the same index of the last added phoneme, and the buffer position is // advanced by 1 byte. // // If this fails, return a 0. // // On success: // // 1. phonemeIndex[] will contain the index of all the phonemes. // 2. The last index in phonemeIndex[] will be 255. // 3. stress[] will contain the stress value for each phoneme // input[] holds the string of phonemes, each two bytes wide // signInputTable1[] holds the first character of each phoneme // signInputTable2[] holds te second character of each phoneme // phonemeIndex[] holds the indexes of the phonemes after parsing input[] // // The parser scans through the input[], finding the names of the phonemes // by searching signInputTable1[] and signInputTable2[]. On a match, it // copies the index of the phoneme into the phonemeIndexTable[]. // // The character <0x9B> marks the end of text in input[]. When it is reached, // the index 255 is placed at the end of the phonemeIndexTable[], and the // function returns with a 1 indicating success. static int Parser1() { byte sign1; byte position = 0; byte srcpos = 0; stress = new byte[256]; // Clear the stress table. while ((sign1 = input[srcpos]) != 155) { // 155 (\233) is end of line marker int match; byte sign2 = input[++srcpos]; if ((match = full_match(sign1, sign2)) != -1) { // Matched both characters (no wildcards) phonemeindex[position++] = (byte)match; ++srcpos; // Skip the second character of the input as we've matched it } else if ((match = wild_match(sign1)) != -1) { // Matched just the first character (with second character matching '*' phonemeindex[position++] = (byte)match; } else { // Should be a stress character. Search through the // stress table backwards. match = 8; // End of stress table. FIXME: Don't hardcode. while ((sign1 != stressInputTable[match]) && (match > 0)) --match; if (match == 0) return 0; // failure stress[position - 1] = (byte)match; // Set stress for prior phoneme } } //while phonemeindex[position] = (byte)Handler.END; return 1; } //change phonemelength depedendent on stress static void SetPhonemeLength() { int position = 0; while (phonemeindex[position] != 255) { byte A = stress[position]; if ((A == 0) || ((A & 128) != 0)) { phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; } else { phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; } position++; } } static void Code41240() { byte pos = 0; while (phonemeindex[pos] != (byte)Handler.END) { byte index = phonemeindex[pos]; if ((flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0) { if ((flags[index] & (ushort)Flags.FLAG_PLOSIVE) != 0) { byte A; byte X = pos; while (phonemeindex[++X] == 0) ; /* Skip pause */ A = phonemeindex[X]; if (A != (byte)Handler.END) { if ((flags[A] & 8) != 0 || (A == 36) || (A == 37)) { ++pos; continue; } // '/H' '/X' } } Insert((byte)(pos + 1), (byte)(index + 1), phonemeLengthTable[index + 1], stress[pos]); Insert((byte)(pos + 2), (byte)(index + 2), phonemeLengthTable[index + 2], stress[pos]); pos += 2; } ++pos; } } static void ChangeRule(byte position, byte mem60) { phonemeindex[position] = 13; //rule; Insert((byte)(position + 1), mem60, 0, stress[position]); } // Rewrites the phonemes using the following rules: // // -> WX // -> YX // UL -> AX L // UM -> AX M // -> Q // T R -> CH R // D R -> J R // R -> RX // L -> LX // G S -> G Z // K -> KX // G -> GX // S P -> S B // S T -> S D // S K -> S G // S KX -> S GX // UW -> UX // CH -> CH CH' (CH requires two phonemes to represent it) // J -> J J' (J requires two phonemes to represent it) // T -> DX // D -> DX static void rule_alveolar_uw(byte X) { // ALVEOLAR flag set? if ((flags[phonemeindex[X - 1]] & (ushort)Flags.FLAG_ALVEOLAR) != 0) phonemeindex[X] = 16; } static void rule_ch(byte X) { Insert((byte)(X + 1), 43, 0, stress[X]); } static void rule_j(byte X) { Insert((byte)(X + 1), 45, 0, stress[X]); } static void rule_g(byte pos) { // G -> GX // Example: GO byte index = phonemeindex[pos + 1]; // If dipthong ending with YX, move continue processing next phoneme if ((index != 255) && ((flags[index] & (ushort)Flags.FLAG_DIP_YX) == 0)) // replace G with GX and continue processing next phoneme phonemeindex[pos] = 63; // 'GX' } static void change(byte pos, byte val) { phonemeindex[pos] = val; } static void rule_dipthong(byte p, ushort pf, byte pos) { // -> WX // -> YX // Example: OIL, COW // If ends with IY, use YX, else use WX byte A = ((pf & (ushort)Flags.FLAG_DIP_YX) != 0) ? (byte)21 : (byte)20; // 'WX' = 20 'YX' = 21 // Insert at WX or YX following, copying the stress Insert((byte)(pos + 1), A, 0, stress[pos]); if (p == 53) rule_alveolar_uw(pos); // Example: NEW, DEW, SUE, ZOO, THOO, TOO else if (p == 42) rule_ch(pos); // Example: CHEW else if (p == 44) rule_j(pos); // Example: JAY } static void Parser2() { byte pos = 0; //mem66; byte p; while ((p = phonemeindex[pos]) != (byte)Handler.END) { ushort pf; byte prior; if (p == 0) { // Is phoneme pause? ++pos; continue; } pf = flags[p]; prior = phonemeindex[pos - 1]; if ((pf & (ushort)Flags.FLAG_DIPTHONG) != 0) rule_dipthong(p, pf, pos); else if (p == 78) ChangeRule(pos, 24); // Example: MEDDLE else if (p == 79) ChangeRule(pos, 27); // Example: ASTRONOMY else if (p == 80) ChangeRule(pos, 28); // Example: FUNCTION else if ((pf & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos] != 0) { // RULE: // -> Q // EXAMPLE: AWAY EIGHT if (phonemeindex[pos + 1] == 0) { // If following phoneme is a pause, get next p = phonemeindex[pos + 2]; if (p != (byte)Handler.END && (flags[p] & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos + 2] != 0) Insert((byte)(pos + 2), 31, 0, 0); // 31 = 'Q' } } else if (p == (byte)Handler.pR) { // RULES FOR PHONEMES BEFORE R if (prior == (byte)Handler.pT) change((byte)(pos - 1), 42); // Example: TRACK else if (prior == (byte)Handler.pD) change((byte)(pos - 1), 44); // Example: DRY else if ((flags[prior] & (ushort)Flags.FLAG_VOWEL) != 0) change(pos, 18); // Example: ART } else if (p == 24 && (flags[prior] & (ushort)Flags.FLAG_VOWEL) != 0) change(pos, 19); // Example: ALL else if (prior == 60 && p == 32) { // 'G' 'S' // Can't get to fire - // 1. The G -> GX rule intervenes // 2. Reciter already replaces GS -> GZ change(pos, 38); } else if (p == 60) rule_g(pos); else { if (p == 72) { // 'K' // K -> KX // Example: COW byte Y = phonemeindex[pos + 1]; // If at end, replace current phoneme with KX if ((flags[Y] & (ushort)Flags.FLAG_DIP_YX) == 0 || Y == (byte)Handler.END) { // VOWELS AND DIPTHONGS ENDING WITH IY SOUND flag set? change(pos, 75); p = 75; pf = flags[p]; } } // Replace with softer version? if ((flags[p] & (ushort)Flags.FLAG_PLOSIVE) != 0 && (prior == 32)) { // 'S' // RULE: // S P -> S B // S T -> S D // S K -> S G // S KX -> S GX // Examples: SPY, STY, SKY, SCOWL phonemeindex[pos] = (byte)(p - 12); } else if ((pf & (ushort)Flags.FLAG_PLOSIVE) == 0) { p = phonemeindex[pos]; if (p == 53) rule_alveolar_uw(pos); // Example: NEW, DEW, SUE, ZOO, THOO, TOO else if (p == 42) rule_ch(pos); // Example: CHEW else if (p == 44) rule_j(pos); // Example: JAY } if (p == 69 || p == 57) { // 'T', 'D' // RULE: Soften T following vowel // NOTE: This rule fails for cases such as "ODD" // T -> DX // D -> DX // Example: PARTY, TARDY if (flags[phonemeindex[pos - 1]] != 0 & (ushort)Flags.FLAG_VOWEL != 0) { p = phonemeindex[pos + 1]; if (p == 0) p = phonemeindex[pos + 2]; if ((flags[p] & (ushort)Flags.FLAG_VOWEL) != 0 && stress[pos + 1] == 0) change(pos, 30); } } } pos++; } // while } // Applies various rules that adjust the lengths of phonemes // // Lengthen or between and by 1.5 // - decrease length by 1 // - decrease vowel by 1/8th // - increase vowel by 1/2 + 1 // - set nasal = 5, consonant = 6 // {optional silence} - shorten both to 1/2 + 1 // - decrease by 2 // static void AdjustLengths() { // LENGTHEN VOWELS PRECEDING PUNCTUATION // // Search for punctuation. If found, back up to the first vowel, then // process all phonemes between there and up to (but not including) the punctuation. // If any phoneme is found that is a either a fricative or voiced, the duration is // increased by (length * 1.5) + 1 // loop index { byte X = 0; byte index1; while ((index1 = phonemeindex[X]) != (byte)Handler.END) { byte loopIndex1; // not punctuation? if ((flags[index1] & (ushort)Flags.FLAG_PUNCT) == 0) { ++X; continue; } loopIndex1 = X; while (--X != 0 && (flags[phonemeindex[X]] & (ushort)Flags.FLAG_VOWEL) == 0) ; // back up while not a vowel if (X == 0) break; do { // test for vowel index1 = phonemeindex[X]; // test for fricative/unvoiced or not voiced if ((flags[index1] & (ushort)Flags.FLAG_FRICATIVE) == 0 || (flags[index1] & (ushort)Flags.FLAG_VOICED) != 0) { //nochmal überprüfen byte A = phonemeLength[X]; // change phoneme length to (length * 1.5) + 1 phonemeLength[X] = (byte)((A >> 1) + A + 1); } } while (++X != loopIndex1); X++; } // while } // Similar to the above routine, but shorten vowels under some circumstances // Loop through all phonemes byte loopIndex = 0; byte index; while ((index = phonemeindex[loopIndex]) != (byte)Handler.END) { byte X = loopIndex; if ((flags[index] & (ushort)Flags.FLAG_VOWEL) != 0) { index = phonemeindex[loopIndex + 1]; if ((flags[index] & (ushort)Flags.FLAG_CONSONANT) == 0) { if ((index == 18) || (index == 19)) { // 'RX', 'LX' index = phonemeindex[loopIndex + 2]; if ((flags[index] & (ushort)Flags.FLAG_CONSONANT) != 0) phonemeLength[loopIndex]--; } } else { // Got here if not ushort flag = (index == (byte)Handler.END) ? (ushort)65 : (ushort)flags[index]; // 65 if end marker if ((flag & (ushort)Flags.FLAG_VOICED) == 0) { // Unvoiced // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX if ((flag & (ushort)Flags.FLAG_PLOSIVE) != 0) { // unvoiced plosive // RULE: // phonemeLength[loopIndex] -= (byte)(phonemeLength[loopIndex] >> 3); } } else { byte A; // decrease length A = phonemeLength[loopIndex]; phonemeLength[loopIndex] = (byte)((A >> 2) + A + 1); // 5/4*A + 1 } } } else if ((flags[index] & (ushort)Flags.FLAG_NASAL) != 0) { // nasal? // RULE: // Set punctuation length to 6 // Set stop consonant length to 5 index = phonemeindex[++X]; if (index != (byte)Handler.END && (flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0) { phonemeLength[X] = 6; // set stop consonant length to 6 phonemeLength[X - 1] = 5; // set nasal length to 5 } } else if ((flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0) { // (voiced) stop consonant? // RULE: {optional silence} // Shorten both to (length/2 + 1) // move past silence while ((index = phonemeindex[++X]) == 0) ; if (index != (byte)Handler.END && (flags[index] & (ushort)Flags.FLAG_STOPCONS) != 0) { // FIXME, this looks wrong? // RULE: {optional silence} phonemeLength[X] = (byte)((phonemeLength[X] >> 1) + 1); phonemeLength[loopIndex] = (byte)((phonemeLength[loopIndex] >> 1) + 1); X = loopIndex; } } else if ((flags[index] & (ushort)Flags.FLAG_LIQUIC) != 0) { // liquic consonant? // RULE: // Decrease by 2 index = phonemeindex[X - 1]; // prior phoneme; phonemeLength[X] -= 2; // 20ms } ++loopIndex; } } } }