14a2fb39cf
This commit has been reported to cause distortions in phoneme pitches and speeds.master
| void DisplayVoices(FILE *f_out, char *language) | void DisplayVoices(FILE *f_out, char *language) | ||||
| { | { | ||||
| int ix; | |||||
| const char *p; | const char *p; | ||||
| int len; | int len; | ||||
| int count; | int count; | ||||
| fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | ||||
| for (int ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
| for (ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
| count = 0; | count = 0; | ||||
| p = v->languages; | p = v->languages; | ||||
| while (*p != 0) { | while (*p != 0) { | ||||
| static void Write4Bytes(FILE *f, int value) | static void Write4Bytes(FILE *f, int value) | ||||
| { | { | ||||
| // Write 4 bytes to a file, least significant first | // Write 4 bytes to a file, least significant first | ||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| int ix; | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| fputc(value & 0xff, f); | fputc(value & 0xff, f); | ||||
| value = value >> 8; | value = value >> 8; | ||||
| } | } | ||||
| static void CloseWavFile() | static void CloseWavFile() | ||||
| { | { | ||||
| unsigned int pos; | |||||
| if ((f_wavfile == NULL) || (f_wavfile == stdout)) | if ((f_wavfile == NULL) || (f_wavfile == stdout)) | ||||
| return; | return; | ||||
| fflush(f_wavfile); | fflush(f_wavfile); | ||||
| unsigned int pos = ftell(f_wavfile); | |||||
| pos = ftell(f_wavfile); | |||||
| fseek(f_wavfile, 4, SEEK_SET); | fseek(f_wavfile, 4, SEEK_SET); | ||||
| Write4Bytes(f_wavfile, pos - 8); | Write4Bytes(f_wavfile, pos - 8); | ||||
| static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) | static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events) | ||||
| { | { | ||||
| char fname[210]; | |||||
| if (quiet) return 0; // -q quiet mode | if (quiet) return 0; // -q quiet mode | ||||
| if (wav == NULL) { | if (wav == NULL) { | ||||
| } | } | ||||
| if (f_wavfile == NULL) { | if (f_wavfile == NULL) { | ||||
| char fname[210]; | |||||
| if (samples_split > 0) { | if (samples_split > 0) { | ||||
| sprintf(fname, "%s_%.2d%s", wavefile, wavefile_count+1, filetype); | sprintf(fname, "%s_%.2d%s", wavefile, wavefile_count+1, filetype); | ||||
| if (OpenWavFile(fname, samplerate) != 0) | if (OpenWavFile(fname, samplerate) != 0) | ||||
| int option_index = 0; | int option_index = 0; | ||||
| int c; | int c; | ||||
| int ix; | |||||
| char *optarg2; | char *optarg2; | ||||
| int value; | int value; | ||||
| int flag_stdin = 0; | int flag_stdin = 0; | ||||
| optind = 1; | optind = 1; | ||||
| opt_string = ""; | opt_string = ""; | ||||
| while (optind < argc) { | while (optind < argc) { | ||||
| int len; | |||||
| char *p; | char *p; | ||||
| if ((c = *opt_string) == 0) { | if ((c = *opt_string) == 0) { | ||||
| break; // -- means don't interpret further - as commands | break; // -- means don't interpret further - as commands | ||||
| opt_string = ""; | opt_string = ""; | ||||
| for (int ix = 0;; ix++) { | |||||
| for (ix = 0;; ix++) { | |||||
| if (long_options[ix].name == 0) | if (long_options[ix].name == 0) | ||||
| break; | break; | ||||
| size_t len = strlen(long_options[ix].name); | |||||
| len = strlen(long_options[ix].name); | |||||
| if (memcmp(long_options[ix].name, p, len) == 0) { | if (memcmp(long_options[ix].name, p, len) == 0) { | ||||
| c = long_options[ix].val; | c = long_options[ix].val; | ||||
| optarg2 = NULL; | optarg2 = NULL; | ||||
| case 0x103: // --punct | case 0x103: // --punct | ||||
| option_punctuation = 1; | option_punctuation = 1; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| int ix = 0; | |||||
| ix = 0; | |||||
| while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | ||||
| option_punctlist[N_PUNCTLIST-1] = 0; | option_punctlist[N_PUNCTLIST-1] = 0; | ||||
| option_punctuation = 2; | option_punctuation = 2; | ||||
| } | } | ||||
| } else { | } else { | ||||
| // bulk input on stdin | // bulk input on stdin | ||||
| int ix = 0; | |||||
| ix = 0; | |||||
| while (!feof(stdin)) { | while (!feof(stdin)) { | ||||
| p_text[ix++] = fgetc(stdin); | p_text[ix++] = fgetc(stdin); | ||||
| if (ix >= (max-1)) { | if (ix >= (max-1)) { |
| // Read the phondata-manifest file | // Read the phondata-manifest file | ||||
| FILE *f; | FILE *f; | ||||
| int n_lines = 0; | int n_lines = 0; | ||||
| int ix; | |||||
| char *p; | char *p; | ||||
| unsigned int value; | unsigned int value; | ||||
| char buf[sizeof(path_home)+40]; | char buf[sizeof(path_home)+40]; | ||||
| rewind(f); | rewind(f); | ||||
| if (manifest != NULL) { | if (manifest != NULL) { | ||||
| for (int ix = 0; ix < n_manifest; ix++) | |||||
| for (ix = 0; ix < n_manifest; ix++) | |||||
| free(manifest[ix].name); | free(manifest[ix].name); | ||||
| } | } | ||||
| static void DecompilePhoneme(FILE *f_out, PHONEME_TAB *ph, int compile_phoneme) | static void DecompilePhoneme(FILE *f_out, PHONEME_TAB *ph, int compile_phoneme) | ||||
| { | { | ||||
| USHORT *pc; | USHORT *pc; | ||||
| int instn; | |||||
| int instn_category; | |||||
| int address, address2; | int address, address2; | ||||
| int data1; | |||||
| int type2; | |||||
| int ix; | |||||
| int any; | |||||
| const char *name; | const char *name; | ||||
| char buf[120]; | char buf[120]; | ||||
| pc = prog_buf; | pc = prog_buf; | ||||
| while (pc < prog_out) { | while (pc < prog_out) { | ||||
| int instn = *pc++; | |||||
| int instn_category = (instn >> 12) & 0xf; | |||||
| int data1 = instn & 0xff; | |||||
| int type2 = (instn >> 8) & 0xf; | |||||
| instn = *pc++; | |||||
| instn_category = (instn >> 12) & 0xf; | |||||
| data1 = instn & 0xff; | |||||
| type2 = (instn >> 8) & 0xf; | |||||
| fprintf(f_out, " %.3x: %.4x %s", (unsigned int)(pc-prog_buf), instn, instn_category_string[instn_category]); | fprintf(f_out, " %.3x: %.4x %s", (unsigned int)(pc-prog_buf), instn, instn_category_string[instn_category]); | ||||
| switch (instn_category) | switch (instn_category) | ||||
| data1 = 0; | data1 = 0; | ||||
| fprintf(f_out, "%s", instn0_string[data1]); | fprintf(f_out, "%s", instn0_string[data1]); | ||||
| } else if (type2 == i_IPA_NAME) { | } else if (type2 == i_IPA_NAME) { | ||||
| int ix; | |||||
| for (ix = 0; ix < data1; ix += 2) { | for (ix = 0; ix < data1; ix += 2) { | ||||
| instn = *pc++; | instn = *pc++; | ||||
| buf[ix] = instn >> 8; | buf[ix] = instn >> 8; | ||||
| } else if (type2 == 8) { | } else if (type2 == 8) { | ||||
| // list of numbers | // list of numbers | ||||
| fprintf(f_out, " StressLevel("); | fprintf(f_out, " StressLevel("); | ||||
| int any = 0; | |||||
| for (int ix = 0; ix < 8; ix++) { | |||||
| any = 0; | |||||
| for (ix = 0; ix < 8; ix++) { | |||||
| if (data1 & (1 << ix)) { | if (data1 & (1 << ix)) { | ||||
| if (any) | if (any) | ||||
| fputc(',', f_out); | fputc(',', f_out); | ||||
| static int ref_sorter(char **a, char **b) | static int ref_sorter(char **a, char **b) | ||||
| { | { | ||||
| int ix; | |||||
| REF_HASH_TAB *p1 = (REF_HASH_TAB *)(*a); | REF_HASH_TAB *p1 = (REF_HASH_TAB *)(*a); | ||||
| REF_HASH_TAB *p2 = (REF_HASH_TAB *)(*b); | REF_HASH_TAB *p2 = (REF_HASH_TAB *)(*b); | ||||
| int ix = strcoll(p1->string, p2->string); | |||||
| ix = strcoll(p1->string, p2->string); | |||||
| if (ix != 0) | if (ix != 0) | ||||
| return ix; | return ix; | ||||
| static void CompileReport(void) | static void CompileReport(void) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| int hash; | |||||
| int n; | |||||
| REF_HASH_TAB *p; | REF_HASH_TAB *p; | ||||
| REF_HASH_TAB **list; | REF_HASH_TAB **list; | ||||
| const char *data_path; | const char *data_path; | ||||
| int prev_table; | |||||
| int procedure_num; | |||||
| int prev_mnemonic; | |||||
| if (f_report == NULL) | if (f_report == NULL) | ||||
| return; | return; | ||||
| fprintf(f_report, "Data file Used by\n"); | fprintf(f_report, "Data file Used by\n"); | ||||
| ix = 0; | ix = 0; | ||||
| for (int hash = 0; (hash < 256) && (ix < count_references); hash++) { | |||||
| for (hash = 0; (hash < 256) && (ix < count_references); hash++) { | |||||
| p = ref_hash_tab[hash]; | p = ref_hash_tab[hash]; | ||||
| while (p != NULL) { | while (p != NULL) { | ||||
| list[ix++] = p; | list[ix++] = p; | ||||
| p = (REF_HASH_TAB *)(p->link); | p = (REF_HASH_TAB *)(p->link); | ||||
| } | } | ||||
| } | } | ||||
| int n = ix; | |||||
| n = ix; | |||||
| qsort((void *)list, n, sizeof(REF_HASH_TAB *), (int (*)(const void *, const void *))ref_sorter); | qsort((void *)list, n, sizeof(REF_HASH_TAB *), (int (*)(const void *, const void *))ref_sorter); | ||||
| data_path = ""; | data_path = ""; | ||||
| int prev_mnemonic = 0; | |||||
| int prev_table = 0; | |||||
| prev_mnemonic = 0; | |||||
| prev_table = 0; | |||||
| for (ix = 0; ix < n; ix++) { | for (ix = 0; ix < n; ix++) { | ||||
| int j = 0; | int j = 0; | ||||
| prev_mnemonic = list[ix]->ph_mnemonic; | prev_mnemonic = list[ix]->ph_mnemonic; | ||||
| if ((prev_mnemonic >> 24) == 'P') { | if ((prev_mnemonic >> 24) == 'P') { | ||||
| // a procedure, not a phoneme | // a procedure, not a phoneme | ||||
| int procedure_num = atoi(WordToString(prev_mnemonic)); | |||||
| procedure_num = atoi(WordToString(prev_mnemonic)); | |||||
| fprintf(f_report, " %s %s", phoneme_tab_list2[prev_table = list[ix]->ph_table].name, proc_names[procedure_num]); | fprintf(f_report, " %s %s", phoneme_tab_list2[prev_table = list[ix]->ph_table].name, proc_names[procedure_num]); | ||||
| } else | } else | ||||
| fprintf(f_report, " [%s] %s", WordToString(prev_mnemonic), phoneme_tab_list2[prev_table = list[ix]->ph_table].name); | fprintf(f_report, " [%s] %s", WordToString(prev_mnemonic), phoneme_tab_list2[prev_table = list[ix]->ph_table].name); | ||||
| static unsigned int StringToWord(const char *string) | static unsigned int StringToWord(const char *string) | ||||
| { | { | ||||
| // Pack 4 characters into a word | // Pack 4 characters into a word | ||||
| int ix; | |||||
| unsigned char c; | unsigned char c; | ||||
| unsigned int word; | |||||
| if (string == NULL) | if (string == NULL) | ||||
| return 0; | return 0; | ||||
| unsigned int word = 0; | |||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| word = 0; | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| if (string[ix] == 0) break; | if (string[ix] == 0) break; | ||||
| c = string[ix]; | c = string[ix]; | ||||
| word |= (c << (ix*8)); | word |= (c << (ix*8)); | ||||
| // control = 1 declare phoneme if not found | // control = 1 declare phoneme if not found | ||||
| // control = 2 start looking after control & stress phonemes | // control = 2 start looking after control & stress phonemes | ||||
| int ix; | |||||
| int start; | |||||
| int use; | |||||
| unsigned int word; | |||||
| if (strcmp(string, "NULL") == 0) | if (strcmp(string, "NULL") == 0) | ||||
| return 1; | return 1; | ||||
| int ix = strlen(string); | |||||
| ix = strlen(string); | |||||
| if ((ix == 0) || (ix > 4)) | if ((ix == 0) || (ix > 4)) | ||||
| error("Bad phoneme name '%s'", string); | error("Bad phoneme name '%s'", string); | ||||
| unsigned int word = StringToWord(string); | |||||
| word = StringToWord(string); | |||||
| // don't use phoneme number 0, reserved for string terminator | // don't use phoneme number 0, reserved for string terminator | ||||
| int start = 1; | |||||
| start = 1; | |||||
| if (control == 2) { | if (control == 2) { | ||||
| // don't look for control and stress phonemes (allows these characters to be | // don't look for control and stress phonemes (allows these characters to be | ||||
| start = 8; | start = 8; | ||||
| } | } | ||||
| int use = 0; | |||||
| use = 0; | |||||
| for (ix = start; ix < n_phcodes; ix++) { | for (ix = start; ix < n_phcodes; ix++) { | ||||
| if (phoneme_tab2[ix].mnemonic == word) | if (phoneme_tab2[ix].mnemonic == word) | ||||
| return ix; | return ix; | ||||
| static unsigned int get_char() | static unsigned int get_char() | ||||
| { | { | ||||
| unsigned int c = fgetc(f_in); | |||||
| unsigned int c; | |||||
| c = fgetc(f_in); | |||||
| if (c == '\n') | if (c == '\n') | ||||
| linenum++; | linenum++; | ||||
| return c; | return c; | ||||
| int acc; | int acc; | ||||
| unsigned char c = 0; | unsigned char c = 0; | ||||
| unsigned char c2; | unsigned char c2; | ||||
| int ix; | |||||
| int sign; | |||||
| char *p; | char *p; | ||||
| keywtab_t *pk; | keywtab_t *pk; | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| int ix = 0; | |||||
| ix = 0; | |||||
| while (!feof(f_in) && !isspace(c) && (c != '(') && (c != ')') && (c != ',')) { | while (!feof(f_in) && !isspace(c) && (c != '(') && (c != ')') && (c != ',')) { | ||||
| if (c == '\\') | if (c == '\\') | ||||
| c = get_char(); | c = get_char(); | ||||
| if ((type == tNUMBER) || (type == tSIGNEDNUMBER)) { | if ((type == tNUMBER) || (type == tSIGNEDNUMBER)) { | ||||
| acc = 0; | acc = 0; | ||||
| int sign = 1; | |||||
| sign = 1; | |||||
| p = item_string; | p = item_string; | ||||
| if ((*p == '-') && (type == tSIGNEDNUMBER)) { | if ((*p == '-') && (type == tSIGNEDNUMBER)) { | ||||
| static int NextItemMax(int max) | static int NextItemMax(int max) | ||||
| { | { | ||||
| // Get a number, but restrict value to max | // Get a number, but restrict value to max | ||||
| int value; | |||||
| char msg[80]; | char msg[80]; | ||||
| int value = NextItem(tNUMBER); | |||||
| value = NextItem(tNUMBER); | |||||
| if (value > max) { | if (value > max) { | ||||
| sprintf(msg, "Value %d is greater than maximum %d", value, max); | sprintf(msg, "Value %d is greater than maximum %d", value, max); | ||||
| error(msg, NULL); | error(msg, NULL); | ||||
| // control: bit 0 0= need ( | // control: bit 0 0= need ( | ||||
| // bit 1 1= allow comma | // bit 1 1= allow comma | ||||
| int value; | |||||
| if ((control & 1) == 0) { | if ((control & 1) == 0) { | ||||
| if (!NextItem(tOPENBRACKET)) | if (!NextItem(tOPENBRACKET)) | ||||
| error("Expected '('", NULL); | error("Expected '('", NULL); | ||||
| } | } | ||||
| int value = NextItem(type); | |||||
| value = NextItem(type); | |||||
| if ((control & 2) && (item_terminator == ',')) | if ((control & 2) && (item_terminator == ',')) | ||||
| return value; | return value; | ||||
| int CompileVowelTransition(int which) | int CompileVowelTransition(int which) | ||||
| { | { | ||||
| // Compile a vowel transition | // Compile a vowel transition | ||||
| int key; | |||||
| int len = 0; | int len = 0; | ||||
| int rms = 0; | int rms = 0; | ||||
| int f1 = 0; | int f1 = 0; | ||||
| int f3_amp = 0; | int f3_amp = 0; | ||||
| int flags = 0; | int flags = 0; | ||||
| int vcolour = 0; | int vcolour = 0; | ||||
| int x; | |||||
| int instn = i_VOWELIN; | int instn = i_VOWELIN; | ||||
| int word1; | |||||
| int word2; | |||||
| if (which == 1) { | if (which == 1) { | ||||
| len = 50 / 2; // defaults for transition into vowel | len = 50 / 2; // defaults for transition into vowel | ||||
| } | } | ||||
| for (;;) { | for (;;) { | ||||
| int key = NextItem(tKEYWORD); | |||||
| key = NextItem(tKEYWORD); | |||||
| if (item_type != tTRANSITION) { | if (item_type != tTRANSITION) { | ||||
| UngetItem(); | UngetItem(); | ||||
| break; | break; | ||||
| f2_min = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f; | f2_min = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f; | ||||
| f2_max = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f; | f2_max = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f; | ||||
| if (f2_min > f2_max) { | if (f2_min > f2_max) { | ||||
| int x = f2_min; | |||||
| x = f2_min; | |||||
| f2_min = f2_max; | f2_min = f2_max; | ||||
| f2_max = x; | f2_max = x; | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| int word1 = len + (rms << 6) + (flags << 12); | |||||
| int word2 = f2 + (f2_min << 6) + (f2_max << 11) + (f3_adj << 16) + (f3_amp << 21) + (f1 << 26) + (vcolour << 29); | |||||
| word1 = len + (rms << 6) + (flags << 12); | |||||
| word2 = f2 + (f2_min << 6) + (f2_max << 11) + (f3_adj << 16) + (f3_amp << 21) + (f1 << 26) + (vcolour << 29); | |||||
| prog_out[0] = instn + ((word1 >> 16) & 0xff); | prog_out[0] = instn + ((word1 >> 16) & 0xff); | ||||
| prog_out[1] = word1; | prog_out[1] = word1; | ||||
| prog_out[2] = word2 >> 16; | prog_out[2] = word2 >> 16; | ||||
| int LoadSpect(const char *path, int control) | int LoadSpect(const char *path, int control) | ||||
| { | { | ||||
| SpectSeq *spectseq; | SpectSeq *spectseq; | ||||
| int peak; | |||||
| int displ; | |||||
| int frame; | |||||
| int n_frames; | |||||
| int ix; | int ix; | ||||
| int x, x2; | int x, x2; | ||||
| int rms; | |||||
| float total; | |||||
| float pkheight; | float pkheight; | ||||
| int marker1_set = 0; | int marker1_set = 0; | ||||
| int frame_vowelbreak = 0; | int frame_vowelbreak = 0; | ||||
| } | } | ||||
| // do we need additional klatt data ? | // do we need additional klatt data ? | ||||
| for (int frame = 0; frame < spectseq->numframes; frame++) { | |||||
| for (frame = 0; frame < spectseq->numframes; frame++) { | |||||
| for (ix = 5; ix < N_KLATTP2; ix++) { | for (ix = 5; ix < N_KLATTP2; ix++) { | ||||
| if (spectseq->frames[frame]->klatt_param[ix] != 0) | if (spectseq->frames[frame]->klatt_param[ix] != 0) | ||||
| klatt_flag = FRFLAG_KLATT; | klatt_flag = FRFLAG_KLATT; | ||||
| } | } | ||||
| } | } | ||||
| int displ = ftell(f_phdata); | |||||
| displ = ftell(f_phdata); | |||||
| seq_out.n_frames = 0; | seq_out.n_frames = 0; | ||||
| seq_out.sqflags = 0; | seq_out.sqflags = 0; | ||||
| seq_out.length_total = 0; | seq_out.length_total = 0; | ||||
| float total = 0.0f; | |||||
| for (int frame = 0; frame < spectseq->numframes; frame++) { | |||||
| total = 0; | |||||
| for (frame = 0; frame < spectseq->numframes; frame++) { | |||||
| if (spectseq->frames[frame]->keyframe) { | if (spectseq->frames[frame]->keyframe) { | ||||
| if (seq_out.n_frames == 1) | if (seq_out.n_frames == 1) | ||||
| frame_vowelbreak = frame; | frame_vowelbreak = frame; | ||||
| spectseq->frames[frame_vowelbreak]->markers |= FRFLAG_VOWEL_CENTRE; | spectseq->frames[frame_vowelbreak]->markers |= FRFLAG_VOWEL_CENTRE; | ||||
| } | } | ||||
| int n_frames = 0; | |||||
| for (int frame = 0; frame < spectseq->numframes; frame++) { | |||||
| n_frames = 0; | |||||
| for (frame = 0; frame < spectseq->numframes; frame++) { | |||||
| fr = spectseq->frames[frame]; | fr = spectseq->frames[frame]; | ||||
| if (fr->keyframe) { | if (fr->keyframe) { | ||||
| fr_out->frflags = fr->markers | klatt_flag; | fr_out->frflags = fr->markers | klatt_flag; | ||||
| int rms = (int)GetFrameRms(fr, spectseq->amplitude); | |||||
| rms = (int)GetFrameRms(fr, spectseq->amplitude); | |||||
| if (rms > 255) rms = 255; | if (rms > 255) rms = 255; | ||||
| fr_out->rms = rms; | fr_out->rms = rms; | ||||
| // write: peak data | // write: peak data | ||||
| count_frames++; | count_frames++; | ||||
| for (int peak = 0; peak < 8; peak++) { | |||||
| for (peak = 0; peak < 8; peak++) { | |||||
| if (peak < 7) | if (peak < 7) | ||||
| fr_out->ffreq[peak] = fr->peaks[peak].pkfreq; | fr_out->ffreq[peak] = fr->peaks[peak].pkfreq; | ||||
| for (ix = 0; ix < 5; ix++) | for (ix = 0; ix < 5; ix++) | ||||
| fr_out->klattp2[ix] = fr->klatt_param[ix+5]; | fr_out->klattp2[ix] = fr->klatt_param[ix+5]; | ||||
| for (int peak = 0; peak < 7; peak++) { | |||||
| for (peak = 0; peak < 7; peak++) { | |||||
| fr_out->klatt_ap[ix] = fr->peaks[peak].klt_ap; | fr_out->klatt_ap[ix] = fr->peaks[peak].klt_ap; | ||||
| x = fr->peaks[peak].klt_bp / 2; | x = fr->peaks[peak].klt_bp / 2; | ||||
| static int LoadWavefile(FILE *f, const char *fname) | static int LoadWavefile(FILE *f, const char *fname) | ||||
| { | { | ||||
| int displ; | |||||
| unsigned char c1; | unsigned char c1; | ||||
| unsigned char c3; | unsigned char c3; | ||||
| int c2; | int c2; | ||||
| int sample; | int sample; | ||||
| int sample2; | int sample2; | ||||
| float x; | |||||
| int max = 0; | int max = 0; | ||||
| int length; | |||||
| int sr1, sr2; | |||||
| int failed; | int failed; | ||||
| int len; | |||||
| int resample_wav = 0; | int resample_wav = 0; | ||||
| const char *fname2; | const char *fname2; | ||||
| char fname_temp[100]; | char fname_temp[100]; | ||||
| int scale_factor = 0; | int scale_factor = 0; | ||||
| fseek(f, 24, SEEK_SET); | fseek(f, 24, SEEK_SET); | ||||
| int sr1 = Read4Bytes(f); | |||||
| int sr2 = Read4Bytes(f); | |||||
| sr1 = Read4Bytes(f); | |||||
| sr2 = Read4Bytes(f); | |||||
| fseek(f, 40, SEEK_SET); | fseek(f, 40, SEEK_SET); | ||||
| if ((sr1 != samplerate_native) || (sr2 != sr1*2)) { | if ((sr1 != samplerate_native) || (sr2 != sr1*2)) { | ||||
| #endif | #endif | ||||
| fname2 = fname; | fname2 = fname; | ||||
| size_t len = strlen(fname); | |||||
| len = strlen(fname); | |||||
| if (strcmp(&fname[len-4], ".wav") == 0) { | if (strcmp(&fname[len-4], ".wav") == 0) { | ||||
| strcpy(msg, fname); | strcpy(msg, fname); | ||||
| msg[len-4] = 0; | msg[len-4] = 0; | ||||
| fseek(f, 40, SEEK_SET); // skip past the WAV header, up to before "data length" | fseek(f, 40, SEEK_SET); // skip past the WAV header, up to before "data length" | ||||
| } | } | ||||
| int displ = ftell(f_phdata); | |||||
| displ = ftell(f_phdata); | |||||
| // data contains: 4 bytes of length (n_samples * 2), followed by 2-byte samples (lsb byte first) | // data contains: 4 bytes of length (n_samples * 2), followed by 2-byte samples (lsb byte first) | ||||
| int length = Read4Bytes(f); | |||||
| length = Read4Bytes(f); | |||||
| while (!feof(f)) { | while (!feof(f)) { | ||||
| c1 = fgetc(f); | c1 = fgetc(f); | ||||
| fputc(sample & 0xff, f_phdata); | fputc(sample & 0xff, f_phdata); | ||||
| fputc(sample >> 8, f_phdata); | fputc(sample >> 8, f_phdata); | ||||
| } else { | } else { | ||||
| float x = ((float)sample / scale_factor) + 0.5; | |||||
| x = ((float)sample / scale_factor) + 0.5; | |||||
| sample2 = (int)x; | sample2 = (int)x; | ||||
| if (sample2 > 127) | if (sample2 > 127) | ||||
| sample2 = 127; | sample2 = 127; | ||||
| static int LoadEnvelope(FILE *f, const char *fname) | static int LoadEnvelope(FILE *f, const char *fname) | ||||
| { | { | ||||
| int displ; | |||||
| char buf[128]; | char buf[128]; | ||||
| int displ = ftell(f_phdata); | |||||
| displ = ftell(f_phdata); | |||||
| fseek(f, 12, SEEK_SET); | fseek(f, 12, SEEK_SET); | ||||
| if (fread(buf, 128, 1, f) == 0) | if (fread(buf, 128, 1, f) == 0) | ||||
| static int LoadEnvelope2(FILE *f, const char *fname) | static int LoadEnvelope2(FILE *f, const char *fname) | ||||
| { | { | ||||
| int ix, ix2; | |||||
| int n; | int n; | ||||
| int x, y; | |||||
| int displ; | |||||
| int n_points; | |||||
| double yy; | |||||
| char line_buf[128]; | char line_buf[128]; | ||||
| float env_x[20]; | float env_x[20]; | ||||
| float env_y[20]; | float env_y[20]; | ||||
| int env_lin[20]; | int env_lin[20]; | ||||
| unsigned char env[ENV_LEN]; | unsigned char env[ENV_LEN]; | ||||
| int n_points = 0; | |||||
| n_points = 0; | |||||
| fgets(line_buf, sizeof(line_buf), f); // skip first line | fgets(line_buf, sizeof(line_buf), f); // skip first line | ||||
| while (!feof(f)) { | while (!feof(f)) { | ||||
| if (fgets(line_buf, sizeof(line_buf), f) == NULL) | if (fgets(line_buf, sizeof(line_buf), f) == NULL) | ||||
| break; | break; | ||||
| env_x[n_points] = env_x[n_points-1]; | env_x[n_points] = env_x[n_points-1]; | ||||
| env_y[n_points] = env_y[n_points-1]; | env_y[n_points] = env_y[n_points-1]; | ||||
| int ix = -1; | |||||
| int ix2 = 0; | |||||
| int y = 0; | |||||
| for (int x = 0; x < ENV_LEN; x++) { | |||||
| ix = -1; | |||||
| ix2 = 0; | |||||
| for (x = 0; x < ENV_LEN; x++) { | |||||
| if (x > env_x[ix+4]) | if (x > env_x[ix+4]) | ||||
| ix++; | ix++; | ||||
| if (x >= env_x[ix2+1]) | if (x >= env_x[ix2+1]) | ||||
| ix2++; | ix2++; | ||||
| if (env_lin[ix2] > 0) { | if (env_lin[ix2] > 0) { | ||||
| double yy = env_y[ix2] + (env_y[ix2+1] - env_y[ix2]) * ((float)x - env_x[ix2]) / (env_x[ix2+1] - env_x[ix2]); | |||||
| yy = env_y[ix2] + (env_y[ix2+1] - env_y[ix2]) * ((float)x - env_x[ix2]) / (env_x[ix2+1] - env_x[ix2]); | |||||
| y = (int)(yy * 2.55); | y = (int)(yy * 2.55); | ||||
| } else if (n_points > 3) | } else if (n_points > 3) | ||||
| y = (int)(polint(&env_x[ix], &env_y[ix], 4, x) * 2.55); // convert to range 0-255 | y = (int)(polint(&env_x[ix], &env_y[ix], 4, x) * 2.55); // convert to range 0-255 | ||||
| n_envelopes++; | n_envelopes++; | ||||
| } | } | ||||
| int displ = ftell(f_phdata); | |||||
| displ = ftell(f_phdata); | |||||
| fwrite(env, 1, 128, f_phdata); | fwrite(env, 1, 128, f_phdata); | ||||
| return displ; | return displ; | ||||
| // return index into spect or sample data area. bit 23=1 if a sample | // return index into spect or sample data area. bit 23=1 if a sample | ||||
| FILE *f; | FILE *f; | ||||
| int id; | |||||
| int hash; | |||||
| int addr = 0; | int addr = 0; | ||||
| int type_code = ' '; | int type_code = ' '; | ||||
| REF_HASH_TAB *p, *p2; | REF_HASH_TAB *p, *p2; | ||||
| count_references++; | count_references++; | ||||
| int hash = Hash8(path); | |||||
| hash = Hash8(path); | |||||
| p = ref_hash_tab[hash]; | p = ref_hash_tab[hash]; | ||||
| while (p != NULL) { | while (p != NULL) { | ||||
| if (strcmp(path, p->string) == 0) { | if (strcmp(path, p->string) == 0) { | ||||
| } | } | ||||
| } | } | ||||
| int id = Read4Bytes(f); | |||||
| id = Read4Bytes(f); | |||||
| rewind(f); | rewind(f); | ||||
| if (id == 0x43455053) { | if (id == 0x43455053) { | ||||
| static int CompileToneSpec(void) | static int CompileToneSpec(void) | ||||
| { | { | ||||
| int pitch1 = 0; | |||||
| int pitch2 = 0; | |||||
| int pitch_env = 0; | int pitch_env = 0; | ||||
| int amp_env = 0; | int amp_env = 0; | ||||
| int pitch1 = NextItemBrackets(tNUMBER, 2); | |||||
| int pitch2 = NextItemBrackets(tNUMBER, 3); | |||||
| pitch1 = NextItemBrackets(tNUMBER, 2); | |||||
| pitch2 = NextItemBrackets(tNUMBER, 3); | |||||
| if (item_terminator == ',') { | if (item_terminator == ',') { | ||||
| NextItemBrackets(tSTRING, 3); | NextItemBrackets(tSTRING, 3); | ||||
| int CompileSound(int keyword, int isvowel) | int CompileSound(int keyword, int isvowel) | ||||
| { | { | ||||
| int addr; | |||||
| int value = 0; | int value = 0; | ||||
| char path[N_ITEM_STRING]; | char path[N_ITEM_STRING]; | ||||
| static int sound_instns[] = { i_FMT, i_WAV, i_VWLSTART, i_VWLENDING, i_WAVADD }; | static int sound_instns[] = { i_FMT, i_WAV, i_VWLSTART, i_VWLENDING, i_WAVADD }; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| int addr = LoadDataFile(path, isvowel); | |||||
| addr = LoadDataFile(path, isvowel); | |||||
| addr = addr / 4; // addr is words not bytes | addr = addr / 4; // addr is words not bytes | ||||
| *prog_out++ = sound_instns[keyword-kFMT] + ((value & 0xff) << 4) + ((addr >> 16) & 0xf); | *prog_out++ = sound_instns[keyword-kFMT] + ((value & 0xff) << 4) + ((addr >> 16) & 0xf); | ||||
| int word = 0; | int word = 0; | ||||
| int word2; | int word2; | ||||
| int data; | int data; | ||||
| int bitmap; | |||||
| int brackets; | |||||
| int not_flag; | int not_flag; | ||||
| USHORT *prog_last_if = NULL; | USHORT *prog_last_if = NULL; | ||||
| } | } | ||||
| } else if (item_type == tTEST) { | } else if (item_type == tTEST) { | ||||
| if (key == kTHISSTRESS) { | if (key == kTHISSTRESS) { | ||||
| int bitmap = 0; | |||||
| int brackets = 2; | |||||
| bitmap = 0; | |||||
| brackets = 2; | |||||
| do { | do { | ||||
| data = NextItemBrackets(tNUMBER, brackets); | data = NextItemBrackets(tNUMBER, brackets); | ||||
| if (data > 7) | if (data > 7) | ||||
| void FillThen(int add) | void FillThen(int add) | ||||
| { | { | ||||
| USHORT *p; | USHORT *p; | ||||
| int offset; | |||||
| p = if_stack[if_level].p_then; | p = if_stack[if_level].p_then; | ||||
| if (p != NULL) { | if (p != NULL) { | ||||
| int offset = prog_out - p + add; | |||||
| offset = prog_out - p + add; | |||||
| if ((then_count == 1) && (if_level == 1)) { | if ((then_count == 1) && (if_level == 1)) { | ||||
| // The THEN part only contains one statement, we can remove the THEN jump | // The THEN part only contains one statement, we can remove the THEN jump | ||||
| static PHONEME_TAB_LIST *FindPhonemeTable(const char *string) | static PHONEME_TAB_LIST *FindPhonemeTable(const char *string) | ||||
| { | { | ||||
| for (int ix = 0; ix < n_phoneme_tabs; ix++) { | |||||
| int ix; | |||||
| for (ix = 0; ix < n_phoneme_tabs; ix++) { | |||||
| if (strcmp(phoneme_tab_list2[ix].name, string) == 0) | if (strcmp(phoneme_tab_list2[ix].name, string) == 0) | ||||
| return &phoneme_tab_list2[ix]; | return &phoneme_tab_list2[ix]; | ||||
| } | } | ||||
| { | { | ||||
| PHONEME_TAB_LIST *phtab = NULL; | PHONEME_TAB_LIST *phtab = NULL; | ||||
| int ix; | int ix; | ||||
| unsigned int mnem; | |||||
| char *phname; | char *phname; | ||||
| char buf[200]; | char buf[200]; | ||||
| if (phtab == NULL) | if (phtab == NULL) | ||||
| return NULL; // phoneme table not found | return NULL; // phoneme table not found | ||||
| unsigned int mnem = StringToWord(phname); | |||||
| mnem = StringToWord(phname); | |||||
| for (ix = 1; ix < 256; ix++) { | for (ix = 1; ix < 256; ix++) { | ||||
| if (mnem == phtab->phoneme_tab_ptr[ix].mnemonic) | if (mnem == phtab->phoneme_tab_ptr[ix].mnemonic) | ||||
| return &phtab->phoneme_tab_ptr[ix]; | return &phtab->phoneme_tab_ptr[ix]; | ||||
| static void ImportPhoneme(void) | static void ImportPhoneme(void) | ||||
| { | { | ||||
| unsigned int ph_mnem; | |||||
| unsigned int ph_code; | |||||
| PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
| NextItem(tSTRING); | NextItem(tSTRING); | ||||
| if ((ph = FindPhoneme(item_string)) == NULL) | if ((ph = FindPhoneme(item_string)) == NULL) | ||||
| return; | return; | ||||
| unsigned int ph_mnem = phoneme_out->mnemonic; | |||||
| unsigned int ph_code = phoneme_out->code; | |||||
| ph_mnem = phoneme_out->mnemonic; | |||||
| ph_code = phoneme_out->code; | |||||
| memcpy(phoneme_out, ph, sizeof(PHONEME_TAB)); | memcpy(phoneme_out, ph, sizeof(PHONEME_TAB)); | ||||
| phoneme_out->mnemonic = ph_mnem; | phoneme_out->mnemonic = ph_mnem; | ||||
| phoneme_out->code = ph_code; | phoneme_out->code = ph_code; | ||||
| static void InstnPlusPhoneme(int instn) | static void InstnPlusPhoneme(int instn) | ||||
| { | { | ||||
| int phcode = NextItemBrackets(tPHONEMEMNEM, 0); | |||||
| int phcode; | |||||
| phcode = NextItemBrackets(tPHONEMEMNEM, 0); | |||||
| *prog_out++ = instn + phcode; | *prog_out++ = instn + phcode; | ||||
| } | } | ||||
| int keyword; | int keyword; | ||||
| int value; | int value; | ||||
| int phcode = 0; | int phcode = 0; | ||||
| int flags; | |||||
| int ix; | |||||
| int start; | |||||
| int count; | |||||
| int c; | int c; | ||||
| char *p; | char *p; | ||||
| int vowel_length_factor = 100; // for testing | int vowel_length_factor = 100; // for testing | ||||
| strcpy(item_string, " "); | strcpy(item_string, " "); | ||||
| // copy the string, recognize characters in the form U+9999 | // copy the string, recognize characters in the form U+9999 | ||||
| int flags = 0; | |||||
| int count = 0; | |||||
| int ix = 1; | |||||
| flags = 0; | |||||
| count = 0; | |||||
| ix = 1; | |||||
| for (p = item_string; *p != 0;) { | for (p = item_string; *p != 0;) { | ||||
| p += utf8_in(&c, p); | p += utf8_in(&c, p); | ||||
| ipa_buf[0] = flags; | ipa_buf[0] = flags; | ||||
| ipa_buf[ix] = 0; | ipa_buf[ix] = 0; | ||||
| int start = 1; | |||||
| start = 1; | |||||
| if (flags != 0) | if (flags != 0) | ||||
| start = 0; // only include the flags byte if bits are set | start = 0; // only include the flags byte if bits are set | ||||
| value = strlen(&ipa_buf[start]); // number of UTF-8 bytes | value = strlen(&ipa_buf[start]); // number of UTF-8 bytes | ||||
| static void WritePhonemeTables() | static void WritePhonemeTables() | ||||
| { | { | ||||
| int ix; | |||||
| int j; | |||||
| int n; | int n; | ||||
| int value; | |||||
| int count; | int count; | ||||
| PHONEME_TAB *p; | PHONEME_TAB *p; | ||||
| int value = n_phoneme_tabs; | |||||
| value = n_phoneme_tabs; | |||||
| fputc(value, f_phtab); | fputc(value, f_phtab); | ||||
| fputc(0, f_phtab); | fputc(0, f_phtab); | ||||
| fputc(0, f_phtab); | fputc(0, f_phtab); | ||||
| fputc(0, f_phtab); | fputc(0, f_phtab); | ||||
| for (int ix = 0; ix < n_phoneme_tabs; ix++) { | |||||
| for (ix = 0; ix < n_phoneme_tabs; ix++) { | |||||
| p = phoneme_tab_list2[ix].phoneme_tab_ptr; | p = phoneme_tab_list2[ix].phoneme_tab_ptr; | ||||
| n = n_phcodes_list[ix]; | n = n_phcodes_list[ix]; | ||||
| p[n].mnemonic = 0; // terminate the phoneme table | p[n].mnemonic = 0; // terminate the phoneme table | ||||
| // count number of locally declared phonemes | // count number of locally declared phonemes | ||||
| count = 0; | count = 0; | ||||
| for (int j = 0; j < n; j++) { | |||||
| for (j = 0; j < n; j++) { | |||||
| if (ix == 0) | if (ix == 0) | ||||
| p[j].phflags |= phLOCAL; // write all phonemes in the base phoneme table | p[j].phflags |= phLOCAL; // write all phonemes in the base phoneme table | ||||
| fwrite(phoneme_tab_list2[ix].name, 1, N_PHONEME_TAB_NAME, f_phtab); | fwrite(phoneme_tab_list2[ix].name, 1, N_PHONEME_TAB_NAME, f_phtab); | ||||
| for (int j = 0; j < n; j++) { | |||||
| for (j = 0; j < n; j++) { | |||||
| if (p[j].phflags & phLOCAL) { | if (p[j].phflags & phLOCAL) { | ||||
| // this bit is set temporarily to incidate a local phoneme, declared in | // this bit is set temporarily to incidate a local phoneme, declared in | ||||
| // in the current phoneme file | // in the current phoneme file | ||||
| static void StartPhonemeTable(const char *name) | static void StartPhonemeTable(const char *name) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| int j; | |||||
| PHONEME_TAB *p; | PHONEME_TAB *p; | ||||
| fprintf(f_errors, "______________________________\nPhoneme Table: '%s'\n", name); | fprintf(f_errors, "______________________________\nPhoneme Table: '%s'\n", name); | ||||
| n_phcodes = n_phcodes_list[ix]; | n_phcodes = n_phcodes_list[ix]; | ||||
| // clear "local phoneme" bit" | // clear "local phoneme" bit" | ||||
| for (int j = 0; j < n_phcodes; j++) | |||||
| for (j = 0; j < n_phcodes; j++) | |||||
| phoneme_tab2[j].phflags &= ~phLOCAL; | phoneme_tab2[j].phflags &= ~phLOCAL; | ||||
| break; | break; | ||||
| } | } | ||||
| { | { | ||||
| // a list of phonemes in another language and the equivalent phoneme strings in this language | // a list of phonemes in another language and the equivalent phoneme strings in this language | ||||
| int ix; | |||||
| int n_names; | int n_names; | ||||
| int n_bytes; | int n_bytes; | ||||
| int foreign_phoneme; | int foreign_phoneme; | ||||
| if ((p = strstr(line_buf, "//")) != NULL) | if ((p = strstr(line_buf, "//")) != NULL) | ||||
| *p = 0; | *p = 0; | ||||
| for (int ix = 0; ix < 6; ix++) | |||||
| for (ix = 0; ix < 6; ix++) | |||||
| names[ix][0] = 0; | names[ix][0] = 0; | ||||
| n_names = sscanf(line_buf, "%s %s %s %s %s %s", names[0], names[1], names[2], names[3], names[4], names[5]); | n_names = sscanf(line_buf, "%s %s %s %s %s %s", names[0], names[1], names[2], names[3], names[4], names[5]); | ||||
| if (n_names < 1) | if (n_names < 1) | ||||
| } | } | ||||
| } | } | ||||
| for (int ix = 1; ix < n_names; ix++) | |||||
| for (ix = 1; ix < n_names; ix++) | |||||
| phcode[ix] = LookupPhoneme(names[ix], 1); | phcode[ix] = LookupPhoneme(names[ix], 1); | ||||
| // only write a translation if it has an effect | // only write a translation if it has an effect |
| void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len) | void print_dictionary_flags(unsigned int *flags, char *buf, int buf_len) | ||||
| { | { | ||||
| int stress; | int stress; | ||||
| int ix; | |||||
| const char *name; | const char *name; | ||||
| int len; | int len; | ||||
| int total = 0; | int total = 0; | ||||
| buf += total; | buf += total; | ||||
| } | } | ||||
| for (int ix = 8; ix < 64; ix++) { | |||||
| for (ix = 8; ix < 64; ix++) { | |||||
| if (((ix < 30) && (flags[0] & (1 << ix))) || ((ix >= 0x20) && (flags[1] & (1 << (ix-0x20))))) { | if (((ix < 30) && (flags[0] & (1 << ix))) || ((ix >= 0x20) && (flags[1] & (1 << (ix-0x20))))) { | ||||
| name = LookupMnemName(mnem_flags, ix); | name = LookupMnemName(mnem_flags, ix); | ||||
| len = strlen(name) + 1; | len = strlen(name) + 1; | ||||
| unsigned char c; | unsigned char c; | ||||
| char *p; | char *p; | ||||
| char *p_end; | char *p_end; | ||||
| int ix; | |||||
| int match_type; | |||||
| int finished = 0; | int finished = 0; | ||||
| int value; | int value; | ||||
| int linenum = 0; | int linenum = 0; | ||||
| static char symbols_lg[] = { 'A', 'B', 'C', 'H', 'F', 'G', 'Y' }; | static char symbols_lg[] = { 'A', 'B', 'C', 'H', 'F', 'G', 'Y' }; | ||||
| int match_type = 0; | |||||
| match_type = 0; | |||||
| buf_pre[0] = 0; | buf_pre[0] = 0; | ||||
| int ix; | |||||
| for (ix = 0; ix < group_length; ix++) | for (ix = 0; ix < group_length; ix++) | ||||
| buf[ix] = group_chars[ix]; | buf[ix] = group_chars[ix]; | ||||
| buf[ix] = 0; | buf[ix] = 0; | ||||
| char *word; | char *word; | ||||
| char *phonetic; | char *phonetic; | ||||
| unsigned int ix; | unsigned int ix; | ||||
| int step; | |||||
| unsigned int n_flag_codes = 0; | unsigned int n_flag_codes = 0; | ||||
| int flagnum; | |||||
| int flag_offset; | |||||
| int length; | int length; | ||||
| int multiple_words = 0; | int multiple_words = 0; | ||||
| int multiple_numeric_hyphen = 0; | int multiple_numeric_hyphen = 0; | ||||
| char *multiple_string = NULL; | char *multiple_string = NULL; | ||||
| char *multiple_string_end = NULL; | char *multiple_string_end = NULL; | ||||
| int len_word; | |||||
| int len_phonetic; | |||||
| int text_not_phonemes; // this word specifies replacement text, not phonemes | |||||
| unsigned int wc; | unsigned int wc; | ||||
| int all_upper_case; | |||||
| char *mnemptr; | char *mnemptr; | ||||
| unsigned char flag_codes[100]; | unsigned char flag_codes[100]; | ||||
| char encoded_ph[200]; | char encoded_ph[200]; | ||||
| char bad_phoneme_str[4]; | |||||
| int bad_phoneme; | |||||
| static char nullstring[] = { 0 }; | static char nullstring[] = { 0 }; | ||||
| int text_not_phonemes = 0; // this word specifies replacement text, not phonemes | |||||
| text_not_phonemes = 0; | |||||
| phonetic = word = nullstring; | phonetic = word = nullstring; | ||||
| p = linebuf; | p = linebuf; | ||||
| int step = 0; | |||||
| step = 0; | |||||
| c = 0; | c = 0; | ||||
| while (c != '\n') { | while (c != '\n') { | ||||
| if ((c == '?') && (step == 0)) { | if ((c == '?') && (step == 0)) { | ||||
| // conditional rule, allow only if the numbered condition is set for the voice | // conditional rule, allow only if the numbered condition is set for the voice | ||||
| int flag_offset = 100; | |||||
| flag_offset = 100; | |||||
| p++; | p++; | ||||
| if (*p == '!') { | if (*p == '!') { | ||||
| while (!isspace2(c = *p)) p++; | while (!isspace2(c = *p)) p++; | ||||
| *p = 0; | *p = 0; | ||||
| int flagnum = LookupMnem(mnem_flags, mnemptr); | |||||
| flagnum = LookupMnem(mnem_flags, mnemptr); | |||||
| if (flagnum > 0) { | if (flagnum > 0) { | ||||
| if (flagnum == 200) | if (flagnum == 200) | ||||
| text_mode = 1; | text_mode = 1; | ||||
| // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word | // this is replacement text, so don't encode as phonemes. Restrict the length of the replacement word | ||||
| strncpy0(encoded_ph, phonetic, N_WORD_BYTES-4); | strncpy0(encoded_ph, phonetic, N_WORD_BYTES-4); | ||||
| } else { | } else { | ||||
| int bad_phoneme; | |||||
| EncodePhonemes(phonetic, encoded_ph, &bad_phoneme); | EncodePhonemes(phonetic, encoded_ph, &bad_phoneme); | ||||
| if (strchr(encoded_ph, phonSWITCH) != 0) | if (strchr(encoded_ph, phonSWITCH) != 0) | ||||
| flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages | flag_codes[n_flag_codes++] = BITNUM_FLAG_ONLY_S; // don't match on suffixes (except 's') when switching languages | ||||
| // check for errors in the phonemes codes | // check for errors in the phonemes codes | ||||
| if (bad_phoneme != 0) { | if (bad_phoneme != 0) { | ||||
| char bad_phoneme_str[4]; | |||||
| // unrecognised phoneme, report error | // unrecognised phoneme, report error | ||||
| bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0; | bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0; | ||||
| fprintf(f_log, "%5d: Bad phoneme [%s] (U+%x) in: %s %s\n", linenum, bad_phoneme_str, bad_phoneme, word, phonetic); | fprintf(f_log, "%5d: Bad phoneme [%s] (U+%x) in: %s %s\n", linenum, bad_phoneme_str, bad_phoneme, word, phonetic); | ||||
| // convert to lower case, and note if the word is all-capitals | // convert to lower case, and note if the word is all-capitals | ||||
| int c2; | int c2; | ||||
| int all_upper_case = 1; | |||||
| all_upper_case = 1; | |||||
| p = word; | p = word; | ||||
| for (p = word;;) { | for (p = word;;) { | ||||
| // this assumes that the lower case char is the same length as the upper case char | // this assumes that the lower case char is the same length as the upper case char | ||||
| flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS; | flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS; | ||||
| } | } | ||||
| int len_word = strlen(word); | |||||
| len_word = strlen(word); | |||||
| if (translator->transpose_min > 0) | if (translator->transpose_min > 0) | ||||
| len_word = TransposeAlphabet(translator, word); | len_word = TransposeAlphabet(translator, word); | ||||
| *hash = HashDictionary(word); | *hash = HashDictionary(word); | ||||
| int len_phonetic = strlen(encoded_ph); | |||||
| len_phonetic = strlen(encoded_ph); | |||||
| dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed | dict_line[1] = len_word; // bit 6 indicates whether the word has been compressed | ||||
| len_word &= 0x3f; | len_word &= 0x3f; | ||||
| static void compile_dictlist_start(void) | static void compile_dictlist_start(void) | ||||
| { | { | ||||
| // initialise dictionary list | // initialise dictionary list | ||||
| int ix; | |||||
| char *p; | char *p; | ||||
| char *p2; | char *p2; | ||||
| for (int ix = 0; ix < N_HASH_DICT; ix++) { | |||||
| for (ix = 0; ix < N_HASH_DICT; ix++) { | |||||
| p = hash_chains[ix]; | p = hash_chains[ix]; | ||||
| while (p != NULL) { | while (p != NULL) { | ||||
| memcpy(&p2, p, sizeof(char *)); | memcpy(&p2, p, sizeof(char *)); | ||||
| static void compile_dictlist_end(FILE *f_out) | static void compile_dictlist_end(FILE *f_out) | ||||
| { | { | ||||
| // Write out the compiled dictionary list | // Write out the compiled dictionary list | ||||
| int hash; | |||||
| int length; | int length; | ||||
| char *p; | char *p; | ||||
| for (int hash = 0; hash < N_HASH_DICT; hash++) { | |||||
| for (hash = 0; hash < N_HASH_DICT; hash++) { | |||||
| p = hash_chains[hash]; | p = hash_chains[hash]; | ||||
| hash_counts[hash] = (int)ftell(f_out); | hash_counts[hash] = (int)ftell(f_out); | ||||
| char *p; | char *p; | ||||
| int count = 0; | int count = 0; | ||||
| FILE *f_in; | FILE *f_in; | ||||
| char buf[200]; | |||||
| char fname[sizeof(path_home)+45]; | char fname[sizeof(path_home)+45]; | ||||
| char dict_line[128]; | char dict_line[128]; | ||||
| linenum = 0; | linenum = 0; | ||||
| char buf[200]; | |||||
| while (fgets(buf, sizeof(buf), f_in) != NULL) { | while (fgets(buf, sizeof(buf), f_in) != NULL) { | ||||
| linenum++; | linenum++; | ||||
| int len; | int len; | ||||
| char c; | char c; | ||||
| int c2, c3; | int c2, c3; | ||||
| int sxflags; | |||||
| int value; | int value; | ||||
| int literal; | int literal; | ||||
| int hexdigit_input = 0; | int hexdigit_input = 0; | ||||
| rule_phonemes[len++] = ' '; | rule_phonemes[len++] = ' '; | ||||
| output = &rule_phonemes[len]; | output = &rule_phonemes[len]; | ||||
| } | } | ||||
| int sxflags = 0x808000; // to ensure non-zero bytes | |||||
| sxflags = 0x808000; // to ensure non-zero bytes | |||||
| for (p = string, ix = 0;;) { | for (p = string, ix = 0;;) { | ||||
| literal = 0; | literal = 0; | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| unsigned char c; | unsigned char c; | ||||
| int wc; | |||||
| char *p; | char *p; | ||||
| char *prule; | char *prule; | ||||
| int len; | |||||
| int len_name; | |||||
| int start; | |||||
| int state = 2; | int state = 2; | ||||
| int finish = 0; | int finish = 0; | ||||
| char buf[80]; | char buf[80]; | ||||
| char output[150]; | char output[150]; | ||||
| int bad_phoneme; | |||||
| char bad_phoneme_str[4]; | |||||
| buf[0] = 0; | buf[0] = 0; | ||||
| rule_cond[0] = 0; | rule_cond[0] = 0; | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| int bad_phoneme; | |||||
| char bad_phoneme_str[4]; | |||||
| EncodePhonemes(rule_phonemes, buf, &bad_phoneme); | EncodePhonemes(rule_phonemes, buf, &bad_phoneme); | ||||
| if (bad_phoneme != 0) { | if (bad_phoneme != 0) { | ||||
| bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0; | bad_phoneme_str[utf8_out(bad_phoneme, bad_phoneme_str)] = 0; | ||||
| error_count++; | error_count++; | ||||
| } | } | ||||
| strcpy(output, buf); | strcpy(output, buf); | ||||
| size_t len = strlen(buf)+1; | |||||
| len = strlen(buf)+1; | |||||
| size_t len_name = strlen(group_name); | |||||
| len_name = strlen(group_name); | |||||
| if ((len_name > 0) && (memcmp(rule_match, group_name, len_name) != 0)) { | if ((len_name > 0) && (memcmp(rule_match, group_name, len_name) != 0)) { | ||||
| int wc; | |||||
| utf8_in(&wc, rule_match); | utf8_in(&wc, rule_match); | ||||
| if ((group_name[0] == '9') && IsDigit(wc)) { | if ((group_name[0] == '9') && IsDigit(wc)) { | ||||
| // numeric group, rule_match starts with a digit, so OK | // numeric group, rule_match starts with a digit, so OK | ||||
| } | } | ||||
| } | } | ||||
| if (rule_pre[0] != 0) { | if (rule_pre[0] != 0) { | ||||
| int start = 0; | |||||
| start = 0; | |||||
| if (rule_pre[0] == RULE_SPACE) { | if (rule_pre[0] == RULE_SPACE) { | ||||
| // omit '_' at the beginning of the pre-string and imply it by using RULE_PRE_ATSTART | // omit '_' at the beginning of the pre-string and imply it by using RULE_PRE_ATSTART | ||||
| c = RULE_PRE_ATSTART; | c = RULE_PRE_ATSTART; | ||||
| static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b) | static int __cdecl rgroup_sorter(RGROUP *a, RGROUP *b) | ||||
| { | { | ||||
| // Sort long names before short names | // Sort long names before short names | ||||
| int ix = strlen(b->name) - strlen(a->name); | |||||
| int ix; | |||||
| ix = strlen(b->name) - strlen(a->name); | |||||
| if (ix != 0) return ix; | if (ix != 0) return ix; | ||||
| ix = strcmp(a->name, b->name); | ix = strcmp(a->name, b->name); | ||||
| if (ix != 0) return ix; | if (ix != 0) return ix; | ||||
| static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | static void output_rule_group(FILE *f_out, int n_rules, char **rules, char *name) | ||||
| { | { | ||||
| int ix; | |||||
| int len1; | int len1; | ||||
| int len2; | int len2; | ||||
| int len_name; | |||||
| char *p; | char *p; | ||||
| char *p2, *p3; | char *p2, *p3; | ||||
| const char *common; | const char *common; | ||||
| short nextchar_count[256]; | short nextchar_count[256]; | ||||
| memset(nextchar_count, 0, sizeof(nextchar_count)); | memset(nextchar_count, 0, sizeof(nextchar_count)); | ||||
| int len_name = strlen(name); | |||||
| len_name = strlen(name); | |||||
| // sort the rules in this group by their phoneme string | // sort the rules in this group by their phoneme string | ||||
| common = ""; | common = ""; | ||||
| if (strcmp(name, "9") == 0) | if (strcmp(name, "9") == 0) | ||||
| len_name = 0; // don't remove characters from numeric match strings | len_name = 0; // don't remove characters from numeric match strings | ||||
| for (int ix = 0; ix < n_rules; ix++) { | |||||
| for (ix = 0; ix < n_rules; ix++) { | |||||
| p = rules[ix]; | p = rules[ix]; | ||||
| len1 = strlen(p) + 1; // phoneme string | len1 = strlen(p) + 1; // phoneme string | ||||
| p3 = &p[len1]; | p3 = &p[len1]; | ||||
| { | { | ||||
| char *p; | char *p; | ||||
| char *p_start; | char *p_start; | ||||
| int group; | |||||
| int ix; | |||||
| int n_items; | |||||
| int length; | int length; | ||||
| int max_length = 0; | int max_length = 0; | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| int group = atoi(&p[0]); | |||||
| group = atoi(&p[0]); | |||||
| if (group >= N_LETTER_GROUPS) { | if (group >= N_LETTER_GROUPS) { | ||||
| fprintf(f_log, "%5d: lettergroup out of range (01-%.2d)\n", linenum, N_LETTER_GROUPS-1); | fprintf(f_log, "%5d: lettergroup out of range (01-%.2d)\n", linenum, N_LETTER_GROUPS-1); | ||||
| error_count++; | error_count++; | ||||
| } | } | ||||
| letterGroupsDefined[group] = 1; | letterGroupsDefined[group] = 1; | ||||
| int n_items = 0; | |||||
| n_items = 0; | |||||
| while (n_items < N_LETTERGP_ITEMS) { | while (n_items < N_LETTERGP_ITEMS) { | ||||
| while (isspace2(*p)) p++; | while (isspace2(*p)) p++; | ||||
| if (*p == 0) | if (*p == 0) | ||||
| // write out the items, longest first | // write out the items, longest first | ||||
| while (max_length > 1) { | while (max_length > 1) { | ||||
| for (int ix = 0; ix < n_items; ix++) { | |||||
| for (ix = 0; ix < n_items; ix++) { | |||||
| if (item_length[ix] == max_length) | if (item_length[ix] == max_length) | ||||
| fwrite(items[ix], 1, max_length, f_out); | fwrite(items[ix], 1, max_length, f_out); | ||||
| } | } | ||||
| FILE *f_in; | FILE *f_in; | ||||
| FILE *f_out; | FILE *f_out; | ||||
| int offset_rules = 0; | |||||
| int value; | |||||
| char fname_in[sizeof(path_home)+45]; | char fname_in[sizeof(path_home)+45]; | ||||
| char fname_out[sizeof(path_home)+15]; | char fname_out[sizeof(path_home)+15]; | ||||
| char fname_temp[sizeof(path_home)+15]; | char fname_temp[sizeof(path_home)+15]; | ||||
| } | } | ||||
| sprintf(fname_temp, "%s%ctemp", path_home, PATHSEP); | sprintf(fname_temp, "%s%ctemp", path_home, PATHSEP); | ||||
| int value = N_HASH_DICT; | |||||
| int offset_rules = 0; | |||||
| value = N_HASH_DICT; | |||||
| Write4Bytes(f_out, value); | Write4Bytes(f_out, value); | ||||
| Write4Bytes(f_out, offset_rules); | Write4Bytes(f_out, offset_rules); | ||||
| static unsigned int StringToWord(const char *string) | static unsigned int StringToWord(const char *string) | ||||
| { | { | ||||
| // Pack 4 characters into a word | // Pack 4 characters into a word | ||||
| int ix; | |||||
| unsigned char c; | unsigned char c; | ||||
| unsigned int word; | unsigned int word; | ||||
| return 0; | return 0; | ||||
| word = 0; | word = 0; | ||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| if (string[ix] == 0) break; | if (string[ix] == 0) break; | ||||
| c = string[ix]; | c = string[ix]; | ||||
| word |= (c << (ix*8)); | word |= (c << (ix*8)); |
| { | { | ||||
| // reverse the order of bytes from little-endian to big-endian | // reverse the order of bytes from little-endian to big-endian | ||||
| #ifdef ARCH_BIG | #ifdef ARCH_BIG | ||||
| int ix; | |||||
| int word2 = 0; | int word2 = 0; | ||||
| for (int ix = 0; ix <= 24; ix += 8) { | |||||
| for (ix = 0; ix <= 24; ix += 8) { | |||||
| word2 = word2 << 8; | word2 = word2 << 8; | ||||
| word2 |= (word >> ix) & 0xff; | word2 |= (word >> ix) & 0xff; | ||||
| } | } | ||||
| int LoadDictionary(Translator *tr, const char *name, int no_error) | int LoadDictionary(Translator *tr, const char *name, int no_error) | ||||
| { | { | ||||
| int hash; | |||||
| char *p; | char *p; | ||||
| int *pw; | int *pw; | ||||
| int length; | |||||
| FILE *f; | FILE *f; | ||||
| unsigned int size; | |||||
| char fname[sizeof(path_home)+20]; | char fname[sizeof(path_home)+20]; | ||||
| strcpy(dictionary_name, name); // currently loaded dictionary name | strcpy(dictionary_name, name); // currently loaded dictionary name | ||||
| // bytes 0-3: offset to rules data | // bytes 0-3: offset to rules data | ||||
| // bytes 4-7: number of hash table entries | // bytes 4-7: number of hash table entries | ||||
| sprintf(fname, "%s%c%s_dict", path_home, PATHSEP, name); | sprintf(fname, "%s%c%s_dict", path_home, PATHSEP, name); | ||||
| unsigned int size = GetFileLength(fname); | |||||
| size = GetFileLength(fname); | |||||
| if (tr->data_dictlist != NULL) { | if (tr->data_dictlist != NULL) { | ||||
| Free(tr->data_dictlist); | Free(tr->data_dictlist); | ||||
| fclose(f); | fclose(f); | ||||
| pw = (int *)(tr->data_dictlist); | pw = (int *)(tr->data_dictlist); | ||||
| int length = Reverse4Bytes(pw[1]); | |||||
| length = Reverse4Bytes(pw[1]); | |||||
| if (size <= (N_HASH_DICT + sizeof(int)*2)) { | if (size <= (N_HASH_DICT + sizeof(int)*2)) { | ||||
| fprintf(stderr, "Empty _dict file: '%s\n", fname); | fprintf(stderr, "Empty _dict file: '%s\n", fname); | ||||
| // set up hash table for data_dictlist | // set up hash table for data_dictlist | ||||
| p = &(tr->data_dictlist[8]); | p = &(tr->data_dictlist[8]); | ||||
| for (int hash = 0; hash < N_HASH_DICT; hash++) { | |||||
| for (hash = 0; hash < N_HASH_DICT; hash++) { | |||||
| tr->dict_hashtab[hash] = p; | tr->dict_hashtab[hash] = p; | ||||
| while ((length = *p) != 0) | while ((length = *p) != 0) | ||||
| p += length; | p += length; | ||||
| */ | */ | ||||
| const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme) | const char *EncodePhonemes(const char *p, char *outptr, int *bad_phoneme) | ||||
| { | { | ||||
| int ix; | |||||
| unsigned char c; | unsigned char c; | ||||
| int count; // num. of matching characters | int count; // num. of matching characters | ||||
| int max; // highest num. of matching found so far | int max; // highest num. of matching found so far | ||||
| max = -1; | max = -1; | ||||
| max_ph = 0; | max_ph = 0; | ||||
| for (int ix = 1; ix < n_phoneme_tab; ix++) { | |||||
| for (ix = 1; ix < n_phoneme_tab; ix++) { | |||||
| if (phoneme_tab[ix] == NULL) | if (phoneme_tab[ix] == NULL) | ||||
| continue; | continue; | ||||
| if (phoneme_tab[ix]->type == phINVALID) | if (phoneme_tab[ix]->type == phINVALID) | ||||
| char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags) | char *WritePhMnemonic(char *phon_out, PHONEME_TAB *ph, PHONEME_LIST *plist, int use_ipa, int *flags) | ||||
| { | { | ||||
| int c; | int c; | ||||
| int mnem; | |||||
| int len; | |||||
| int first; | |||||
| int ix = 0; | int ix = 0; | ||||
| char *p; | char *p; | ||||
| PHONEME_DATA phdata; | PHONEME_DATA phdata; | ||||
| p++; | p++; | ||||
| } | } | ||||
| int len = strlen(p); | |||||
| len = strlen(p); | |||||
| if (len > 0) { | if (len > 0) { | ||||
| strcpy(phon_out, p); | strcpy(phon_out, p); | ||||
| phon_out += len; | phon_out += len; | ||||
| } | } | ||||
| } | } | ||||
| int first = 1; | |||||
| for (int mnem = ph->mnemonic; (c = mnem & 0xff) != 0; mnem = mnem >> 8) { | |||||
| first = 1; | |||||
| for (mnem = ph->mnemonic; (c = mnem & 0xff) != 0; mnem = mnem >> 8) { | |||||
| if ((c == '/') && (option_phoneme_variants == 0)) | if ((c == '/') && (option_phoneme_variants == 0)) | ||||
| break; // discard phoneme variant indicator | break; // discard phoneme variant indicator | ||||
| */ | */ | ||||
| int ix; | |||||
| unsigned int len; | unsigned int len; | ||||
| int phon_out_ix = 0; | int phon_out_ix = 0; | ||||
| int stress; | int stress; | ||||
| use_tie = 0; | use_tie = 0; | ||||
| } | } | ||||
| for (int ix = 1; ix < (n_phoneme_list-2); ix++) { | |||||
| for (ix = 1; ix < (n_phoneme_list-2); ix++) { | |||||
| buf = phon_buf; | buf = phon_buf; | ||||
| plist = &phoneme_list[ix]; | plist = &phoneme_list[ix]; | ||||
| static int Unpronouncable2(Translator *tr, char *word) | static int Unpronouncable2(Translator *tr, char *word) | ||||
| { | { | ||||
| int c; | |||||
| int end_flags; | |||||
| char ph_buf[N_WORD_PHONEMES]; | char ph_buf[N_WORD_PHONEMES]; | ||||
| ph_buf[0] = 0; | ph_buf[0] = 0; | ||||
| int c = word[-1]; | |||||
| c = word[-1]; | |||||
| word[-1] = ' '; // ensure there is a space before the "word" | word[-1] = ' '; // ensure there is a space before the "word" | ||||
| int end_flags = TranslateRules(tr, word, ph_buf, sizeof(ph_buf), NULL, FLAG_UNPRON_TEST, NULL); | |||||
| end_flags = TranslateRules(tr, word, ph_buf, sizeof(ph_buf), NULL, FLAG_UNPRON_TEST, NULL); | |||||
| word[-1] = c; | word[-1] = c; | ||||
| if ((end_flags == 0) || (end_flags & SUFX_UNPRON)) | if ((end_flags == 0) || (end_flags & SUFX_UNPRON)) | ||||
| return 1; | return 1; | ||||
| int c; | int c; | ||||
| int c1 = 0; | int c1 = 0; | ||||
| int vowel_posn = 9; | int vowel_posn = 9; | ||||
| int index; | |||||
| int count; | |||||
| ALPHABET *alphabet; | ALPHABET *alphabet; | ||||
| utf8_in(&c, word); | utf8_in(&c, word); | ||||
| if (((c = *word) == ' ') || (c == 0) || (c == '\'')) | if (((c = *word) == ' ') || (c == 0) || (c == '\'')) | ||||
| return 0; | return 0; | ||||
| int index = 0; | |||||
| int count = 0; | |||||
| index = 0; | |||||
| count = 0; | |||||
| for (;;) { | for (;;) { | ||||
| index += utf8_in(&c, &word[index]); | index += utf8_in(&c, &word[index]); | ||||
| if ((c == 0) || (c == ' ')) | if ((c == 0) || (c == ' ')) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| unsigned char *p; | unsigned char *p; | ||||
| int max_stress; | |||||
| int vowel_count; // num of vowels + 1 | int vowel_count; // num of vowels + 1 | ||||
| int stressed_syllable = 0; // position of stressed syllable | int stressed_syllable = 0; // position of stressed syllable | ||||
| unsigned char phonetic[N_WORD_PHONEMES]; | unsigned char phonetic[N_WORD_PHONEMES]; | ||||
| signed char vowel_stress[N_WORD_PHONEMES/2]; | signed char vowel_stress[N_WORD_PHONEMES/2]; | ||||
| strcpy((char *)phonetic, word); | strcpy((char *)phonetic, word); | ||||
| int max_stress = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 0); | |||||
| max_stress = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 0); | |||||
| if (new_stress >= 4) { | if (new_stress >= 4) { | ||||
| // promote to primary stress | // promote to primary stress | ||||
| int mnem; | int mnem; | ||||
| int opt_length; | int opt_length; | ||||
| int done; | int done; | ||||
| int stressflags; | |||||
| int dflags = 0; | int dflags = 0; | ||||
| int first_primary; | int first_primary; | ||||
| int long_vowel; | int long_vowel; | ||||
| 3 secondary stress | 3 secondary stress | ||||
| 4 main stress */ | 4 main stress */ | ||||
| int stressflags = tr->langopts.stress_flags; | |||||
| stressflags = tr->langopts.stress_flags; | |||||
| if (dictionary_flags != NULL) | if (dictionary_flags != NULL) | ||||
| dflags = dictionary_flags[0]; | dflags = dictionary_flags[0]; | ||||
| { | { | ||||
| // apply after the translation is complete | // apply after the translation is complete | ||||
| int ix; | |||||
| int len; | |||||
| char *p; | char *p; | ||||
| int len = strlen(phonemes); | |||||
| len = strlen(phonemes); | |||||
| if (tr->langopts.param[LOPT_ALT] & 2) { | if (tr->langopts.param[LOPT_ALT] & 2) { | ||||
| for (int ix = 0; ix < (len-1); ix++) { | |||||
| for (ix = 0; ix < (len-1); ix++) { | |||||
| if (phonemes[ix] == phonSTRESS_P) { | if (phonemes[ix] == phonSTRESS_P) { | ||||
| p = &phonemes[ix+1]; | p = &phonemes[ix+1]; | ||||
| if ((dict_flags & FLAG_ALT2_TRANS) != 0) { | if ((dict_flags & FLAG_ALT2_TRANS) != 0) { | ||||
| int c; | int c; | ||||
| int c2; | int c2; | ||||
| int ix; | int ix; | ||||
| int offset; | |||||
| int min; | |||||
| int max; | |||||
| const char *map; | const char *map; | ||||
| char *p = text; | char *p = text; | ||||
| char *p2; | char *p2; | ||||
| int all_alpha = 1; | int all_alpha = 1; | ||||
| int bits; | int bits; | ||||
| int acc; | int acc; | ||||
| int pairs_start; | |||||
| const short *pairs_list; | const short *pairs_list; | ||||
| int bufix; | |||||
| char buf[N_WORD_BYTES+1]; | char buf[N_WORD_BYTES+1]; | ||||
| int offset = tr->transpose_min - 1; | |||||
| int min = tr->transpose_min; | |||||
| int max = tr->transpose_max; | |||||
| offset = tr->transpose_min - 1; | |||||
| min = tr->transpose_min; | |||||
| max = tr->transpose_max; | |||||
| map = tr->transpose_map; | map = tr->transpose_map; | ||||
| int pairs_start = max - min + 2; | |||||
| pairs_start = max - min + 2; | |||||
| int bufix = 0; | |||||
| bufix = 0; | |||||
| do { | do { | ||||
| p += utf8_in(&c, p); | p += utf8_in(&c, p); | ||||
| if (c != 0) { | if (c != 0) { | ||||
| { | { | ||||
| char *p; | char *p; | ||||
| char *next; | char *next; | ||||
| int hash; | |||||
| int phoneme_len; | int phoneme_len; | ||||
| int wlen; | int wlen; | ||||
| unsigned char flag; | unsigned char flag; | ||||
| const char *word_end; | const char *word_end; | ||||
| const char *word1; | const char *word1; | ||||
| int wflags = 0; | int wflags = 0; | ||||
| int lookup_symbol; | |||||
| char word_buf[N_WORD_BYTES+1]; | char word_buf[N_WORD_BYTES+1]; | ||||
| char dict_flags_buf[80]; | char dict_flags_buf[80]; | ||||
| if (wtab != NULL) | if (wtab != NULL) | ||||
| wflags = wtab->flags; | wflags = wtab->flags; | ||||
| int lookup_symbol = flags[1] & FLAG_LOOKUP_SYMBOL; | |||||
| lookup_symbol = flags[1] & FLAG_LOOKUP_SYMBOL; | |||||
| word1 = word; | word1 = word; | ||||
| if (tr->transpose_min > 0) { | if (tr->transpose_min > 0) { | ||||
| strncpy0(word_buf, word, N_WORD_BYTES); | strncpy0(word_buf, word, N_WORD_BYTES); | ||||
| } else | } else | ||||
| wlen = strlen(word); | wlen = strlen(word); | ||||
| int hash = HashDictionary(word); | |||||
| hash = HashDictionary(word); | |||||
| p = tr->dict_hashtab[hash]; | p = tr->dict_hashtab[hash]; | ||||
| if (p == NULL) { | if (p == NULL) { | ||||
| */ | */ | ||||
| int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *flags, int end_flags, WORD_TAB *wtab) | int LookupDictList(Translator *tr, char **wordptr, char *ph_out, unsigned int *flags, int end_flags, WORD_TAB *wtab) | ||||
| { | { | ||||
| int length; | |||||
| const char *found; | const char *found; | ||||
| const char *word1; | const char *word1; | ||||
| const char *word2; | const char *word2; | ||||
| char word[N_WORD_BYTES]; | char word[N_WORD_BYTES]; | ||||
| static char word_replacement[N_WORD_BYTES]; | static char word_replacement[N_WORD_BYTES]; | ||||
| int length = 0; | |||||
| length = 0; | |||||
| word2 = word1 = *wordptr; | word2 = word1 = *wordptr; | ||||
| while ((word2[nbytes = utf8_nbytes(word2)] == ' ') && (word2[nbytes+1] == '.')) { | while ((word2[nbytes = utf8_nbytes(word2)] == ' ') && (word2[nbytes+1] == '.')) { | ||||
| int flags0; | int flags0; | ||||
| unsigned int flags[2]; | unsigned int flags[2]; | ||||
| int say_as; | |||||
| char *word1 = (char *)word; | char *word1 = (char *)word; | ||||
| char text[80]; | char text[80]; | ||||
| flags0 = flags[0]; | flags0 = flags[0]; | ||||
| if (flags[0] & FLAG_TEXTMODE) { | if (flags[0] & FLAG_TEXTMODE) { | ||||
| int say_as = option_sayas; | |||||
| say_as = option_sayas; | |||||
| option_sayas = 0; // don't speak replacement word as letter names | option_sayas = 0; // don't speak replacement word as letter names | ||||
| text[0] = 0; | text[0] = 0; | ||||
| strncpy0(&text[1], word1, sizeof(text)); | strncpy0(&text[1], word1, sizeof(text)); | ||||
| that were done when the suffix was added to the original word. | that were done when the suffix was added to the original word. | ||||
| */ | */ | ||||
| int i; | |||||
| char *word_end; | char *word_end; | ||||
| int len_ending; | |||||
| int end_flags; | |||||
| const char *p; | const char *p; | ||||
| int len; | int len; | ||||
| char ending[50]; | char ending[50]; | ||||
| if (*word_end == REPLACED_E) | if (*word_end == REPLACED_E) | ||||
| *word_end = 'e'; | *word_end = 'e'; | ||||
| } | } | ||||
| int i = word_end - word; | |||||
| i = word_end - word; | |||||
| if (word_copy != NULL) { | if (word_copy != NULL) { | ||||
| memcpy(word_copy, word, i); | memcpy(word_copy, word, i); | ||||
| } | } | ||||
| // look for multibyte characters to increase the number of bytes to remove | // look for multibyte characters to increase the number of bytes to remove | ||||
| int len_ending; | |||||
| for (len_ending = i = (end_type & 0x3f); i > 0; i--) { // num.of characters of the suffix | for (len_ending = i = (end_type & 0x3f); i > 0; i--) { // num.of characters of the suffix | ||||
| word_end--; | word_end--; | ||||
| while ((*word_end & 0xc0) == 0x80) { | while ((*word_end & 0xc0) == 0x80) { | ||||
| ending[i] = 0; | ending[i] = 0; | ||||
| word_end--; // now pointing at last character of stem | word_end--; // now pointing at last character of stem | ||||
| int end_flags = (end_type & 0xfff0) | FLAG_SUFX; | |||||
| end_flags = (end_type & 0xfff0) | FLAG_SUFX; | |||||
| /* add an 'e' to the stem if appropriate, | /* add an 'e' to the stem if appropriate, | ||||
| if stem ends in vowel+consonant | if stem ends in vowel+consonant |
| double ConvertFromIeeeExtended(unsigned char *bytes /* LCN */) | double ConvertFromIeeeExtended(unsigned char *bytes /* LCN */) | ||||
| { | { | ||||
| double f; | double f; | ||||
| int expon; | |||||
| unsigned long hiMant, loMant; | |||||
| int expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); | |||||
| int hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) | |||||
| expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); | |||||
| hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) | |||||
| | ((unsigned long)(bytes[3] & 0xFF) << 16) | | ((unsigned long)(bytes[3] & 0xFF) << 16) | ||||
| | ((unsigned long)(bytes[4] & 0xFF) << 8) | | ((unsigned long)(bytes[4] & 0xFF) << 8) | ||||
| | ((unsigned long)(bytes[5] & 0xFF)); | | ((unsigned long)(bytes[5] & 0xFF)); | ||||
| int loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) | |||||
| loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) | |||||
| | ((unsigned long)(bytes[7] & 0xFF) << 16) | | ((unsigned long)(bytes[7] & 0xFF) << 16) | ||||
| | ((unsigned long)(bytes[8] & 0xFF) << 8) | | ((unsigned long)(bytes[8] & 0xFF) << 8) | ||||
| | ((unsigned long)(bytes[9] & 0xFF)); | | ((unsigned long)(bytes[9] & 0xFF)); |
| static void count_pitch_vowels(int start, int end, int clause_end) | static void count_pitch_vowels(int start, int end, int clause_end) | ||||
| { | { | ||||
| int ix; | |||||
| int stress; | int stress; | ||||
| int max_stress = 0; | int max_stress = 0; | ||||
| int max_stress_posn = 0; // last syllable ot the highest stress | int max_stress_posn = 0; // last syllable ot the highest stress | ||||
| number_tail = 0; // number between tonic syllable and next primary | number_tail = 0; // number between tonic syllable and next primary | ||||
| last_primary = -1; | last_primary = -1; | ||||
| for (int ix = start; ix < end; ix++) { | |||||
| for (ix = start; ix < end; ix++) { | |||||
| stress = syllable_tab[ix].stress; // marked stress level | stress = syllable_tab[ix].stress; // marked stress level | ||||
| if (stress >= max_stress) { | if (stress >= max_stress) { | ||||
| // Set the pitch of a vowel in syllable_tab | // Set the pitch of a vowel in syllable_tab | ||||
| static void set_pitch(SYLLABLE *syl, int base, int drop) | static void set_pitch(SYLLABLE *syl, int base, int drop) | ||||
| { | { | ||||
| int pitch1, pitch2; | |||||
| int flags = 0; | int flags = 0; | ||||
| if (base < 0) base = 0; | if (base < 0) base = 0; | ||||
| int pitch2 = base; | |||||
| pitch2 = base; | |||||
| if (drop < 0) { | if (drop < 0) { | ||||
| flags = SYL_RISE; | flags = SYL_RISE; | ||||
| drop = -drop; | drop = -drop; | ||||
| } | } | ||||
| int pitch1 = pitch2 + drop; | |||||
| pitch1 = pitch2 + drop; | |||||
| if (pitch1 < 0) | if (pitch1 < 0) | ||||
| pitch1 = 0; | pitch1 = 0; | ||||
| static int CountUnstressed(int start, int end, int limit) | static int CountUnstressed(int start, int end, int limit) | ||||
| { | { | ||||
| int ix; | |||||
| int ix; | |||||
| for (ix = start; ix <= end; ix++) { | for (ix = start; ix <= end; ix++) { | ||||
| if (syllable_tab[ix].stress >= limit) | if (syllable_tab[ix].stress >= limit) | ||||
| break; | break; | ||||
| int increment = 0; | int increment = 0; | ||||
| int n_steps = 0; | int n_steps = 0; | ||||
| int stage; // onset, head, last | int stage; // onset, head, last | ||||
| int initial; | |||||
| int overflow_ix = 0; | int overflow_ix = 0; | ||||
| int pitch_range; | |||||
| int pitch_range_abs; | |||||
| int *drops; | int *drops; | ||||
| int n_unstressed = 0; | int n_unstressed = 0; | ||||
| int unstressed_ix = 0; | int unstressed_ix = 0; | ||||
| int head_final = end_ix; | int head_final = end_ix; | ||||
| int secondary = 2; | int secondary = 2; | ||||
| int pitch_range = (tune->head_end - tune->head_start) << 8; | |||||
| int pitch_range_abs = abs(pitch_range); | |||||
| pitch_range = (tune->head_end - tune->head_start) << 8; | |||||
| pitch_range_abs = abs(pitch_range); | |||||
| drops = drops_0; // this should be controled by tune->head_drops | drops = drops_0; // this should be controled by tune->head_drops | ||||
| int initial = 1; | |||||
| initial = 1; | |||||
| stage = 0; | stage = 0; | ||||
| if (tune->onset == 255) | if (tune->onset == 255) | ||||
| int initial; | int initial; | ||||
| int overflow = 0; | int overflow = 0; | ||||
| int n_overflow; | int n_overflow; | ||||
| int pitch_range; | |||||
| int pitch_range_abs; | |||||
| int *drops; | int *drops; | ||||
| signed char *overflow_tab; | signed char *overflow_tab; | ||||
| SYLLABLE *syl; | SYLLABLE *syl; | ||||
| static signed char continue_tab[5] = { -26, 32, 20, 8, 0 }; | static signed char continue_tab[5] = { -26, 32, 20, 8, 0 }; | ||||
| drops = th->body_drops; | drops = th->body_drops; | ||||
| int pitch_range = (th->body_end - th->body_start) << 8; | |||||
| int pitch_range_abs = abs(pitch_range); | |||||
| pitch_range = (th->body_end - th->body_start) << 8; | |||||
| pitch_range_abs = abs(pitch_range); | |||||
| if (continuing) { | if (continuing) { | ||||
| initial = 0; | initial = 0; | ||||
| // Set a linear pitch change over a number of syllables. | // Set a linear pitch change over a number of syllables. | ||||
| // Used for pre-head, unstressed syllables in the body, and the tail | // Used for pre-head, unstressed syllables in the body, and the tail | ||||
| int ix; | |||||
| int stress; | int stress; | ||||
| int pitch; | |||||
| int increment; | |||||
| int n_increments; | |||||
| int drop; | int drop; | ||||
| SYLLABLE *syl; | SYLLABLE *syl; | ||||
| int increment = (end_pitch - start_pitch) << 8; | |||||
| int n_increments = end_ix - start_ix; | |||||
| increment = (end_pitch - start_pitch) << 8; | |||||
| n_increments = end_ix - start_ix; | |||||
| if (n_increments <= 0) | if (n_increments <= 0) | ||||
| return; | return; | ||||
| if (n_increments > 1) | if (n_increments > 1) | ||||
| increment = increment / n_increments; | increment = increment / n_increments; | ||||
| int pitch = start_pitch << 8; | |||||
| pitch = start_pitch << 8; | |||||
| for (int ix = start_ix; ix < end_ix; ix++) { | |||||
| for (ix = start_ix; ix < end_ix; ix++) { | |||||
| syl = &syllable_tab[ix]; | syl = &syllable_tab[ix]; | ||||
| stress = syl->stress; | stress = syl->stress; | ||||
| // Calculate pitch values for the vowels in this tone group | // Calculate pitch values for the vowels in this tone group | ||||
| static int calc_pitches2(int start, int end, int tune_number) | static int calc_pitches2(int start, int end, int tune_number) | ||||
| { | { | ||||
| int ix; | |||||
| TUNE *tune; | TUNE *tune; | ||||
| int drop; | |||||
| tune = &tunes[tune_number]; | tune = &tunes[tune_number]; | ||||
| int ix = start; | |||||
| ix = start; | |||||
| // vowels before the first primary stress | // vowels before the first primary stress | ||||
| // tonic syllable | // tonic syllable | ||||
| int drop; | |||||
| if (number_tail == 0) { | if (number_tail == 0) { | ||||
| tone_pitch_env = tune->nucleus0_env; | tone_pitch_env = tune->nucleus0_env; | ||||
| drop = tune->nucleus0_max - tune->nucleus0_min; | drop = tune->nucleus0_max - tune->nucleus0_min; | ||||
| // Calculate pitch values for the vowels in this tone group | // Calculate pitch values for the vowels in this tone group | ||||
| static int calc_pitches(int control, int start, int end, int tune_number) | static int calc_pitches(int control, int start, int end, int tune_number) | ||||
| { | { | ||||
| int ix; | |||||
| TONE_HEAD *th; | TONE_HEAD *th; | ||||
| TONE_NUCLEUS *tn; | TONE_NUCLEUS *tn; | ||||
| int drop; | |||||
| int continuing = 0; | int continuing = 0; | ||||
| if (control == 0) | if (control == 0) | ||||
| th = &tone_head_table[tune_number]; | th = &tone_head_table[tune_number]; | ||||
| tn = &tone_nucleus_table[tune_number]; | tn = &tone_nucleus_table[tune_number]; | ||||
| int ix = start; | |||||
| ix = start; | |||||
| // vowels before the first primary stress | // vowels before the first primary stress | ||||
| if (tn->flags & T_EMPH) | if (tn->flags & T_EMPH) | ||||
| syllable_tab[ix].flags |= SYL_EMPHASIS; | syllable_tab[ix].flags |= SYL_EMPHASIS; | ||||
| int drop; | |||||
| if (number_tail == 0) { | if (number_tail == 0) { | ||||
| tone_pitch_env = tn->pitch_env0; | tone_pitch_env = tn->pitch_env0; | ||||
| drop = tn->tonic_max0 - tn->tonic_min0; | drop = tn->tonic_max0 - tn->tonic_min0; | ||||
| // clause_tone: 0=. 1=, 2=?, 3=! 4=none | // clause_tone: 0=. 1=, 2=?, 3=! 4=none | ||||
| PHONEME_LIST *p; | PHONEME_LIST *p; | ||||
| int ix; | |||||
| int count_stressed = 0; | int count_stressed = 0; | ||||
| int final_stressed = 0; | int final_stressed = 0; | ||||
| // count number of stressed syllables | // count number of stressed syllables | ||||
| p = &phoneme_list[0]; | p = &phoneme_list[0]; | ||||
| for (int ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| for (ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| if ((p->type == phVOWEL) && (p->stresslevel >= 4)) { | if ((p->type == phVOWEL) && (p->stresslevel >= 4)) { | ||||
| if (count_stressed == 0) | if (count_stressed == 0) | ||||
| final_stressed = ix; | final_stressed = ix; | ||||
| prev_tph = prevw_tph = phoneme_tab[phonPAUSE]; | prev_tph = prevw_tph = phoneme_tab[phonPAUSE]; | ||||
| // perform tone sandhi | // perform tone sandhi | ||||
| for (int ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| for (ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| if ((p->type == phPAUSE) && (p->ph->std_length > 50)) { | if ((p->type == phPAUSE) && (p->ph->std_length > 50)) { | ||||
| pause = 1; // there is a pause since the previous vowel | pause = 1; // there is a pause since the previous vowel | ||||
| prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone | prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone | ||||
| // convert tone numbers to pitch | // convert tone numbers to pitch | ||||
| p = &phoneme_list[0]; | p = &phoneme_list[0]; | ||||
| for (int ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| for (ix = 0; ix < n_phoneme_list; ix++, p++) { | |||||
| if (p->synthflags & SFLAG_SYLLABLE) { | if (p->synthflags & SFLAG_SYLLABLE) { | ||||
| tone_ph = p->tone_ph; | tone_ph = p->tone_ph; | ||||
| int ix; | int ix; | ||||
| int x; | int x; | ||||
| int st_ix; | int st_ix; | ||||
| int n_st; | |||||
| int option; | int option; | ||||
| int group_tone; | int group_tone; | ||||
| int group_tone_comma; | int group_tone_comma; | ||||
| int ph_start = 0; | int ph_start = 0; | ||||
| int st_start; | |||||
| int st_clause_end; | int st_clause_end; | ||||
| int count; | int count; | ||||
| int n_primary; | |||||
| int count_primary; | |||||
| PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
| int ph_end = n_phoneme_list; | int ph_end = n_phoneme_list; | ||||
| SYLLABLE syllable_tab2[N_PHONEME_LIST]; | SYLLABLE syllable_tab2[N_PHONEME_LIST]; | ||||
| syllable_tab = syllable_tab2; // don't use permanent storage. it's only needed during the call of CalcPitches() | syllable_tab = syllable_tab2; // don't use permanent storage. it's only needed during the call of CalcPitches() | ||||
| int n_st = 0; | |||||
| int n_primary = 0; | |||||
| n_st = 0; | |||||
| n_primary = 0; | |||||
| for (ix = 0; ix < (n_phoneme_list-1); ix++) { | for (ix = 0; ix < (n_phoneme_list-1); ix++) { | ||||
| p = &phoneme_list[ix]; | p = &phoneme_list[ix]; | ||||
| if (p->synthflags & SFLAG_SYLLABLE) { | if (p->synthflags & SFLAG_SYLLABLE) { | ||||
| else | else | ||||
| no_tonic = 0; | no_tonic = 0; | ||||
| int st_start = 0; | |||||
| int count_primary = 0; | |||||
| st_start = 0; | |||||
| count_primary = 0; | |||||
| for (st_ix = 0; st_ix < n_st; st_ix++) { | for (st_ix = 0; st_ix < n_st; st_ix++) { | ||||
| syl = &syllable_tab[st_ix]; | syl = &syllable_tab[st_ix]; | ||||
| static double resonator(resonator_ptr r, double input) | static double resonator(resonator_ptr r, double input) | ||||
| { | { | ||||
| double x = (double)((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2); | |||||
| double x; | |||||
| x = (double)((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2); | |||||
| r->p2 = (double)r->p1; | r->p2 = (double)r->p1; | ||||
| r->p1 = (double)x; | r->p1 = (double)x; | ||||
| static double resonator2(resonator_ptr r, double input) | static double resonator2(resonator_ptr r, double input) | ||||
| { | { | ||||
| double x = (double)((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2); | |||||
| double x; | |||||
| x = (double)((double)r->a * (double)input + (double)r->b * (double)r->p1 + (double)r->c * (double)r->p2); | |||||
| r->p2 = (double)r->p1; | r->p2 = (double)r->p1; | ||||
| r->p1 = (double)x; | r->p1 = (double)x; | ||||
| static void flutter(klatt_frame_ptr frame) | static void flutter(klatt_frame_ptr frame) | ||||
| { | { | ||||
| static int time_count; | static int time_count; | ||||
| double fla = (double)kt_globals.f0_flutter / 50; | |||||
| double flb = (double)kt_globals.original_f0 / 100; | |||||
| double flc = sin(PI*12.7*time_count); // because we are calling flutter() more frequently, every 2.9mS | |||||
| double fld = sin(PI*7.1*time_count); | |||||
| double fle = sin(PI*4.7*time_count); | |||||
| double delta_f0 = fla * flb * (flc + fld + fle) * 10; | |||||
| double delta_f0; | |||||
| double fla, flb, flc, fld, fle; | |||||
| fla = (double)kt_globals.f0_flutter / 50; | |||||
| flb = (double)kt_globals.original_f0 / 100; | |||||
| flc = sin(PI*12.7*time_count); // because we are calling flutter() more frequently, every 2.9mS | |||||
| fld = sin(PI*7.1*time_count); | |||||
| fle = sin(PI*4.7*time_count); | |||||
| delta_f0 = fla * flb * (flc + fld + fle) * 10; | |||||
| frame->F0hz10 = frame->F0hz10 + (long)delta_f0; | frame->F0hz10 = frame->F0hz10 + (long)delta_f0; | ||||
| time_count++; | time_count++; | ||||
| } | } | ||||
| static double sampled_source(int source_num) | static double sampled_source(int source_num) | ||||
| { | { | ||||
| int itemp; | |||||
| double ftemp; | |||||
| double result; | double result; | ||||
| double diff_value; | |||||
| int current_value; | |||||
| int next_value; | |||||
| double temp_diff; | |||||
| short *samples; | short *samples; | ||||
| if (source_num == 0) { | if (source_num == 0) { | ||||
| } | } | ||||
| if (kt_globals.T0 != 0) { | if (kt_globals.T0 != 0) { | ||||
| double ftemp = (double)kt_globals.nper; | |||||
| ftemp = (double)kt_globals.nper; | |||||
| ftemp = ftemp / kt_globals.T0; | ftemp = ftemp / kt_globals.T0; | ||||
| ftemp = ftemp * kt_globals.num_samples; | ftemp = ftemp * kt_globals.num_samples; | ||||
| int itemp = (int)ftemp; | |||||
| itemp = (int)ftemp; | |||||
| double temp_diff = ftemp - (double)itemp; | |||||
| temp_diff = ftemp - (double)itemp; | |||||
| int current_value = samples[itemp]; | |||||
| int next_value = samples[itemp+1]; | |||||
| current_value = samples[itemp]; | |||||
| next_value = samples[itemp+1]; | |||||
| double diff_value = (double)next_value - (double)current_value; | |||||
| diff_value = (double)next_value - (double)current_value; | |||||
| diff_value = diff_value * temp_diff; | diff_value = diff_value * temp_diff; | ||||
| result = samples[itemp] + diff_value; | result = samples[itemp] + diff_value; | ||||
| static double natural_source() | static double natural_source() | ||||
| { | { | ||||
| double lgtemp; | |||||
| static double vwave; | static double vwave; | ||||
| if (kt_globals.nper < kt_globals.nopen) { | if (kt_globals.nper < kt_globals.nopen) { | ||||
| kt_globals.pulse_shape_a -= kt_globals.pulse_shape_b; | kt_globals.pulse_shape_a -= kt_globals.pulse_shape_b; | ||||
| vwave += kt_globals.pulse_shape_a; | vwave += kt_globals.pulse_shape_a; | ||||
| double lgtemp = vwave * 0.028; | |||||
| lgtemp = vwave * 0.028; | |||||
| return lgtemp; | return lgtemp; | ||||
| } | } | ||||
| static void setabc(long int f, long int bw, resonator_ptr rp) | static void setabc(long int f, long int bw, resonator_ptr rp) | ||||
| { | { | ||||
| double r; | |||||
| double arg; | |||||
| // Let r = exp(-pi bw t) | // Let r = exp(-pi bw t) | ||||
| double arg = kt_globals.minus_pi_t * bw; | |||||
| double r = exp(arg); | |||||
| arg = kt_globals.minus_pi_t * bw; | |||||
| r = exp(arg); | |||||
| // Let c = -r**2 | // Let c = -r**2 | ||||
| rp->c = -(r * r); | rp->c = -(r * r); | ||||
| static void setzeroabc(long int f, long int bw, resonator_ptr rp) | static void setzeroabc(long int f, long int bw, resonator_ptr rp) | ||||
| { | { | ||||
| double r; | |||||
| double arg; | |||||
| f = -f; | f = -f; | ||||
| // First compute ordinary resonator coefficients | // First compute ordinary resonator coefficients | ||||
| // Let r = exp(-pi bw t) | // Let r = exp(-pi bw t) | ||||
| double arg = kt_globals.minus_pi_t * bw; | |||||
| double r = exp(arg); | |||||
| arg = kt_globals.minus_pi_t * bw; | |||||
| r = exp(arg); | |||||
| // Let c = -r**2 | // Let c = -r**2 | ||||
| rp->c = -(r * r); | rp->c = -(r * r); | ||||
| static double gen_noise(double noise) | static double gen_noise(double noise) | ||||
| { | { | ||||
| long temp; | |||||
| static double nlast; | static double nlast; | ||||
| long temp = (long)getrandom(-8191, 8191); | |||||
| temp = (long)getrandom(-8191, 8191); | |||||
| kt_globals.nrand = (long)temp; | kt_globals.nrand = (long)temp; | ||||
| noise = kt_globals.nrand + (0.75 * nlast); | noise = kt_globals.nrand + (0.75 * nlast); | ||||
| void KlattInit() | void KlattInit() | ||||
| { | { | ||||
| static short formant_hz[10] = { 280, 688, 1064, 2806, 3260, 3700, 6500, 7000, 8000, 280 }; | static short formant_hz[10] = { 280, 688, 1064, 2806, 3260, 3700, 6500, 7000, 8000, 280 }; | ||||
| static short bandwidth[10] = { 89, 160, 70, 160, 200, 200, 500, 500, 500, 89 }; | static short bandwidth[10] = { 89, 160, 70, 160, 200, 200, 500, 500, 500, 89 }; | ||||
| static short parallel_amp[10] = { 0, 59, 59, 59, 59, 59, 59, 0, 0, 0 }; | static short parallel_amp[10] = { 0, 59, 59, 59, 59, 59, 59, 0, 0, 0 }; | ||||
| static short parallel_bw[10] = { 59, 59, 89, 149, 200, 200, 500, 0, 0, 0 }; | static short parallel_bw[10] = { 59, 59, 89, 149, 200, 200, 500, 0, 0, 0 }; | ||||
| int ix; | |||||
| sample_count = 0; | sample_count = 0; | ||||
| kt_globals.synthesis_model = CASCADE_PARALLEL; | kt_globals.synthesis_model = CASCADE_PARALLEL; | ||||
| KlattReset(2); | KlattReset(2); | ||||
| // set default values for frame parameters | // set default values for frame parameters | ||||
| for (int ix = 0; ix <= 9; ix++) { | |||||
| for (ix = 0; ix <= 9; ix++) { | |||||
| kt_frame.Fhz[ix] = formant_hz[ix]; | kt_frame.Fhz[ix] = formant_hz[ix]; | ||||
| kt_frame.Bhz[ix] = bandwidth[ix]; | kt_frame.Bhz[ix] = bandwidth[ix]; | ||||
| kt_frame.Ap[ix] = parallel_amp[ix]; | kt_frame.Ap[ix] = parallel_amp[ix]; |
| static int start_mbrola(const char *voice_path) | static int start_mbrola(const char *voice_path) | ||||
| { | { | ||||
| int p_stdin[2], p_stdout[2], p_stderr[2]; | |||||
| int error, p_stdin[2], p_stdout[2], p_stderr[2]; | |||||
| ssize_t written; | ssize_t written; | ||||
| char charbuf[20]; | char charbuf[20]; | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| int error = create_pipes(p_stdin, p_stdout, p_stderr); | |||||
| error = create_pipes(p_stdin, p_stdout, p_stderr); | |||||
| if (error) | if (error) | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| if (mbr_pid == 0) { | if (mbr_pid == 0) { | ||||
| int i; | |||||
| if (dup2(p_stdin[0], 0) == -1 || | if (dup2(p_stdin[0], 0) == -1 || | ||||
| dup2(p_stdout[1], 1) == -1 || | dup2(p_stdout[1], 1) == -1 || | ||||
| dup2(p_stderr[1], 2) == -1) { | dup2(p_stderr[1], 2) == -1) { | ||||
| (void)written; // suppress 'variable not used' warning | (void)written; // suppress 'variable not used' warning | ||||
| _exit(1); | _exit(1); | ||||
| } | } | ||||
| for (int i = p_stderr[1]; i > 2; i--) | |||||
| for (i = p_stderr[1]; i > 2; i--) | |||||
| close(i); | close(i); | ||||
| signal(SIGHUP, SIG_IGN); | signal(SIGHUP, SIG_IGN); | ||||
| signal(SIGINT, SIG_IGN); | signal(SIGINT, SIG_IGN); | ||||
| static int mbrola_died(void) | static int mbrola_died(void) | ||||
| { | { | ||||
| pid_t pid; | pid_t pid; | ||||
| int status; | |||||
| int status, len; | |||||
| const char *msg; | const char *msg; | ||||
| char msgbuf[80]; | char msgbuf[80]; | ||||
| log("mbrowrap error: %s", msg); | log("mbrowrap error: %s", msg); | ||||
| size_t len = strlen(mbr_errorbuf); | |||||
| len = strlen(mbr_errorbuf); | |||||
| if (!len) | if (!len) | ||||
| snprintf(mbr_errorbuf, sizeof(mbr_errorbuf), "%s", msg); | snprintf(mbr_errorbuf, sizeof(mbr_errorbuf), "%s", msg); | ||||
| else | else | ||||
| static int send_to_mbrola(const char *cmd) | static int send_to_mbrola(const char *cmd) | ||||
| { | { | ||||
| ssize_t result; | ssize_t result; | ||||
| int len; | |||||
| if (!mbr_pid) | if (!mbr_pid) | ||||
| return -1; | return -1; | ||||
| int len = strlen(cmd); | |||||
| len = strlen(cmd); | |||||
| result = write(mbr_cmd_fd, cmd, len); | result = write(mbr_cmd_fd, cmd, len); | ||||
| if (result == -1) { | if (result == -1) { | ||||
| int init_MBR(const char *voice_path) | int init_MBR(const char *voice_path) | ||||
| { | { | ||||
| int error, result; | |||||
| unsigned char wavhdr[45]; | unsigned char wavhdr[45]; | ||||
| int error = start_mbrola(voice_path); | |||||
| error = start_mbrola(voice_path); | |||||
| if (error) | if (error) | ||||
| return -1; | return -1; | ||||
| int result = send_to_mbrola("#\n"); | |||||
| result = send_to_mbrola("#\n"); | |||||
| if (result != 2) { | if (result != 2) { | ||||
| stop_mbrola(); | stop_mbrola(); | ||||
| return -1; | return -1; | ||||
| int reset_MBR() | int reset_MBR() | ||||
| { | { | ||||
| int success = 1; | |||||
| int result, success = 1; | |||||
| char dummybuf[4096]; | char dummybuf[4096]; | ||||
| if (mbr_state == MBR_IDLE) | if (mbr_state == MBR_IDLE) | ||||
| if (kill(mbr_pid, SIGUSR1) == -1) | if (kill(mbr_pid, SIGUSR1) == -1) | ||||
| success = 0; | success = 0; | ||||
| free_pending_data(); | free_pending_data(); | ||||
| int result = write(mbr_cmd_fd, "\n#\n", 3); | |||||
| result = write(mbr_cmd_fd, "\n#\n", 3); | |||||
| if (result != 3) | if (result != 3) | ||||
| success = 0; | success = 0; | ||||
| do { | do { | ||||
| int lastErrorStr_MBR(char *buffer, int bufsize) | int lastErrorStr_MBR(char *buffer, int bufsize) | ||||
| { | { | ||||
| int result; | |||||
| if (mbr_pid) | if (mbr_pid) | ||||
| mbrola_has_errors(); | mbrola_has_errors(); | ||||
| int result = snprintf(buffer, bufsize, "%s", mbr_errorbuf); | |||||
| result = snprintf(buffer, bufsize, "%s", mbr_errorbuf); | |||||
| return result >= bufsize ? (bufsize - 1) : result; | return result >= bufsize ? (bufsize - 1) : result; | ||||
| } | } | ||||
| static int LookupLetter2(Translator *tr, unsigned int letter, char *ph_buf) | static int LookupLetter2(Translator *tr, unsigned int letter, char *ph_buf) | ||||
| { | { | ||||
| int len; | |||||
| char single_letter[10]; | char single_letter[10]; | ||||
| single_letter[0] = 0; | single_letter[0] = 0; | ||||
| single_letter[1] = '_'; | single_letter[1] = '_'; | ||||
| int len = utf8_out(letter, &single_letter[2]); | |||||
| len = utf8_out(letter, &single_letter[2]); | |||||
| single_letter[len+2] = ' '; | single_letter[len+2] = ' '; | ||||
| single_letter[len+3] = 0; | single_letter[len+3] = 0; | ||||
| { | { | ||||
| // control, bit 0: not the first letter of a word | // control, bit 0: not the first letter of a word | ||||
| int len; | |||||
| static char single_letter[10] = { 0, 0 }; | static char single_letter[10] = { 0, 0 }; | ||||
| unsigned int dict_flags[2]; | unsigned int dict_flags[2]; | ||||
| char ph_buf3[40]; | char ph_buf3[40]; | ||||
| ph_buf1[0] = 0; | ph_buf1[0] = 0; | ||||
| int len = utf8_out(letter, &single_letter[2]); | |||||
| len = utf8_out(letter, &single_letter[2]); | |||||
| single_letter[len+2] = ' '; | single_letter[len+2] = ' '; | ||||
| if (next_byte == -1) { | if (next_byte == -1) { | ||||
| int IsSuperscript(int letter) | int IsSuperscript(int letter) | ||||
| { | { | ||||
| // is this a subscript or superscript letter ? | // is this a subscript or superscript letter ? | ||||
| int ix; | |||||
| int c; | int c; | ||||
| for (int ix = 0; (c = derived_letters[ix]) != 0; ix += 2) { | |||||
| for (ix = 0; (c = derived_letters[ix]) != 0; ix += 2) { | |||||
| if (c > letter) | if (c > letter) | ||||
| break; | break; | ||||
| if (c == letter) | if (c == letter) | ||||
| // bit 1: say 'capital' | // bit 1: say 'capital' | ||||
| // bit 2: say character code for unknown letters | // bit 2: say character code for unknown letters | ||||
| int n_bytes; | |||||
| int letter; | int letter; | ||||
| int len; | int len; | ||||
| int ix; | int ix; | ||||
| char *pbuf; | char *pbuf; | ||||
| const char *modifier; | const char *modifier; | ||||
| ALPHABET *alphabet; | ALPHABET *alphabet; | ||||
| int al_offset; | |||||
| int al_flags; | |||||
| int language; | int language; | ||||
| int number; | int number; | ||||
| int phontab_1; | |||||
| int speak_letter_number; | int speak_letter_number; | ||||
| char capital[30]; | char capital[30]; | ||||
| char ph_buf[80]; | char ph_buf[80]; | ||||
| ph_buf[0] = 0; | ph_buf[0] = 0; | ||||
| ph_alphabet[0] = 0; | ph_alphabet[0] = 0; | ||||
| capital[0] = 0; | capital[0] = 0; | ||||
| int phontab_1 = translator->phoneme_tab_ix; | |||||
| phontab_1 = translator->phoneme_tab_ix; | |||||
| int n_bytes = utf8_in(&letter, word); | |||||
| n_bytes = utf8_in(&letter, word); | |||||
| if ((letter & 0xfff00) == 0x0e000) | if ((letter & 0xfff00) == 0x0e000) | ||||
| letter &= 0xff; // uncode private usage area | letter &= 0xff; // uncode private usage area | ||||
| LookupLetter(tr, number, 0, ph_buf, control & 1); | LookupLetter(tr, number, 0, ph_buf, control & 1); | ||||
| } | } | ||||
| int al_offset = 0; | |||||
| int al_flags = 0; | |||||
| al_offset = 0; | |||||
| al_flags = 0; | |||||
| if ((alphabet = AlphabetFromChar(letter)) != NULL) { | if ((alphabet = AlphabetFromChar(letter)) != NULL) { | ||||
| al_offset = alphabet->offset; | al_offset = alphabet->offset; | ||||
| al_flags = alphabet->flags; | al_flags = alphabet->flags; | ||||
| unsigned int c; | unsigned int c; | ||||
| int n_stress = 0; | int n_stress = 0; | ||||
| int prev = 0; | int prev = 0; | ||||
| int count; | |||||
| unsigned char buf[N_WORD_PHONEMES]; | unsigned char buf[N_WORD_PHONEMES]; | ||||
| for (ix = 0; (c = phonemes[ix]) != 0; ix++) { | for (ix = 0; (c = phonemes[ix]) != 0; ix++) { | ||||
| } | } | ||||
| buf[ix] = 0; | buf[ix] = 0; | ||||
| int count = 0; | |||||
| count = 0; | |||||
| prev = 0; | prev = 0; | ||||
| for (ix = 0; (c = buf[ix]) != 0; ix++) { | for (ix = 0; (c = buf[ix]) != 0; ix++) { | ||||
| if ((c == phonSTRESS_P) && (n_chars > 1) && (prev != phonSWITCH)) { | if ((c == phonSTRESS_P) && (n_chars > 1) && (prev != phonSWITCH)) { | ||||
| int c; | int c; | ||||
| char *p; | char *p; | ||||
| const char *p2; | const char *p2; | ||||
| int acc; | |||||
| int prev; | |||||
| int value; | int value; | ||||
| int subtract; | |||||
| int repeat = 0; | int repeat = 0; | ||||
| int n_digits = 0; | int n_digits = 0; | ||||
| char *word_start; | char *word_start; | ||||
| static const char *roman_numbers = "ixcmvld"; | static const char *roman_numbers = "ixcmvld"; | ||||
| static int roman_values[] = { 1, 10, 100, 1000, 5, 50, 500 }; | static int roman_values[] = { 1, 10, 100, 1000, 5, 50, 500 }; | ||||
| int acc = 0; | |||||
| int prev = 0; | |||||
| int subtract = 0x7fff; | |||||
| acc = 0; | |||||
| prev = 0; | |||||
| subtract = 0x7fff; | |||||
| ph_out[0] = 0; | ph_out[0] = 0; | ||||
| flags[0] = 0; | flags[0] = 0; | ||||
| flags[1] = 0; | flags[1] = 0; | ||||
| // bit 8 followed by decimal fraction | // bit 8 followed by decimal fraction | ||||
| // bit 9: use #f form for both tens and units (lang=ml) | // bit 9: use #f form for both tens and units (lang=ml) | ||||
| int found; | |||||
| int ix; | int ix; | ||||
| int units; | |||||
| int tens; | |||||
| int is_ordinal; | |||||
| int used_and = 0; | int used_and = 0; | ||||
| int found_ordinal = 0; | int found_ordinal = 0; | ||||
| int next_phtype; | int next_phtype; | ||||
| char ph_digits[50]; | char ph_digits[50]; | ||||
| char ph_and[12]; | char ph_and[12]; | ||||
| int units = value % 10; | |||||
| int tens = value / 10; | |||||
| units = value % 10; | |||||
| tens = value / 10; | |||||
| int found = 0; | |||||
| found = 0; | |||||
| ph_ordinal[0] = 0; | ph_ordinal[0] = 0; | ||||
| ph_tens[0] = 0; | ph_tens[0] = 0; | ||||
| ph_digits[0] = 0; | ph_digits[0] = 0; | ||||
| if (control & 0x20) | if (control & 0x20) | ||||
| ord_type = 'q'; | ord_type = 'q'; | ||||
| int is_ordinal = control & 1; | |||||
| is_ordinal = control & 1; | |||||
| if ((control & 2) && (n_digit_lookup == 2)) { | if ((control & 2) && (n_digit_lookup == 2)) { | ||||
| // pronunciation of the final 2 digits has already been found | // pronunciation of the final 2 digits has already been found | ||||
| // bit 8 followed by decimal fraction | // bit 8 followed by decimal fraction | ||||
| int found; | int found; | ||||
| int hundreds; | |||||
| int tensunits; | |||||
| int x; | int x; | ||||
| int ix; | |||||
| int exact; | |||||
| int ordinal; | |||||
| int tplex; | |||||
| int say_zero_hundred = 0; | int say_zero_hundred = 0; | ||||
| int say_one_hundred; | |||||
| char string[12]; // for looking up entries in **_list | char string[12]; // for looking up entries in **_list | ||||
| char buf1[100]; | char buf1[100]; | ||||
| char buf2[100]; | char buf2[100]; | ||||
| char ph_hundred_and[12]; | char ph_hundred_and[12]; | ||||
| char ph_thousand_and[12]; | char ph_thousand_and[12]; | ||||
| int ordinal = control & 0x22; | |||||
| int hundreds = value / 100; | |||||
| int tensunits = value % 100; | |||||
| ordinal = control & 0x22; | |||||
| hundreds = value / 100; | |||||
| tensunits = value % 100; | |||||
| buf1[0] = 0; | buf1[0] = 0; | ||||
| ph_thousands[0] = 0; | ph_thousands[0] = 0; | ||||
| } else if (hundreds >= 10) { | } else if (hundreds >= 10) { | ||||
| ph_digits[0] = 0; | ph_digits[0] = 0; | ||||
| int exact = 0; | |||||
| exact = 0; | |||||
| if ((value % 1000) == 0) | if ((value % 1000) == 0) | ||||
| exact = 1; | exact = 1; | ||||
| int tplex = thousandplex+1; | |||||
| tplex = thousandplex+1; | |||||
| if (tr->langopts.numbers2 & NUM2_MYRIADS) | if (tr->langopts.numbers2 & NUM2_MYRIADS) | ||||
| tplex = 0; | tplex = 0; | ||||
| if (found) | if (found) | ||||
| ph_100[0] = 0; | ph_100[0] = 0; | ||||
| else { | else { | ||||
| int say_one_hundred = 1; | |||||
| say_one_hundred = 1; | |||||
| if (hundreds == 1) { | if (hundreds == 1) { | ||||
| if ((tr->langopts.numbers & NUM_OMIT_1_HUNDRED) != 0) | if ((tr->langopts.numbers & NUM_OMIT_1_HUNDRED) != 0) | ||||
| say_one_hundred = 0; | say_one_hundred = 0; | ||||
| } | } | ||||
| } else { | } else { | ||||
| if (ph_ordinal2[0] != 0) { | if (ph_ordinal2[0] != 0) { | ||||
| int ix = strlen(buf1); | |||||
| ix = strlen(buf1); | |||||
| if ((ix > 0) && (buf1[ix-1] == phonPAUSE_SHORT)) | if ((ix > 0) && (buf1[ix-1] == phonPAUSE_SHORT)) | ||||
| buf1[ix-1] = 0; // remove pause before addding ordinal suffix | buf1[ix-1] = 0; // remove pause before addding ordinal suffix | ||||
| strcpy(buf2, ph_ordinal2); | strcpy(buf2, ph_ordinal2); |
| { | { | ||||
| // Copy the phonemes list and perform any substitutions that are required for the | // Copy the phonemes list and perform any substitutions that are required for the | ||||
| // current voice | // current voice | ||||
| int ix; | |||||
| int k; | int k; | ||||
| int replace_flags; | int replace_flags; | ||||
| int n_plist_out = 0; | int n_plist_out = 0; | ||||
| PHONEME_LIST2 *plist2; | PHONEME_LIST2 *plist2; | ||||
| PHONEME_TAB *next = NULL; | PHONEME_TAB *next = NULL; | ||||
| for (int ix = 0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++) { | |||||
| for (ix = 0; (ix < n_ph_list2) && (n_plist_out < N_PHONEME_LIST); ix++) { | |||||
| plist2 = &ph_list2[ix]; | plist2 = &ph_list2[ix]; | ||||
| // don't do any substitution if the language has been temporarily changed | // don't do any substitution if the language has been temporarily changed | ||||
| PHONEME_TAB *next, *next2; | PHONEME_TAB *next, *next2; | ||||
| int unstress_count = 0; | int unstress_count = 0; | ||||
| int word_stress = 0; | int word_stress = 0; | ||||
| int current_phoneme_tab; | |||||
| int max_stress; | |||||
| int voicing; | int voicing; | ||||
| int regression; | int regression; | ||||
| int end_sourceix; | |||||
| int alternative; | int alternative; | ||||
| int delete_count; | |||||
| int word_start; | |||||
| int inserted; | int inserted; | ||||
| int deleted; | int deleted; | ||||
| PHONEME_DATA phdata; | PHONEME_DATA phdata; | ||||
| int n_ph_list3; | |||||
| PHONEME_LIST *plist3; | PHONEME_LIST *plist3; | ||||
| PHONEME_LIST *plist3_inserted = NULL; | PHONEME_LIST *plist3_inserted = NULL; | ||||
| PHONEME_LIST ph_list3[N_PHONEME_LIST]; | PHONEME_LIST ph_list3[N_PHONEME_LIST]; | ||||
| memset(&worddata, 0, sizeof(worddata)); | memset(&worddata, 0, sizeof(worddata)); | ||||
| plist2 = ph_list2; | plist2 = ph_list2; | ||||
| phlist = phoneme_list; | phlist = phoneme_list; | ||||
| int end_sourceix = plist2[n_ph_list2-1].sourceix; | |||||
| end_sourceix = plist2[n_ph_list2-1].sourceix; | |||||
| // is the last word of the clause unstressed ? | // is the last word of the clause unstressed ? | ||||
| int max_stress = 0; | |||||
| max_stress = 0; | |||||
| for (j = n_ph_list2-3; j >= 0; j--) { | for (j = n_ph_list2-3; j >= 0; j--) { | ||||
| // start with the last phoneme (before the terminating pauses) and move backwards | // start with the last phoneme (before the terminating pauses) and move backwards | ||||
| if ((plist2[j].stresslevel & 0x7f) > max_stress) | if ((plist2[j].stresslevel & 0x7f) > max_stress) | ||||
| } | } | ||||
| // look for switch of phoneme tables | // look for switch of phoneme tables | ||||
| int delete_count = 0; | |||||
| int current_phoneme_tab = tr->phoneme_tab_ix; | |||||
| delete_count = 0; | |||||
| current_phoneme_tab = tr->phoneme_tab_ix; | |||||
| for (j = 0; j < n_ph_list2; j++) { | for (j = 0; j < n_ph_list2; j++) { | ||||
| if (current_phoneme_tab != tr->phoneme_tab_ix) | if (current_phoneme_tab != tr->phoneme_tab_ix) | ||||
| plist2[j].synthflags |= SFLAG_SWITCHED_LANG; | plist2[j].synthflags |= SFLAG_SWITCHED_LANG; | ||||
| } | } | ||||
| } | } | ||||
| int n_ph_list3 = SubstitutePhonemes(tr, ph_list3) - 2; | |||||
| n_ph_list3 = SubstitutePhonemes(tr, ph_list3) - 2; | |||||
| for (j = 0; (j < n_ph_list3) && (ix < N_PHONEME_LIST-3);) { | for (j = 0; (j < n_ph_list3) && (ix < N_PHONEME_LIST-3);) { | ||||
| if (ph_list3[j].sourceix) { | if (ph_list3[j].sourceix) { | ||||
| // transfer all the phonemes of the clause into phoneme_list | // transfer all the phonemes of the clause into phoneme_list | ||||
| ph = phoneme_tab[phonPAUSE]; | ph = phoneme_tab[phonPAUSE]; | ||||
| ph_list3[0].ph = ph; | ph_list3[0].ph = ph; | ||||
| int word_start = 1; | |||||
| word_start = 1; | |||||
| for (j = 0; insert_ph || ((j < n_ph_list3) && (ix < N_PHONEME_LIST-3)); j++) { | for (j = 0; insert_ph || ((j < n_ph_list3) && (ix < N_PHONEME_LIST-3)); j++) { | ||||
| plist3 = &ph_list3[j]; | plist3 = &ph_list3[j]; |
| int towlower(int c) | int towlower(int c) | ||||
| { | { | ||||
| int x; | int x; | ||||
| int ix; | |||||
| if (c < 0x80) | if (c < 0x80) | ||||
| return tolower(c); | return tolower(c); | ||||
| if (x == 0xfd) { | if (x == 0xfd) { | ||||
| // special cases, lookup translation table | // special cases, lookup translation table | ||||
| for (int ix = 0; wchar_tolower[ix] != 0; ix += 2) { | |||||
| for (ix = 0; wchar_tolower[ix] != 0; ix += 2) { | |||||
| if (wchar_tolower[ix] == c) | if (wchar_tolower[ix] == c) | ||||
| return wchar_tolower[ix+1]; | return wchar_tolower[ix+1]; | ||||
| } | } | ||||
| int towupper(int c) | int towupper(int c) | ||||
| { | { | ||||
| int ix; | |||||
| // check whether a previous character code is the upper-case equivalent of this character | // check whether a previous character code is the upper-case equivalent of this character | ||||
| if (towlower(c-32) == c) | if (towlower(c-32) == c) | ||||
| return c-32; // yes, use it | return c-32; // yes, use it | ||||
| if (towlower(c-1) == c) | if (towlower(c-1) == c) | ||||
| return c-1; | return c-1; | ||||
| for (int ix = 0; wchar_toupper[ix] != 0; ix += 2) { | |||||
| for (ix = 0; wchar_toupper[ix] != 0; ix += 2) { | |||||
| if (wchar_toupper[ix] == c) | if (wchar_toupper[ix] == c) | ||||
| return wchar_toupper[ix+1]; | return wchar_toupper[ix+1]; | ||||
| } | } | ||||
| int towlower2(unsigned int c) | int towlower2(unsigned int c) | ||||
| { | { | ||||
| int x; | int x; | ||||
| int ix; | |||||
| // check for non-standard upper to lower case conversions | // check for non-standard upper to lower case conversions | ||||
| if (c == 'I') { | if (c == 'I') { | ||||
| if (x == 0xfd) { | if (x == 0xfd) { | ||||
| // special cases, lookup translation table | // special cases, lookup translation table | ||||
| for (int ix = 0; wchar_tolower[ix] != 0; ix += 2) { | |||||
| for (ix = 0; wchar_tolower[ix] != 0; ix += 2) { | |||||
| if (wchar_tolower[ix] == (int)c) | if (wchar_tolower[ix] == (int)c) | ||||
| return wchar_tolower[ix+1]; | return wchar_tolower[ix+1]; | ||||
| } | } | ||||
| int towupper2(unsigned int c) | int towupper2(unsigned int c) | ||||
| { | { | ||||
| int ix; | |||||
| if (c > MAX_WALPHA) | if (c > MAX_WALPHA) | ||||
| return towupper(c); | return towupper(c); | ||||
| return c-32; // yes, use it | return c-32; // yes, use it | ||||
| if (towlower2(c-1) == (int)c) | if (towlower2(c-1) == (int)c) | ||||
| return c-1; | return c-1; | ||||
| for (int ix = 0; wchar_toupper[ix] != 0; ix += 2) { | |||||
| for (ix = 0; wchar_toupper[ix] != 0; ix += 2) { | |||||
| if (wchar_toupper[ix] == (int)c) | if (wchar_toupper[ix] == (int)c) | ||||
| return wchar_toupper[ix+1]; | return wchar_toupper[ix+1]; | ||||
| } | } | ||||
| static int GetC_get(void) | static int GetC_get(void) | ||||
| { | { | ||||
| unsigned int c; | unsigned int c; | ||||
| unsigned int c2; | |||||
| if (f_input != NULL) { | if (f_input != NULL) { | ||||
| c = fgetc(f_input); | c = fgetc(f_input); | ||||
| if (feof(f_input)) c = ' '; | if (feof(f_input)) c = ' '; | ||||
| if (option_multibyte == espeakCHARS_16BIT) { | if (option_multibyte == espeakCHARS_16BIT) { | ||||
| unsigned int c2 = fgetc(f_input); | |||||
| c2 = fgetc(f_input); | |||||
| if (feof(f_input)) c2 = 0; | if (feof(f_input)) c2 = 0; | ||||
| c = c + (c2 << 8); | c = c + (c2 << 8); | ||||
| } | } | ||||
| // Returns a unicode wide character | // Returns a unicode wide character | ||||
| // Performs UTF8 checking and conversion | // Performs UTF8 checking and conversion | ||||
| int c; | |||||
| int c1; | int c1; | ||||
| int c2; | int c2; | ||||
| int cbuf[4]; | int cbuf[4]; | ||||
| int ix; | int ix; | ||||
| int n_bytes; | |||||
| static int ungot2 = 0; | static int ungot2 = 0; | ||||
| static const unsigned char mask[4] = { 0xff, 0x1f, 0x0f, 0x07 }; | static const unsigned char mask[4] = { 0xff, 0x1f, 0x0f, 0x07 }; | ||||
| if ((option_multibyte < 2) && (c1 & 0x80)) { | if ((option_multibyte < 2) && (c1 & 0x80)) { | ||||
| // multi-byte utf8 encoding, convert to unicode | // multi-byte utf8 encoding, convert to unicode | ||||
| int n_bytes = 0; | |||||
| n_bytes = 0; | |||||
| if (((c1 & 0xe0) == 0xc0) && ((c1 & 0x1e) != 0)) | if (((c1 & 0xe0) == 0xc0) && ((c1 & 0x1e) != 0)) | ||||
| n_bytes = 1; | n_bytes = 1; | ||||
| n_bytes = 3; | n_bytes = 3; | ||||
| if ((ix = n_bytes) > 0) { | if ((ix = n_bytes) > 0) { | ||||
| int c = c1 & mask[ix]; | |||||
| c = c1 & mask[ix]; | |||||
| while (ix > 0) { | while (ix > 0) { | ||||
| if ((c2 = cbuf[ix] = GetC_get()) == 0) { | if ((c2 = cbuf[ix] = GetC_get()) == 0) { | ||||
| if (option_multibyte == espeakCHARS_AUTO) | if (option_multibyte == espeakCHARS_AUTO) | ||||
| const char *WordToString2(unsigned int word) | const char *WordToString2(unsigned int word) | ||||
| { | { | ||||
| // Convert a language mnemonic word into a string | // Convert a language mnemonic word into a string | ||||
| int ix; | |||||
| static char buf[5]; | static char buf[5]; | ||||
| char *p; | char *p; | ||||
| p = buf; | p = buf; | ||||
| for (int ix = 3; ix >= 0; ix--) { | |||||
| for (ix = 3; ix >= 0; ix--) { | |||||
| if ((*p = word >> (ix*8)) != 0) | if ((*p = word >> (ix*8)) != 0) | ||||
| p++; | p++; | ||||
| } | } | ||||
| // Find the phoneme string (in ascii) to speak the name of character c | // Find the phoneme string (in ascii) to speak the name of character c | ||||
| // Used for punctuation characters and symbols | // Used for punctuation characters and symbols | ||||
| int ix; | |||||
| unsigned int flags[2]; | unsigned int flags[2]; | ||||
| char single_letter[24]; | char single_letter[24]; | ||||
| char phonemes[60]; | char phonemes[60]; | ||||
| flags[1] = 0; | flags[1] = 0; | ||||
| single_letter[0] = 0; | single_letter[0] = 0; | ||||
| single_letter[1] = '_'; | single_letter[1] = '_'; | ||||
| int ix = utf8_out(c, &single_letter[2]); | |||||
| ix = utf8_out(c, &single_letter[2]); | |||||
| single_letter[2+ix] = 0; | single_letter[2+ix] = 0; | ||||
| if (only) { | if (only) { | ||||
| FILE *f; | FILE *f; | ||||
| char *p; | char *p; | ||||
| int *ip; | int *ip; | ||||
| int length; | |||||
| char fname_temp[100]; | char fname_temp[100]; | ||||
| char fname2[sizeof(path_home)+13+40]; | char fname2[sizeof(path_home)+13+40]; | ||||
| f = NULL; | f = NULL; | ||||
| #ifdef PLATFORM_POSIX | #ifdef PLATFORM_POSIX | ||||
| if ((f = fopen(fname, "rb")) != NULL) { | if ((f = fopen(fname, "rb")) != NULL) { | ||||
| int ix; | |||||
| int fd_temp; | int fd_temp; | ||||
| int header[3]; | int header[3]; | ||||
| char command[sizeof(fname2)+sizeof(fname2)+40]; | char command[sizeof(fname2)+sizeof(fname2)+40]; | ||||
| fseek(f, 20, SEEK_SET); | fseek(f, 20, SEEK_SET); | ||||
| for (int ix = 0; ix < 3; ix++) | |||||
| for (ix = 0; ix < 3; ix++) | |||||
| header[ix] = Read4Bytes(f); | header[ix] = Read4Bytes(f); | ||||
| // if the sound file is not mono, 16 bit signed, at the correct sample rate, then convert it | // if the sound file is not mono, 16 bit signed, at the correct sample rate, then convert it | ||||
| } | } | ||||
| } | } | ||||
| int length = GetFileLength(fname); | |||||
| length = GetFileLength(fname); | |||||
| fseek(f, 0, SEEK_SET); | fseek(f, 0, SEEK_SET); | ||||
| if ((p = (char *)realloc(soundicon_tab[index].data, length)) == NULL) { | if ((p = (char *)realloc(soundicon_tab[index].data, length)) == NULL) { | ||||
| fclose(f); | fclose(f); | ||||
| static int LookupSoundicon(int c) | static int LookupSoundicon(int c) | ||||
| { | { | ||||
| // Find the sound icon number for a punctuation chatacter | // Find the sound icon number for a punctuation chatacter | ||||
| for (int ix = N_SOUNDICON_SLOTS; ix < n_soundicon_tab; ix++) { | |||||
| int ix; | |||||
| for (ix = N_SOUNDICON_SLOTS; ix < n_soundicon_tab; ix++) { | |||||
| if (soundicon_tab[ix].name == c) { | if (soundicon_tab[ix].name == c) { | ||||
| if (soundicon_tab[ix].length == 0) { | if (soundicon_tab[ix].length == 0) { | ||||
| if (LoadSoundFile(NULL, ix) != 0) | if (LoadSoundFile(NULL, ix) != 0) | ||||
| // Load a sound file into one of the reserved slots in the sound icon table | // Load a sound file into one of the reserved slots in the sound icon table | ||||
| // (if it'snot already loaded) | // (if it'snot already loaded) | ||||
| int ix; | |||||
| int ix; | |||||
| static int slot = -1; | static int slot = -1; | ||||
| for (ix = 0; ix < n_soundicon_tab; ix++) { | for (ix = 0; ix < n_soundicon_tab; ix++) { | ||||
| int soundicon; | int soundicon; | ||||
| int attributes; | int attributes; | ||||
| int short_pause; | int short_pause; | ||||
| int c2; | |||||
| int len; | |||||
| int bufix1; | |||||
| char buf[200]; | char buf[200]; | ||||
| char buf2[80]; | char buf2[80]; | ||||
| char ph_buf[30]; | char ph_buf[30]; | ||||
| int c2 = *c2_ptr; | |||||
| c2 = *c2_ptr; | |||||
| buf[0] = 0; | buf[0] = 0; | ||||
| if ((soundicon = LookupSoundicon(c1)) >= 0) { | if ((soundicon = LookupSoundicon(c1)) >= 0) { | ||||
| } | } | ||||
| } | } | ||||
| int bufix1 = *bufix; | |||||
| int len = strlen(buf); | |||||
| bufix1 = *bufix; | |||||
| len = strlen(buf); | |||||
| strcpy(&output[*bufix], buf); | strcpy(&output[*bufix], buf); | ||||
| *bufix += len; | *bufix += len; | ||||
| // Use the voice properties from the SSML stack to choose a voice, and switch | // Use the voice properties from the SSML stack to choose a voice, and switch | ||||
| // to that voice if it's not the current voice | // to that voice if it's not the current voice | ||||
| int ix; | |||||
| const char *p; | const char *p; | ||||
| SSML_STACK *sp; | SSML_STACK *sp; | ||||
| const char *v_id; | const char *v_id; | ||||
| voice_select.variant = ssml_stack[0].voice_variant_number; | voice_select.variant = ssml_stack[0].voice_variant_number; | ||||
| voice_select.identifier = NULL; | voice_select.identifier = NULL; | ||||
| for (int ix = 0; ix < n_ssml_stack; ix++) { | |||||
| for (ix = 0; ix < n_ssml_stack; ix++) { | |||||
| sp = &ssml_stack[ix]; | sp = &ssml_stack[ix]; | ||||
| voice_name_specified = 0; | voice_name_specified = 0; | ||||
| { | { | ||||
| // Set the speech parameters from the parameter stack | // Set the speech parameters from the parameter stack | ||||
| int param; | int param; | ||||
| int ix; | |||||
| int value; | int value; | ||||
| char buf[20]; | char buf[20]; | ||||
| int new_parameters[N_SPEECH_PARAM]; | int new_parameters[N_SPEECH_PARAM]; | ||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | for (param = 0; param < N_SPEECH_PARAM; param++) | ||||
| new_parameters[param] = -1; | new_parameters[param] = -1; | ||||
| for (int ix = 0; ix < n_param_stack; ix++) { | |||||
| for (ix = 0; ix < n_param_stack; ix++) { | |||||
| for (param = 0; param < N_SPEECH_PARAM; param++) { | for (param = 0; param < N_SPEECH_PARAM; param++) { | ||||
| if (param_stack[ix].parameter[param] >= 0) | if (param_stack[ix].parameter[param] >= 0) | ||||
| new_parameters[param] = param_stack[ix].parameter[param]; | new_parameters[param] = param_stack[ix].parameter[param]; | ||||
| static PARAM_STACK *PushParamStack(int tag_type) | static PARAM_STACK *PushParamStack(int tag_type) | ||||
| { | { | ||||
| int ix; | |||||
| PARAM_STACK *sp; | PARAM_STACK *sp; | ||||
| sp = ¶m_stack[n_param_stack]; | sp = ¶m_stack[n_param_stack]; | ||||
| n_param_stack++; | n_param_stack++; | ||||
| sp->type = tag_type; | sp->type = tag_type; | ||||
| for (int ix = 0; ix < N_SPEECH_PARAM; ix++) | |||||
| for (ix = 0; ix < N_SPEECH_PARAM; ix++) | |||||
| sp->parameter[ix] = -1; | sp->parameter[ix] = -1; | ||||
| return sp; | return sp; | ||||
| } | } | ||||
| static void PopParamStack(int tag_type, char *outbuf, int *outix) | static void PopParamStack(int tag_type, char *outbuf, int *outix) | ||||
| { | { | ||||
| // unwind the stack up to and including the previous tag of this type | // unwind the stack up to and including the previous tag of this type | ||||
| int ix; | |||||
| int top = 0; | int top = 0; | ||||
| if (tag_type >= SSML_CLOSE) | if (tag_type >= SSML_CLOSE) | ||||
| tag_type -= SSML_CLOSE; | tag_type -= SSML_CLOSE; | ||||
| for (int ix = 0; ix < n_param_stack; ix++) { | |||||
| for (ix = 0; ix < n_param_stack; ix++) { | |||||
| if (param_stack[ix].type == tag_type) | if (param_stack[ix].type == tag_type) | ||||
| top = ix; | top = ix; | ||||
| } | } | ||||
| static int attrcmp(const wchar_t *string1, const char *string2) | static int attrcmp(const wchar_t *string1, const char *string2) | ||||
| { | { | ||||
| int ix; | |||||
| int ix; | |||||
| if (string1 == NULL) | if (string1 == NULL) | ||||
| return 1; | return 1; | ||||
| { | { | ||||
| int sign = 0; | int sign = 0; | ||||
| wchar_t *tail; | wchar_t *tail; | ||||
| double value; | |||||
| while (iswspace(*pw)) pw++; | while (iswspace(*pw)) pw++; | ||||
| if (*pw == '+') { | if (*pw == '+') { | ||||
| pw++; | pw++; | ||||
| sign = -1; | sign = -1; | ||||
| } | } | ||||
| double value = (double)wcstod(pw, &tail); | |||||
| value = (double)wcstod(pw, &tail); | |||||
| if (tail == pw) { | if (tail == pw) { | ||||
| // failed to find a number, return 100% | // failed to find a number, return 100% | ||||
| *value_out = 100; | *value_out = 100; | ||||
| } | } | ||||
| if ((tail[0] == 's') && (tail[1] == 't')) { | if ((tail[0] == 's') && (tail[1] == 't')) { | ||||
| double x; | |||||
| // convert from semitones to a frequency percentage | // convert from semitones to a frequency percentage | ||||
| double x = pow((double)2.0, (double)((value*sign)/12)) * 100; | |||||
| x = pow((double)2.0, (double)((value*sign)/12)) * 100; | |||||
| *value_out = (int)x; | *value_out = (int)x; | ||||
| return 2; // percentage | return 2; // percentage | ||||
| } | } | ||||
| static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp) | static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp) | ||||
| { | { | ||||
| int value; | int value; | ||||
| int sign; | |||||
| static const MNEM_TAB mnem_volume[] = { | static const MNEM_TAB mnem_volume[] = { | ||||
| { "default", 100 }, | { "default", 100 }, | ||||
| // mnemonic specifies a value as a percentage of the base pitch/range/rate/volume | // mnemonic specifies a value as a percentage of the base pitch/range/rate/volume | ||||
| sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; | sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; | ||||
| } else { | } else { | ||||
| int sign = attr_prosody_value(param_type, attr1, &value); | |||||
| sign = attr_prosody_value(param_type, attr1, &value); | |||||
| if (sign == 0) | if (sign == 0) | ||||
| sp->parameter[param_type] = value; // absolute value in Hz | sp->parameter[param_type] = value; // absolute value in Hz | ||||
| { NULL, 0 } | { NULL, 0 } | ||||
| }; | }; | ||||
| int ix; | |||||
| int letter; | int letter; | ||||
| char *p; | char *p; | ||||
| p = &outbuf[index]; | p = &outbuf[index]; | ||||
| if ((letter = LookupMnem(keynames, p)) != 0) { | if ((letter = LookupMnem(keynames, p)) != 0) { | ||||
| int ix = utf8_out(letter, p); | |||||
| ix = utf8_out(letter, p); | |||||
| *outix = index + ix; | *outix = index + ix; | ||||
| return letter; | return letter; | ||||
| } | } | ||||
| void InitText2(void) | void InitText2(void) | ||||
| { | { | ||||
| int param; | |||||
| ungot_char = 0; | ungot_char = 0; | ||||
| ungot_char2 = 0; | ungot_char2 = 0; | ||||
| n_param_stack = 1; | n_param_stack = 1; | ||||
| ssml_stack[0].tag_type = 0; | ssml_stack[0].tag_type = 0; | ||||
| for (int param = 0; param < N_SPEECH_PARAM; param++) | |||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | |||||
| speech_parameters[param] = param_stack[0].parameter[param]; // set all speech parameters to defaults | speech_parameters[param] = param_stack[0].parameter[param]; // set all speech parameters to defaults | ||||
| option_punctuation = speech_parameters[espeakPUNCTUATION]; | option_punctuation = speech_parameters[espeakPUNCTUATION]; |
| void SetSpeed(int control) | void SetSpeed(int control) | ||||
| { | { | ||||
| int x; | int x; | ||||
| int s1; | |||||
| int wpm; | |||||
| int wpm2; | int wpm2; | ||||
| int wpm_value; | |||||
| double sonic; | |||||
| speed.loud_consonants = 0; | speed.loud_consonants = 0; | ||||
| speed.min_sample_len = 450; | speed.min_sample_len = 450; | ||||
| speed.lenmod2_factor = 100; | speed.lenmod2_factor = 100; | ||||
| speed.min_pause = 5; | speed.min_pause = 5; | ||||
| int wpm = embedded_value[EMBED_S]; | |||||
| wpm = embedded_value[EMBED_S]; | |||||
| if (control == 2) | if (control == 2) | ||||
| wpm = embedded_value[EMBED_S2]; | wpm = embedded_value[EMBED_S2]; | ||||
| int wpm_value = wpm; | |||||
| wpm_value = wpm; | |||||
| if (voice->speed_percent > 0) | if (voice->speed_percent > 0) | ||||
| wpm = (wpm * voice->speed_percent)/100; | wpm = (wpm * voice->speed_percent)/100; | ||||
| speed3 = (x * voice->speedf3)/256; | speed3 = (x * voice->speedf3)/256; | ||||
| } | } | ||||
| if (control & 2) { | if (control & 2) { | ||||
| double sonic = ((double)wpm2)/wpm; | |||||
| sonic = ((double)wpm2)/wpm; | |||||
| DoSonicSpeed((int)(sonic * 1024)); | DoSonicSpeed((int)(sonic * 1024)); | ||||
| speed.pause_factor = 85; | speed.pause_factor = 85; | ||||
| speed.clause_pause_factor = 80; | speed.clause_pause_factor = 80; | ||||
| speed.lenmod2_factor = 110 - (wpm - 250)/2; | speed.lenmod2_factor = 110 - (wpm - 250)/2; | ||||
| } | } | ||||
| int s1 = (x * voice->speedf1)/256; | |||||
| s1 = (x * voice->speedf1)/256; | |||||
| if (wpm >= 170) | if (wpm >= 170) | ||||
| speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds | speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds | ||||
| void SetSpeed(int control) | void SetSpeed(int control) | ||||
| { | { | ||||
| // This is the earlier version of SetSpeed() before sonic speed-up was added | // This is the earlier version of SetSpeed() before sonic speed-up was added | ||||
| int x; | |||||
| int s1; | |||||
| int wpm; | |||||
| int wpm2; | |||||
| speed.loud_consonants = 0; | speed.loud_consonants = 0; | ||||
| speed.min_sample_len = 450; | speed.min_sample_len = 450; | ||||
| speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change | speed.lenmod_factor = 110; // controls the effect of FRFLAG_LEN_MOD reduce length change | ||||
| speed.lenmod2_factor = 100; | speed.lenmod2_factor = 100; | ||||
| int wpm = embedded_value[EMBED_S]; | |||||
| wpm = embedded_value[EMBED_S]; | |||||
| if (control == 2) | if (control == 2) | ||||
| wpm = embedded_value[EMBED_S2]; | wpm = embedded_value[EMBED_S2]; | ||||
| if (wpm > 360) | if (wpm > 360) | ||||
| speed.loud_consonants = (wpm - 360) / 8; | speed.loud_consonants = (wpm - 360) / 8; | ||||
| int wpm2 = wpm; | |||||
| wpm2 = wpm; | |||||
| if (wpm > 359) wpm2 = 359; | if (wpm > 359) wpm2 = 359; | ||||
| if (wpm < 80) wpm2 = 80; | if (wpm < 80) wpm2 = 80; | ||||
| int x = speed_lookup[wpm2-80]; | |||||
| x = speed_lookup[wpm2-80]; | |||||
| if (wpm >= 380) | if (wpm >= 380) | ||||
| x = 7; | x = 7; | ||||
| speed.lenmod2_factor = 110 - (wpm - 250)/2; | speed.lenmod2_factor = 110 - (wpm - 250)/2; | ||||
| } | } | ||||
| int s1 = (x * voice->speedf1)/256; | |||||
| s1 = (x * voice->speedf1)/256; | |||||
| if (wpm >= 170) | if (wpm >= 170) | ||||
| speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds | speed.wav_factor = 110 + (150*s1)/128; // reduced speed adjustment, used for playing recorded sounds | ||||
| // relative 0=absolute 1=relative | // relative 0=absolute 1=relative | ||||
| int new_value = value; | int new_value = value; | ||||
| int default_value; | |||||
| if (relative) { | if (relative) { | ||||
| if (parameter < 5) { | if (parameter < 5) { | ||||
| int default_value = param_defaults[parameter]; | |||||
| default_value = param_defaults[parameter]; | |||||
| new_value = default_value + (default_value * value)/100; | new_value = default_value + (default_value * value)/100; | ||||
| } | } | ||||
| } | } | ||||
| void CalcLengths(Translator *tr) | void CalcLengths(Translator *tr) | ||||
| { | { | ||||
| int ix; | |||||
| int ix2; | int ix2; | ||||
| PHONEME_LIST *prev; | PHONEME_LIST *prev; | ||||
| PHONEME_LIST *next; | PHONEME_LIST *next; | ||||
| unsigned char *pitch_env = NULL; | unsigned char *pitch_env = NULL; | ||||
| PHONEME_DATA phdata_tone; | PHONEME_DATA phdata_tone; | ||||
| for (int ix = 1; ix < n_phoneme_list; ix++) { | |||||
| for (ix = 1; ix < n_phoneme_list; ix++) { | |||||
| prev = &phoneme_list[ix-1]; | prev = &phoneme_list[ix-1]; | ||||
| p = &phoneme_list[ix]; | p = &phoneme_list[ix]; | ||||
| stress = p->stresslevel & 0x7; | stress = p->stresslevel & 0x7; |
| { | { | ||||
| #ifdef PLATFORM_WINDOWS | #ifdef PLATFORM_WINDOWS | ||||
| HKEY RegKey; | HKEY RegKey; | ||||
| unsigned long size; | |||||
| unsigned long var_type; | |||||
| char *env; | char *env; | ||||
| unsigned char buf[sizeof(path_home)-13]; | unsigned char buf[sizeof(path_home)-13]; | ||||
| buf[0] = 0; | buf[0] = 0; | ||||
| RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey); | RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\Tokens\\eSpeak", 0, KEY_READ, &RegKey); | ||||
| unsigned long size = sizeof(buf); | |||||
| unsigned long var_type = REG_SZ; | |||||
| size = sizeof(buf); | |||||
| var_type = REG_SZ; | |||||
| RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size); | RegQueryValueExA(RegKey, "path", 0, &var_type, buf, &size); | ||||
| sprintf(path_home, "%s\\espeak-data", buf); | sprintf(path_home, "%s\\espeak-data", buf); | ||||
| static int initialise(int control) | static int initialise(int control) | ||||
| { | { | ||||
| int param; | |||||
| int result; | int result; | ||||
| int srate = 22050; // default sample rate 22050 Hz | int srate = 22050; // default sample rate 22050 Hz | ||||
| SynthesizeInit(); | SynthesizeInit(); | ||||
| InitNamedata(); | InitNamedata(); | ||||
| for (int param = 0; param < N_SPEECH_PARAM; param++) | |||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | |||||
| param_stack[0].parameter[param] = param_defaults[param]; | param_stack[0].parameter[param] = param_defaults[param]; | ||||
| return 0; | return 0; | ||||
| { | { | ||||
| // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme | // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme | ||||
| espeak_EVENT *ep; | espeak_EVENT *ep; | ||||
| double time; | |||||
| if ((event_list == NULL) || (event_list_ix >= (n_event_list-2))) | if ((event_list == NULL) || (event_list_ix >= (n_event_list-2))) | ||||
| return; | return; | ||||
| ep->text_position = char_position & 0xffffff; | ep->text_position = char_position & 0xffffff; | ||||
| ep->length = char_position >> 24; | ep->length = char_position >> 24; | ||||
| double time = ((double)(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate; | |||||
| time = ((double)(count_samples + mbrola_delay + (out_ptr - out_start)/2)*1000.0)/samplerate; | |||||
| ep->audio_position = (int)time; | ep->audio_position = (int)time; | ||||
| ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2); | ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2); | ||||
| { | { | ||||
| // symbolic name, symbolicname_character - is there a system resource of symbolic names per language? | // symbolic name, symbolicname_character - is there a system resource of symbolic names per language? | ||||
| int letter; | int letter; | ||||
| int ix; | |||||
| int ix = utf8_in(&letter, key); | |||||
| ix = utf8_in(&letter, key); | |||||
| if (key[ix] == 0) { | if (key[ix] == 0) { | ||||
| // a single character | // a single character | ||||
| sync_espeak_Char(letter); | sync_espeak_Char(letter); | ||||
| ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options) | ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options) | ||||
| { | { | ||||
| int param; | |||||
| // It seems that the wctype functions don't work until the locale has been set | // It seems that the wctype functions don't work until the locale has been set | ||||
| // to something other than the default "C". Then, not only Latin1 but also the | // to something other than the default "C". Then, not only Latin1 but also the | ||||
| // other characters give the correct results with iswalpha() etc. | // other characters give the correct results with iswalpha() etc. | ||||
| VoiceReset(0); | VoiceReset(0); | ||||
| for (int param = 0; param < N_SPEECH_PARAM; param++) | |||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | |||||
| param_stack[0].parameter[param] = saved_parameters[param] = param_defaults[param]; | param_stack[0].parameter[param] = saved_parameters[param] = param_defaults[param]; | ||||
| SetParameter(espeakRATE, 175, 0); | SetParameter(espeakRATE, 175, 0); |
| { | { | ||||
| // General polinomial interpolation routine, xa[1...n] ya[1...n] | // General polinomial interpolation routine, xa[1...n] ya[1...n] | ||||
| int i, m, ns = 1; | int i, m, ns = 1; | ||||
| float den, dift, ho, hp, w; | |||||
| float den, dif, dift, ho, hp, w; | |||||
| float y; // result | float y; // result | ||||
| float c[9], d[9]; | float c[9], d[9]; | ||||
| float dif = fabs(x-xa[1]); | |||||
| dif = fabs(x-xa[1]); | |||||
| for (i = 1; i <= n; i++) { | for (i = 1; i <= n; i++) { | ||||
| if ((dift = fabs(x-xa[i])) < dif) { | if ((dift = fabs(x-xa[i])) < dif) { | ||||
| static SpectFrame *SpectFrameCreate() | static SpectFrame *SpectFrameCreate() | ||||
| { | { | ||||
| int ix; | |||||
| SpectFrame *frame; | SpectFrame *frame; | ||||
| frame = malloc(sizeof(SpectFrame)); | frame = malloc(sizeof(SpectFrame)); | ||||
| frame->amp_adjust = 100; | frame->amp_adjust = 100; | ||||
| frame->length_adjust = 0; | frame->length_adjust = 0; | ||||
| for (int ix = 0; ix < N_PEAKS; ix++) { | |||||
| for (ix = 0; ix < N_PEAKS; ix++) { | |||||
| frame->formants[ix].freq = 0; | frame->formants[ix].freq = 0; | ||||
| frame->peaks[ix].pkfreq = default_freq[ix]; | frame->peaks[ix].pkfreq = default_freq[ix]; | ||||
| frame->peaks[ix].pkheight = 0; | frame->peaks[ix].pkheight = 0; | ||||
| { | { | ||||
| int h; | int h; | ||||
| float total = 0; | float total = 0; | ||||
| int maxh; | |||||
| int height; | int height; | ||||
| int htab[400]; | int htab[400]; | ||||
| wavegen_peaks_t wpeaks[9]; | wavegen_peaks_t wpeaks[9]; | ||||
| wpeaks[h].right = frame->peaks[h].pkright << 16; | wpeaks[h].right = frame->peaks[h].pkright << 16; | ||||
| } | } | ||||
| int maxh = PeaksToHarmspect(wpeaks, 90<<16, htab, 0); | |||||
| maxh = PeaksToHarmspect(wpeaks, 90<<16, htab, 0); | |||||
| for (h = 1; h < maxh; h++) | for (h = 1; h < maxh; h++) | ||||
| total += ((htab[h] * htab[h]) >> 10); | total += ((htab[h] * htab[h]) >> 10); | ||||
| frame->rms = sqrt(total) / 7.25; | frame->rms = sqrt(total) / 7.25; | ||||
| void SpectSeqDestroy(SpectSeq *spect) | void SpectSeqDestroy(SpectSeq *spect) | ||||
| { | { | ||||
| int ix; | |||||
| if (spect->frames != NULL) { | if (spect->frames != NULL) { | ||||
| for (int ix = 0; ix < spect->numframes; ix++) { | |||||
| for (ix = 0; ix < spect->numframes; ix++) { | |||||
| if (spect->frames[ix] != NULL) | if (spect->frames[ix] != NULL) | ||||
| SpectFrameDestroy(spect->frames[ix]); | SpectFrameDestroy(spect->frames[ix]); | ||||
| } | } |
| { | { | ||||
| // Load a phoneme name translation table from espeak-data/mbrola | // Load a phoneme name translation table from espeak-data/mbrola | ||||
| int size; | |||||
| int ix; | |||||
| int *pw; | int *pw; | ||||
| FILE *f_in; | FILE *f_in; | ||||
| char path[sizeof(path_home)+15]; | char path[sizeof(path_home)+15]; | ||||
| // read eSpeak's mbrola phoneme translation data, eg. en1_phtrans | // read eSpeak's mbrola phoneme translation data, eg. en1_phtrans | ||||
| sprintf(path, "%s/mbrola_ph/%s", path_home, phtrans); | sprintf(path, "%s/mbrola_ph/%s", path_home, phtrans); | ||||
| int size = GetFileLength(path); | |||||
| size = GetFileLength(path); | |||||
| if ((f_in = fopen(path, "rb")) == NULL) { | if ((f_in = fopen(path, "rb")) == NULL) { | ||||
| close_MBR(); | close_MBR(); | ||||
| return EE_NOT_FOUND; | return EE_NOT_FOUND; | ||||
| mbrola_control = Read4Bytes(f_in); | mbrola_control = Read4Bytes(f_in); | ||||
| pw = (int *)mbrola_tab; | pw = (int *)mbrola_tab; | ||||
| for (int ix = 4; ix < size; ix += 4) | |||||
| for (ix = 4; ix < size; ix += 4) | |||||
| *pw++ = Read4Bytes(f_in); | *pw++ = Read4Bytes(f_in); | ||||
| size = fread(mbrola_tab, 1, size, f_in); | size = fread(mbrola_tab, 1, size, f_in); | ||||
| fclose(f_in); | fclose(f_in); | ||||
| static char *WritePitch(int env, int pitch1, int pitch2, int split, int final) | static char *WritePitch(int env, int pitch1, int pitch2, int split, int final) | ||||
| { | { | ||||
| // final=1: only give the final pitch value. | // final=1: only give the final pitch value. | ||||
| int x; | |||||
| int ix; | |||||
| int pitch_base; | int pitch_base; | ||||
| int pitch_range; | int pitch_range; | ||||
| int p2; | |||||
| int p1, p2, p_end; | |||||
| unsigned char *pitch_env; | unsigned char *pitch_env; | ||||
| int max = -1; | int max = -1; | ||||
| int min = 999; | int min = 999; | ||||
| int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s) | int env100 = 80; // apply the pitch change only over this proportion of the mbrola phoneme(s) | ||||
| int y2; | int y2; | ||||
| int y[4]; | int y[4]; | ||||
| int env_split; | |||||
| char buf[50]; | char buf[50]; | ||||
| static char output[50]; | static char output[50]; | ||||
| SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range); | SetPitch2(voice, pitch1, pitch2, &pitch_base, &pitch_range); | ||||
| int env_split = (split * 128)/100; | |||||
| env_split = (split * 128)/100; | |||||
| if (env_split < 0) | if (env_split < 0) | ||||
| env_split = 0-env_split; | env_split = 0-env_split; | ||||
| // find max and min in the pitch envelope | // find max and min in the pitch envelope | ||||
| for (int x = 0; x < 128; x++) { | |||||
| for (x = 0; x < 128; x++) { | |||||
| if (pitch_env[x] > max) { | if (pitch_env[x] > max) { | ||||
| max = pitch_env[x]; | max = pitch_env[x]; | ||||
| y_max = x; | y_max = x; | ||||
| y[3] = y[2] + (127 - y[2])/2; | y[3] = y[2] + (127 - y[2])/2; | ||||
| // set initial pitch | // set initial pitch | ||||
| int p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12 | |||||
| int p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | |||||
| p1 = ((pitch_env[0]*pitch_range)>>8) + pitch_base; // Hz << 12 | |||||
| p_end = ((pitch_env[127]*pitch_range)>>8) + pitch_base; | |||||
| if (split >= 0) { | if (split >= 0) { | ||||
| sprintf(buf, " 0 %d", p1/4096); | sprintf(buf, " 0 %d", p1/4096); | ||||
| // don't use intermediate pitch points for linear rise and fall | // don't use intermediate pitch points for linear rise and fall | ||||
| if (env > 1) { | if (env > 1) { | ||||
| for (int ix = 1; ix < 4; ix++) { | |||||
| for (ix = 1; ix < 4; ix++) { | |||||
| p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base; | p2 = ((pitch_env[y[ix]]*pitch_range)>>8) + pitch_base; | ||||
| if (split > 0) | if (split > 0) | ||||
| // Read audio data from Mbrola (length is in millisecs) | // Read audio data from Mbrola (length is in millisecs) | ||||
| static int n_samples; | static int n_samples; | ||||
| int req_samples, result; | |||||
| int ix; | |||||
| short value16; | short value16; | ||||
| int value; | int value; | ||||
| if (!resume) | if (!resume) | ||||
| n_samples = samplerate * length / 1000; | n_samples = samplerate * length / 1000; | ||||
| int req_samples = (out_end - out_ptr)/2; | |||||
| req_samples = (out_end - out_ptr)/2; | |||||
| if (req_samples > n_samples) | if (req_samples > n_samples) | ||||
| req_samples = n_samples; | req_samples = n_samples; | ||||
| int result = read_MBR((short *)out_ptr, req_samples); | |||||
| result = read_MBR((short *)out_ptr, req_samples); | |||||
| if (result <= 0) | if (result <= 0) | ||||
| return 0; | return 0; | ||||
| for (int ix = 0; ix < result; ix++) { | |||||
| for (ix = 0; ix < result; ix++) { | |||||
| value16 = out_ptr[0] + (out_ptr[1] << 8); | value16 = out_ptr[0] + (out_ptr[1] << 8); | ||||
| value = value16 * amplitude; | value = value16 * amplitude; | ||||
| value = value / 40; // adjust this constant to give a suitable amplitude for mbrola voices | value = value / 40; // adjust this constant to give a suitable amplitude for mbrola voices |
| { | { | ||||
| FILE *f_in; | FILE *f_in; | ||||
| char *p; | char *p; | ||||
| unsigned int length; | |||||
| char buf[sizeof(path_home)+40]; | char buf[sizeof(path_home)+40]; | ||||
| sprintf(buf, "%s%c%s", path_home, PATHSEP, fname); | sprintf(buf, "%s%c%s", path_home, PATHSEP, fname); | ||||
| unsigned int length = GetFileLength(buf); | |||||
| length = GetFileLength(buf); | |||||
| if ((f_in = fopen(buf, "rb")) == NULL) { | if ((f_in = fopen(buf, "rb")) == NULL) { | ||||
| fprintf(stderr, "Can't read data file: '%s'\n", buf); | fprintf(stderr, "Can't read data file: '%s'\n", buf); | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| int n_phonemes; | int n_phonemes; | ||||
| int version; | |||||
| int result = 1; | int result = 1; | ||||
| int length; | int length; | ||||
| int rate; | |||||
| unsigned char *p; | unsigned char *p; | ||||
| int *pw; | int *pw; | ||||
| n_tunes = length / sizeof(TUNE); | n_tunes = length / sizeof(TUNE); | ||||
| // read the version number and sample rate from the first 8 bytes of phondata | // read the version number and sample rate from the first 8 bytes of phondata | ||||
| int version = 0; // bytes 0-3, version number | |||||
| int rate = 0; // bytes 4-7, sample rate | |||||
| version = 0; // bytes 0-3, version number | |||||
| rate = 0; // bytes 4-7, sample rate | |||||
| for (ix = 0; ix < 4; ix++) { | for (ix = 0; ix < 4; ix++) { | ||||
| version += (wavefile_data[ix] << (ix*8)); | version += (wavefile_data[ix] << (ix*8)); | ||||
| rate += (wavefile_data[ix+4] << (ix*8)); | rate += (wavefile_data[ix+4] << (ix*8)); | ||||
| int PhonemeCode(unsigned int mnem) | int PhonemeCode(unsigned int mnem) | ||||
| { | { | ||||
| for (int ix = 0; ix < n_phoneme_tab; ix++) { | |||||
| int ix; | |||||
| for (ix = 0; ix < n_phoneme_tab; ix++) { | |||||
| if (phoneme_tab[ix] == NULL) | if (phoneme_tab[ix] == NULL) | ||||
| continue; | continue; | ||||
| if (phoneme_tab[ix]->mnemonic == mnem) | if (phoneme_tab[ix]->mnemonic == mnem) | ||||
| int LookupPhonemeString(const char *string) | int LookupPhonemeString(const char *string) | ||||
| { | { | ||||
| int ix; | |||||
| unsigned char c; | unsigned char c; | ||||
| unsigned int mnem; | |||||
| // Pack up to 4 characters into a word | // Pack up to 4 characters into a word | ||||
| unsigned int mnem = 0; | |||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| mnem = 0; | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| if (string[ix] == 0) break; | if (string[ix] == 0) break; | ||||
| c = string[ix]; | c = string[ix]; | ||||
| mnem |= (c << (ix*8)); | mnem |= (c << (ix*8)); | ||||
| frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist) | frameref_t *LookupSpect(PHONEME_TAB *this_ph, int which, FMT_PARAMS *fmt_params, int *n_frames, PHONEME_LIST *plist) | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| int nf; | |||||
| int nf1; | int nf1; | ||||
| int seq_break; | |||||
| frameref_t *frames; | frameref_t *frames; | ||||
| int length1; | int length1; | ||||
| int length_std; | int length_std; | ||||
| seq = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt_addr]); | seq = (SPECT_SEQ *)(&phondata_ptr[fmt_params->fmt_addr]); | ||||
| seqk = (SPECT_SEQK *)seq; | seqk = (SPECT_SEQK *)seq; | ||||
| int nf = seq->n_frames; | |||||
| nf = seq->n_frames; | |||||
| if (nf >= N_SEQ_FRAMES) | if (nf >= N_SEQ_FRAMES) | ||||
| nf = N_SEQ_FRAMES - 1; | nf = N_SEQ_FRAMES - 1; | ||||
| seq_len_adjust = fmt_params->fmt2_lenadj + fmt_params->fmt_length; | seq_len_adjust = fmt_params->fmt2_lenadj + fmt_params->fmt_length; | ||||
| int seq_break = 0; | |||||
| seq_break = 0; | |||||
| for (ix = 0; ix < nf; ix++) { | for (ix = 0; ix < nf; ix++) { | ||||
| if (seq->frame[0].frflags & FRFLAG_KLATT) | if (seq->frame[0].frflags & FRFLAG_KLATT) | ||||
| static void SetUpPhonemeTable(int number, int recursing) | static void SetUpPhonemeTable(int number, int recursing) | ||||
| { | { | ||||
| int ix; | |||||
| int includes; | int includes; | ||||
| int ph_code; | int ph_code; | ||||
| PHONEME_TAB *phtab; | PHONEME_TAB *phtab; | ||||
| // now add the phonemes from this table | // now add the phonemes from this table | ||||
| phtab = phoneme_tab_list[number].phoneme_tab_ptr; | phtab = phoneme_tab_list[number].phoneme_tab_ptr; | ||||
| for (int ix = 0; ix < phoneme_tab_list[number].n_phonemes; ix++) { | |||||
| for (ix = 0; ix < phoneme_tab_list[number].n_phonemes; ix++) { | |||||
| ph_code = phtab[ix].code; | ph_code = phtab[ix].code; | ||||
| phoneme_tab[ph_code] = &phtab[ix]; | phoneme_tab[ph_code] = &phtab[ix]; | ||||
| if (ph_code > n_phoneme_tab) | if (ph_code > n_phoneme_tab) | ||||
| // condition: | // condition: | ||||
| // 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress | // 0 if diminished, 1 if unstressed, 2 if not stressed, 3 if stressed, 4 if max stress | ||||
| int stress_level; | |||||
| PHONEME_LIST *pl; | PHONEME_LIST *pl; | ||||
| static int condition_level[4] = { 1, 2, 4, 15 }; | static int condition_level[4] = { 1, 2, 4, 15 }; | ||||
| return false; // no stress elevel for this consonant | return false; // no stress elevel for this consonant | ||||
| } | } | ||||
| int stress_level = pl->stresslevel & 0xf; | |||||
| stress_level = pl->stresslevel & 0xf; | |||||
| if (tr != NULL) { | if (tr != NULL) { | ||||
| if ((control & 1) && (plist->synthflags & SFLAG_DICTIONARY) && ((tr->langopts.param[LOPT_REDUCE] & 1) == 0)) { | if ((control & 1) && (plist->synthflags & SFLAG_DICTIONARY) && ((tr->langopts.param[LOPT_REDUCE] & 1) == 0)) { | ||||
| { | { | ||||
| int which; | int which; | ||||
| int ix; | int ix; | ||||
| unsigned int data; | |||||
| int instn; | |||||
| int instn2; | |||||
| int count; | int count; | ||||
| int check_endtype = 0; | int check_endtype = 0; | ||||
| PHONEME_TAB *ph; | PHONEME_TAB *ph; | ||||
| // bits 8-10 = 7, other conditions | // bits 8-10 = 7, other conditions | ||||
| int instn = (*p_prog) & 0xfff; | |||||
| unsigned int data = instn & 0xff; | |||||
| int instn2 = instn >> 8; | |||||
| instn = (*p_prog) & 0xfff; | |||||
| data = instn & 0xff; | |||||
| instn2 = instn >> 8; | |||||
| if (instn2 < 14) { | if (instn2 < 14) { | ||||
| plist_this = plist; | plist_this = plist; | ||||
| { | { | ||||
| USHORT *prog; | USHORT *prog; | ||||
| int voweltype; | int voweltype; | ||||
| signed char x; | |||||
| if (instn_type == 2) { | if (instn_type == 2) { | ||||
| phdata->pd_control |= pd_FORNEXTPH; | phdata->pd_control |= pd_FORNEXTPH; | ||||
| if ((voweltype >= 0) && (voweltype < 6)) { | if ((voweltype >= 0) && (voweltype < 6)) { | ||||
| prog = *p_prog + voweltype*2; | prog = *p_prog + voweltype*2; | ||||
| phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4; | phdata->sound_addr[instn_type] = (((prog[1] & 0xf) << 16) + prog[2]) * 4; | ||||
| signed char x = (prog[1] >> 4) & 0xff; | |||||
| x = (prog[1] >> 4) & 0xff; | |||||
| phdata->sound_param[instn_type] = x; // sign extend | phdata->sound_param[instn_type] = x; // sign extend | ||||
| } | } | ||||
| int NumInstnWords(USHORT *prog) | int NumInstnWords(USHORT *prog) | ||||
| { | { | ||||
| int instn; | |||||
| int instn2; | |||||
| int instn_type; | |||||
| int n; | int n; | ||||
| int type2; | |||||
| static const char n_words[16] = { 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 4, 0, 0, 0, 0, 0 }; | static const char n_words[16] = { 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 4, 0, 0, 0, 0, 0 }; | ||||
| int instn2; | |||||
| int type2; | |||||
| int instn = *prog; | |||||
| int instn_type = instn >> 12; | |||||
| instn = *prog; | |||||
| instn_type = instn >> 12; | |||||
| if ((n = n_words[instn_type]) > 0) | if ((n = n_words[instn_type]) > 0) | ||||
| return n; | return n; | ||||
| bool truth; | bool truth; | ||||
| bool truth2; | bool truth2; | ||||
| int data; | int data; | ||||
| int end_flag; | |||||
| int ix; | int ix; | ||||
| signed char param_sc; | signed char param_sc; | ||||
| if (ph->program == 0) | if (ph->program == 0) | ||||
| return; | return; | ||||
| int end_flag = 0; | |||||
| end_flag = 0; | |||||
| for (prog = &phoneme_index[ph->program]; end_flag != 1; prog++) { | for (prog = &phoneme_index[ph->program]; end_flag != 1; prog++) { | ||||
| instn = *prog; | instn = *prog; | ||||
| void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) | void InterpretPhoneme2(int phcode, PHONEME_DATA *phdata) | ||||
| { | { | ||||
| // Examine the program of a single isolated phoneme | // Examine the program of a single isolated phoneme | ||||
| int ix; | |||||
| PHONEME_LIST plist[4]; | PHONEME_LIST plist[4]; | ||||
| memset(plist, 0, sizeof(plist)); | memset(plist, 0, sizeof(plist)); | ||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| plist[ix].phcode = phonPAUSE; | plist[ix].phcode = phonPAUSE; | ||||
| plist[ix].ph = phoneme_tab[phonPAUSE]; | plist[ix].ph = phoneme_tab[phonPAUSE]; | ||||
| } | } |
| const char *WordToString(unsigned int word) | const char *WordToString(unsigned int word) | ||||
| { | { | ||||
| // Convert a phoneme mnemonic word into a string | // Convert a phoneme mnemonic word into a string | ||||
| int ix; | |||||
| static char buf[5]; | static char buf[5]; | ||||
| for (int ix = 0; ix < 4; ix++) | |||||
| for (ix = 0; ix < 4; ix++) | |||||
| buf[ix] = word >> (ix*8); | buf[ix] = word >> (ix*8); | ||||
| buf[4] = 0; | buf[4] = 0; | ||||
| return buf; | return buf; | ||||
| // control = 1, less shortening at fast speeds | // control = 1, less shortening at fast speeds | ||||
| unsigned int len; | unsigned int len; | ||||
| int srate2; | |||||
| if (length == 0) | if (length == 0) | ||||
| len = 0; | len = 0; | ||||
| if (len < 90000) | if (len < 90000) | ||||
| len = (len * samplerate) / 1000; // convert from mS to number of samples | len = (len * samplerate) / 1000; // convert from mS to number of samples | ||||
| else { | else { | ||||
| int srate2 = samplerate / 25; // avoid overflow | |||||
| srate2 = samplerate / 25; // avoid overflow | |||||
| len = (len * srate2) / 40; | len = (len * srate2) / 40; | ||||
| } | } | ||||
| } | } | ||||
| static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp) | static int DoSample2(int index, int which, int std_length, int control, int length_mod, int amp) | ||||
| { | { | ||||
| int length; | |||||
| int wav_length; | |||||
| int wav_scale; | |||||
| int min_length; | |||||
| int x; | int x; | ||||
| int len4; | |||||
| intptr_t *q; | intptr_t *q; | ||||
| unsigned char *p; | unsigned char *p; | ||||
| index = index & 0x7fffff; | index = index & 0x7fffff; | ||||
| p = &wavefile_data[index]; | p = &wavefile_data[index]; | ||||
| int wav_scale = p[2]; | |||||
| int wav_length = (p[1] * 256); | |||||
| wav_scale = p[2]; | |||||
| wav_length = (p[1] * 256); | |||||
| wav_length += p[0]; // length in bytes | wav_length += p[0]; // length in bytes | ||||
| if (wav_length == 0) | if (wav_length == 0) | ||||
| return 0; | return 0; | ||||
| int min_length = speed.min_sample_len; | |||||
| min_length = speed.min_sample_len; | |||||
| if (wav_scale == 0) | if (wav_scale == 0) | ||||
| min_length *= 2; // 16 bit samples | min_length *= 2; // 16 bit samples | ||||
| if (length_mod > 0) | if (length_mod > 0) | ||||
| std_length = (std_length * length_mod)/256; | std_length = (std_length * length_mod)/256; | ||||
| int length = (std_length * speed.wav_factor)/256; | |||||
| length = (std_length * speed.wav_factor)/256; | |||||
| if (control & pd_DONTLENGTHEN) { | if (control & pd_DONTLENGTHEN) { | ||||
| // this option is used for Stops, with short noise bursts. | // this option is used for Stops, with short noise bursts. | ||||
| if (amp < 0) | if (amp < 0) | ||||
| return length; | return length; | ||||
| int len4 = wav_length / 4; | |||||
| len4 = wav_length / 4; | |||||
| index += 4; | index += 4; | ||||
| // Each frame includes its RMS amplitude value, so to set a new | // Each frame includes its RMS amplitude value, so to set a new | ||||
| // RMS just adjust the formant amplitudes by the appropriate ratio | // RMS just adjust the formant amplitudes by the appropriate ratio | ||||
| int x; | |||||
| int h; | |||||
| int ix; | |||||
| static const short sqrt_tab[200] = { | static const short sqrt_tab[200] = { | ||||
| 0, 64, 90, 110, 128, 143, 156, 169, 181, 192, 202, 212, 221, 230, 239, 247, | 0, 64, 90, 110, 128, 143, 156, 169, 181, 192, 202, 212, 221, 230, 239, 247, | ||||
| 256, 263, 271, 278, 286, 293, 300, 306, 313, 320, 326, 332, 338, 344, 350, 356, | 256, 263, 271, 278, 286, 293, 300, 306, 313, 320, 326, 332, 338, 344, 350, 356, | ||||
| } | } | ||||
| if (fr->rms == 0) return; // check for divide by zero | if (fr->rms == 0) return; // check for divide by zero | ||||
| int x = (new_rms * 64)/fr->rms; | |||||
| x = (new_rms * 64)/fr->rms; | |||||
| if (x >= 200) x = 199; | if (x >= 200) x = 199; | ||||
| x = sqrt_tab[x]; // sqrt(new_rms/fr->rms)*0x200; | x = sqrt_tab[x]; // sqrt(new_rms/fr->rms)*0x200; | ||||
| int h; | |||||
| for (int ix = 0; ix < 8; ix++) { | |||||
| for (ix = 0; ix < 8; ix++) { | |||||
| h = fr->fheight[ix] * x; | h = fr->fheight[ix] * x; | ||||
| fr->fheight[ix] = h/0x200; | fr->fheight[ix] = h/0x200; | ||||
| } | } | ||||
| static void formants_reduce_hf(frame_t *fr, int level) | static void formants_reduce_hf(frame_t *fr, int level) | ||||
| { | { | ||||
| // change height of peaks 2 to 8, percentage | // change height of peaks 2 to 8, percentage | ||||
| int ix; | |||||
| int x; | |||||
| if (voice->klattv[0]) | if (voice->klattv[0]) | ||||
| return; | return; | ||||
| int x; | |||||
| for (int ix = 2; ix < 8; ix++) { | |||||
| for (ix = 2; ix < 8; ix++) { | |||||
| x = fr->fheight[ix] * level; | x = fr->fheight[ix] * level; | ||||
| fr->fheight[ix] = x/100; | fr->fheight[ix] = x/100; | ||||
| } | } | ||||
| static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags) | static void AdjustFormants(frame_t *fr, int target, int min, int max, int f1_adj, int f3_adj, int hf_reduce, int flags) | ||||
| { | { | ||||
| int x; | |||||
| target = (target * voice->formant_factor)/256; | target = (target * voice->formant_factor)/256; | ||||
| int x = (target - fr->ffreq[2]) / 2; | |||||
| x = (target - fr->ffreq[2]) / 2; | |||||
| if (x > max) x = max; | if (x > max) x = max; | ||||
| if (x < min) x = min; | if (x < min) x = min; | ||||
| fr->ffreq[2] += x; | fr->ffreq[2] += x; | ||||
| int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | int FormantTransition2(frameref_t *seq, int *n_frames, unsigned int data1, unsigned int data2, PHONEME_TAB *other_ph, int which) | ||||
| { | { | ||||
| int ix; | |||||
| int formant; | |||||
| int next_rms; | |||||
| int len; | |||||
| int rms; | |||||
| int f1; | |||||
| int f2; | |||||
| int f2_min; | |||||
| int f2_max; | |||||
| int f3_adj; | |||||
| int f3_amp; | |||||
| int flags; | |||||
| int vcolour; | |||||
| #define N_VCOLOUR 2 | #define N_VCOLOUR 2 | ||||
| // percentage change for each formant in 256ths | // percentage change for each formant in 256ths | ||||
| static short vcolouring[N_VCOLOUR][5] = { | static short vcolouring[N_VCOLOUR][5] = { | ||||
| if (*n_frames < 2) | if (*n_frames < 2) | ||||
| return 0; | return 0; | ||||
| int len = (data1 & 0x3f) * 2; | |||||
| int rms = (data1 >> 6) & 0x3f; | |||||
| int flags = (data1 >> 12); | |||||
| len = (data1 & 0x3f) * 2; | |||||
| rms = (data1 >> 6) & 0x3f; | |||||
| flags = (data1 >> 12); | |||||
| int f2 = (data2 & 0x3f) * 50; | |||||
| int f2_min = (((data2 >> 6) & 0x1f) - 15) * 50; | |||||
| int f2_max = (((data2 >> 11) & 0x1f) - 15) * 50; | |||||
| int f3_adj = (((data2 >> 16) & 0x1f) - 15) * 50; | |||||
| int f3_amp = ((data2 >> 21) & 0x1f) * 8; | |||||
| int f1 = ((data2 >> 26) & 0x7); | |||||
| int vcolour = (data2 >> 29); | |||||
| f2 = (data2 & 0x3f) * 50; | |||||
| f2_min = (((data2 >> 6) & 0x1f) - 15) * 50; | |||||
| f2_max = (((data2 >> 11) & 0x1f) - 15) * 50; | |||||
| f3_adj = (((data2 >> 16) & 0x1f) - 15) * 50; | |||||
| f3_amp = ((data2 >> 21) & 0x1f) * 8; | |||||
| f1 = ((data2 >> 26) & 0x7); | |||||
| vcolour = (data2 >> 29); | |||||
| if ((other_ph != NULL) && (other_ph->mnemonic == '?')) | if ((other_ph != NULL) && (other_ph->mnemonic == '?')) | ||||
| flags |= 8; | flags |= 8; | ||||
| seq[0].frflags |= FRFLAG_LEN_MOD2; // reduce length modification | seq[0].frflags |= FRFLAG_LEN_MOD2; // reduce length modification | ||||
| fr->frflags |= FRFLAG_LEN_MOD2; | fr->frflags |= FRFLAG_LEN_MOD2; | ||||
| int next_rms = seq[1].frame->rms; | |||||
| next_rms = seq[1].frame->rms; | |||||
| if (voice->klattv[0]) | if (voice->klattv[0]) | ||||
| fr->klattp[KLATT_AV] = seq[1].frame->klattp[KLATT_AV] - 4; | fr->klattp[KLATT_AV] = seq[1].frame->klattp[KLATT_AV] - 4; | ||||
| set_frame_rms(fr, rms); | set_frame_rms(fr, rms); | ||||
| if ((vcolour > 0) && (vcolour <= N_VCOLOUR)) { | if ((vcolour > 0) && (vcolour <= N_VCOLOUR)) { | ||||
| for (int ix = 0; ix < *n_frames; ix++) { | |||||
| for (ix = 0; ix < *n_frames; ix++) { | |||||
| fr = CopyFrame(seq[ix].frame, 0); | fr = CopyFrame(seq[ix].frame, 0); | ||||
| seq[ix].frame = fr; | seq[ix].frame = fr; | ||||
| for (int formant = 1; formant <= 5; formant++) { | |||||
| for (formant = 1; formant <= 5; formant++) { | |||||
| int x; | int x; | ||||
| x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1]; | x = fr->ffreq[formant] * vcolouring[vcolour-1][formant-1]; | ||||
| fr->ffreq[formant] = x / 256; | fr->ffreq[formant] = x / 256; | ||||
| frame_t *frame2; | frame_t *frame2; | ||||
| frame_t *frame1; | frame_t *frame1; | ||||
| frame_t *frame_centre; | frame_t *frame_centre; | ||||
| int ix; | |||||
| int len; | int len; | ||||
| int pk; | int pk; | ||||
| int modified; | int modified; | ||||
| frame_centre = (frame_t *)q[2]; | frame_centre = (frame_t *)q[2]; | ||||
| // backwards | // backwards | ||||
| int ix = syllable_centre -1; | |||||
| ix = syllable_centre -1; | |||||
| frame = frame2 = frame_centre; | frame = frame2 = frame_centre; | ||||
| for (;;) { | for (;;) { | ||||
| if (ix < 0) ix = N_WCMDQ-1; | if (ix < 0) ix = N_WCMDQ-1; | ||||
| int len; | int len; | ||||
| int frame_length; | int frame_length; | ||||
| int length_factor; | int length_factor; | ||||
| int length_mod; | |||||
| int length_sum; | |||||
| int length_min; | |||||
| int total_len = 0; | int total_len = 0; | ||||
| static int wave_flag = 0; | static int wave_flag = 0; | ||||
| int wcmd_spect = WCMD_SPECT; | int wcmd_spect = WCMD_SPECT; | ||||
| if (fmt_params->fmt_addr == 0) | if (fmt_params->fmt_addr == 0) | ||||
| return 0; | return 0; | ||||
| int length_mod = plist->length; | |||||
| length_mod = plist->length; | |||||
| if (length_mod == 0) length_mod = 256; | if (length_mod == 0) length_mod = 256; | ||||
| int length_min = (samplerate/70); // greater than one cycle at low pitch (Hz) | |||||
| length_min = (samplerate/70); // greater than one cycle at low pitch (Hz) | |||||
| if (which == 2) { | if (which == 2) { | ||||
| if ((translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] > 0) && ((this_ph->std_length >= translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD]) || (plist->synthflags & SFLAG_LENGTHEN) || (this_ph->phflags & phLONG))) | if ((translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD] > 0) && ((this_ph->std_length >= translator->langopts.param[LOPT_LONG_VOWEL_THRESHOLD]) || (plist->synthflags & SFLAG_LENGTHEN) || (this_ph->phflags & phLONG))) | ||||
| length_min *= 2; // ensure long vowels are longer | length_min *= 2; // ensure long vowels are longer | ||||
| syllable_centre = wcmdq_tail; | syllable_centre = wcmdq_tail; | ||||
| } | } | ||||
| int length_sum = 0; | |||||
| length_sum = 0; | |||||
| for (frameix = 1; frameix < n_frames; frameix++) { | for (frameix = 1; frameix < n_frames; frameix++) { | ||||
| length_factor = length_mod; | length_factor = length_mod; | ||||
| if (frames[frameix-1].frflags & FRFLAG_LEN_MOD) // reduce effect of length mod | if (frames[frameix-1].frflags & FRFLAG_LEN_MOD) // reduce effect of length mod |
| static void ResetLetterBits(Translator *tr, int groups) | static void ResetLetterBits(Translator *tr, int groups) | ||||
| { | { | ||||
| // Clear all the specified groups | // Clear all the specified groups | ||||
| unsigned int mask = ~groups; | |||||
| unsigned int ix; | |||||
| unsigned int mask; | |||||
| for (int ix = 0; ix < sizeof(tr->letter_bits); ix++) | |||||
| mask = ~groups; | |||||
| for (ix = 0; ix < sizeof(tr->letter_bits); ix++) | |||||
| tr->letter_bits[ix] &= mask; | tr->letter_bits[ix] &= mask; | ||||
| } | } | ||||
| static void SetLetterBits(Translator *tr, int group, const char *string) | static void SetLetterBits(Translator *tr, int group, const char *string) | ||||
| { | { | ||||
| int bits; | |||||
| unsigned char c; | unsigned char c; | ||||
| int bits = (1L << group); | |||||
| bits = (1L << group); | |||||
| while ((c = *string++) != 0) | while ((c = *string++) != 0) | ||||
| tr->letter_bits[c] |= bits; | tr->letter_bits[c] |= bits; | ||||
| } | } | ||||
| static void SetLetterBitsRange(Translator *tr, int group, int first, int last) | static void SetLetterBitsRange(Translator *tr, int group, int first, int last) | ||||
| { | { | ||||
| int bits = (1L << group); | |||||
| for (int ix = first; ix <= last; ix++) | |||||
| int bits; | |||||
| int ix; | |||||
| bits = (1L << group); | |||||
| for (ix = first; ix <= last; ix++) | |||||
| tr->letter_bits[ix] |= bits; | tr->letter_bits[ix] |= bits; | ||||
| } | } | ||||
| static Translator *NewTranslator(void) | static Translator *NewTranslator(void) | ||||
| { | { | ||||
| Translator *tr; | Translator *tr; | ||||
| int ix; | |||||
| static const unsigned char stress_amps2[] = { 18, 18, 20, 20, 20, 22, 22, 20 }; | static const unsigned char stress_amps2[] = { 18, 18, 20, 20, 20, 22, 22, 20 }; | ||||
| static const short stress_lengths2[8] = { 182, 140, 220, 220, 220, 240, 260, 280 }; | static const short stress_lengths2[8] = { 182, 140, 220, 220, 220, 240, 260, 280 }; | ||||
| static const wchar_t empty_wstring[1] = { 0 }; | static const wchar_t empty_wstring[1] = { 0 }; | ||||
| tr->punct_within_word = punct_in_word; | tr->punct_within_word = punct_in_word; | ||||
| tr->chars_ignore = chars_ignore_default; | tr->chars_ignore = chars_ignore_default; | ||||
| for (int ix = 0; ix < 8; ix++) { | |||||
| for (ix = 0; ix < 8; ix++) { | |||||
| tr->stress_amps[ix] = stress_amps2[ix]; | tr->stress_amps[ix] = stress_amps2[ix]; | ||||
| tr->stress_amps_r[ix] = stress_amps2[ix] - 1; | tr->stress_amps_r[ix] = stress_amps2[ix] - 1; | ||||
| tr->stress_lengths[ix] = stress_lengths2[ix]; | tr->stress_lengths[ix] = stress_lengths2[ix]; |
| int lookupwchar(const unsigned short *list, int c) | int lookupwchar(const unsigned short *list, int c) | ||||
| { | { | ||||
| // Is the character c in the list ? | // Is the character c in the list ? | ||||
| int ix; | |||||
| for (int ix = 0; list[ix] != 0; ix++) { | |||||
| for (ix = 0; list[ix] != 0; ix++) { | |||||
| if (list[ix] == c) | if (list[ix] == c) | ||||
| return ix+1; | return ix+1; | ||||
| } | } | ||||
| // Replace character c by another character. | // Replace character c by another character. | ||||
| // Returns 0 = not found, 1 = delete character | // Returns 0 = not found, 1 = delete character | ||||
| for (int ix = 0; list[ix] != 0; ix += 2) { | |||||
| int ix; | |||||
| for (ix = 0; list[ix] != 0; ix += 2) { | |||||
| if (list[ix] == c) | if (list[ix] == c) | ||||
| return list[ix+1]; | return list[ix+1]; | ||||
| } | } | ||||
| // returns the number of bytes written | // returns the number of bytes written | ||||
| int n_bytes; | int n_bytes; | ||||
| int j; | |||||
| int shift; | |||||
| static char unsigned code[4] = { 0, 0xc0, 0xe0, 0xf0 }; | static char unsigned code[4] = { 0, 0xc0, 0xe0, 0xf0 }; | ||||
| if (c < 0x80) { | if (c < 0x80) { | ||||
| else | else | ||||
| n_bytes = 3; | n_bytes = 3; | ||||
| int shift = 6*n_bytes; | |||||
| shift = 6*n_bytes; | |||||
| buf[0] = code[n_bytes] | (c >> shift); | buf[0] = code[n_bytes] | (c >> shift); | ||||
| for (int j = 0; j < n_bytes; j++) { | |||||
| for (j = 0; j < n_bytes; j++) { | |||||
| shift -= 6; | shift -= 6; | ||||
| buf[j+1] = 0x80 + ((c >> shift) & 0x3f); | buf[j+1] = 0x80 + ((c >> shift) & 0x3f); | ||||
| } | } | ||||
| // backwards: set if we are moving backwards through the UTF8 string | // backwards: set if we are moving backwards through the UTF8 string | ||||
| int c1; | int c1; | ||||
| int n_bytes; | |||||
| int ix; | |||||
| static const unsigned char mask[4] = { 0xff, 0x1f, 0x0f, 0x07 }; | static const unsigned char mask[4] = { 0xff, 0x1f, 0x0f, 0x07 }; | ||||
| // find the start of the next/previous character | // find the start of the next/previous character | ||||
| buf++; | buf++; | ||||
| } | } | ||||
| int n_bytes = 0; | |||||
| n_bytes = 0; | |||||
| if ((c1 = *buf++) & 0x80) { | if ((c1 = *buf++) & 0x80) { | ||||
| if ((c1 & 0xe0) == 0xc0) | if ((c1 & 0xe0) == 0xc0) | ||||
| n_bytes = 3; | n_bytes = 3; | ||||
| c1 &= mask[n_bytes]; | c1 &= mask[n_bytes]; | ||||
| for (int ix = 0; ix < n_bytes; ix++) | |||||
| for (ix = 0; ix < n_bytes; ix++) | |||||
| c1 = (c1 << 6) + (*buf++ & 0x3f); | c1 = (c1 << 6) + (*buf++ & 0x3f); | ||||
| } | } | ||||
| *c = c1; | *c = c1; | ||||
| int count = 0; | int count = 0; | ||||
| int nbytes; | int nbytes; | ||||
| int ok; | int ok; | ||||
| int ix; | |||||
| char *word; | char *word; | ||||
| char *wbuf; | char *wbuf; | ||||
| char word_buf[80]; | char word_buf[80]; | ||||
| word = word1; | word = word1; | ||||
| wbuf = word_buf; | wbuf = word_buf; | ||||
| int ix = 0; | |||||
| ix = 0; | |||||
| for (;;) { | for (;;) { | ||||
| ok = 0; | ok = 0; | ||||
| char *eqlist; | char *eqlist; | ||||
| char *p_out; | char *p_out; | ||||
| char *p_in; | char *p_in; | ||||
| int remove_stress = 0; | |||||
| char phonbuf[N_WORD_PHONEMES]; | char phonbuf[N_WORD_PHONEMES]; | ||||
| // has a phoneme equivalence table been specified for this language pair? | // has a phoneme equivalence table been specified for this language pair? | ||||
| len = (pb[2] << 8) + pb[3]; // size of this table in words | len = (pb[2] << 8) + pb[3]; // size of this table in words | ||||
| pb += (len * 4); | pb += (len * 4); | ||||
| } | } | ||||
| int remove_stress = pb[1]; | |||||
| remove_stress = pb[1]; | |||||
| if (option_phonemes & espeakPHONEMES_TRACE) { | if (option_phonemes & espeakPHONEMES_TRACE) { | ||||
| DecodePhonemes(phonemes, phonbuf); | DecodePhonemes(phonemes, phonbuf); | ||||
| // word1 is terminated by space (0x20) character | // word1 is terminated by space (0x20) character | ||||
| char *word1; | char *word1; | ||||
| int word_length; | |||||
| int ix; | int ix; | ||||
| char *p; | char *p; | ||||
| int pfix; | int pfix; | ||||
| char end_phonemes2[N_WORD_PHONEMES]; | char end_phonemes2[N_WORD_PHONEMES]; | ||||
| char word_copy[N_WORD_BYTES]; | char word_copy[N_WORD_BYTES]; | ||||
| char word_copy2[N_WORD_BYTES]; | char word_copy2[N_WORD_BYTES]; | ||||
| int word_copy_length; | |||||
| char prefix_chars[0x3f + 2]; | char prefix_chars[0x3f + 2]; | ||||
| int found = 0; | int found = 0; | ||||
| int end_flags; | int end_flags; | ||||
| int spell_word; | int spell_word; | ||||
| int stress_bits; | int stress_bits; | ||||
| int emphasize_allcaps = 0; | int emphasize_allcaps = 0; | ||||
| int wflags; | |||||
| int wmark; | |||||
| int was_unpronouncable = 0; | int was_unpronouncable = 0; | ||||
| int loopcount; | int loopcount; | ||||
| int add_suffix_phonemes = 0; | int add_suffix_phonemes = 0; | ||||
| memset(wtab_null, 0, sizeof(wtab_null)); | memset(wtab_null, 0, sizeof(wtab_null)); | ||||
| wtab = wtab_null; | wtab = wtab_null; | ||||
| } | } | ||||
| int wflags = wtab->flags; | |||||
| int wmark = wtab->wmark; | |||||
| wflags = wtab->flags; | |||||
| wmark = wtab->wmark; | |||||
| dictionary_flags[0] = 0; | dictionary_flags[0] = 0; | ||||
| dictionary_flags[1] = 0; | dictionary_flags[1] = 0; | ||||
| wordx = word1; | wordx = word1; | ||||
| utf8_in(&first_char, wordx); | utf8_in(&first_char, wordx); | ||||
| int word_length = 0; | |||||
| word_length = 0; | |||||
| while ((*wordx != 0) && (*wordx != ' ')) { | while ((*wordx != 0) && (*wordx != ' ')) { | ||||
| wordx += utf8_in(&last_char, wordx); | wordx += utf8_in(&last_char, wordx); | ||||
| word_length++; | word_length++; | ||||
| } | } | ||||
| int word_copy_length = wordx - word_start; | |||||
| word_copy_length = wordx - word_start; | |||||
| if (word_copy_length >= N_WORD_BYTES) | if (word_copy_length >= N_WORD_BYTES) | ||||
| word_copy_length = N_WORD_BYTES-1; | word_copy_length = N_WORD_BYTES-1; | ||||
| memcpy(word_copy2, word_start, word_copy_length); | memcpy(word_copy2, word_start, word_copy_length); | ||||
| // word's pronunciation is not given in the dictionary list, although | // word's pronunciation is not given in the dictionary list, although | ||||
| // dictionary_flags may have ben set there | // dictionary_flags may have ben set there | ||||
| int posn = 0; | |||||
| int non_initial = 0; | |||||
| int length = 999; | |||||
| int posn; | |||||
| int non_initial; | |||||
| int length; | |||||
| posn = 0; | |||||
| non_initial = 0; | |||||
| length = 999; | |||||
| wordx = word1; | wordx = word1; | ||||
| while (((length < 3) && (length > 0)) || (word_length > 1 && Unpronouncable(tr, wordx, posn))) { | while (((length < 3) && (length > 0)) || (word_length > 1 && Unpronouncable(tr, wordx, posn))) { | ||||
| int UpperCaseInWord(Translator *tr, char *word, int c) | int UpperCaseInWord(Translator *tr, char *word, int c) | ||||
| { | { | ||||
| int ix; | |||||
| int len; | int len; | ||||
| const char *p; | const char *p; | ||||
| if (tr->translator_name == L('g', 'a')) { | if (tr->translator_name == L('g', 'a')) { | ||||
| // Irish | // Irish | ||||
| for (int ix = 0;; ix++) { | |||||
| for (ix = 0;; ix++) { | |||||
| if ((p = UCase_ga[ix]) == NULL) | if ((p = UCase_ga[ix]) == NULL) | ||||
| break; | break; | ||||
| void ReadTonePoints(char *string, int *tone_pts) | void ReadTonePoints(char *string, int *tone_pts) | ||||
| { | { | ||||
| // tone_pts[] is int[12] | // tone_pts[] is int[12] | ||||
| int ix; | |||||
| for (int ix = 0; ix < 12; ix++) | |||||
| for (ix = 0; ix < 12; ix++) | |||||
| tone_pts[ix] = -1; | tone_pts[ix] = -1; | ||||
| sscanf(string, "%d %d %d %d %d %d %d %d %d %d", | sscanf(string, "%d %d %d %d %d %d %d %d %d %d", | ||||
| char *p; | char *p; | ||||
| espeak_VOICE *voice_data; | espeak_VOICE *voice_data; | ||||
| int priority; | int priority; | ||||
| int age; | |||||
| int n_variants = 4; // default, number of variants of this voice before using another voice | int n_variants = 4; // default, number of variants of this voice before using another voice | ||||
| int gender; | |||||
| #ifdef PLATFORM_WINDOWS | #ifdef PLATFORM_WINDOWS | ||||
| char fname_buf[sizeof(path_home)+15]; | char fname_buf[sizeof(path_home)+15]; | ||||
| vname[0] = 0; | vname[0] = 0; | ||||
| vgender[0] = 0; | vgender[0] = 0; | ||||
| int age = 0; | |||||
| age = 0; | |||||
| while (fgets_strip(linebuf, sizeof(linebuf), f_in) != NULL) { | while (fgets_strip(linebuf, sizeof(linebuf), f_in) != NULL) { | ||||
| if (memcmp(linebuf, "name", 4) == 0) { | if (memcmp(linebuf, "name", 4) == 0) { | ||||
| } | } | ||||
| languages[langix++] = 0; | languages[langix++] = 0; | ||||
| int gender = LookupMnem(genders, vgender); | |||||
| gender = LookupMnem(genders, vgender); | |||||
| if (n_languages == 0) | if (n_languages == 0) | ||||
| return NULL; // no language lines in the voice file | return NULL; // no language lines in the voice file | ||||
| { | { | ||||
| // Set voice to the default values | // Set voice to the default values | ||||
| int pk; | |||||
| static unsigned char default_heights[N_PEAKS] = { 130, 128, 120, 116, 100, 100, 128, 128, 128 }; // changed for v.1.47 | static unsigned char default_heights[N_PEAKS] = { 130, 128, 120, 116, 100, 100, 128, 128, 128 }; // changed for v.1.47 | ||||
| static unsigned char default_widths[N_PEAKS] = { 140, 128, 128, 160, 171, 171, 128, 128, 128 }; | static unsigned char default_widths[N_PEAKS] = { 140, 128, 128, 160, 171, 171, 128, 128, 128 }; | ||||
| voice->roughness = 2; | voice->roughness = 2; | ||||
| InitBreath(); | InitBreath(); | ||||
| for (int pk = 0; pk < N_PEAKS; pk++) { | |||||
| for (pk = 0; pk < N_PEAKS; pk++) { | |||||
| voice->freq[pk] = 256; | voice->freq[pk] = 256; | ||||
| voice->height[pk] = default_heights[pk]*2; | voice->height[pk] = default_heights[pk]*2; | ||||
| voice->width[pk] = default_widths[pk]*2; | voice->width[pk] = default_widths[pk]*2; | ||||
| static void VoiceFormant(char *p) | static void VoiceFormant(char *p) | ||||
| { | { | ||||
| // Set parameters for a formant | // Set parameters for a formant | ||||
| int ix; | |||||
| int formant; | int formant; | ||||
| int freq = 100; | int freq = 100; | ||||
| int height = 100; | int height = 100; | ||||
| int width = 100; | int width = 100; | ||||
| int freqadd = 0; | int freqadd = 0; | ||||
| int ix = sscanf(p, "%d %d %d %d %d", &formant, &freq, &height, &width, &freqadd); | |||||
| ix = sscanf(p, "%d %d %d %d %d", &formant, &freq, &height, &width, &freqadd); | |||||
| if (ix < 2) | if (ix < 2) | ||||
| return; | return; | ||||
| static void PhonemeReplacement(int type, char *p) | static void PhonemeReplacement(int type, char *p) | ||||
| { | { | ||||
| int n; | |||||
| int phon; | int phon; | ||||
| int flags = 0; | int flags = 0; | ||||
| char phon_string1[12]; | char phon_string1[12]; | ||||
| char phon_string2[12]; | char phon_string2[12]; | ||||
| strcpy(phon_string2, "NULL"); | strcpy(phon_string2, "NULL"); | ||||
| int n = sscanf(p, "%d %s %s", &flags, phon_string1, phon_string2); | |||||
| n = sscanf(p, "%d %s %s", &flags, phon_string1, phon_string2); | |||||
| if ((n < 2) || (n_replace_phonemes >= N_REPLACE_PHONEMES)) | if ((n < 2) || (n_replace_phonemes >= N_REPLACE_PHONEMES)) | ||||
| return; | return; | ||||
| static unsigned int StringToWord2(const char *string) | static unsigned int StringToWord2(const char *string) | ||||
| { | { | ||||
| // Convert a language name string to a word such as L('e','n') | // Convert a language name string to a word such as L('e','n') | ||||
| int ix; | |||||
| int c; | int c; | ||||
| unsigned int value = 0; | unsigned int value = 0; | ||||
| for (int ix = 0; (ix < 4) && ((c = string[ix]) != 0); ix++) | |||||
| for (ix = 0; (ix < 4) && ((c = string[ix]) != 0); ix++) | |||||
| value = (value << 8) | (c & 0xff); | value = (value << 8) | (c & 0xff); | ||||
| return value; | return value; | ||||
| } | } | ||||
| static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control) | static int SetVoiceScores(espeak_VOICE *voice_select, espeak_VOICE **voices, int control) | ||||
| { | { | ||||
| // control: bit0=1 include mbrola voices | // control: bit0=1 include mbrola voices | ||||
| int ix; | |||||
| int score; | int score; | ||||
| int nv; // number of candidates | int nv; // number of candidates | ||||
| int n_parts = 0; | int n_parts = 0; | ||||
| if ((voice_select->languages != NULL) && (voice_select->languages[0] != 0)) { | if ((voice_select->languages != NULL) && (voice_select->languages[0] != 0)) { | ||||
| n_parts = 1; | n_parts = 1; | ||||
| lang_len = strlen(voice_select->languages); | lang_len = strlen(voice_select->languages); | ||||
| for (int ix = 0; (ix <= lang_len) && ((unsigned)ix < sizeof(language)); ix++) { | |||||
| for (ix = 0; (ix <= lang_len) && ((unsigned)ix < sizeof(language)); ix++) { | |||||
| if ((language[ix] = tolower(voice_select->languages[ix])) == '-') | if ((language[ix] = tolower(voice_select->languages[ix])) == '-') | ||||
| n_parts++; | n_parts++; | ||||
| } | } | ||||
| // select those voices which match the specified language | // select those voices which match the specified language | ||||
| nv = 0; | nv = 0; | ||||
| for (int ix = 0; ix < n_voices_list; ix++) { | |||||
| for (ix = 0; ix < n_voices_list; ix++) { | |||||
| vp = voices_list[ix]; | vp = voices_list[ix]; | ||||
| if (((control & 1) == 0) && (memcmp(vp->identifier, "mb/", 3) == 0)) | if (((control & 1) == 0) && (memcmp(vp->identifier, "mb/", 3) == 0)) | ||||
| espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2) | espeak_VOICE *SelectVoiceByName(espeak_VOICE **voices, const char *name2) | ||||
| { | { | ||||
| int ix; | |||||
| int match_fname = -1; | int match_fname = -1; | ||||
| int match_fname2 = -1; | int match_fname2 = -1; | ||||
| int match_name = -1; | int match_name = -1; | ||||
| const char *id; // this is the filename within espeak-data/voices | const char *id; // this is the filename within espeak-data/voices | ||||
| char *variant_name; | char *variant_name; | ||||
| int last_part_len; | |||||
| char last_part[41]; | char last_part[41]; | ||||
| char name[40]; | char name[40]; | ||||
| } | } | ||||
| sprintf(last_part, "%c%s", PATHSEP, name); | sprintf(last_part, "%c%s", PATHSEP, name); | ||||
| int last_part_len = strlen(last_part); | |||||
| last_part_len = strlen(last_part); | |||||
| for (int ix = 0; voices[ix] != NULL; ix++) { | |||||
| for (ix = 0; voices[ix] != NULL; ix++) { | |||||
| if (strcmp(name, voices[ix]->name) == 0) { | if (strcmp(name, voices[ix]->name) == 0) { | ||||
| match_name = ix; // found matching voice name | match_name = ix; // found matching voice name | ||||
| break; | break; | ||||
| espeak_ERROR SetVoiceByName(const char *name) | espeak_ERROR SetVoiceByName(const char *name) | ||||
| { | { | ||||
| espeak_VOICE *v; | espeak_VOICE *v; | ||||
| int ix; | |||||
| espeak_VOICE voice_selector; | espeak_VOICE voice_selector; | ||||
| char *variant_name; | char *variant_name; | ||||
| static char buf[60]; | static char buf[60]; | ||||
| variant_name = ExtractVoiceVariantName(buf, 0, 1); | variant_name = ExtractVoiceVariantName(buf, 0, 1); | ||||
| for (int ix = 0;; ix++) { | |||||
| for (ix = 0;; ix++) { | |||||
| // convert voice name to lower case (ascii) | // convert voice name to lower case (ascii) | ||||
| if ((buf[ix] = tolower(buf[ix])) == 0) | if ((buf[ix] = tolower(buf[ix])) == 0) | ||||
| break; | break; | ||||
| void FreeVoiceList() | void FreeVoiceList() | ||||
| { | { | ||||
| for (int ix = 0; ix < n_voices_list; ix++) { | |||||
| int ix; | |||||
| for (ix = 0; ix < n_voices_list; ix++) { | |||||
| if (voices_list[ix] != NULL) { | if (voices_list[ix] != NULL) { | ||||
| free(voices_list[ix]); | free(voices_list[ix]); | ||||
| voices_list[ix] = NULL; | voices_list[ix] = NULL; | ||||
| { | { | ||||
| char path_voices[sizeof(path_home)+12]; | char path_voices[sizeof(path_home)+12]; | ||||
| int ix; | |||||
| int j; | |||||
| espeak_VOICE *v; | espeak_VOICE *v; | ||||
| static espeak_VOICE **voices = NULL; | static espeak_VOICE **voices = NULL; | ||||
| SetVoiceScores(voice_spec, voices, 1); | SetVoiceScores(voice_spec, voices, 1); | ||||
| } else { | } else { | ||||
| // list all: omit variant voices and mbrola voices and test voices | // list all: omit variant voices and mbrola voices and test voices | ||||
| int j = 0; | |||||
| for (int ix = 0; (v = voices_list[ix]) != NULL; ix++) { | |||||
| j = 0; | |||||
| for (ix = 0; (v = voices_list[ix]) != NULL; ix++) { | |||||
| if ((v->languages[0] != 0) && (strcmp(&v->languages[1], "variant") != 0) | if ((v->languages[0] != 0) && (strcmp(&v->languages[1], "variant") != 0) | ||||
| && (memcmp(v->identifier, "mb/", 3) != 0) && (memcmp(v->identifier, "test/", 5) != 0)) | && (memcmp(v->identifier, "mb/", 3) != 0) && (memcmp(v->identifier, "test/", 5) != 0)) | ||||
| voices[j++] = v; | voices[j++] = v; |
| char *theMono16BitsWaveBuffer, | char *theMono16BitsWaveBuffer, | ||||
| size_t theSize) | size_t theSize) | ||||
| { | { | ||||
| size_t num; | |||||
| if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) | if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| size_t num = write((int)theHandler, theMono16BitsWaveBuffer, theSize); | |||||
| num = write((int)theHandler, theMono16BitsWaveBuffer, theSize); | |||||
| // Keep track of the total number of samples sent -- we use this in | // Keep track of the total number of samples sent -- we use this in | ||||
| // wave_get_read_position and also use it to help calculate the | // wave_get_read_position and also use it to help calculate the | ||||
| // | // | ||||
| int wave_close(void *theHandler) | int wave_close(void *theHandler) | ||||
| { | { | ||||
| int ret; | |||||
| audio_info_t ainfo; | audio_info_t ainfo; | ||||
| int audio_fd = (int)theHandler; | int audio_fd = (int)theHandler; | ||||
| if (!audio_fd) | if (!audio_fd) | ||||
| audio_fd = sun_audio_fd; | audio_fd = sun_audio_fd; | ||||
| // [[[WDW: maybe do a pause/resume ioctl???]]] | // [[[WDW: maybe do a pause/resume ioctl???]]] | ||||
| int ret = ioctl(audio_fd, I_FLUSH, FLUSHRW); | |||||
| ret = ioctl(audio_fd, I_FLUSH, FLUSHRW); | |||||
| ioctl(audio_fd, AUDIO_GETINFO, &ainfo); | ioctl(audio_fd, AUDIO_GETINFO, &ainfo); | ||||
| // Calculate the number of samples that won't get | // Calculate the number of samples that won't get |
| int WcmdqFree() | int WcmdqFree() | ||||
| { | { | ||||
| int i = wcmdq_head - wcmdq_tail; | |||||
| int i; | |||||
| i = wcmdq_head - wcmdq_tail; | |||||
| if (i <= 0) i += N_WCMDQ; | if (i <= 0) i += N_WCMDQ; | ||||
| return i; | return i; | ||||
| } | } | ||||
| PaStreamCallbackFlags flags, void *userData) | PaStreamCallbackFlags flags, void *userData) | ||||
| #endif | #endif | ||||
| { | { | ||||
| int ix; | |||||
| int result; | |||||
| unsigned char *p; | unsigned char *p; | ||||
| unsigned char *out_buf; | unsigned char *out_buf; | ||||
| unsigned char *out_end2; | unsigned char *out_end2; | ||||
| int pa_size; | |||||
| int pa_size = framesPerBuffer*2; | |||||
| pa_size = framesPerBuffer*2; | |||||
| // make a buffer 3x size of the portaudio output | // make a buffer 3x size of the portaudio output | ||||
| int ix = pa_size*3; | |||||
| ix = pa_size*3; | |||||
| if (ix > outbuffer_size) { | if (ix > outbuffer_size) { | ||||
| outbuffer = (unsigned char *)realloc(outbuffer, ix); | outbuffer = (unsigned char *)realloc(outbuffer, ix); | ||||
| if (outbuffer == NULL) { | if (outbuffer == NULL) { | ||||
| event_list_ix = 0; | event_list_ix = 0; | ||||
| int result = WavegenFill(1); | |||||
| result = WavegenFill(1); | |||||
| // copy from the outbut buffer into the portaudio buffer | // copy from the outbut buffer into the portaudio buffer | ||||
| if (result && (out_ptr > out_end2)) | if (result && (out_ptr > out_end2)) | ||||
| void WavegenInit(int rate, int wavemult_fact) | void WavegenInit(int rate, int wavemult_fact) | ||||
| { | { | ||||
| int ix; | |||||
| double x; | double x; | ||||
| if (wavemult_fact == 0) | if (wavemult_fact == 0) | ||||
| wdata.amplitude = 32; | wdata.amplitude = 32; | ||||
| wdata.amplitude_fmt = 100; | wdata.amplitude_fmt = 100; | ||||
| for (int ix = 0; ix < N_EMBEDDED_VALUES; ix++) | |||||
| for (ix = 0; ix < N_EMBEDDED_VALUES; ix++) | |||||
| embedded_value[ix] = embedded_default[ix]; | embedded_value[ix] = embedded_default[ix]; | ||||
| // set up window to generate a spread of harmonics from a | // set up window to generate a spread of harmonics from a | ||||
| if (samplerate != 22050) { | if (samplerate != 22050) { | ||||
| // wavemult table has preset values for 22050 Hz, we only need to | // wavemult table has preset values for 22050 Hz, we only need to | ||||
| // recalculate them if we have a different sample rate | // recalculate them if we have a different sample rate | ||||
| for (int ix = 0; ix < wavemult_max; ix++) { | |||||
| for (ix = 0; ix < wavemult_max; ix++) { | |||||
| x = 127*(1.0 - cos(PI2*ix/wavemult_max)); | x = 127*(1.0 - cos(PI2*ix/wavemult_max)); | ||||
| wavemult[ix] = (int)x; | wavemult[ix] = (int)x; | ||||
| } | } | ||||
| int GetAmplitude(void) | int GetAmplitude(void) | ||||
| { | { | ||||
| int amp; | |||||
| // normal, none, reduced, moderate, strong | // normal, none, reduced, moderate, strong | ||||
| static const unsigned char amp_emphasis[5] = { 16, 16, 10, 16, 22 }; | static const unsigned char amp_emphasis[5] = { 16, 16, 10, 16, 22 }; | ||||
| int amp = (embedded_value[EMBED_A])*55/100; | |||||
| amp = (embedded_value[EMBED_A])*55/100; | |||||
| general_amplitude = amp * amp_emphasis[embedded_value[EMBED_F]] / 16; | general_amplitude = amp * amp_emphasis[embedded_value[EMBED_F]] / 16; | ||||
| return general_amplitude; | return general_amplitude; | ||||
| } | } | ||||
| static void WavegenSetEcho(void) | static void WavegenSetEcho(void) | ||||
| { | { | ||||
| int delay; | |||||
| int amp; | |||||
| voicing = wvoice->voicing; | voicing = wvoice->voicing; | ||||
| int delay = wvoice->echo_delay; | |||||
| int amp = wvoice->echo_amp; | |||||
| delay = wvoice->echo_delay; | |||||
| amp = wvoice->echo_amp; | |||||
| if (delay >= N_ECHO_BUF) | if (delay >= N_ECHO_BUF) | ||||
| delay = N_ECHO_BUF-1; | delay = N_ECHO_BUF-1; | ||||
| int fp; // centre freq of peak | int fp; // centre freq of peak | ||||
| int fhi; // high freq of peak | int fhi; // high freq of peak | ||||
| int h; // harmonic number | int h; // harmonic number | ||||
| int pk; | |||||
| int pk; | |||||
| int hmax; | |||||
| int hmax_samplerate; // highest harmonic allowed for the samplerate | int hmax_samplerate; // highest harmonic allowed for the samplerate | ||||
| int x; | int x; | ||||
| int ix; | |||||
| int ix; | |||||
| int h1; | |||||
| // initialise as much of *out as we will need | // initialise as much of *out as we will need | ||||
| if (wvoice == NULL) | if (wvoice == NULL) | ||||
| return 1; | return 1; | ||||
| int hmax = (peaks[wvoice->n_harmonic_peaks].freq + peaks[wvoice->n_harmonic_peaks].right)/pitch; | |||||
| hmax = (peaks[wvoice->n_harmonic_peaks].freq + peaks[wvoice->n_harmonic_peaks].right)/pitch; | |||||
| if (hmax >= MAX_HARMONIC) | if (hmax >= MAX_HARMONIC) | ||||
| hmax = MAX_HARMONIC-1; | hmax = MAX_HARMONIC-1; | ||||
| htab[h++] += pk_shape[(f-fp)/(p->right>>8)] * p->height; | htab[h++] += pk_shape[(f-fp)/(p->right>>8)] * p->height; | ||||
| } | } | ||||
| int y; | |||||
| int h2; | |||||
| // increase bass | // increase bass | ||||
| int y = peaks[1].height * 10; // addition as a multiple of 1/256s | |||||
| int h2 = (1000<<16)/pitch; // decrease until 1000Hz | |||||
| y = peaks[1].height * 10; // addition as a multiple of 1/256s | |||||
| h2 = (1000<<16)/pitch; // decrease until 1000Hz | |||||
| if (h2 > 0) { | if (h2 > 0) { | ||||
| x = y/h2; | x = y/h2; | ||||
| h = 1; | h = 1; | ||||
| } | } | ||||
| // adjust the amplitude of the first harmonic, affects tonal quality | // adjust the amplitude of the first harmonic, affects tonal quality | ||||
| int h1 = htab[1] * option_harmonic1; | |||||
| h1 = htab[1] * option_harmonic1; | |||||
| htab[1] = h1/8; | htab[1] = h1/8; | ||||
| // calc intermediate increments of LF harmonics | // calc intermediate increments of LF harmonics | ||||
| { | { | ||||
| // Called every 64 samples to increment the formant freq, height, and widths | // Called every 64 samples to increment the formant freq, height, and widths | ||||
| int x; | |||||
| int ix; | int ix; | ||||
| static int Flutter_ix = 0; | static int Flutter_ix = 0; | ||||
| // advance the pitch | // advance the pitch | ||||
| wdata.pitch_ix += wdata.pitch_inc; | wdata.pitch_ix += wdata.pitch_inc; | ||||
| if ((ix = wdata.pitch_ix>>8) > 127) ix = 127; | if ((ix = wdata.pitch_ix>>8) > 127) ix = 127; | ||||
| int x = wdata.pitch_env[ix] * wdata.pitch_range; | |||||
| x = wdata.pitch_env[ix] * wdata.pitch_range; | |||||
| wdata.pitch = (x>>8) + wdata.pitch_base; | wdata.pitch = (x>>8) + wdata.pitch_base; | ||||
| amp_ix += amp_inc; | amp_ix += amp_inc; | ||||
| static double resonator(RESONATOR *r, double input) | static double resonator(RESONATOR *r, double input) | ||||
| { | { | ||||
| double x = r->a * input + r->b * r->x1 + r->c * r->x2; | |||||
| double x; | |||||
| x = r->a * input + r->b * r->x1 + r->c * r->x2; | |||||
| r->x2 = r->x1; | r->x2 = r->x1; | ||||
| r->x1 = x; | r->x1 = x; | ||||
| // bwidth Bandwidth of resonator in Hz | // bwidth Bandwidth of resonator in Hz | ||||
| // init Initialize internal data | // init Initialize internal data | ||||
| double x; | |||||
| double arg; | |||||
| if (init) { | if (init) { | ||||
| rp->x1 = 0; | rp->x1 = 0; | ||||
| rp->x2 = 0; | rp->x2 = 0; | ||||
| } | } | ||||
| double arg = minus_pi_t * bwidth; | |||||
| double x = exp(arg); | |||||
| arg = minus_pi_t * bwidth; | |||||
| x = exp(arg); | |||||
| rp->c = -(x * x); | rp->c = -(x * x); | ||||
| void InitBreath(void) | void InitBreath(void) | ||||
| { | { | ||||
| int ix; | |||||
| minus_pi_t = -PI / samplerate; | minus_pi_t = -PI / samplerate; | ||||
| two_pi_t = -2.0 * minus_pi_t; | two_pi_t = -2.0 * minus_pi_t; | ||||
| for (int ix = 0; ix < N_PEAKS; ix++) | |||||
| for (ix = 0; ix < N_PEAKS; ix++) | |||||
| setresonator(&rbreath[ix], 2000, 200, 1); | setresonator(&rbreath[ix], 2000, 200, 1); | ||||
| } | } | ||||
| static void SetBreath() | static void SetBreath() | ||||
| { | { | ||||
| int pk; | |||||
| if (wvoice->breath[0] == 0) | if (wvoice->breath[0] == 0) | ||||
| return; | return; | ||||
| for (int pk = 1; pk < N_PEAKS; pk++) { | |||||
| for (pk = 1; pk < N_PEAKS; pk++) { | |||||
| if (wvoice->breath[pk] != 0) { | if (wvoice->breath[pk] != 0) { | ||||
| // breath[0] indicates that some breath formants are needed | // breath[0] indicates that some breath formants are needed | ||||
| // set the freq from the current ynthesis formant and the width from the voice data | // set the freq from the current ynthesis formant and the width from the voice data | ||||
| static int ApplyBreath(void) | static int ApplyBreath(void) | ||||
| { | { | ||||
| int value = 0; | int value = 0; | ||||
| int noise; | |||||
| int ix; | |||||
| int amp; | int amp; | ||||
| // use two random numbers, for alternate formants | // use two random numbers, for alternate formants | ||||
| int noise = (rand() & 0x3fff) - 0x2000; | |||||
| noise = (rand() & 0x3fff) - 0x2000; | |||||
| for (int ix = 1; ix < N_PEAKS; ix++) { | |||||
| for (ix = 1; ix < N_PEAKS; ix++) { | |||||
| if ((amp = wvoice->breath[ix]) != 0) { | if ((amp = wvoice->breath[ix]) != 0) { | ||||
| amp *= (peaks[ix].height >> 14); | amp *= (peaks[ix].height >> 14); | ||||
| value += (int)resonator(&rbreath[ix], noise) * amp; | value += (int)resonator(&rbreath[ix], noise) * amp; | ||||
| static void SetPitchFormants() | static void SetPitchFormants() | ||||
| { | { | ||||
| int ix; | |||||
| int factor = 256; | int factor = 256; | ||||
| int pitch_value; | int pitch_value; | ||||
| factor = 256 + (25 * (pitch_value - 50))/50; | factor = 256 + (25 * (pitch_value - 50))/50; | ||||
| } | } | ||||
| for (int ix = 0; ix <= 5; ix++) | |||||
| for (ix = 0; ix <= 5; ix++) | |||||
| wvoice->freq[ix] = (wvoice->freq2[ix] * factor)/256; | wvoice->freq[ix] = (wvoice->freq2[ix] * factor)/256; | ||||
| factor = embedded_value[EMBED_T]*3; | factor = embedded_value[EMBED_T]*3; | ||||
| { | { | ||||
| // there was an embedded command in the text at this point | // there was an embedded command in the text at this point | ||||
| int sign = 0; | int sign = 0; | ||||
| int command; | |||||
| int command = control & 0x1f; | |||||
| command = control & 0x1f; | |||||
| if ((control & 0x60) == 0x60) | if ((control & 0x60) == 0x60) | ||||
| sign = -1; | sign = -1; | ||||
| else if ((control & 0x60) == 0x40) | else if ((control & 0x60) == 0x40) | ||||
| void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | void SetPitch2(voice_t *voice, int pitch1, int pitch2, int *pitch_base, int *pitch_range) | ||||
| { | { | ||||
| int x; | |||||
| int base; | |||||
| int range; | |||||
| int pitch_value; | int pitch_value; | ||||
| if (pitch1 > pitch2) { | if (pitch1 > pitch2) { | ||||
| int x = pitch1; // swap values | |||||
| x = pitch1; // swap values | |||||
| pitch1 = pitch2; | pitch1 = pitch2; | ||||
| pitch2 = x; | pitch2 = x; | ||||
| } | } | ||||
| if (pitch_value < 0) | if (pitch_value < 0) | ||||
| pitch_value = 0; | pitch_value = 0; | ||||
| int base = (voice->pitch_base * pitch_adjust_tab[pitch_value])/128; | |||||
| int range = (voice->pitch_range * embedded_value[EMBED_R])/50; | |||||
| base = (voice->pitch_base * pitch_adjust_tab[pitch_value])/128; | |||||
| range = (voice->pitch_range * embedded_value[EMBED_R])/50; | |||||
| // compensate for change in pitch when the range is narrowed or widened | // compensate for change in pitch when the range is narrowed or widened | ||||
| base -= (range - voice->pitch_range)*18; | base -= (range - voice->pitch_range)*18; | ||||
| { | { | ||||
| int ix; | int ix; | ||||
| DOUBLEX next; | DOUBLEX next; | ||||
| int length2; | |||||
| int length4; | |||||
| int qix; | |||||
| int cmd; | |||||
| static int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256 | static int glottal_reduce_tab1[4] = { 0x30, 0x30, 0x40, 0x50 }; // vowel before [?], amp * 1/256 | ||||
| static int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256 | static int glottal_reduce_tab2[4] = { 0x90, 0xa0, 0xb0, 0xc0 }; // vowel after [?], amp * 1/256 | ||||
| glottal_reduce = glottal_reduce_tab2[(modn >> 8) & 3]; | glottal_reduce = glottal_reduce_tab2[(modn >> 8) & 3]; | ||||
| } | } | ||||
| for (int qix = wcmdq_head+1;; qix++) { | |||||
| for (qix = wcmdq_head+1;; qix++) { | |||||
| if (qix >= N_WCMDQ) qix = 0; | if (qix >= N_WCMDQ) qix = 0; | ||||
| if (qix == wcmdq_tail) break; | if (qix == wcmdq_tail) break; | ||||
| int cmd = wcmdq[qix][0]; | |||||
| cmd = wcmdq[qix][0]; | |||||
| if (cmd == WCMD_SPECT) { | if (cmd == WCMD_SPECT) { | ||||
| end_wave = 0; // next wave generation is from another spectrum | end_wave = 0; // next wave generation is from another spectrum | ||||
| break; | break; | ||||
| } | } | ||||
| // round the length to a multiple of the stepsize | // round the length to a multiple of the stepsize | ||||
| int length2 = (length + STEPSIZE/2) & ~0x3f; | |||||
| length2 = (length + STEPSIZE/2) & ~0x3f; | |||||
| if (length2 == 0) | if (length2 == 0) | ||||
| length2 = STEPSIZE; | length2 = STEPSIZE; | ||||
| samplecount_start = samplecount; | samplecount_start = samplecount; | ||||
| nsamples += length2; | nsamples += length2; | ||||
| int length4 = length2/4; | |||||
| length4 = length2/4; | |||||
| peaks[7].freq = (7800 * v->freq[7] + v->freqadd[7]*256) << 8; | peaks[7].freq = (7800 * v->freq[7] + v->freqadd[7]*256) << 8; | ||||
| peaks[8].freq = (9000 * v->freq[8] + v->freqadd[8]*256) << 8; | peaks[8].freq = (9000 * v->freq[8] + v->freqadd[8]*256) << 8; | ||||
| void Write4Bytes(FILE *f, int value) | void Write4Bytes(FILE *f, int value) | ||||
| { | { | ||||
| // Write 4 bytes to a file, least significant first | // Write 4 bytes to a file, least significant first | ||||
| for (int ix = 0; ix < 4; ix++) { | |||||
| int ix; | |||||
| for (ix = 0; ix < 4; ix++) { | |||||
| fputc(value & 0xff, f); | fputc(value & 0xff, f); | ||||
| value = value >> 8; | value = value >> 8; | ||||
| } | } | ||||
| // Call WavegenFill2, and then speed up the output samples. | // Call WavegenFill2, and then speed up the output samples. | ||||
| int WavegenFill(int fill_zeros) | int WavegenFill(int fill_zeros) | ||||
| { | { | ||||
| int finished; | |||||
| unsigned char *p_start; | unsigned char *p_start; | ||||
| p_start = out_ptr; | p_start = out_ptr; | ||||
| // fill_zeros is ignored. It is now done in the portaudio callback | // fill_zeros is ignored. It is now done in the portaudio callback | ||||
| int finished = WavegenFill2(0); | |||||
| finished = WavegenFill2(0); | |||||
| #if HAVE_SONIC_H | #if HAVE_SONIC_H | ||||
| if (sonicSpeed > 1.0) { | if (sonicSpeed > 1.0) { |
| void DisplayVoices(FILE *f_out, char *language) | void DisplayVoices(FILE *f_out, char *language) | ||||
| { | { | ||||
| int ix; | |||||
| const char *p; | const char *p; | ||||
| int len; | int len; | ||||
| int count; | int count; | ||||
| fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | ||||
| for (int ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
| for (ix = 0; (v = voices[ix]) != NULL; ix++) { | |||||
| count = 0; | count = 0; | ||||
| p = v->languages; | p = v->languages; | ||||
| while (*p != 0) { | while (*p != 0) { | ||||
| static void CloseWaveFile() | static void CloseWaveFile() | ||||
| { | { | ||||
| unsigned int pos; | |||||
| if ((f_wave == NULL) || (f_wave == stdout)) | if ((f_wave == NULL) || (f_wave == stdout)) | ||||
| return; | return; | ||||
| fflush(f_wave); | fflush(f_wave); | ||||
| unsigned int pos = ftell(f_wave); | |||||
| pos = ftell(f_wave); | |||||
| fseek(f_wave, 4, SEEK_SET); | fseek(f_wave, 4, SEEK_SET); | ||||
| Write4Bytes(f_wave, pos - 8); | Write4Bytes(f_wave, pos - 8); | ||||
| fseek(f_wave, 40, SEEK_SET); | fseek(f_wave, 40, SEEK_SET); | ||||
| Write4Bytes(f_wave, pos - 44); | Write4Bytes(f_wave, pos - 44); | ||||
| fclose(f_wave); | fclose(f_wave); | ||||
| f_wave = NULL; | f_wave = NULL; | ||||
| } | } | ||||
| static int WavegenFile(void) | static int WavegenFile(void) | ||||
| { | { | ||||
| int finished; | |||||
| unsigned char wav_outbuf[1024]; | unsigned char wav_outbuf[1024]; | ||||
| char fname[210]; | char fname[210]; | ||||
| out_ptr = out_start = wav_outbuf; | out_ptr = out_start = wav_outbuf; | ||||
| out_end = wav_outbuf + sizeof(wav_outbuf); | out_end = wav_outbuf + sizeof(wav_outbuf); | ||||
| int finished = WavegenFill(0); | |||||
| finished = WavegenFill(0); | |||||
| if (quiet) | if (quiet) | ||||
| return finished; | return finished; | ||||
| static int initialise(void) | static int initialise(void) | ||||
| { | { | ||||
| int param; | |||||
| int result; | |||||
| int srate = 22050; // default sample rate | int srate = 22050; // default sample rate | ||||
| // It seems that the wctype functions don't work until the locale has been set | // It seems that the wctype functions don't work until the locale has been set | ||||
| setlocale(LC_CTYPE, ""); | setlocale(LC_CTYPE, ""); | ||||
| } | } | ||||
| int result; | |||||
| if ((result = LoadPhData(&srate)) != 1) { | if ((result = LoadPhData(&srate)) != 1) { | ||||
| if (result == -1) { | if (result == -1) { | ||||
| fprintf(stderr, "Failed to load espeak-data\n"); | fprintf(stderr, "Failed to load espeak-data\n"); | ||||
| SetVoiceStack(NULL, ""); | SetVoiceStack(NULL, ""); | ||||
| SynthesizeInit(); | SynthesizeInit(); | ||||
| for (int param = 0; param < N_SPEECH_PARAM; param++) | |||||
| for (param = 0; param < N_SPEECH_PARAM; param++) | |||||
| param_stack[0].parameter[param] = param_defaults[param]; | param_stack[0].parameter[param] = param_defaults[param]; | ||||
| return 0; | return 0; | ||||
| int c; | int c; | ||||
| int value; | int value; | ||||
| int speed = 175; | int speed = 175; | ||||
| int ix; | |||||
| char *optarg2; | char *optarg2; | ||||
| int amp = 100; // default | int amp = 100; // default | ||||
| int wordgap = 0; | int wordgap = 0; | ||||
| optind = 1; | optind = 1; | ||||
| opt_string = ""; | opt_string = ""; | ||||
| while (optind < argc) { | while (optind < argc) { | ||||
| int len; | |||||
| char *p; | char *p; | ||||
| if ((c = *opt_string) == 0) { | if ((c = *opt_string) == 0) { | ||||
| break; // -- means don't interpret further - as commands | break; // -- means don't interpret further - as commands | ||||
| opt_string = ""; | opt_string = ""; | ||||
| for (int ix = 0;; ix++) { | |||||
| for (ix = 0;; ix++) { | |||||
| if (long_options[ix].name == 0) | if (long_options[ix].name == 0) | ||||
| break; | break; | ||||
| size_t len = strlen(long_options[ix].name); | |||||
| len = strlen(long_options[ix].name); | |||||
| if (memcmp(long_options[ix].name, p, len) == 0) { | if (memcmp(long_options[ix].name, p, len) == 0) { | ||||
| c = long_options[ix].val; | c = long_options[ix].val; | ||||
| optarg2 = NULL; | optarg2 = NULL; | ||||
| case 0x103: // --punct | case 0x103: // --punct | ||||
| option_punctuation = 1; | option_punctuation = 1; | ||||
| if (optarg2 != NULL) { | if (optarg2 != NULL) { | ||||
| int ix = 0; | |||||
| ix = 0; | |||||
| while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | while ((ix < N_PUNCTLIST) && ((option_punctlist[ix] = optarg2[ix]) != 0)) ix++; | ||||
| option_punctlist[N_PUNCTLIST-1] = 0; | option_punctlist[N_PUNCTLIST-1] = 0; | ||||
| option_punctuation = 2; | option_punctuation = 2; | ||||
| InitText(0); | InitText(0); | ||||
| SpeakNextClause(f_text, p_text, 0); | SpeakNextClause(f_text, p_text, 0); | ||||
| int ix = 1; | |||||
| ix = 1; | |||||
| for (;;) { | for (;;) { | ||||
| if (WavegenFile() != 0) { | if (WavegenFile() != 0) { | ||||
| if (ix == 0) | if (ix == 0) |