diff --git a/Makefile b/Makefile index 44db0e31..1153e0cf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -OBJS = reciter.o sam.o render.o main.o debug.o +OBJS = reciter.o sam.o render.o main.o debug.o processframes.o createtransitions.o CC = gcc @@ -21,4 +21,3 @@ package: clean: rm *.o - diff --git a/src/RenderTabs.h b/src/RenderTabs.h index be356c26..10bcdd0d 100755 --- a/src/RenderTabs.h +++ b/src/RenderTabs.h @@ -268,17 +268,17 @@ unsigned char multtable[] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 , 1 , 1 , 2 , 2 , 3 , 3 , - 4 , 4 , 5 , 5 , 6 , 6 , 7 , 7 , - 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , - 8 , 9 ,0xA ,0xB ,0xC ,0xD ,0xE ,0xF , - 0x00 , 1 , 3 , 4 , 6 , 7 , 9 ,0xA , - 0xC ,0xD ,0xF , 0x10 , 0x12 , 0x13 , 0x15 , 0x16 , - 0x00 , 2 , 4 , 6 , 8 ,0xA ,0xC ,0xE , + 0x00 , 0x00 , 0x01 , 0x01 , 0x02 , 0x02 , 0x03 , 0x03 , + 0x04 , 0x04 , 0x05 , 0x05 , 0x06 , 0x06 , 0x07 , 0x07 , + 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , + 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F , + 0x00 , 0x01 , 0x03 , 0x04 , 0x06 , 0x07 , 0x09 , 0x0A , + 0x0C , 0x0D , 0x0F , 0x10 , 0x12 , 0x13 , 0x15 , 0x16 , + 0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0E , 0x10 , 0x12 , 0x14 , 0x16 , 0x18 , 0x1A , 0x1C , 0x1E , - 0x00 , 2 , 5 , 7 ,0xA ,0xC ,0xF , 0x11 , + 0x00 , 0x02 , 0x05 , 0x07 , 0x0A , 0x0C , 0x0F , 0x11 , 0x14 , 0x16 , 0x19 , 0x1B , 0x1E , 0x20 , 0x23 , 0x25 , - 0x00 , 3 , 6 , 9 ,0xC ,0xF , 0x12 , 0x15 , + 0x00 , 0x03 , 0x06 , 0x09 , 0x0C , 0x0F , 0x12 , 0x15 , 0x18 , 0x1B , 0x1E , 0x21 , 0x24 , 0x27 , 0x2A , 0x2D , 0x00 , 0x03 , 0x07 , 0x0A , 0x0E , 0x11 , 0x15 , 0x18 , 0x1C , 0x1F , 0x23 , 0x26 , 0x2A , 0x2D , 0x31 , 0x34 , @@ -506,7 +506,4 @@ unsigned char sampleTable[0x500] = , 0xFE , 1 , 0xFC , 3 , 0xE0 ,0xF , 0 , 0xFC }; - - #endif - diff --git a/src/SamTabs.h b/src/SamTabs.h index 59209129..2ba75745 100644 --- a/src/SamTabs.h +++ b/src/SamTabs.h @@ -3,107 +3,107 @@ //tab40672 unsigned char stressInputTable[] = -{ - '*', '1', '2', '3', '4', '5', '6', '7', '8' -}; - -//tab40682 -unsigned char signInputTable1[]={ - ' ', '.', '?', ',', '-', 'I', 'I', 'E', - 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', - 'U', 'O', 'R', 'L', 'W', 'Y', 'W', 'R', - 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', - 'S', 'S', 'F', 'T', '/', '/', 'Z', 'Z', - 'V', 'D', 'C', '*', 'J', '*', '*', '*', - 'E', 'A', 'O', 'A', 'O', 'U', 'B', '*', - '*', 'D', '*', '*', 'G', '*', '*', 'G', - '*', '*', 'P', '*', '*', 'T', '*', '*', - 'K', '*', '*', 'K', '*', '*', 'U', 'U', - 'U' -}; - -//tab40763 -unsigned char signInputTable2[] = -{ - '*', '*', '*', '*', '*', 'Y', 'H', 'H', - 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', - 'X', 'H', 'X', 'X', 'X', 'X', 'H', '*', - '*', '*', '*', '*', '*', 'X', 'X', '*', - '*', 'H', '*', 'H', 'H', 'X', '*', 'H', - '*', 'H', 'H', '*', '*', '*', '*', '*', - 'Y', 'Y', 'Y', 'W', 'W', 'W', '*', '*', - '*', '*', '*', '*', '*', '*', '*', 'X', - '*', '*', '*', '*', '*', '*', '*', '*', - '*', '*', '*', 'X', '*', '*', 'L', 'M', - 'N' -}; - -//loc_9F8C -unsigned char flags[]={ - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xA4 , 0xA4 , 0xA4 , - 0xA4 , 0xA4 , 0xA4 , 0x84 , 0x84 , 0xA4 , 0xA4 , 0x84 , - 0x84 , 0x84 , 0x84 , 0x84 , 0x84 , 0x84 , 0x44 , 0x44 , - 0x44 , 0x44 , 0x44 , 0x4C , 0x4C , 0x4C , 0x48 , 0x4C , - 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x40 , 0x44 , 0x44 , - 0x44 , 0x44 , 0x48 , 0x40 , 0x4C , 0x44 , 0x00 , 0x00 , - 0xB4 , 0xB4 , 0xB4 , 0x94 , 0x94 , 0x94 , 0x4E , 0x4E , - 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , 0x4E , - 0x4E , 0x4E , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , - 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x4B , 0x80 , 0xC1 , - 0xC1 - - -}; - -//??? flags overlap flags2 -//loc_9FDA -unsigned char flags2[] = -{ - 0x80 , 0xC1 , 0xC1 , 0xC1 , 0xC1 , 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x10 , - 0x10 , 0x10 , 0x10 , 0x08 , 0x0C , 0x08 , 0x04 , 0x40 , - 0x24 , 0x20 , 0x20 , 0x24 , 0x00 , 0x00 , 0x24 , 0x20 , - 0x20 , 0x24 , 0x20 , 0x20 , 0x00 , 0x20 , 0x00 , 0x00 , - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , - 0x00 , 0x04 , 0x04 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x04 , 0x04 , 0x04 , - 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 +{ + '*', '1', '2', '3', '4', '5', '6', '7', '8' }; - - - -//tab45616??? + +//tab40682 +unsigned char signInputTable1[]={ + ' ', '.', '?', ',', '-', 'I', 'I', 'E', + 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', + 'U', 'O', 'R', 'L', 'W', 'Y', 'W', 'R', + 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', + 'S', 'S', 'F', 'T', '/', '/', 'Z', 'Z', + 'V', 'D', 'C', '*', 'J', '*', '*', '*', + 'E', 'A', 'O', 'A', 'O', 'U', 'B', '*', + '*', 'D', '*', '*', 'G', '*', '*', 'G', + '*', '*', 'P', '*', '*', 'T', '*', '*', + 'K', '*', '*', 'K', '*', '*', 'U', 'U', + 'U' +}; + +//tab40763 +unsigned char signInputTable2[] = +{ + '*', '*', '*', '*', '*', 'Y', 'H', 'H', + 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', + 'X', 'H', 'X', 'X', 'X', 'X', 'H', '*', + '*', '*', '*', '*', '*', 'X', 'X', '*', + '*', 'H', '*', 'H', 'H', 'X', '*', 'H', + '*', 'H', 'H', '*', '*', '*', '*', '*', + 'Y', 'Y', 'Y', 'W', 'W', 'W', '*', '*', + '*', '*', '*', '*', '*', '*', '*', 'X', + '*', '*', '*', '*', '*', '*', '*', '*', + '*', '*', '*', 'X', '*', '*', 'L', 'M', + 'N' +}; + +//loc_9F8C +enum { + 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 +}; + +unsigned short 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??? unsigned char 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 , +{ + 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??? + +//tab45536??? unsigned char 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 , +{ + 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 }; - + /* diff --git a/src/createtransitions.c b/src/createtransitions.c new file mode 100644 index 00000000..71d3c7f3 --- /dev/null +++ b/src/createtransitions.c @@ -0,0 +1,206 @@ +#include +#include +#include "render.h" + +// CREATE TRANSITIONS +// +// Linear transitions are now created to smoothly connect each +// phoeneme. This transition is spread between the ending frames +// of the old phoneme (outBlendLength), and the beginning frames +// of the new phoneme (inBlendLength). +// +// To determine how many frames to use, the two phonemes are +// compared using the blendRank[] table. The phoneme with the +// smaller score is used. In case of a tie, a blend of each is used: +// +// if blendRank[phoneme1] == blendRank[phomneme2] +// // use lengths from each phoneme +// outBlendFrames = outBlend[phoneme1] +// inBlendFrames = outBlend[phoneme2] +// else if blendRank[phoneme1] < blendRank[phoneme2] +// // use lengths from first phoneme +// outBlendFrames = outBlendLength[phoneme1] +// inBlendFrames = inBlendLength[phoneme1] +// else +// // use lengths from the second phoneme +// // note that in and out are swapped around! +// outBlendFrames = inBlendLength[phoneme2] +// inBlendFrames = outBlendLength[phoneme2] +// +// Blend lengths can't be less than zero. +// +// For most of the parameters, SAM interpolates over the range of the last +// outBlendFrames-1 and the first inBlendFrames. +// +// The exception to this is the Pitch[] parameter, which is interpolates the +// pitch from the center of the current phoneme to the center of the next +// phoneme. + +// From render.c +extern unsigned char phonemeIndexOutput[60]; //tab47296 +extern unsigned char phonemeLengthOutput[60]; //tab47416 + +// from RenderTabs.h +extern unsigned char blendRank[]; +extern unsigned char outBlendLength[]; +extern unsigned char inBlendLength[]; +extern unsigned char pitches[]; + +extern unsigned char frequency1[256]; +extern unsigned char frequency2[256]; +extern unsigned char frequency3[256]; + +extern unsigned char amplitude1[256]; +extern unsigned char amplitude2[256]; +extern unsigned char amplitude3[256]; + +//written by me because of different table positions. +// mem[47] = ... +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 +unsigned char Read(unsigned char p, unsigned char Y) +{ + switch(p) + { + case 168: return pitches[Y]; + case 169: return frequency1[Y]; + case 170: return frequency2[Y]; + case 171: return frequency3[Y]; + case 172: return amplitude1[Y]; + case 173: return amplitude2[Y]; + case 174: return amplitude3[Y]; + default: + printf("Error reading from tables"); + return 0; + } +} + +void Write(unsigned char p, unsigned char Y, unsigned char value) +{ + switch(p) + { + case 168: pitches[Y] = value; return; + case 169: frequency1[Y] = value; return; + case 170: frequency2[Y] = value; return; + case 171: frequency3[Y] = value; return; + case 172: amplitude1[Y] = value; return; + case 173: amplitude2[Y] = value; return; + case 174: amplitude3[Y] = value; return; + default: + printf("Error writing to tables\n"); + return; + } +} + + +// linearly interpolate values +void interpolate(unsigned char width, unsigned char table, unsigned char frame, char mem53) +{ + unsigned char sign = (mem53 < 0); + unsigned char remainder = abs(mem53) % width; + unsigned char div = mem53 / width; + + unsigned char error = 0; + unsigned char pos = width; + unsigned char val = Read(table, frame) + div; + + while(--pos) { + error += remainder; + if (error >= width) { // accumulated a whole integer error, so adjust output + error -= width; + if (sign) val--; + else if (val) val++; // if input is 0, we always leave it alone + } + Write(table, ++frame, val); // Write updated value back to next frame. + val += div; + } +} + +void interpolate_pitch(unsigned char pos, unsigned char mem49, unsigned char phase3) { + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + // half the width of the current and next phoneme + unsigned char cur_width = phonemeLengthOutput[pos] / 2; + unsigned char next_width = phonemeLengthOutput[pos+1] / 2; + // sum the values + unsigned char width = cur_width + next_width; + char pitch = pitches[next_width + mem49] - pitches[mem49- cur_width]; + interpolate(width, 168, phase3, pitch); +} + + +unsigned char CreateTransitions() +{ + unsigned char mem49 = 0; + unsigned char pos = 0; + while(1) { + unsigned char next_rank; + unsigned char rank; + unsigned char speedcounter; + unsigned char phase1; + unsigned char phase2; + unsigned char phase3; + unsigned char transition; + + unsigned char phoneme = phonemeIndexOutput[pos]; + unsigned char next_phoneme = phonemeIndexOutput[pos+1]; + + if (next_phoneme == 255) break; // 255 == end_token + + // get the ranking of each phoneme + next_rank = blendRank[next_phoneme]; + rank = blendRank[phoneme]; + + // compare the rank - lower rank value is stronger + if (rank == next_rank) { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[phoneme]; + phase2 = outBlendLength[next_phoneme]; + } else if (rank < next_rank) { + // next phoneme is stronger, so us its blend lengths + phase1 = inBlendLength[next_phoneme]; + phase2 = outBlendLength[next_phoneme]; + } else { + // current phoneme is stronger, so use its blend lengths + // note the out/in are swapped + phase1 = outBlendLength[phoneme]; + phase2 = inBlendLength[phoneme]; + } + + mem49 += phonemeLengthOutput[pos]; + + speedcounter = mem49 + phase2; + phase3 = mem49 - phase1; + transition = phase1 + phase2; // total transition? + + if (((transition - 2) & 128) == 0) { + unsigned char table = 169; + interpolate_pitch(pos, mem49, phase3); + while (table < 175) { + // tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + char value = Read(table, speedcounter) - Read(table, phase3); + interpolate(transition, table, phase3, value); + table++; + } + } + ++pos; + } + + // add the length of this phoneme + return mem49 + phonemeLengthOutput[pos]; +} diff --git a/src/debug.c b/src/debug.c index 38941334..e7ff8192 100755 --- a/src/debug.c +++ b/src/debug.c @@ -43,9 +43,9 @@ void PrintOutput( unsigned char *a3, unsigned char *p) { + int i = 0; printf("===========================================\n"); printf("Final data for speech output:\n\n"); - int i = 0; printf(" flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch\n"); printf("------------------------------------------------\n"); while(i < 255) @@ -59,9 +59,9 @@ void PrintOutput( extern unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); -void PrintRule(int offset) +void PrintRule(unsigned short offset) { - int i = 1; + unsigned char i = 1; unsigned char A = 0; printf("Applying rule: "); do @@ -72,4 +72,3 @@ void PrintRule(int offset) } while ((A&128)==0); printf("\n"); } - diff --git a/src/debug.h b/src/debug.h index 0ac71e69..8a394eda 100755 --- a/src/debug.h +++ b/src/debug.h @@ -12,6 +12,6 @@ void PrintOutput( unsigned char *a3, unsigned char *p); -void PrintRule(int offset); +void PrintRule(unsigned short offset); -#endif \ No newline at end of file +#endif diff --git a/src/main.c b/src/main.c index 8ab17f3e..3e8fdde3 100644 --- a/src/main.c +++ b/src/main.c @@ -12,30 +12,47 @@ #include #endif + +// Approximations of some Windows functions to ease portability +#if defined __GNU_LIBRARY__ || defined __GLIBC__ +static int min(int l, int r) { return l < r ? l : r; } +static void strcat_s(char * dest, int size, char * str) { + unsigned int dlen = strlen(dest); + if (dlen >= size-1) return; + strncat(dest+dlen, str, size - dlen - 1); +} +void fopen_s(FILE ** f, const char * filename, const char * mode) { + *f = fopen(filename,mode); +} +#endif + void WriteWav(char* filename, char* buffer, int bufferlength) { - FILE *file = fopen(filename, "wb"); + unsigned int filesize; + unsigned int fmtlength = 16; + unsigned short int format=1; //PCM + unsigned short int channels=1; + unsigned int samplerate = 22050; + unsigned short int blockalign = 1; + unsigned short int bitspersample=8; + + FILE *file; + fopen_s(&file, filename, "wb"); if (file == NULL) return; //RIFF header fwrite("RIFF", 4, 1,file); - unsigned int filesize=bufferlength + 12 + 16 + 8 - 8; + filesize=bufferlength + 12 + 16 + 8 - 8; fwrite(&filesize, 4, 1, file); fwrite("WAVE", 4, 1, file); //format chunk fwrite("fmt ", 4, 1, file); - unsigned int fmtlength = 16; fwrite(&fmtlength, 4, 1, file); - unsigned short int format=1; //PCM fwrite(&format, 2, 1, file); - unsigned short int channels=1; fwrite(&channels, 2, 1, file); - unsigned int samplerate = 22050; fwrite(&samplerate, 4, 1, file); fwrite(&samplerate, 4, 1, file); // bytes/second - unsigned short int blockalign = 1; fwrite(&blockalign, 2, 1, file); - unsigned short int bitspersample=8; fwrite(&bitspersample, 2, 1, file); //data chunk @@ -154,9 +171,9 @@ int main(int argc, char **argv) int phonetic = 0; char* wavfilename = NULL; - char input[256]; + unsigned char input[256]; - for(i=0; i<256; i++) input[i] = 0; + memset(input, 0, 256); if (argc <= 1) { @@ -169,8 +186,8 @@ int main(int argc, char **argv) { if (argv[i][0] != '-') { - strcat(input, argv[i]); - strcat(input, " "); + strcat_s((char*)input, 256, argv[i]); + strcat_s((char*)input, 256, " "); } else { if (strcmp(&argv[i][1], "wav")==0) @@ -192,22 +209,22 @@ int main(int argc, char **argv) } else if (strcmp(&argv[i][1], "pitch")==0) { - SetPitch(atoi(argv[i+1])); + SetPitch((unsigned char)min(atoi(argv[i+1]),255)); i++; } else if (strcmp(&argv[i][1], "speed")==0) { - SetSpeed(atoi(argv[i+1])); + SetSpeed((unsigned char)min(atoi(argv[i+1]),255)); i++; } else if (strcmp(&argv[i][1], "mouth")==0) { - SetMouth(atoi(argv[i+1])); + SetMouth((unsigned char)min(atoi(argv[i+1]),255)); i++; } else if (strcmp(&argv[i][1], "throat")==0) { - SetThroat(atoi(argv[i+1])); + SetThroat((unsigned char)min(atoi(argv[i+1]),255)); i++; } else { @@ -220,7 +237,7 @@ int main(int argc, char **argv) } //while for(i=0; input[i] != 0; i++) - input[i] = toupper((int)input[i]); + input[i] = (unsigned char)toupper((int)input[i]); if (debug) { @@ -230,11 +247,11 @@ int main(int argc, char **argv) if (!phonetic) { - strcat(input, "["); + strcat_s((char*)input, 256, "["); if (!TextToPhonemes(input)) return 1; if (debug) printf("phonetic input: %s\n", input); - } else strcat(input, "\x9b"); + } else strcat_s((char*)input, 256, "\x9b"); #ifdef USESDL if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) @@ -251,19 +268,11 @@ int main(int argc, char **argv) PrintUsage(); return 1; } - + if (wavfilename != NULL) WriteWav(wavfilename, GetBuffer(), GetBufferLength()/50); else OutputSound(); - return 0; - } - - - - - - diff --git a/src/processframes.c b/src/processframes.c new file mode 100644 index 00000000..92bb86d8 --- /dev/null +++ b/src/processframes.c @@ -0,0 +1,112 @@ +#include "render.h" + +extern unsigned char speed; + +// From RenderTabs.h +extern unsigned char multtable[]; +extern unsigned char sinus[]; +extern unsigned char rectangle[]; + +// From render.c +extern unsigned char pitches[256]; +extern unsigned char sampledConsonantFlag[256]; // tab44800 +extern unsigned char amplitude1[256]; +extern unsigned char amplitude2[256]; +extern unsigned char amplitude3[256]; +extern unsigned char frequency1[256]; +extern unsigned char frequency2[256]; +extern unsigned char frequency3[256]; + +extern void Output(int index, unsigned char A); + +static void CombineGlottalAndFormants(unsigned char phase1, unsigned char phase2, unsigned char phase3, unsigned char Y) +{ + unsigned int tmp; + + tmp = multtable[sinus[phase1] | amplitude1[Y]]; + tmp += multtable[sinus[phase2] | amplitude2[Y]]; + tmp += tmp > 255 ? 1 : 0; // if addition above overflows, we for some reason add one; + tmp += multtable[rectangle[phase3] | amplitude3[Y]]; + tmp += 136; + tmp >>= 4; // Scale down to 0..15 range of C64 audio. + + Output(0, tmp & 0xf); +} + +// PROCESS THE FRAMES +// +// In traditional vocal synthesis, the glottal pulse drives filters, which +// are attenuated to the frequencies of the formants. +// +// SAM generates these formants directly with sin and rectangular waves. +// To simulate them being driven by the glottal pulse, the waveforms are +// reset at the beginning of each glottal pulse. +// +void ProcessFrames(unsigned char mem48) +{ + unsigned char speedcounter = 72; + unsigned char phase1 = 0; + unsigned char phase2 = 0; + unsigned char phase3 = 0; + unsigned char mem66 = 0; //!! was not initialized + + unsigned char Y = 0; + + unsigned char glottal_pulse = pitches[0]; + unsigned char mem38 = glottal_pulse - (glottal_pulse >> 2); // mem44 * 0.75 + + while(mem48) { + unsigned char flags = sampledConsonantFlag[Y]; + + // unvoiced sampled phoneme? + if(flags & 248) { + RenderSample(&mem66, flags,Y); + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + speedcounter = speed; + } else { + CombineGlottalAndFormants(phase1, phase2, phase3, Y); + + speedcounter--; + if (speedcounter == 0) { + Y++; //go to next amplitude + // decrement the frame count + mem48--; + if(mem48 == 0) return; + speedcounter = speed; + } + + --glottal_pulse; + + if(glottal_pulse != 0) { + // not finished with a glottal pulse + + --mem38; + // within the first 75% of the glottal pulse? + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (flags == 0)) { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66, flags,Y); + } + } + + glottal_pulse = pitches[Y]; + mem38 = glottal_pulse - (glottal_pulse>>2); // mem44 * 0.75 + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + } +} diff --git a/src/reciter.c b/src/reciter.c index ca976e92..5b72d3b3 100644 --- a/src/reciter.c +++ b/src/reciter.c @@ -4,36 +4,31 @@ #include "ReciterTabs.h" #include "debug.h" -unsigned char A, X, Y; +unsigned char A, X; extern int debug; static unsigned char inputtemp[256]; // secure copy of input tab36096 -void Code37055(unsigned char mem59) +/* Retrieve flags for character at mem59-1 */ +unsigned char Code37055(unsigned char npos, unsigned char mask) { - X = mem59; - X--; - A = inputtemp[X]; - Y = A; - A = tab36376[Y]; - return; + X = npos; + return tab36376[inputtemp[X]] & mask; } -void Code37066(unsigned char mem58) -{ - X = mem58; - X++; - A = inputtemp[X]; - Y = A; - A = tab36376[Y]; +unsigned int match(const char * str) { + while (*str) { + unsigned char ch = *str; + A = inputtemp[X++]; + if (A != ch) return 0; + ++str; + } + return 1; } -unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) -{ +unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) { unsigned int address = mem62; - - if (mem62 >= 37541) - { + if (mem62 >= 37541) { address -= 37541; return rules2[address+Y]; } @@ -41,10 +36,50 @@ unsigned char GetRuleByte(unsigned short mem62, unsigned char Y) return rules[address+Y]; } -int TextToPhonemes(char *input) // Code36484 -{ - //unsigned char *tab39445 = &mem[39445]; //input and output - //unsigned char mem29; +int handle_ch2(unsigned char ch, unsigned char mem) { + unsigned char tmp; + X = mem; + tmp = tab36376[inputtemp[mem]]; + if (ch == ' ') { + if(tmp & 128) return 1; + } else if (ch == '#') { + if(!(tmp & 64)) return 1; + } else if (ch == '.') { + if(!(tmp & 8)) return 1; + } else if (ch == '^') { + if(!(tmp & 32)) return 1; + } else return -1; + return 0; +} + + +int handle_ch(unsigned char ch, unsigned char mem) { + unsigned char tmp; + X = mem; + tmp = tab36376[inputtemp[X]]; + if (ch == ' ') { + if ((tmp & 128) != 0) return 1; + } else if (ch == '#') { + if ((tmp & 64) == 0) return 1; + } else if (ch == '.') { + if((tmp & 8) == 0) return 1; + } else if (ch == '&') { + if((tmp & 16) == 0) { + if (inputtemp[X] != 72) return 1; + ++X; + } + } else if (ch == '^') { + if ((tmp & 32) == 0) return 1; + } else if (ch == '+') { + X = mem; + ch = inputtemp[X]; + if ((ch != 69) && (ch != 73) && (ch != 89)) return 1; + } else return -1; + return 0; +} + + +int TextToPhonemes(unsigned char *input) { unsigned char mem56; //output position for phonemes unsigned char mem57; unsigned char mem58; @@ -56,499 +91,213 @@ int TextToPhonemes(char *input) // Code36484 unsigned char mem64; // position of '=' or current character unsigned char mem65; // position of ')' unsigned char mem66; // position of '(' - unsigned char mem36653; - inputtemp[0] = 32; + unsigned char Y; + + int r; + + inputtemp[0] = ' '; // secure copy of input // because input will be overwritten by phonemes - X = 1; - Y = 0; - do - { - //pos36499: - A = input[Y] & 127; + X = 0; + do { + A = input[X] & 127; if ( A >= 112) A = A & 95; else if ( A >= 96) A = A & 79; - - inputtemp[X] = A; - X++; - Y++; - } while (Y != 255); - - - X = 255; - inputtemp[X] = 27; - mem61 = 255; - - -pos36550: - A = 255; - mem56 = 255; - + inputtemp[++X] = A; + } while (X < 255); + inputtemp[255] = 27; + mem56 = mem61 = 255; pos36554: - while(1) - { - mem61++; - X = mem61; - A = inputtemp[X]; - mem64 = A; - if (A == '[') - { - mem56++; - X = mem56; - A = 155; - input[X] = 155; - //goto pos36542; - // Code39771(); //Code39777(); - return 1; - } - - //pos36579: - if (A != '.') break; - X++; - Y = inputtemp[X]; - A = tab36376[Y] & 1; - if(A != 0) break; - mem56++; - X = mem56; - A = '.'; - input[X] = '.'; - } //while - - - //pos36607: - A = mem64; - Y = A; - A = tab36376[A]; - mem57 = A; - if((A&2) != 0) - { - mem62 = 37541; - goto pos36700; - } - - //pos36630: - A = mem57; - if(A != 0) goto pos36677; - A = 32; - inputtemp[X] = ' '; - mem56++; - X = mem56; - if (X > 120) goto pos36654; - input[X] = A; - goto pos36554; - - // ----- - - //36653 is unknown. Contains position - -pos36654: - input[X] = 155; - A = mem61; - mem36653 = A; - // mem29 = A; // not used - // Code36538(); das ist eigentlich - return 1; - //Code39771(); - //go on if there is more input ??? - mem61 = mem36653; - goto pos36550; - -pos36677: - A = mem57 & 128; - if(A == 0) - { - //36683: BRK - return 0; - } + while (1) { + while(1) { + X = ++mem61; + mem64 = inputtemp[X]; + if (mem64 == '[') { + X = ++mem56; + input[X] = 155; + return 1; + } + + if (mem64 != '.') break; + X++; + A = tab36376[inputtemp[X]] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } + mem57 = tab36376[mem64]; + if((mem57&2) != 0) { + mem62 = 37541; + goto pos36700; + } + + if(mem57 != 0) break; + inputtemp[X] = ' '; + X = ++mem56; + if (X > 120) { + input[X] = 155; + return 1; + } + input[X] = 32; + } + + if(!(mem57 & 128)) return 0; // go to the right rules for this character. - X = mem64 - 'A'; - mem62 = tab37489[X] | (tab37515[X]<<8); - - // ------------------------------------- - // go to next rule - // ------------------------------------- + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X]<<8); pos36700: - // find next rule + while ((GetRuleByte(++mem62, 0) & 128) == 0); Y = 0; - do - { - mem62 += 1; - A = GetRuleByte(mem62, Y); - } while ((A & 128) == 0); - Y++; - - //pos36720: - // find '(' - while(1) - { - A = GetRuleByte(mem62, Y); - if (A == '(') break; - Y++; - } + while(GetRuleByte(mem62, ++Y) != '('); mem66 = Y; - - //pos36732: - // find ')' - do - { - Y++; - A = GetRuleByte(mem62, Y); - } while(A != ')'); + while(GetRuleByte(mem62, ++Y) != ')'); mem65 = Y; - - //pos36741: - // find '=' - do - { - Y++; - A = GetRuleByte(mem62, Y); - A = A & 127; - } while (A != '='); + while((GetRuleByte(mem62, ++Y) & 127) != '='); mem64 = Y; - X = mem61; - mem60 = X; - + + mem60 = X = mem61; // compare the string within the bracket - Y = mem66; - Y++; - //pos36759: - while(1) - { - mem57 = inputtemp[X]; - A = GetRuleByte(mem62, Y); - if (A != mem57) goto pos36700; - Y++; - if(Y == mem65) break; - X++; - mem60 = X; - } - -// the string in the bracket is correct - -//pos36787: - A = mem61; - mem59 = mem61; + Y = mem66 + 1; -pos36791: - while(1) - { - mem66--; - Y = mem66; - A = GetRuleByte(mem62, Y); - mem57 = A; - //36800: BPL 36805 - if ((A & 128) != 0) goto pos37180; - X = A & 127; - A = tab36376[X] & 128; - if (A == 0) break; - X = mem59-1; - A = inputtemp[X]; - if (A != mem57) goto pos36700; - mem59 = X; + while(1) { + if (GetRuleByte(mem62, Y) != inputtemp[X]) goto pos36700; + if(++Y == mem65) break; + mem60 = ++X; } -//pos36833: - A = mem57; - if (A == ' ') goto pos36895; - if (A == '#') goto pos36910; - if (A == '.') goto pos36920; - if (A == '&') goto pos36935; - if (A == '@') goto pos36967; - if (A == '^') goto pos37004; - if (A == '+') goto pos37019; - if (A == ':') goto pos37040; - // Code42041(); //Error - //36894: BRK - return 0; - - // -------------- - -pos36895: - Code37055(mem59); - A = A & 128; - if(A != 0) goto pos36700; -pos36905: - mem59 = X; - goto pos36791; - - // -------------- - -pos36910: - Code37055(mem59); - A = A & 64; - if(A != 0) goto pos36905; - goto pos36700; - - // -------------- - - -pos36920: - Code37055(mem59); - A = A & 8; - if(A == 0) goto pos36700; -pos36930: - mem59 = X; - goto pos36791; + // the string in the bracket is correct - // -------------- - -pos36935: - Code37055(mem59); - A = A & 16; - if(A != 0) goto pos36930; - A = inputtemp[X]; - if (A != 72) goto pos36700; - X--; - A = inputtemp[X]; - if ((A == 67) || (A == 83)) goto pos36930; - goto pos36700; - - // -------------- - -pos36967: - Code37055(mem59); - A = A & 4; - if(A != 0) goto pos36930; - A = inputtemp[X]; - if (A != 72) goto pos36700; - if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; - mem59 = X; - goto pos36791; - - // -------------- - - -pos37004: - Code37055(mem59); - A = A & 32; - if(A == 0) goto pos36700; - -pos37014: - mem59 = X; - goto pos36791; - - // -------------- - -pos37019: - X = mem59; - X--; - A = inputtemp[X]; - if ((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; - goto pos36700; - // -------------- - -pos37040: - Code37055(mem59); - A = A & 32; - if(A == 0) goto pos36791; - mem59 = X; - goto pos37040; - -//--------------------------------------- - - -pos37077: - X = mem58+1; - A = inputtemp[X]; - if (A != 'E') goto pos37157; - X++; - Y = inputtemp[X]; - X--; - A = tab36376[Y] & 128; - if(A == 0) goto pos37108; - X++; - A = inputtemp[X]; - if (A != 'R') goto pos37113; -pos37108: - mem58 = X; - goto pos37184; -pos37113: - if ((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' - if (A != 76) goto pos37135; // 'L' - X++; - A = inputtemp[X]; - if (A != 89) goto pos36700; - goto pos37108; - -pos37135: - if (A != 70) goto pos36700; - X++; - A = inputtemp[X]; - if (A != 85) goto pos36700; - X++; - A = inputtemp[X]; - if (A == 76) goto pos37108; - goto pos36700; - -pos37157: - if (A != 73) goto pos36700; - X++; - A = inputtemp[X]; - if (A != 78) goto pos36700; - X++; - A = inputtemp[X]; - if (A == 71) goto pos37108; - //pos37177: - goto pos36700; - - // ----------------------------------------- - -pos37180: - - A = mem60; - mem58 = A; + mem59 = mem61; + while(1) { + unsigned char ch; + while(1) { + mem66--; + mem57 = GetRuleByte(mem62, mem66); + if ((mem57 & 128) != 0) { + mem58 = mem60; + goto pos37184; + } + X = mem57 & 127; + if ((tab36376[X] & 128) == 0) break; + if (inputtemp[mem59-1] != mem57) goto pos36700; + --mem59; + } + + ch = mem57; + + r = handle_ch2(ch, mem59-1); + if (r == -1) { + switch (ch) { + case '&': + if (!Code37055(mem59-1,16)) { + if (inputtemp[X] != 'H') r = 1; + else { + A = inputtemp[--X]; + if ((A != 'C') && (A != 'S')) r = 1; + } + } + break; + + case '@': + if(!Code37055(mem59-1,4)) { + A = inputtemp[X]; + if (A != 72) r = 1; + if ((A != 84) && (A != 67) && (A != 83)) r = 1; + } + break; + case '+': + X = mem59; + A = inputtemp[--X]; + if ((A != 'E') && (A != 'I') && (A != 'Y')) r = 1; + break; + case ':': + while (Code37055(mem59-1,32)) --mem59; + continue; + default: + return 0; + } + } + + if (r == 1) goto pos36700; + + mem59 = X; + } + + do { + X = mem58+1; + if (inputtemp[X] == 'E') { + if((tab36376[inputtemp[X+1]] & 128) != 0) { + A = inputtemp[++X]; + if (A == 'L') { + if (inputtemp[++X] != 'Y') goto pos36700; + } else if ((A != 'R') && (A != 'S') && (A != 'D') && !match("FUL")) goto pos36700; + } + } else { + if (!match("ING")) goto pos36700; + mem58 = X; + } + pos37184: - Y = mem65 + 1; - - //37187: CPY 64 - // if(? != 0) goto pos37194; - if(Y == mem64) goto pos37455; - mem65 = Y; - //37196: LDA (62),y - A = GetRuleByte(mem62, Y); - mem57 = A; - X = A; - A = tab36376[X] & 128; - if(A == 0) goto pos37226; - X = mem58+1; - A = inputtemp[X]; - if (A != mem57) goto pos36700; - mem58 = X; - goto pos37184; -pos37226: - A = mem57; - if (A == 32) goto pos37295; // ' ' - if (A == 35) goto pos37310; // '#' - if (A == 46) goto pos37320; // '.' - if (A == 38) goto pos37335; // '&' - if (A == 64) goto pos37367; // '' - if (A == 94) goto pos37404; // '' - if (A == 43) goto pos37419; // '+' - if (A == 58) goto pos37440; // ':' - if (A == 37) goto pos37077; // '%' - //pos37291: - // Code42041(); //Error - //37294: BRK + r = 0; + do { + while (1) { + Y = mem65 + 1; + if(Y == mem64) { + mem61 = mem60; + + if (debug) PrintRule(mem62); + + while(1) { + mem57 = A = GetRuleByte(mem62, Y); + A = A & 127; + if (A != '=') input[++mem56] = A; + if ((mem57 & 128) != 0) goto pos36554; + Y++; + } + } + mem65 = Y; + mem57 = GetRuleByte(mem62, Y); + if((tab36376[mem57] & 128) == 0) break; + if (inputtemp[mem58+1] != mem57) { + r = 1; + break; + } + ++mem58; + } + + if (r == 0) { + A = mem57; + if (A == '@') { + if(Code37055(mem58+1, 4) == 0) { + A = inputtemp[X]; + if ((A != 82) && (A != 84) && + (A != 67) && (A != 83)) r = 1; + } else { + r = -2; + } + } else if (A == ':') { + while (Code37055(mem58+1, 32)) mem58 = X; + r = -2; + } else r = handle_ch(A, mem58+1); + } + + if (r == 1) goto pos36700; + if (r == -2) { + r = 0; + continue; + } + if (r == 0) mem58 = X; + } while (r == 0); + } while (A == '%'); return 0; - - // -------------- -pos37295: - Code37066(mem58); - A = A & 128; - if(A != 0) goto pos36700; -pos37305: - mem58 = X; - goto pos37184; - - // -------------- - -pos37310: - Code37066(mem58); - A = A & 64; - if(A != 0) goto pos37305; - goto pos36700; - - // -------------- - - -pos37320: - Code37066(mem58); - A = A & 8; - if(A == 0) goto pos36700; - -pos37330: - mem58 = X; - goto pos37184; - - // -------------- - -pos37335: - Code37066(mem58); - A = A & 16; - if(A != 0) goto pos37330; - A = inputtemp[X]; - if (A != 72) goto pos36700; - X++; - A = inputtemp[X]; - if ((A == 67) || (A == 83)) goto pos37330; - goto pos36700; - - // -------------- - - -pos37367: - Code37066(mem58); - A = A & 4; - if(A != 0) goto pos37330; - A = inputtemp[X]; - if (A != 72) goto pos36700; - if ((A != 84) && (A != 67) && (A != 83)) goto pos36700; - mem58 = X; - goto pos37184; - - // -------------- - -pos37404: - Code37066(mem58); - A = A & 32; - if(A == 0) goto pos36700; -pos37414: - mem58 = X; - goto pos37184; - - // -------------- - -pos37419: - X = mem58; - X++; - A = inputtemp[X]; - if ((A == 69) || (A == 73) || (A == 89)) goto pos37414; - goto pos36700; - -// ---------------------- - -pos37440: - - Code37066(mem58); - A = A & 32; - if(A == 0) goto pos37184; - mem58 = X; - goto pos37440; -pos37455: - Y = mem64; - mem61 = mem60; - - if (debug) - PrintRule(mem62); - -pos37461: - //37461: LDA (62),y - A = GetRuleByte(mem62, Y); - mem57 = A; - A = A & 127; - if (A != '=') - { - mem56++; - X = mem56; - input[X] = A; - } - - //37478: BIT 57 - //37480: BPL 37485 //not negative flag - if ((mem57 & 128) == 0) goto pos37485; //??? - goto pos36554; -pos37485: - Y++; - goto pos37461; } - - - diff --git a/src/reciter.h b/src/reciter.h index 1429ec69..b23ac550 100644 --- a/src/reciter.h +++ b/src/reciter.h @@ -1,9 +1,9 @@ #ifndef RECITER_C #define RECITER_C -//int TextToPhonemes(char *input, char *output); +//int TextToPhonemes(unsigned char *input, unsigned char *output); -int TextToPhonemes(char *input); +int TextToPhonemes(unsigned char *input); #endif diff --git a/src/render.c b/src/render.c index fa7b8a38..9aebeb09 100755 --- a/src/render.c +++ b/src/render.c @@ -1,995 +1,389 @@ -#include -#include -#include - -#include "render.h" -#include "RenderTabs.h" - -#include "debug.h" -extern int debug; - -unsigned char wait1 = 7; -unsigned char wait2 = 6; - -extern unsigned char A, X, Y; -extern unsigned char mem44; -extern unsigned char mem47; -extern unsigned char mem49; -extern unsigned char mem39; -extern unsigned char mem50; -extern unsigned char mem51; -extern unsigned char mem53; -extern unsigned char mem56; - -extern unsigned char speed; -extern unsigned char pitch; -extern int singmode; - - -extern unsigned char phonemeIndexOutput[60]; //tab47296 -extern unsigned char stressOutput[60]; //tab47365 -extern unsigned char phonemeLengthOutput[60]; //tab47416 - -unsigned char pitches[256]; // tab43008 - -unsigned char frequency1[256]; -unsigned char frequency2[256]; -unsigned char frequency3[256]; - -unsigned char amplitude1[256]; -unsigned char amplitude2[256]; -unsigned char amplitude3[256]; - -unsigned char sampledConsonantFlag[256]; // tab44800 - - -void AddInflection(unsigned char mem48, unsigned char phase1); -unsigned char trans(unsigned char mem39212, unsigned char mem39213); - - -// contains the final soundbuffer -extern int bufferpos; -extern char *buffer; - - - -//timetable for more accurate c64 simulation -int timetable[5][5] = -{ - {162, 167, 167, 127, 128}, - {226, 60, 60, 0, 0}, - {225, 60, 59, 0, 0}, - {200, 0, 0, 54, 55}, - {199, 0, 0, 54, 54} -}; - -void Output(int index, unsigned char A) -{ - static unsigned oldtimetableindex = 0; - int k; - bufferpos += timetable[oldtimetableindex][index]; - oldtimetableindex = index; - // write a little bit in advance - for(k=0; k<5; k++) - buffer[bufferpos/50 + k] = (A & 15)*16; -} - - - - - - - -//written by me because of different table positions. -// mem[47] = ... -// 168=pitches -// 169=frequency1 -// 170=frequency2 -// 171=frequency3 -// 172=amplitude1 -// 173=amplitude2 -// 174=amplitude3 -unsigned char Read(unsigned char p, unsigned char Y) -{ - switch(p) - { - case 168: return pitches[Y]; - case 169: return frequency1[Y]; - case 170: return frequency2[Y]; - case 171: return frequency3[Y]; - case 172: return amplitude1[Y]; - case 173: return amplitude2[Y]; - case 174: return amplitude3[Y]; - } - printf("Error reading to tables"); - return 0; -} - -void Write(unsigned char p, unsigned char Y, unsigned char value) -{ - - switch(p) - { - case 168: pitches[Y] = value; return; - case 169: frequency1[Y] = value; return; - case 170: frequency2[Y] = value; return; - case 171: frequency3[Y] = value; return; - case 172: amplitude1[Y] = value; return; - case 173: amplitude2[Y] = value; return; - case 174: amplitude3[Y] = value; return; - } - printf("Error writing to tables\n"); -} - - - -// ------------------------------------------------------------------------- -//Code48227 -// Render a sampled sound from the sampleTable. -// -// Phoneme Sample Start Sample End -// 32: S* 15 255 -// 33: SH 257 511 -// 34: F* 559 767 -// 35: TH 583 767 -// 36: /H 903 1023 -// 37: /X 1135 1279 -// 38: Z* 84 119 -// 39: ZH 340 375 -// 40: V* 596 639 -// 41: DH 596 631 -// -// 42: CH -// 43: ** 399 511 -// -// 44: J* -// 45: ** 257 276 -// 46: ** -// -// 66: P* -// 67: ** 743 767 -// 68: ** -// -// 69: T* -// 70: ** 231 255 -// 71: ** -// -// The SampledPhonemesTable[] holds flags indicating if a phoneme is -// voiced or not. If the upper 5 bits are zero, the sample is voiced. -// -// Samples in the sampleTable are compressed, with bits being converted to -// bytes from high bit to low, as follows: -// -// unvoiced 0 bit -> X -// unvoiced 1 bit -> 5 -// -// voiced 0 bit -> 6 -// voiced 1 bit -> 24 -// -// Where X is a value from the table: -// -// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; -// -// The index into this table is determined by masking off the lower -// 3 bits from the SampledPhonemesTable: -// -// index = (SampledPhonemesTable[i] & 7) - 1; -// -// For voices samples, samples are interleaved between voiced output. - - -// Code48227() -void RenderSample(unsigned char *mem66) -{ - int tempA; - // current phoneme's index - mem49 = Y; - - // mask low three bits and subtract 1 get value to - // convert 0 bits on unvoiced samples. - A = mem39&7; - X = A-1; - - // store the result - mem56 = X; - - // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } - // T, S, Z 0 0x18 - // CH, J, SH, ZH 1 0x1A - // P, F*, V, TH, DH 2 0x17 - // /H 3 0x17 - // /X 4 0x17 - - // get value from the table - mem53 = tab48426[X]; - mem47 = X; //46016+mem[56]*256 - - // voiced sample? - A = mem39 & 248; - if(A == 0) - { - // voiced phoneme: Z*, ZH, V*, DH - Y = mem49; - A = pitches[mem49] >> 4; - - // jump to voiced portion - goto pos48315; - } - - Y = A ^ 255; -pos48274: - - // step through the 8 bits in the sample - mem56 = 8; - - // get the next sample from the table - // mem47*256 = offset to start of samples - A = sampleTable[mem47*256+Y]; -pos48280: - - // left shift to get the high bit - tempA = A; - A = A << 1; - //48281: BCC 48290 - - // bit not set? - if ((tempA & 128) == 0) - { - // convert the bit to value from table - X = mem53; - //mem[54296] = X; - // output the byte - Output(1, X); - // if X != 0, exit loop - if(X != 0) goto pos48296; - } - - // output a 5 for the on bit - Output(2, 5); - - //48295: NOP -pos48296: - - X = 0; - - // decrement counter - mem56--; - - // if not done, jump to top of loop - if (mem56 != 0) goto pos48280; - - // increment position - Y++; - if (Y != 0) goto pos48274; - - // restore values and return - mem44 = 1; - Y = mem49; - return; - - - unsigned char phase1; - -pos48315: -// handle voiced samples here - - // number of samples? - phase1 = A ^ 255; - - Y = *mem66; - do - { - //pos48321: - - // shift through all 8 bits - mem56 = 8; - //A = Read(mem47, Y); - - // fetch value from table - A = sampleTable[mem47*256+Y]; - - // loop 8 times - //pos48327: - do - { - //48327: ASL A - //48328: BCC 48337 - - // left shift and check high bit - tempA = A; - A = A << 1; - if ((tempA & 128) != 0) - { - // if bit set, output 26 - X = 26; - Output(3, X); - } else - { - //timetable 4 - // bit is not set, output a 6 - X=6; - Output(4, X); - } - - mem56--; - } while(mem56 != 0); - - // move ahead in the table - Y++; - - // continue until counter done - phase1++; - - } while (phase1 != 0); - // if (phase1 != 0) goto pos48321; - - // restore values and return - A = 1; - mem44 = 1; - *mem66 = Y; - Y = mem49; - return; -} - - - -// RENDER THE PHONEMES IN THE LIST -// -// The phoneme list is converted into sound through the steps: -// -// 1. Copy each phoneme number of times into the frames list, -// where each frame represents 10 milliseconds of sound. -// -// 2. Determine the transitions lengths between phonemes, and linearly -// interpolate the values across the frames. -// -// 3. Offset the pitches by the fundamental frequency. -// -// 4. Render the each frame. - - - -//void Code47574() -void Render() -{ - unsigned char phase1 = 0; //mem43 - unsigned char phase2; - unsigned char phase3; - unsigned char mem66; - unsigned char mem38; - unsigned char mem40; - unsigned char speedcounter; //mem45 - unsigned char mem48; - int i; - int carry; - if (phonemeIndexOutput[0] == 255) return; //exit if no data - - A = 0; - X = 0; - mem44 = 0; - - -// CREATE FRAMES -// -// The length parameter in the list corresponds to the number of frames -// to expand the phoneme to. Each frame represents 10 milliseconds of time. -// So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. -// -// The parameters are copied from the phoneme to the frame verbatim. - - -// pos47587: -do -{ - // get the index - Y = mem44; - // get the phoneme at the index - A = phonemeIndexOutput[mem44]; - mem56 = A; - - // if terminal phoneme, exit the loop - if (A == 255) break; - - // period phoneme *. - if (A == 1) - { - // add rising inflection - A = 1; - mem48 = 1; - //goto pos48376; - AddInflection(mem48, phase1); - } - /* - if (A == 2) goto pos48372; - */ - - // question mark phoneme? - if (A == 2) - { - // create falling inflection - mem48 = 255; - AddInflection(mem48, phase1); - } - // pos47615: - - // get the stress amount (more stress = higher pitch) - phase1 = tab47492[stressOutput[Y] + 1]; - - // get number of frames to write - phase2 = phonemeLengthOutput[Y]; - Y = mem56; - - // copy from the source to the frames list - do - { - frequency1[X] = freq1data[Y]; // F1 frequency - frequency2[X] = freq2data[Y]; // F2 frequency - frequency3[X] = freq3data[Y]; // F3 frequency - amplitude1[X] = ampl1data[Y]; // F1 amplitude - amplitude2[X] = ampl2data[Y]; // F2 amplitude - amplitude3[X] = ampl3data[Y]; // F3 amplitude - sampledConsonantFlag[X] = sampledConsonantFlags[Y]; // phoneme data for sampled consonants - pitches[X] = pitch + phase1; // pitch - X++; - phase2--; - } while(phase2 != 0); - mem44++; -} while(mem44 != 0); -// ------------------- -//pos47694: - - -// CREATE TRANSITIONS -// -// Linear transitions are now created to smoothly connect each -// phoeneme. This transition is spread between the ending frames -// of the old phoneme (outBlendLength), and the beginning frames -// of the new phoneme (inBlendLength). -// -// To determine how many frames to use, the two phonemes are -// compared using the blendRank[] table. The phoneme with the -// smaller score is used. In case of a tie, a blend of each is used: -// -// if blendRank[phoneme1] == blendRank[phomneme2] -// // use lengths from each phoneme -// outBlendFrames = outBlend[phoneme1] -// inBlendFrames = outBlend[phoneme2] -// else if blendRank[phoneme1] < blendRank[phoneme2] -// // use lengths from first phoneme -// outBlendFrames = outBlendLength[phoneme1] -// inBlendFrames = inBlendLength[phoneme1] -// else -// // use lengths from the second phoneme -// // note that in and out are swapped around! -// outBlendFrames = inBlendLength[phoneme2] -// inBlendFrames = outBlendLength[phoneme2] -// -// Blend lengths can't be less than zero. -// -// For most of the parameters, SAM interpolates over the range of the last -// outBlendFrames-1 and the first inBlendFrames. -// -// The exception to this is the Pitch[] parameter, which is interpolates the -// pitch from the center of the current phoneme to the center of the next -// phoneme. - - A = 0; - mem44 = 0; - mem49 = 0; // mem49 starts at as 0 - X = 0; - while(1) //while No. 1 - { - - // get the current and following phoneme - Y = phonemeIndexOutput[X]; - A = phonemeIndexOutput[X+1]; - X++; - - // exit loop at end token - if (A == 255) break;//goto pos47970; - - - // get the ranking of each phoneme - X = A; - mem56 = blendRank[A]; - A = blendRank[Y]; - - // compare the rank - lower rank value is stronger - if (A == mem56) - { - // same rank, so use out blend lengths from each phoneme - phase1 = outBlendLength[Y]; - phase2 = outBlendLength[X]; - } else - if (A < mem56) - { - // first phoneme is stronger, so us it's blend lengths - phase1 = inBlendLength[X]; - phase2 = outBlendLength[X]; - } else - { - // second phoneme is stronger, so use it's blend lengths - // note the out/in are swapped - phase1 = outBlendLength[Y]; - phase2 = inBlendLength[Y]; - } - - Y = mem44; - A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length - mem49 = A; // mem49 now holds length + position - A = A + phase2; //Maybe Problem because of carry flag - - //47776: ADC 42 - speedcounter = A; - mem47 = 168; - phase3 = mem49 - phase1; // what is mem49 - A = phase1 + phase2; // total transition? - mem38 = A; - - X = A; - X -= 2; - if ((X & 128) == 0) - do //while No. 2 - { - //pos47810: - - // mem47 is used to index the tables: - // 168 pitches[] - // 169 frequency1 - // 170 frequency2 - // 171 frequency3 - // 172 amplitude1 - // 173 amplitude2 - // 174 amplitude3 - - mem40 = mem38; - - if (mem47 == 168) // pitch - { - - // unlike the other values, the pitches[] interpolates from - // the middle of the current phoneme to the middle of the - // next phoneme - - unsigned char mem36, mem37; - // half the width of the current phoneme - mem36 = phonemeLengthOutput[mem44] >> 1; - // half the width of the next phoneme - mem37 = phonemeLengthOutput[mem44+1] >> 1; - // sum the values - mem40 = mem36 + mem37; // length of both halves - mem37 += mem49; // center of next phoneme - mem36 = mem49 - mem36; // center index of current phoneme - A = Read(mem47, mem37); // value at center of next phoneme - end interpolation value - //A = mem[address]; - - Y = mem36; // start index of interpolation - mem53 = A - Read(mem47, mem36); // value to center of current phoneme - } else - { - // value to interpolate to - A = Read(mem47, speedcounter); - // position to start interpolation from - Y = phase3; - // value to interpolate from - mem53 = A - Read(mem47, phase3); - } - - //Code47503(mem40); - // ML : Code47503 is division with remainder, and mem50 gets the sign - - // calculate change per frame - mem50 = (((char)(mem53) < 0) ? 128 : 0); - mem51 = abs((char)mem53) % mem40; - mem53 = (unsigned char)((char)(mem53) / mem40); - - // interpolation range - X = mem40; // number of frames to interpolate over - Y = phase3; // starting frame - - - // linearly interpolate values - - mem56 = 0; - //47907: CLC - //pos47908: - while(1) //while No. 3 - { - A = Read(mem47, Y) + mem53; //carry alway cleared - - mem48 = A; - Y++; - X--; - if(X == 0) break; - - mem56 += mem51; - if (mem56 >= mem40) //??? - { - mem56 -= mem40; //carry? is set - //if ((mem56 & 128)==0) - if ((mem50 & 128)==0) - { - //47935: BIT 50 - //47937: BMI 47943 - if(mem48 != 0) mem48++; - } else mem48--; - } - //pos47945: - Write(mem47, Y, mem48); - } //while No. 3 - - //pos47952: - mem47++; - //if (mem47 != 175) goto pos47810; - } while (mem47 != 175); //while No. 2 - //pos47963: - mem44++; - X = mem44; - } //while No. 1 - - //goto pos47701; - //pos47970: - - // add the length of this phoneme - mem48 = mem49 + phonemeLengthOutput[mem44]; - - -// ASSIGN PITCH CONTOUR -// -// This subtracts the F1 frequency from the pitch to create a -// pitch contour. Without this, the output would be at a single -// pitch level (monotone). - - - // don't adjust pitch if in sing mode - if (!singmode) - { - // iterate through the buffer - for(i=0; i<256; i++) { - // subtract half the frequency of the formant 1. - // this adds variety to the voice - pitches[i] -= (frequency1[i] >> 1); - } - } - - phase1 = 0; - phase2 = 0; - phase3 = 0; - mem49 = 0; - speedcounter = 72; //sam standard speed - -// RESCALE AMPLITUDE -// -// Rescale volume from a linear scale to decibels. -// - - //amplitude rescaling - for(i=255; i>=0; i--) - { - amplitude1[i] = amplitudeRescale[amplitude1[i]]; - amplitude2[i] = amplitudeRescale[amplitude2[i]]; - amplitude3[i] = amplitudeRescale[amplitude3[i]]; - } - - Y = 0; - A = pitches[0]; - mem44 = A; - X = A; - mem38 = A - (A>>2); // 3/4*A ??? - -if (debug) -{ - PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); -} - -// PROCESS THE FRAMES -// -// In traditional vocal synthesis, the glottal pulse drives filters, which -// are attenuated to the frequencies of the formants. -// -// SAM generates these formants directly with sin and rectangular waves. -// To simulate them being driven by the glottal pulse, the waveforms are -// reset at the beginning of each glottal pulse. - - //finally the loop for sound output - //pos48078: - while(1) - { - // get the sampled information on the phoneme - A = sampledConsonantFlag[Y]; - mem39 = A; - - // unvoiced sampled phoneme? - A = A & 248; - if(A != 0) - { - // render the sample for the phoneme - RenderSample(&mem66); - - // skip ahead two in the phoneme buffer - Y += 2; - mem48 -= 2; - } else - { - // simulate the glottal pulse and formants - mem56 = multtable[sinus[phase1] | amplitude1[Y]]; - - carry = 0; - if ((mem56+multtable[sinus[phase2] | amplitude2[Y]] ) > 255) carry = 1; - mem56 += multtable[sinus[phase2] | amplitude2[Y]]; - A = mem56 + multtable[rectangle[phase3] | amplitude3[Y]] + (carry?1:0); - A = ((A + 136) & 255) >> 4; //there must be also a carry - //mem[54296] = A; - - // output the accumulated value - Output(0, A); - speedcounter--; - if (speedcounter != 0) goto pos48155; - Y++; //go to next amplitude - - // decrement the frame count - mem48--; - } - - // if the frame count is zero, exit the loop - if(mem48 == 0) return; - speedcounter = speed; -pos48155: - - // decrement the remaining length of the glottal pulse - mem44--; - - // finished with a glottal pulse? - if(mem44 == 0) - { -pos48159: - // fetch the next glottal pulse length - A = pitches[Y]; - mem44 = A; - A = A - (A>>2); - mem38 = A; - - // reset the formant wave generators to keep them in - // sync with the glottal pulse - phase1 = 0; - phase2 = 0; - phase3 = 0; - continue; - } - - // decrement the count - mem38--; - - // is the count non-zero and the sampled flag is zero? - if((mem38 != 0) || (mem39 == 0)) - { - // reset the phase of the formants to match the pulse - phase1 += frequency1[Y]; - phase2 += frequency2[Y]; - phase3 += frequency3[Y]; - continue; - } - - // voiced sampled phonemes interleave the sample with the - // glottal pulse. The sample flag is non-zero, so render - // the sample for the phoneme. - RenderSample(&mem66); - goto pos48159; - } //while - - - // The following code is never reached. It's left over from when - // the voiced sample code was part of this loop, instead of part - // of RenderSample(); - - //pos48315: - int tempA; - phase1 = A ^ 255; - Y = mem66; - do - { - //pos48321: - - mem56 = 8; - A = Read(mem47, Y); - - //pos48327: - do - { - //48327: ASL A - //48328: BCC 48337 - tempA = A; - A = A << 1; - if ((tempA & 128) != 0) - { - X = 26; - // mem[54296] = X; - bufferpos += 150; - buffer[bufferpos/50] = (X & 15)*16; - } else - { - //mem[54296] = 6; - X=6; - bufferpos += 150; - buffer[bufferpos/50] = (X & 15)*16; - } - - for(X = wait2; X>0; X--); //wait - mem56--; - } while(mem56 != 0); - - Y++; - phase1++; - - } while (phase1 != 0); - // if (phase1 != 0) goto pos48321; - A = 1; - mem44 = 1; - mem66 = Y; - Y = mem49; - return; -} - - -// Create a rising or falling inflection 30 frames prior to -// index X. A rising inflection is used for questions, and -// a falling inflection is used for statements. - -void AddInflection(unsigned char mem48, unsigned char phase1) -{ - //pos48372: - // mem48 = 255; -//pos48376: - - // store the location of the punctuation - mem49 = X; - A = X; - int Atemp = A; - - // backup 30 frames - A = A - 30; - // if index is before buffer, point to start of buffer - if (Atemp <= 30) A=0; - X = A; - - // FIXME: Explain this fix better, it's not obvious - // ML : A =, fixes a problem with invalid pitch with '.' - while( (A=pitches[X]) == 127) X++; - - -pos48398: - //48398: CLC - //48399: ADC 48 - - // add the inflection direction - A += mem48; - phase1 = A; - - // set the inflection - pitches[X] = A; -pos48406: - - // increment the position - X++; - - // exit if the punctuation has been reached - if (X == mem49) return; //goto pos47615; - if (pitches[X] == 255) goto pos48406; - A = phase1; - goto pos48398; -} - -/* - SAM's voice can be altered by changing the frequencies of the - mouth formant (F1) and the throat formant (F2). Only the voiced - phonemes (5-29 and 48-53) are altered. -*/ -void SetMouthThroat(unsigned char mouth, unsigned char throat) -{ - unsigned char initialFrequency; - unsigned char newFrequency = 0; - //unsigned char mouth; //mem38880 - //unsigned char throat; //mem38881 - - // mouth formants (F1) 5..29 - unsigned char mouthFormants5_29[30] = { - 0, 0, 0, 0, 0, 10, - 14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18, - 16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; - - // throat formants (F2) 5..29 - unsigned char throatFormants5_29[30] = { - 255, 255, - 255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49, - 36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; - - // there must be no zeros in this 2 tables - // formant 1 frequencies (mouth) 48..53 - unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; - - // formant 2 frequencies (throat) 48..53 - unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; - - unsigned char pos = 5; //mem39216 -//pos38942: - // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) - while(pos != 30) - { - // recalculate mouth frequency - initialFrequency = mouthFormants5_29[pos]; - if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); - freq1data[pos] = newFrequency; - - // recalculate throat frequency - initialFrequency = throatFormants5_29[pos]; - if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); - freq2data[pos] = newFrequency; - pos++; - } - -//pos39059: - // recalculate formant frequencies 48..53 - pos = 48; - Y = 0; - while(pos != 54) - { - // recalculate F1 (mouth formant) - initialFrequency = mouthFormants48_53[Y]; - newFrequency = trans(mouth, initialFrequency); - freq1data[pos] = newFrequency; - - // recalculate F2 (throat formant) - initialFrequency = throatFormants48_53[Y]; - newFrequency = trans(throat, initialFrequency); - freq2data[pos] = newFrequency; - Y++; - pos++; - } -} - - -//return = (mem39212*mem39213) >> 1 -unsigned char trans(unsigned char mem39212, unsigned char mem39213) -{ - //pos39008: - unsigned char carry; - int temp; - unsigned char mem39214, mem39215; - A = 0; - mem39215 = 0; - mem39214 = 0; - X = 8; - do - { - carry = mem39212 & 1; - mem39212 = mem39212 >> 1; - if (carry != 0) - { - /* - 39018: LSR 39212 - 39021: BCC 39033 - */ - carry = 0; - A = mem39215; - temp = (int)A + (int)mem39213; - A = A + mem39213; - if (temp > 255) carry = 1; - mem39215 = A; - } - temp = mem39215 & 1; - mem39215 = (mem39215 >> 1) | (carry?128:0); - carry = temp; - //39033: ROR 39215 - X--; - } while (X != 0); - temp = mem39214 & 128; - mem39214 = (mem39214 << 1) | (carry?1:0); - carry = temp; - temp = mem39215 & 128; - mem39215 = (mem39215 << 1) | (carry?1:0); - carry = temp; - - return mem39215; -} - - +#include +#include +#include + +#include "render.h" +#include "RenderTabs.h" + +#include "debug.h" +extern int debug; + +//extern unsigned char A, X, Y; +//extern unsigned char mem44; + +extern unsigned char speed; +extern unsigned char pitch; +extern int singmode; + + +extern unsigned char phonemeIndexOutput[60]; //tab47296 +extern unsigned char stressOutput[60]; //tab47365 +extern unsigned char phonemeLengthOutput[60]; //tab47416 + +unsigned char pitches[256]; // tab43008 + +unsigned char frequency1[256]; +unsigned char frequency2[256]; +unsigned char frequency3[256]; + +unsigned char amplitude1[256]; +unsigned char amplitude2[256]; +unsigned char amplitude3[256]; + +unsigned char sampledConsonantFlag[256]; // tab44800 + + +void AddInflection(unsigned char mem48, unsigned char X); + +//return = hibyte(mem39212*mem39213) << 1 +unsigned char trans(unsigned char a, unsigned char b) +{ + return (((unsigned int)a * b) >> 8) << 1; +} + + + + +// contains the final soundbuffer +extern int bufferpos; +extern char *buffer; + + + +//timetable for more accurate c64 simulation +static const int timetable[5][5] = +{ + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54} +}; + +void Output(int index, unsigned char A) +{ + static unsigned oldtimetableindex = 0; + int k; + bufferpos += timetable[oldtimetableindex][index]; + oldtimetableindex = index; + // write a little bit in advance + for(k=0; k<5; k++) + buffer[bufferpos/50 + k] = (A & 15)*16; +} + + +static unsigned char RenderVoicedSample(unsigned short hi, unsigned char off, unsigned char phase1) +{ + do { + unsigned char bit = 8; + unsigned char sample = sampleTable[hi+off]; + do { + if ((sample & 128) != 0) Output(3, 26); + else Output(4, 6); + sample <<= 1; + } while(--bit != 0); + off++; + } while (++phase1 != 0); + return off; +} + +static void RenderUnvoicedSample(unsigned short hi, unsigned char off, unsigned char mem53) +{ + do { + unsigned char bit = 8; + unsigned char sample = sampleTable[hi+off]; + do { + if ((sample & 128) != 0) Output(2, 5); + else Output(1, mem53); + sample <<= 1; + } while (--bit != 0); + } while (++off != 0); +} + + + +// ------------------------------------------------------------------------- +//Code48227 +// Render a sampled sound from the sampleTable. +// +// Phoneme Sample Start Sample End +// 32: S* 15 255 +// 33: SH 257 511 +// 34: F* 559 767 +// 35: TH 583 767 +// 36: /H 903 1023 +// 37: /X 1135 1279 +// 38: Z* 84 119 +// 39: ZH 340 375 +// 40: V* 596 639 +// 41: DH 596 631 +// +// 42: CH +// 43: ** 399 511 +// +// 44: J* +// 45: ** 257 276 +// 46: ** +// +// 66: P* +// 67: ** 743 767 +// 68: ** +// +// 69: T* +// 70: ** 231 255 +// 71: ** +// +// The SampledPhonemesTable[] holds flags indicating if a phoneme is +// voiced or not. If the upper 5 bits are zero, the sample is voiced. +// +// Samples in the sampleTable are compressed, with bits being converted to +// bytes from high bit to low, as follows: +// +// unvoiced 0 bit -> X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + + +void RenderSample(unsigned char *mem66, unsigned char consonantFlag, unsigned char mem49) +{ + // mem49 == current phoneme's index + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + unsigned char hibyte = (consonantFlag & 7)-1; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + unsigned short hi = hibyte*256; + // voiced sample? + unsigned char pitchl = consonantFlag & 248; + if(pitchl == 0) { + // voiced phoneme: Z*, ZH, V*, DH + pitchl = pitches[mem49] >> 4; + *mem66 = RenderVoicedSample(hi, *mem66, pitchl ^ 255); + } + else + RenderUnvoicedSample(hi, pitchl^255, tab48426[hibyte]); +} + + + +// CREATE FRAMES +// +// The length parameter in the list corresponds to the number of frames +// to expand the phoneme to. Each frame represents 10 milliseconds of time. +// So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. +// +// The parameters are copied from the phoneme to the frame verbatim. +// +static void CreateFrames() +{ + unsigned char X = 0; + unsigned int i = 0; + while(i < 256) { + // get the phoneme at the index + unsigned char phoneme = phonemeIndexOutput[i]; + unsigned char phase1; + unsigned phase2; + + // if terminal phoneme, exit the loop + if (phoneme == 255) break; + + if (phoneme == PHONEME_PERIOD) AddInflection(RISING_INFLECTION, X); + else if (phoneme == PHONEME_QUESTION) AddInflection(FALLING_INFLECTION, X); + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[i] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[i]; + + // copy from the source to the frames list + do { + frequency1[X] = freq1data[phoneme]; // F1 frequency + frequency2[X] = freq2data[phoneme]; // F2 frequency + frequency3[X] = freq3data[phoneme]; // F3 frequency + amplitude1[X] = ampl1data[phoneme]; // F1 amplitude + amplitude2[X] = ampl2data[phoneme]; // F2 amplitude + amplitude3[X] = ampl3data[phoneme]; // F3 amplitude + sampledConsonantFlag[X] = sampledConsonantFlags[phoneme]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + ++X; + } while(--phase2 != 0); + + ++i; + } +} + + +// RESCALE AMPLITUDE +// +// Rescale volume from a linear scale to decibels. +// +void RescaleAmplitude() +{ + int i; + for(i=255; i>=0; i--) + { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } +} + + + +// ASSIGN PITCH CONTOUR +// +// This subtracts the F1 frequency from the pitch to create a +// pitch contour. Without this, the output would be at a single +// pitch level (monotone). + +void AssignPitchContour() +{ + int i; + for(i=0; i<256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } +} + + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. +void Render() +{ + unsigned char t; + + if (phonemeIndexOutput[0] == 255) return; //exit if no data + + CreateFrames(); + t = CreateTransitions(); + + if (!singmode) AssignPitchContour(); + RescaleAmplitude(); + + if (debug) { + PrintOutput(sampledConsonantFlag, frequency1, frequency2, frequency3, amplitude1, amplitude2, amplitude3, pitches); + } + + ProcessFrames(t); +} + + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void AddInflection(unsigned char inflection, unsigned char pos) +{ + unsigned char A; + // store the location of the punctuation + unsigned char end = pos; + + if (pos < 30) pos = 0; + else pos -= 30; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while( (A = pitches[pos]) == 127) ++pos; + + while (pos != end) { + // add the inflection direction + A += inflection; + + // set the inflection + pitches[pos] = A; + + while ((++pos != end) && pitches[pos] == 255); + } +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void SetMouthThroat(unsigned char mouth, unsigned char throat) +{ + // mouth formants (F1) 5..29 + static const unsigned char mouthFormants5_29[30] = { + 0, 0, 0, 0, 0, 10, + 14, 19, 24, 27, 23, 21, 16, 20, 14, 18, 14, 18, 18, + 16, 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + static const unsigned char throatFormants5_29[30] = { + 255, 255, + 255, 255, 255, 84, 73, 67, 63, 40, 44, 31, 37, 45, 73, 49, + 36, 30, 51, 37, 29, 69, 24, 50, 30, 24, 83, 46, 54, 86, + }; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + static const unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + static const unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char newFrequency = 0; + unsigned char pos = 5; + + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos < 30) + { + // recalculate mouth frequency + unsigned char initialFrequency = mouthFormants5_29[pos]; + if (initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + + // recalculate formant frequencies 48..53 + pos = 0; + while(pos < 6) { + // recalculate F1 (mouth formant) + unsigned char initialFrequency = mouthFormants48_53[pos]; + freq1data[pos+48] = trans(mouth, initialFrequency); + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[pos]; + freq2data[pos+48] = trans(throat, initialFrequency); + pos++; + } +} diff --git a/src/render.h b/src/render.h index 98ec440b..d44f898c 100755 --- a/src/render.h +++ b/src/render.h @@ -4,4 +4,14 @@ void Render(); void SetMouthThroat(unsigned char mouth, unsigned char throat); +void ProcessFrames(unsigned char mem48); +void RenderSample(unsigned char *mem66, unsigned char consonantFlag, unsigned char mem49); +unsigned char CreateTransitions(); + +#define PHONEME_PERIOD (1) +#define PHONEME_QUESTION (2) + +#define RISING_INFLECTION (1) +#define FALLING_INFLECTION (255) + #endif diff --git a/src/sam.c b/src/sam.c index 66a4bbaa..bce479b1 100644 --- a/src/sam.c +++ b/src/sam.c @@ -1,12 +1,21 @@ #include #include #include + #include "debug.h" #include "sam.h" #include "render.h" #include "SamTabs.h" -char input[256]; //tab39445 +enum { + pR = 23, + pD = 57, + pT = 69, + BREAK = 254, + END = 255 +}; + +unsigned char input[256]; //tab39445 //standard sam sound unsigned char speed = 72; unsigned char pitch = 64; @@ -16,19 +25,6 @@ int singmode = 0; extern int debug; -unsigned char mem39; -unsigned char mem44; -unsigned char mem47; -unsigned char mem49; -unsigned char mem50; -unsigned char mem51; -unsigned char mem53; -unsigned char mem56; - -unsigned char mem59=0; - -unsigned char A, X, Y; - unsigned char stress[256]; //numbers from 0 to 8 unsigned char phonemeLength[256]; //tab40160 unsigned char phonemeindex[256]; @@ -37,18 +33,15 @@ unsigned char phonemeIndexOutput[60]; //tab47296 unsigned char stressOutput[60]; //tab47365 unsigned char phonemeLengthOutput[60]; //tab47416 - - - // contains the final soundbuffer int bufferpos=0; char *buffer = NULL; -void SetInput(char *_input) +void SetInput(unsigned char *_input) { int i, l; - l = strlen(_input); + l = strlen((char*)_input); if (l > 254) l = 254; for(i=0; i 80) - { - phonemeindex[X] = 255; + do { + if (phonemeindex[X] > 80) { + phonemeindex[X] = END; break; // error: delete all behind it } - X++; - } while (X != 0); - - //pos39848: + } while (++X != 0); InsertBreath(); - //mem[40158] = 255; - if (debug) - { - PrintPhonemes(phonemeindex, phonemeLength, stress); - } + if (debug) PrintPhonemes(phonemeindex, phonemeLength, stress); PrepareOutput(); - return 1; } +void PrepareOutput() { + unsigned char srcpos = 0; // Position in source + unsigned char destpos = 0; // Position in output -//void Code48547() -void PrepareOutput() -{ - A = 0; - X = 0; - Y = 0; - - //pos48551: - while(1) - { - A = phonemeindex[X]; - if (A == 255) - { - A = 255; - phonemeIndexOutput[Y] = 255; + while(1) { + unsigned char A = phonemeindex[srcpos]; + phonemeIndexOutput[destpos] = A; + switch(A) { + case END: Render(); return; - } - if (A == 254) - { - X++; - int temp = X; - //mem[48546] = X; - phonemeIndexOutput[Y] = 255; + case BREAK: + phonemeIndexOutput[destpos] = END; Render(); - //X = mem[48546]; - X=temp; - Y = 0; - continue; - } - - if (A == 0) - { - X++; - continue; - } - - phonemeIndexOutput[Y] = A; - phonemeLengthOutput[Y] = phonemeLength[X]; - stressOutput[Y] = stress[X]; - X++; - Y++; + destpos = 0; + break; + case 0: + break; + default: + phonemeLengthOutput[destpos] = phonemeLength[srcpos]; + stressOutput[destpos] = stress[srcpos]; + ++destpos; + break; + } + ++srcpos; } } -//void Code48431() -void InsertBreath() -{ - unsigned char mem54; - unsigned char mem55; + +void InsertBreath() { + unsigned char mem54 = 255; + unsigned char len = 0; unsigned char index; //variable Y - mem54 = 255; - X++; - mem55 = 0; - unsigned char mem66 = 0; - while(1) - { - //pos48440: - X = mem66; - index = phonemeindex[X]; - if (index == 255) return; - mem55 += phonemeLength[X]; - - if (mem55 < 232) - { - if (index != 254) // ML : Prevents an index out of bounds problem - { - A = flags2[index]&1; - if(A != 0) - { - X++; - mem55 = 0; - Insert(X, 254, mem59, 0); - mem66++; - mem66++; - continue; - } - } - if (index == 0) mem54 = X; - mem66++; - continue; - } - X = mem54; - phonemeindex[X] = 31; // 'Q*' glottal stop - phonemeLength[X] = 4; - stress[X] = 0; - X++; - mem55 = 0; - Insert(X, 254, mem59, 0); - X++; - mem66 = X; - } + unsigned char pos = 0; + + while((index = phonemeindex[pos]) != END) { + len += phonemeLength[pos]; + if (len < 232) { + if (index == BREAK) { + } else if (!(flags[index] & FLAG_PUNCT)) { + if (index == 0) mem54 = pos; + } else { + len = 0; + Insert(++pos, BREAK, 0, 0); + } + } else { + pos = mem54; + phonemeindex[pos] = 31; // 'Q*' glottal stop + phonemeLength[pos] = 4; + stress[pos] = 0; + + len = 0; + Insert(++pos, BREAK, 0, 0); + } + ++pos; + } } + // Iterates through the phoneme buffer, copying the stress value from // the following phoneme under the following circumstance: @@ -283,66 +191,76 @@ void InsertBreath() // to the L that precedes it. -//void Code41883() -void CopyStress() -{ + +void CopyStress() { // loop thought all the phonemes to be output unsigned char pos=0; //mem66 - while(1) - { - // get the phomene - Y = phonemeindex[pos]; - - // exit at end of buffer - if (Y == 255) return; - + unsigned char Y; + while((Y = phonemeindex[pos]) != END) { // if CONSONANT_FLAG set, skip - only vowels get stress - if ((flags[Y] & 64) == 0) {pos++; continue;} - // get the next phoneme - Y = phonemeindex[pos+1]; - if (Y == 255) //prevent buffer overflow - { - pos++; continue; - } else - // if the following phoneme is a vowel, skip - if ((flags[Y] & 128) == 0) {pos++; continue;} - - // get the stress value at the next position - Y = stress[pos+1]; - - // if next phoneme is not stressed, skip - if (Y == 0) {pos++; continue;} - - // if next phoneme is not a VOWEL OR ER, skip - if ((Y & 128) != 0) {pos++; continue;} - - // copy stress from prior phoneme to this one - stress[pos] = Y+1; - - // advance pointer - pos++; - } + if (flags[Y] & 64) { + Y = phonemeindex[pos+1]; + + // if the following phoneme is the end, or a vowel, skip + if (Y != END && (flags[Y] & 128) != 0) { + // get the stress value at the next position + Y = stress[pos+1]; + if (Y && !(Y&128)) { + // if next phoneme is stressed, and a VOWEL OR ER + // copy stress from next phoneme to this one + stress[pos] = Y+1; + } + } + } + ++pos; + } } - -//void Code41014() void Insert(unsigned char position/*var57*/, unsigned char mem60, unsigned char mem59, unsigned char mem58) { int i; for(i=253; i >= position; i--) // ML : always keep last safe-guarding 255 { - phonemeindex[i+1] = phonemeindex[i]; + phonemeindex[i+1] = phonemeindex[i]; phonemeLength[i+1] = phonemeLength[i]; - stress[i+1] = stress[i]; + stress[i+1] = stress[i]; } - phonemeindex[position] = mem60; + phonemeindex[position] = mem60; phonemeLength[position] = mem59; - stress[position] = mem58; - return; + stress[position] = mem58; } + +signed int full_match(unsigned char sign1, unsigned char sign2) { + unsigned char Y = 0; + do { + // GET FIRST CHARACTER AT POSITION Y IN signInputTable + // --> should change name to PhonemeNameTable1 + unsigned char 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; +} + +signed int wild_match(unsigned char sign1) { + signed 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: // @@ -396,195 +314,106 @@ void Insert(unsigned char position/*var57*/, unsigned char mem60, unsigned char // function returns with a 1 indicating success. int Parser1() { - int i; unsigned char sign1; - unsigned char sign2; unsigned char position = 0; - X = 0; - A = 0; - Y = 0; - - // CLEAR THE STRESS TABLE - for(i=0; i<256; i++) - stress[i] = 0; - - // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE - // pos41078: - while(1) - { - // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER - sign1 = input[X]; - // TEST FOR 155 (›) END OF LINE MARKER - if (sign1 == 155) - { - // MARK ENDPOINT AND RETURN - phonemeindex[position] = 255; //mark endpoint - // REACHED END OF PHONEMES, SO EXIT - return 1; //all ok - } - - // GET THE NEXT CHARACTER FROM THE BUFFER - X++; - sign2 = input[X]; - - // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME - - // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME - // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS - - // SET INDEX TO 0 - Y = 0; -pos41095: - - // GET FIRST CHARACTER AT POSITION Y IN signInputTable - // --> should change name to PhonemeNameTable1 - A = signInputTable1[Y]; - - // FIRST CHARACTER MATCHES? - if (A == sign1) - { - // GET THE CHARACTER FROM THE PhonemeSecondLetterTable - A = signInputTable2[Y]; - // NOT A SPECIAL AND MATCHES SECOND CHARACTER? - if ((A != '*') && (A == sign2)) - { - // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable - phonemeindex[position] = Y; - - // ADVANCE THE POINTER TO THE phonemeIndexTable - position++; - // ADVANCE THE POINTER TO THE phonemeInputBuffer - X++; - - // CONTINUE PARSING - continue; - } - } - - // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') - - // ADVANCE TO THE NEXT POSITION - Y++; - // IF NOT END OF TABLE, CONTINUE - if (Y != 81) goto pos41095; - -// REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. -// THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS - -// RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE - Y = 0; -pos41134: -// DOES THE PHONEME IN THE TABLE END WITH '*'? - if (signInputTable2[Y] == '*') - { -// DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME - if (signInputTable1[Y] == sign1) - { - // SAVE THE POSITION AND MOVE AHEAD - phonemeindex[position] = Y; - - // ADVANCE THE POINTER - position++; - - // CONTINUE THROUGH THE LOOP - continue; - } - } - Y++; - if (Y != 81) goto pos41134; //81 is size of PHONEME NAME table - -// FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS -// CHARACTER. SEARCH THROUGH THE STRESS TABLE - - // SET INDEX TO POSITION 8 (END OF STRESS TABLE) - Y = 8; - - // WALK BACK THROUGH TABLE LOOKING FOR A MATCH - while( (sign1 != stressInputTable[Y]) && (Y>0)) - { - // DECREMENT INDEX - Y--; - } + unsigned char srcpos = 0; + + memset(stress, 0, 256); // Clear the stress table. + + while((sign1 = input[srcpos]) != 155) { // 155 (\233) is end of line marker + signed int match; + unsigned char sign2 = input[++srcpos]; + if ((match = full_match(sign1, sign2)) != -1) { + // Matched both characters (no wildcards) + phonemeindex[position++] = (unsigned char)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++] = (unsigned char)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 - // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? - if (Y == 0) - { - //mem[39444] = X; - //41181: JSR 42043 //Error - // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE - return 0; - } -// SET THE STRESS FOR THE PRIOR PHONEME - stress[position-1] = Y; + stress[position-1] = (unsigned char)match; // Set stress for prior phoneme + } } //while -} - + phonemeindex[position] = END; + return 1; +} //change phonemelength depedendent on stress -//void Code41203() -void SetPhonemeLength() -{ - unsigned char A; +void SetPhonemeLength() { int position = 0; - while(phonemeindex[position] != 255 ) - { - A = stress[position]; - //41218: BMI 41229 - if ((A == 0) || ((A&128) != 0)) - { + while(phonemeindex[position] != 255) { + unsigned char A = stress[position]; + if ((A == 0) || ((A&128) != 0)) { phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; - } else - { + } else { phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; } position++; } } - -void Code41240() -{ +void Code41240() { unsigned char pos=0; - while(phonemeindex[pos] != 255) - { - unsigned char index; //register AC - X = pos; - index = phonemeindex[pos]; - if ((flags[index]&2) == 0) - { - pos++; - continue; - } else - if ((flags[index]&1) == 0) - { - Insert(pos+1, index+1, phonemeLengthTable[index+1], stress[pos]); - Insert(pos+2, index+2, phonemeLengthTable[index+2], stress[pos]); - pos += 3; - continue; - } + while(phonemeindex[pos] != END) { + unsigned char index = phonemeindex[pos]; + + if ((flags[index] & FLAG_STOPCONS)) { + if ((flags[index] & FLAG_PLOSIVE)) { + unsigned char A; + unsigned char X = pos; + while(!phonemeindex[++X]); /* Skip pause */ + A = phonemeindex[X]; + if (A != END) { + if ((flags[A] & 8) || (A == 36) || (A == 37)) {++pos; continue;} // '/H' '/X' + } + + } + Insert(pos+1, index+1, phonemeLengthTable[index+1], stress[pos]); + Insert(pos+2, index+2, phonemeLengthTable[index+2], stress[pos]); + pos += 2; + } + ++pos; + } +} - do - { - X++; - A = phonemeindex[X]; - } while(A==0); - if (A != 255) - { - if ((flags[A] & 8) != 0) {pos++; continue;} - if ((A == 36) || (A == 37)) {pos++; continue;} // '/H' '/X' - } +void ChangeRule(unsigned char position, unsigned char mem60, const char * descr) +{ + if (debug) printf("RULE: %s\n",descr); + phonemeindex[position] = 13; //rule; + Insert(position+1, mem60, 0, stress[position]); +} - Insert(pos+1, index+1, phonemeLengthTable[index+1], stress[pos]); - Insert(pos+2, index+2, phonemeLengthTable[index+2], stress[pos]); - pos += 3; - }; +void drule(const char * str) { + if (debug) printf("RULE: %s\n",str); +} +void drule_pre(const char *descr, unsigned char X) { + drule(descr); + if (debug) { + printf("PRE\n"); + printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); + } } +void drule_post(unsigned char X) { + if (debug) { + printf("POST\n"); + printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); + } +} + + // Rewrites the phonemes using the following rules: // // -> WX @@ -610,444 +439,157 @@ void Code41240() // D -> DX -//void Code41397() -void Parser2() -{ - if (debug) printf("Parser2\n"); - unsigned char pos = 0; //mem66; - unsigned char mem58 = 0; - - - // Loop through phonemes - while(1) - { -// SET X TO THE CURRENT POSITION - X = pos; -// GET THE PHONEME AT THE CURRENT POSITION - A = phonemeindex[pos]; - -// DEBUG: Print phoneme and index - if (debug && A != 255) printf("%d: %c%c\n", X, signInputTable1[A], signInputTable2[A]); - -// Is phoneme pause? - if (A == 0) - { -// Move ahead to the - pos++; - continue; - } - -// If end of phonemes flag reached, exit routine - if (A == 255) return; - -// Copy the current phoneme index to Y - Y = A; - -// RULE: -// -> WX -// -> YX -// Example: OIL, COW - - -// Check for DIPTHONG - if ((flags[A] & 16) == 0) goto pos41457; - -// Not a dipthong. Get the stress - mem58 = stress[pos]; - -// End in IY sound? - A = flags[Y] & 32; - -// If ends with IY, use YX, else use WX - if (A == 0) A = 20; else A = 21; // 'WX' = 20 'YX' = 21 - //pos41443: -// Insert at WX or YX following, copying the stress - - if (debug) if (A==20) printf("RULE: insert WX following dipthong NOT ending in IY sound\n"); - if (debug) if (A==21) printf("RULE: insert YX following dipthong ending in IY sound\n"); - Insert(pos+1, A, mem59, mem58); - X = pos; -// Jump to ??? - goto pos41749; - - - -pos41457: - -// RULE: -// UL -> AX L -// Example: MEDDLE - -// Get phoneme - A = phonemeindex[X]; -// Skip this rule if phoneme is not UL - if (A != 78) goto pos41487; // 'UL' - A = 24; // 'L' //change 'UL' to 'AX L' - - if (debug) printf("RULE: UL -> AX L\n"); - -pos41466: -// Get current phoneme stress - mem58 = stress[X]; - -// Change UL to AX - phonemeindex[X] = 13; // 'AX' -// Perform insert. Note code below may jump up here with different values - Insert(X+1, A, mem59, mem58); - pos++; -// Move to next phoneme - continue; - -pos41487: - -// RULE: -// UM -> AX M -// Example: ASTRONOMY - -// Skip rule if phoneme != UM - if (A != 79) goto pos41495; // 'UM' - // Jump up to branch - replaces current phoneme with AX and continues - A = 27; // 'M' //change 'UM' to 'AX M' - if (debug) printf("RULE: UM -> AX M\n"); - goto pos41466; -pos41495: - -// RULE: -// UN -> AX N -// Example: FUNCTION - - -// Skip rule if phoneme != UN - if (A != 80) goto pos41503; // 'UN' - - // Jump up to branch - replaces current phoneme with AX and continues - A = 28; // 'N' //change UN to 'AX N' - if (debug) printf("RULE: UN -> AX N\n"); - goto pos41466; -pos41503: - -// RULE: -// -> Q -// EXAMPLE: AWAY EIGHT - - Y = A; -// VOWEL set? - A = flags[A] & 128; - -// Skip if not a vowel - if (A != 0) - { -// Get the stress - A = stress[X]; - -// If stressed... - if (A != 0) - { -// Get the following phoneme - X++; - A = phonemeindex[X]; -// If following phoneme is a pause - - if (A == 0) - { -// Get the phoneme following pause - X++; - Y = phonemeindex[X]; - -// Check for end of buffer flag - if (Y == 255) //buffer overflow -// ??? Not sure about these flags - A = 65&128; - else -// And VOWEL flag to current phoneme's flags - A = flags[Y] & 128; - -// If following phonemes is not a pause - if (A != 0) - { -// If the following phoneme is not stressed - A = stress[X]; - if (A != 0) - { -// Insert a glottal stop and move forward - if (debug) printf("RULE: Insert glottal stop between two stressed vowels with space between them\n"); - // 31 = 'Q' - Insert(X, 31, mem59, 0); - pos++; - continue; - } - } - } - } - } - +void rule_alveolar_uw(unsigned char X) { + // ALVEOLAR flag set? + if (flags[phonemeindex[X-1]] & FLAG_ALVEOLAR) { + drule(" UW -> UX"); + phonemeindex[X] = 16; + } +} -// RULES FOR PHONEMES BEFORE R -// T R -> CH R -// Example: TRACK - - -// Get current position and phoneme - X = pos; - A = phonemeindex[pos]; - if (A != 23) goto pos41611; // 'R' - -// Look at prior phoneme - X--; - A = phonemeindex[pos-1]; - //pos41567: - if (A == 69) // 'T' - { -// Change T to CH - if (debug) printf("RULE: T R -> CH R\n"); - phonemeindex[pos-1] = 42; - goto pos41779; - } +void rule_ch(unsigned char X) { + drule("CH -> CH CH+1"); + Insert(X+1, 43, 0, stress[X]); +} +void rule_j(unsigned char X) { + drule("J -> J J+1"); + Insert(X+1, 45, 0, stress[X]); +} -// RULES FOR PHONEMES BEFORE R -// D R -> J R -// Example: DRY +void rule_g(unsigned char pos) { + // G -> GX + // Example: GO -// Prior phonemes D? - if (A == 57) // 'D' - { -// Change D to J - phonemeindex[pos-1] = 44; - if (debug) printf("RULE: D R -> J R\n"); - goto pos41788; - } + unsigned char index = phonemeindex[pos+1]; + + // If dipthong ending with YX, move continue processing next phoneme + if ((index != 255) && ((flags[index] & FLAG_DIP_YX) == 0)) { + // replace G with GX and continue processing next phoneme + drule("G -> GX "); + phonemeindex[pos] = 63; // 'GX' + } +} -// RULES FOR PHONEMES BEFORE R -// R -> RX -// Example: ART +void change(unsigned char pos, unsigned char val, const char * rule) { + drule(rule); + phonemeindex[pos] = val; +} -// If vowel flag is set change R to RX - A = flags[A] & 128; - if (debug) printf("RULE: R -> RX\n"); - if (A != 0) phonemeindex[pos] = 18; // 'RX' - -// continue to next phoneme - pos++; - continue; -pos41611: +void rule_dipthong(unsigned char p, unsigned short pf, unsigned char pos) { + // -> WX + // -> YX + // Example: OIL, COW -// RULE: -// L -> LX -// Example: ALL - -// Is phoneme L? - if (A == 24) // 'L' - { -// If prior phoneme does not have VOWEL flag set, move to next phoneme - if ((flags[phonemeindex[pos-1]] & 128) == 0) {pos++; continue;} -// Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme - if (debug) printf("RULE: L -> LX\n"); - phonemeindex[X] = 19; // 'LX' - pos++; - continue; - } - -// RULE: -// G S -> G Z -// -// Can't get to fire - -// 1. The G -> GX rule intervenes -// 2. Reciter already replaces GS -> GZ - -// Is current phoneme S? - if (A == 32) // 'S' - { -// If prior phoneme is not G, move to next phoneme - if (phonemeindex[pos-1] != 60) {pos++; continue;} -// Replace S with Z and move on - if (debug) printf("RULE: G S -> G Z\n"); - phonemeindex[pos] = 38; // 'Z' - pos++; - continue; - } + // If ends with IY, use YX, else use WX + unsigned char A = (pf & FLAG_DIP_YX) ? 21 : 20; // 'WX' = 20 'YX' = 21 + + // Insert at WX or YX following, copying the stress + if (A==20) drule("insert WX following dipthong NOT ending in IY sound"); + else if (A==21) drule("insert YX following dipthong ending in IY sound"); + Insert(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 +} -// RULE: -// K -> KX -// Example: COW - -// Is current phoneme K? - if (A == 72) // 'K' - { -// Get next phoneme - Y = phonemeindex[pos+1]; -// If at end, replace current phoneme with KX - if (Y == 255) phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem - else - { -// VOWELS AND DIPTHONGS ENDING WITH IY SOUND flag set? - A = flags[Y] & 32; - if (debug) if (A==0) printf("RULE: K -> KX \n"); -// Replace with KX - if (A == 0) phonemeindex[pos] = 75; // 'KX' - } - } - else - -// RULE: -// G -> GX -// Example: GO - - -// Is character a G? - if (A == 60) // 'G' - { -// Get the following character - unsigned char index = phonemeindex[pos+1]; - -// At end of buffer? - if (index == 255) //prevent buffer overflow - { - pos++; continue; - } - else -// If dipthong ending with YX, move continue processing next phoneme - if ((flags[index] & 32) != 0) {pos++; continue;} -// replace G with GX and continue processing next phoneme - if (debug) printf("RULE: G -> GX \n"); - phonemeindex[pos] = 63; // 'GX' - pos++; - continue; - } - -// RULE: -// S P -> S B -// S T -> S D -// S K -> S G -// S KX -> S GX -// Examples: SPY, STY, SKY, SCOWL - - Y = phonemeindex[pos]; - //pos41719: -// Replace with softer version? - A = flags[Y] & 1; - if (A == 0) goto pos41749; - A = phonemeindex[pos-1]; - if (A != 32) // 'S' - { - A = Y; - goto pos41812; - } - // Replace with softer version - if (debug) printf("RULE: S* %c%c -> S* %c%c\n", signInputTable1[Y], signInputTable2[Y],signInputTable1[Y-12], signInputTable2[Y-12]); - phonemeindex[pos] = Y-12; - pos++; - continue; +void Parser2() { + unsigned char pos = 0; //mem66; + unsigned char p; + if (debug) printf("Parser2\n"); -pos41749: - -// RULE: -// UW -> UX -// -// Example: NEW, DEW, SUE, ZOO, THOO, TOO - -// UW -> UX - - A = phonemeindex[X]; - if (A == 53) // 'UW' - { -// ALVEOLAR flag set? - Y = phonemeindex[X-1]; - A = flags2[Y] & 4; -// If not set, continue processing next phoneme - if (A == 0) {pos++; continue;} - if (debug) printf("RULE: UW -> UX\n"); - phonemeindex[X] = 16; - pos++; - continue; - } -pos41779: + while((p = phonemeindex[pos]) != END) { + unsigned short pf; + unsigned char prior; -// RULE: -// CH -> CH CH' (CH requires two phonemes to represent it) -// Example: CHEW - - if (A == 42) // 'CH' - { - // pos41783: - if (debug) printf("CH -> CH CH+1\n"); - Insert(X+1, A+1, mem59, stress[X]); - pos++; - continue; - } + if (debug) printf("%d: %c%c\n", pos, signInputTable1[p], signInputTable2[p]); -pos41788: - -// RULE: -// J -> J J' (J requires two phonemes to represent it) -// Example: JAY - - - if (A == 44) // 'J' - { - if (debug) printf("J -> J J+1\n"); - Insert(X+1, A+1, mem59, stress[X]); - pos++; + if (p == 0) { // Is phoneme pause? + ++pos; continue; } - -// Jump here to continue -pos41812: -// RULE: Soften T following vowel -// NOTE: This rule fails for cases such as "ODD" -// T -> DX -// D -> DX -// Example: PARTY, TARDY - - -// Past this point, only process if phoneme is T or D - - if (A != 69) // 'T' - if (A != 57) {pos++; continue;} // 'D' - //pos41825: - - -// If prior phoneme is not a vowel, continue processing phonemes - if ((flags[phonemeindex[X-1]] & 128) == 0) {pos++; continue;} - -// Get next phoneme - X++; - A = phonemeindex[X]; - //pos41841 -// Is the next phoneme a pause? - if (A != 0) - { -// If next phoneme is not a pause, continue processing phonemes - if ((flags[A] & 128) == 0) {pos++; continue;} -// If next phoneme is stressed, continue processing phonemes -// FIXME: How does a pause get stressed? - if (stress[X] != 0) {pos++; continue;} -//pos41856: -// Set phonemes to DX - if (debug) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n"); - phonemeindex[pos] = 30; // 'DX' - } else - { - A = phonemeindex[X+1]; - if (A == 255) //prevent buffer overflow - A = 65 & 128; - else -// Is next phoneme a vowel or ER? - A = flags[A] & 128; - if (debug) if (A != 0) printf("RULE: Soften T or D following vowel or ER and preceding a pause -> DX\n"); - if (A != 0) phonemeindex[pos] = 30; // 'DX' - } - - pos++; + pf = flags[p]; + prior = phonemeindex[pos-1]; + + if ((pf & FLAG_DIPTHONG)) rule_dipthong(p, pf, pos); + else if (p == 78) ChangeRule(pos, 24, "UL -> AX L"); // Example: MEDDLE + else if (p == 79) ChangeRule(pos, 27, "UM -> AX M"); // Example: ASTRONOMY + else if (p == 80) ChangeRule(pos, 28, "UN -> AX N"); // Example: FUNCTION + else if ((pf & FLAG_VOWEL) && stress[pos]) { + // RULE: + // -> Q + // EXAMPLE: AWAY EIGHT + if (!phonemeindex[pos+1]) { // If following phoneme is a pause, get next + p = phonemeindex[pos+2]; + if (p!=END && (flags[p] & FLAG_VOWEL) && stress[pos+2]) { + drule("Insert glottal stop between two stressed vowels with space between them"); + Insert(pos+2, 31, 0, 0); // 31 = 'Q' + } + } + } else if (p == pR) { // RULES FOR PHONEMES BEFORE R + if (prior == pT) change(pos-1,42, "T R -> CH R"); // Example: TRACK + else if (prior == pD) change(pos-1,44, "D R -> J R"); // Example: DRY + else if (flags[prior] & FLAG_VOWEL) change(pos, 18, " R -> RX"); // Example: ART + } else if (p == 24 && (flags[prior] & FLAG_VOWEL)) change(pos, 19, " L -> LX"); // 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, "G S -> G Z"); + } else if (p == 60) rule_g(pos); + else { + if (p == 72) { // 'K' + // K -> KX + // Example: COW + unsigned char Y = phonemeindex[pos+1]; + // If at end, replace current phoneme with KX + if ((flags[Y] & FLAG_DIP_YX)==0 || Y==END) { // VOWELS AND DIPTHONGS ENDING WITH IY SOUND flag set? + change(pos, 75, "K -> KX "); + p = 75; + pf = flags[p]; + } + } + // Replace with softer version? + if ((flags[p] & FLAG_PLOSIVE) && (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 + + if (debug) printf("RULE: S* %c%c -> S* %c%c\n", signInputTable1[p], signInputTable2[p],signInputTable1[p-12], signInputTable2[p-12]); + phonemeindex[pos] = p-12; + } else if (!(pf & FLAG_PLOSIVE)) { + 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]] & FLAG_VOWEL) { + p = phonemeindex[pos+1]; + if (!p) p = phonemeindex[pos+2]; + if ((flags[p] & FLAG_VOWEL) && !stress[pos+1]) change(pos,30, "Soften T or D following vowel or ER and preceding a pause -> DX"); + } + } + } + pos++; } // while } - // Applies various rules that adjust the lengths of phonemes // // Lengthen or between and by 1.5 @@ -1057,12 +599,8 @@ void Parser2() // - set nasal = 5, consonant = 6 // {optional silence} - shorten both to 1/2 + 1 // - decrease by 2 - - -//void Code48619() -void AdjustLengths() -{ - +// +void AdjustLengths() { // LENGTHEN VOWELS PRECEDING PUNCTUATION // // Search for punctuation. If found, back up to the first vowel, then @@ -1071,383 +609,121 @@ void AdjustLengths() // increased by (length * 1.5) + 1 // loop index - X = 0; + { + unsigned char X = 0; unsigned char index; - // iterate through the phoneme list - unsigned char loopIndex=0; - while(1) - { - // get a phoneme - index = phonemeindex[X]; - - // exit loop if end on buffer token - if (index == 255) break; + while((index = phonemeindex[X]) != END) { + unsigned char loopIndex; // not punctuation? - if((flags2[index] & 1) == 0) - { - // skip - X++; + if((flags[index] & FLAG_PUNCT) == 0) { + ++X; continue; } - - // hold index + loopIndex = X; - - // Loop backwards from this point -pos48644: - - // back up one phoneme - X--; - - // stop once the beginning is reached - if(X == 0) break; - - // get the preceding phoneme - index = phonemeindex[X]; - - if (index != 255) //inserted to prevent access overrun - if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping - - //pos48657: - do - { + + while (--X && !(flags[phonemeindex[X]] & FLAG_VOWEL)); // back up while not a vowel + if (X == 0) break; + + do { // test for vowel index = phonemeindex[X]; - if (index != 255)//inserted to prevent access overrun // test for fricative/unvoiced or not voiced - if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal überprüfen - { - //A = flags[Y] & 4; - //if(A == 0) goto pos48688; - - // get the phoneme length - A = phonemeLength[X]; - + if(!(flags[index] & FLAG_FRICATIVE) || (flags[index] & FLAG_VOICED)) { //nochmal überprüfen + unsigned char A = phonemeLength[X]; // change phoneme length to (length * 1.5) + 1 - A = (A >> 1) + A + 1; -if (debug) printf("RULE: Lengthen or between and by 1.5\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); - - phonemeLength[X] = A; - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); - + drule_pre("Lengthen or between and by 1.5",X); + phonemeLength[X] = (A >> 1) + A + 1; + drule_post(X); } - // keep moving forward - X++; - } while (X != loopIndex); - // if (X != loopIndex) goto pos48657; + } while (++X != loopIndex); X++; } // while + } // Similar to the above routine, but shorten vowels under some circumstances - // Loop throught all phonemes - loopIndex = 0; - //pos48697 - - while(1) - { - // get a phoneme - X = loopIndex; - index = phonemeindex[X]; - - // exit routine at end token - if (index == 255) return; - - // vowel? - A = flags[index] & 128; - if (A != 0) - { - // get next phoneme - X++; - index = phonemeindex[X]; - - // get flags - if (index == 255) - mem56 = 65; // use if end marker - else - mem56 = flags[index]; - - // not a consonant - if ((flags[index] & 64) == 0) - { - // RX or LX? - if ((index == 18) || (index == 19)) // 'RX' & 'LX' - { - // get the next phoneme - X++; - index = phonemeindex[X]; - - // next phoneme a consonant? - if ((flags[index] & 64) != 0) { - // RULE: RX | LX - - -if (debug) printf("RULE: - decrease length by 1\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]); - - // decrease length of vowel by 1 frame - phonemeLength[loopIndex]--; + // Loop through all phonemes + unsigned char loopIndex=0; + unsigned char index; -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", loopIndex, signInputTable1[phonemeindex[loopIndex]], signInputTable2[phonemeindex[loopIndex]], phonemeLength[loopIndex]); + while((index = phonemeindex[loopIndex]) != END) { + unsigned char X = loopIndex; + if (flags[index] & FLAG_VOWEL) { + index = phonemeindex[loopIndex+1]; + if (!(flags[index] & FLAG_CONSONANT)) { + if ((index == 18) || (index == 19)) { // 'RX', 'LX' + index = phonemeindex[loopIndex+2]; + if ((flags[index] & FLAG_CONSONANT)) { + drule_pre(" - decrease length of vowel by 1\n", loopIndex); + phonemeLength[loopIndex]--; + drule_post(loopIndex); } - // move ahead - loopIndex++; - continue; } - // move ahead - loopIndex++; - continue; - } - - - // Got here if not - - // not voiced - if ((mem56 & 4) == 0) - { - - // Unvoiced - // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX - - // not an unvoiced plosive? - if((mem56 & 1) == 0) { - // move ahead - loopIndex++; - continue; + } else { // Got here if not + unsigned short flag = (index == END) ? 65 : flags[index]; // 65 if end marker + + if (!(flag & FLAG_VOICED)) { // Unvoiced + // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX + if((flag & FLAG_PLOSIVE)) { // unvoiced plosive + // RULE: + // + drule_pre(" - decrease vowel by 1/8th",loopIndex); + phonemeLength[loopIndex] -= (phonemeLength[loopIndex] >> 3); + drule_post(loopIndex); + } + } else { + unsigned char A; + drule_pre(" - increase vowel by 1/2 + 1\n",X-1); + // decrease length + A = phonemeLength[loopIndex]; + phonemeLength[loopIndex] = (A >> 2) + A + 1; // 5/4*A + 1 + drule_post(loopIndex); } - - // P*, T*, K*, KX - - - // RULE: - // - - // move back - X--; - -if (debug) printf("RULE: - decrease vowel by 1/8th\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); - - // decrease length by 1/8th - mem56 = phonemeLength[X] >> 3; - phonemeLength[X] -= mem56; - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); - - // move ahead - loopIndex++; - continue; - } - - // RULE: - // - -if (debug) printf("RULE: - increase vowel by 1/2 + 1\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); - - // decrease length - A = phonemeLength[X-1]; - phonemeLength[X-1] = (A >> 2) + A + 1; // 5/4*A + 1 - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); - - // move ahead - loopIndex++; - continue; - - } - - - // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX - -//pos48821: - - // RULE: - // Set punctuation length to 6 - // Set stop consonant length to 5 - - // nasal? - if((flags2[index] & 8) != 0) - { - - // M*, N*, NX, - - // get the next phoneme - X++; - index = phonemeindex[X]; - - // end of buffer? - if (index == 255) - A = 65&2; //prevent buffer overflow - else - A = flags[index] & 2; // check for stop consonant - - - // is next phoneme a stop consonant? - if (A != 0) - - // B*, D*, G*, GX, P*, T*, K*, KX - - { -if (debug) printf("RULE: - set nasal = 5, consonant = 6\n"); -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); -if (debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); - - // set stop consonant length to 6 - phonemeLength[X] = 6; - - // set nasal length to 5 - phonemeLength[X-1] = 5; - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); -if (debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); - } - // move to next phoneme - loopIndex++; - continue; - } - - - // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX - - // RULE: {optional silence} - // Shorten both to (length/2 + 1) + } else if((flags[index] & FLAG_NASAL) != 0) { // nasal? + // RULE: + // Set punctuation length to 6 + // Set stop consonant length to 5 + index = phonemeindex[++X]; + if (index != END && (flags[index] & FLAG_STOPCONS)) { + drule(" - set nasal = 5, consonant = 6"); + phonemeLength[X] = 6; // set stop consonant length to 6 + phonemeLength[X-1] = 5; // set nasal length to 5 + } + } else if((flags[index] & FLAG_STOPCONS)) { // (voiced) stop consonant? + // RULE: {optional silence} + // Shorten both to (length/2 + 1) - // (voiced) stop consonant? - if((flags[index] & 2) != 0) - { - // B*, D*, G*, GX - // move past silence - do - { - // move ahead - X++; - index = phonemeindex[X]; - } while(index == 0); - - - // check for end of buffer - if (index == 255) //buffer overflow - { - // ignore, overflow code - if ((65 & 2) == 0) {loopIndex++; continue;} - } else if ((flags[index] & 2) == 0) { - // if another stop consonant, move ahead - loopIndex++; - continue; + while ((index = phonemeindex[++X]) == 0); + + if (index != END && (flags[index] & FLAG_STOPCONS)) { + // FIXME, this looks wrong? + // RULE: {optional silence} + drule(" {optional silence} - shorten both to 1/2 + 1"); + phonemeLength[X] = (phonemeLength[X] >> 1) + 1; + phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; + X = loopIndex; } + } else if ((flags[index] & FLAG_LIQUIC)) { // liquic consonant? + // RULE: + // Decrease by 2 + index = phonemeindex[X-1]; // prior phoneme; - // RULE: {optional silence} -if (debug) printf("RULE: {optional silence} - shorten both to 1/2 + 1\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); -if (debug) printf("phoneme %d (%c%c) length %d\n", X-1, signInputTable1[phonemeindex[X-1]], signInputTable2[phonemeindex[X-1]], phonemeLength[X-1]); -// X gets overwritten, so hold prior X value for debug statement -int debugX = X; - // shorten the prior phoneme length to (length/2 + 1) - phonemeLength[X] = (phonemeLength[X] >> 1) + 1; - X = loopIndex; - - // also shorten this phoneme length to (length/2 +1) - phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", debugX, signInputTable1[phonemeindex[debugX]], signInputTable2[phonemeindex[debugX]], phonemeLength[debugX]); -if (debug) printf("phoneme %d (%c%c) length %d\n", debugX-1, signInputTable1[phonemeindex[debugX-1]], signInputTable2[phonemeindex[debugX-1]], phonemeLength[debugX-1]); - - - // move ahead - loopIndex++; - continue; - } - - - // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, - - // RULE: - // Decrease by 2 - - // liquic consonant? - if ((flags2[index] & 16) != 0) - { - // R*, L*, W*, Y* - - // get the prior phoneme - index = phonemeindex[X-1]; - + // FIXME: The debug code here breaks the rule. // prior phoneme a stop consonant> - if((flags[index] & 2) != 0) - // Rule: - -if (debug) printf("RULE: - decrease by 2\n"); -if (debug) printf("PRE\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); - - // decrease the phoneme length by 2 frames (20 ms) - phonemeLength[X] -= 2; - -if (debug) printf("POST\n"); -if (debug) printf("phoneme %d (%c%c) length %d\n", X, signInputTable1[phonemeindex[X]], signInputTable2[phonemeindex[X]], phonemeLength[X]); + if((flags[index] & FLAG_STOPCONS) != 0) + drule_pre(" - decrease by 2",X); + + phonemeLength[X] -= 2; // 20ms + drule_post(X); } - // move to next phoneme - loopIndex++; - continue; + ++loopIndex; } -// goto pos48701; } - -// ------------------------------------------------------------------------- -// ML : Code47503 is division with remainder, and mem50 gets the sign -void Code47503(unsigned char mem52) -{ - - Y = 0; - if ((mem53 & 128) != 0) - { - mem53 = -mem53; - Y = 128; - } - mem50 = Y; - A = 0; - for(X=8; X > 0; X--) - { - int temp = mem53; - mem53 = mem53 << 1; - A = A << 1; - if (temp >= 128) A++; - if (A >= mem52) - { - A = A - mem52; - mem53++; - } - } - - mem51 = A; - if ((mem50 & 128) != 0) mem53 = -mem53; - -} - - - - - - diff --git a/src/sam.h b/src/sam.h index f8a366e4..346c7b3f 100644 --- a/src/sam.h +++ b/src/sam.h @@ -1,13 +1,12 @@ #ifndef SAM_H #define SAM_H -void SetInput(char *_input); +void SetInput(unsigned char *_input); void SetSpeed(unsigned char _speed); void SetPitch(unsigned char _pitch); void SetMouth(unsigned char _mouth); void SetThroat(unsigned char _throat); void EnableSingmode(); -void EnableDebug(); int SAMMain(); @@ -36,4 +35,3 @@ int GetBufferLength(); #endif -