voice_select.gender = 0; | voice_select.gender = 0; | ||||
voice_select.name = NULL; | voice_select.name = NULL; | ||||
voices = espeak_ListVoices(&voice_select); | voices = espeak_ListVoices(&voice_select); | ||||
} else { | |||||
} else | |||||
voices = espeak_ListVoices(NULL); | voices = espeak_ListVoices(NULL); | ||||
} | |||||
fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | ||||
} | } | ||||
fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | ||||
p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | ||||
} else { | |||||
} else | |||||
fprintf(f_out, "(%s %d)", lang_name, p[0]); | fprintf(f_out, "(%s %d)", lang_name, p[0]); | ||||
} | |||||
count++; | count++; | ||||
p += len+2; | p += len+2; | ||||
} | } | ||||
c = long_options[ix].val; | c = long_options[ix].val; | ||||
optarg2 = NULL; | optarg2 = NULL; | ||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) { | |||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) | |||||
optarg2 = &p[len+1]; | optarg2 = &p[len+1]; | ||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
case 0x108: // --phonout | case 0x108: // --phonout | ||||
if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) { | |||||
if ((f_phonemes_out = fopen(optarg2, "w")) == NULL) | |||||
fprintf(stderr, "Can't write to: %s\n", optarg2); | fprintf(stderr, "Can't write to: %s\n", optarg2); | ||||
} | |||||
break; | break; | ||||
case 0x109: // --pho | case 0x109: // --pho | ||||
p_text = argv[optind]; | p_text = argv[optind]; | ||||
} else { | } else { | ||||
f_text = stdin; | f_text = stdin; | ||||
if (flag_stdin == 0) { | |||||
if (flag_stdin == 0) | |||||
flag_stdin = 2; | flag_stdin = 2; | ||||
} | |||||
} | } | ||||
} else { | } else { | ||||
filesize = GetFileLength(filename); | filesize = GetFileLength(filename); |
return; | return; | ||||
if (compile_phoneme) { | |||||
if (compile_phoneme) | |||||
fprintf(f_out, "\nPhoneme %s (%d)\n", WordToString(ph->mnemonic), ph->code); | fprintf(f_out, "\nPhoneme %s (%d)\n", WordToString(ph->mnemonic), ph->code); | ||||
} else { | |||||
else | |||||
fprintf(f_out, "\nProcedure %s\n", proc_names[n_procs]); | fprintf(f_out, "\nProcedure %s\n", proc_names[n_procs]); | ||||
} | |||||
pc = prog_buf; | pc = prog_buf; | ||||
while (pc < prog_out) { | while (pc < prog_out) { | ||||
fprintf(f_report, "\n%d phoneme tables\n", n_phoneme_tabs); | fprintf(f_report, "\n%d phoneme tables\n", n_phoneme_tabs); | ||||
fprintf(f_report, " new total\n"); | fprintf(f_report, " new total\n"); | ||||
for (ix = 0; ix < n_phoneme_tabs; ix++) { | |||||
for (ix = 0; ix < n_phoneme_tabs; ix++) | |||||
fprintf(f_report, "%8s %3d %4d\n", phoneme_tab_list2[ix].name, phoneme_tab_list2[ix].n_phonemes, n_phcodes_list[ix]+1); | fprintf(f_report, "%8s %3d %4d\n", phoneme_tab_list2[ix].name, phoneme_tab_list2[ix].n_phonemes, n_phcodes_list[ix]+1); | ||||
} | |||||
fputc('\n', f_report); | fputc('\n', f_report); | ||||
fprintf(f_report, "Data file Used by\n"); | fprintf(f_report, "Data file Used by\n"); | ||||
// a procedure, not a phoneme | // a procedure, not a phoneme | ||||
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); | ||||
} | |||||
fputc('\n', f_report); | fputc('\n', f_report); | ||||
} | } | ||||
return 1; | return 1; | ||||
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); | ||||
} | |||||
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 | ||||
if (phoneme_tab2[ix].mnemonic == word) | if (phoneme_tab2[ix].mnemonic == word) | ||||
return ix; | return ix; | ||||
if ((use == 0) && (phoneme_tab2[ix].mnemonic == 0)) { | |||||
if ((use == 0) && (phoneme_tab2[ix].mnemonic == 0)) | |||||
use = ix; | use = ix; | ||||
} | |||||
} | } | ||||
if (use == 0) { | if (use == 0) { | ||||
if ((c2 = get_char()) == '/') { | if ((c2 = get_char()) == '/') { | ||||
// comment, ignore to end of line | // comment, ignore to end of line | ||||
while (!feof(f_in) && ((c = get_char()) != '\n')) ; | while (!feof(f_in) && ((c = get_char()) != '\n')) ; | ||||
} else { | |||||
} else | |||||
unget_char(c2); | unget_char(c2); | ||||
} | |||||
} | } | ||||
if (!isspace(c)) { | |||||
if (!isspace(c)) | |||||
break; | break; | ||||
} | |||||
} | } | ||||
if (feof(f_in)) | if (feof(f_in)) | ||||
return -2; | return -2; | ||||
} | } | ||||
item_string[ix] = 0; | item_string[ix] = 0; | ||||
while (isspace(c)) { | |||||
while (isspace(c)) | |||||
c = get_char(); | c = get_char(); | ||||
} | |||||
item_terminator = ' '; | item_terminator = ' '; | ||||
if ((c == ')') || (c == '(') || (c == ',')) | if ((c == ')') || (c == '(') || (c == ',')) | ||||
if (!feof(f_in)) | if (!feof(f_in)) | ||||
unget_char(c); | unget_char(c); | ||||
if (type == tSTRING) { | |||||
if (type == tSTRING) | |||||
return 0; | return 0; | ||||
} | |||||
if ((type == tNUMBER) || (type == tSIGNEDNUMBER)) { | if ((type == tNUMBER) || (type == tSIGNEDNUMBER)) { | ||||
acc = 0; | acc = 0; | ||||
item_type = -1; | item_type = -1; | ||||
return -1; // keyword not found | return -1; // keyword not found | ||||
} | } | ||||
if (type == tPHONEMEMNEM) { | |||||
if (type == tPHONEMEMNEM) | |||||
return LookupPhoneme(item_string, 2); | return LookupPhoneme(item_string, 2); | ||||
} | |||||
return -1; | return -1; | ||||
} | } | ||||
int value; | int value; | ||||
if ((control & 1) == 0) { | if ((control & 1) == 0) { | ||||
if (!NextItem(tOPENBRACKET)) { | |||||
if (!NextItem(tOPENBRACKET)) | |||||
error("Expected '('", NULL); | error("Expected '('", NULL); | ||||
} | |||||
} | } | ||||
value = NextItem(type); | value = NextItem(type); | ||||
if ((control & 2) && (item_terminator == ',')) | if ((control & 2) && (item_terminator == ',')) | ||||
return value; | return value; | ||||
if (item_terminator != ')') { | |||||
if (item_terminator != ')') | |||||
error("Expected ')'", NULL); | error("Expected ')'", NULL); | ||||
} | |||||
return value; | return value; | ||||
} | } | ||||
total = 0; | total = 0; | ||||
for (frame = 0; frame < spectseq->numframes; frame++) { | 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; | ||||
} | |||||
if (spectseq->frames[frame]->markers & 0x2) { | if (spectseq->frames[frame]->markers & 0x2) { | ||||
// marker 1 is set | // marker 1 is set | ||||
marker1_set = 1; | marker1_set = 1; | ||||
if (klatt_flag) { | if (klatt_flag) { | ||||
// additional klatt parameters | // additional klatt parameters | ||||
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 (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; | ||||
#ifdef PLATFORM_POSIX | #ifdef PLATFORM_POSIX | ||||
strcpy(fname_temp, "/tmp/espeakXXXXXX"); | strcpy(fname_temp, "/tmp/espeakXXXXXX"); | ||||
if ((fd_temp = mkstemp(fname_temp)) >= 0) { | |||||
if ((fd_temp = mkstemp(fname_temp)) >= 0) | |||||
close(fd_temp); | close(fd_temp); | ||||
} | |||||
#else | #else | ||||
strcpy(fname_temp, tmpnam(NULL)); | strcpy(fname_temp, tmpnam(NULL)); | ||||
#endif | #endif | ||||
} | } | ||||
sprintf(command, "sox \"%s/../phsource/%s.wav\" -r %d -c1 -t wav %s\n", path_home, fname2, samplerate_native, fname_temp); | sprintf(command, "sox \"%s/../phsource/%s.wav\" -r %d -c1 -t wav %s\n", path_home, fname2, samplerate_native, fname_temp); | ||||
if (system(command) != 0) { | |||||
if (system(command) != 0) | |||||
failed = 1; | failed = 1; | ||||
} | |||||
if (failed || (GetFileLength(fname_temp) <= 0)) { | if (failed || (GetFileLength(fname_temp) <= 0)) { | ||||
if (sr1 != samplerate_native) { | if (sr1 != samplerate_native) { | ||||
sprintf(msg, "Can't resample (%d to %d): %s", sr1, samplerate_native, fname); | sprintf(msg, "Can't resample (%d to %d): %s", sr1, samplerate_native, fname); | ||||
error("%s", msg); | error("%s", msg); | ||||
} else { | |||||
} else | |||||
error("WAV file is not mono: %s", fname); | error("WAV file is not mono: %s", fname); | ||||
} | |||||
remove(fname_temp); | remove(fname_temp); | ||||
return 0; | return 0; | ||||
} | } | ||||
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) | |||||
error("Failed to read envelope: %s", fname); | error("Failed to read envelope: %s", fname); | ||||
} | |||||
fwrite(buf, 128, 1, f_phdata); | fwrite(buf, 128, 1, f_phdata); | ||||
if (n_envelopes < N_ENVELOPES) { | if (n_envelopes < N_ENVELOPES) { | ||||
} | } | ||||
fclose(f); | fclose(f); | ||||
if (addr > 0) { | |||||
if (addr > 0) | |||||
fprintf(f_phcontents, "%c 0x%.5x %s\n", type_code, addr & 0x7fffff, path); | fprintf(f_phcontents, "%c 0x%.5x %s\n", type_code, addr & 0x7fffff, path); | ||||
} | |||||
} | } | ||||
// add this item to the hash table | // add this item to the hash table | ||||
key = key << 8; | key = key << 8; | ||||
data = NextItemBrackets(tPROPERTIES, 0); | data = NextItemBrackets(tPROPERTIES, 0); | ||||
if (data >= 0) { | |||||
if (data >= 0) | |||||
word = key + data + 0x700; | word = key + data + 0x700; | ||||
} else { | |||||
else { | |||||
data = LookupPhoneme(item_string, 2); | data = LookupPhoneme(item_string, 2); | ||||
word = key + data; | word = key + data; | ||||
} | } | ||||
brackets = 3; | brackets = 3; | ||||
} while (item_terminator == ','); | } while (item_terminator == ','); | ||||
word = i_StressLevel | bitmap; | word = i_StressLevel | bitmap; | ||||
} else { | |||||
} else | |||||
word = key; | word = key; | ||||
} | |||||
} else { | } else { | ||||
error("Unexpected keyword '%s'", item_string); | error("Unexpected keyword '%s'", item_string); | ||||
} | } | ||||
} | } | ||||
if (finish != 1) { | |||||
} | |||||
if (elif == 0) { | if (elif == 0) { | ||||
if_level++; | if_level++; | ||||
if_stack[if_level].p_else = NULL; | if_stack[if_level].p_else = NULL; | ||||
} | } | ||||
prog_out--; | prog_out--; | ||||
} else { | } else { | ||||
if (offset > MAX_JUMP) { | |||||
if (offset > MAX_JUMP) | |||||
error("IF block is too long", NULL); | error("IF block is too long", NULL); | ||||
} | |||||
*p = i_JUMP_FALSE + offset; | *p = i_JUMP_FALSE + offset; | ||||
} | } | ||||
if_stack[if_level].p_then = NULL; | if_stack[if_level].p_then = NULL; | ||||
return 0; | return 0; | ||||
} | } | ||||
if (if_stack[if_level].returned == 0) { | |||||
if (if_stack[if_level].returned == 0) | |||||
FillThen(1); | FillThen(1); | ||||
} else { | |||||
else | |||||
FillThen(0); | FillThen(0); | ||||
} | |||||
if (if_stack[if_level].returned == 0) { | if (if_stack[if_level].returned == 0) { | ||||
ref = prog_out; | ref = prog_out; | ||||
*prog_out++ = 0; | *prog_out++ = 0; | ||||
if ((p = if_stack[if_level].p_else) != NULL) { | |||||
if ((p = if_stack[if_level].p_else) != NULL) | |||||
*ref = ref - p; // backwards offset to the previous else | *ref = ref - p; // backwards offset to the previous else | ||||
} | |||||
if_stack[if_level].p_else = ref; | if_stack[if_level].p_else = ref; | ||||
} | } | ||||
chain = *p; // a chain of previous else links | chain = *p; // a chain of previous else links | ||||
offset = prog_out - p; | offset = prog_out - p; | ||||
if (offset > MAX_JUMP) { | |||||
if (offset > MAX_JUMP) | |||||
error("IF block is too long", NULL); | error("IF block is too long", NULL); | ||||
} | |||||
*p = i_JUMP + offset; | *p = i_JUMP + offset; | ||||
p -= chain; | p -= chain; | ||||
int ix; | int ix; | ||||
for (ix = 0; ix < n_phoneme_tabs; 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]; | ||||
} | |||||
} | } | ||||
error("Unknown phoneme table: '%s'", string); | error("Unknown phoneme table: '%s'", string); | ||||
return NULL; | return NULL; | ||||
char buf[200]; | char buf[200]; | ||||
// is this the name of a phoneme which is in scope | // is this the name of a phoneme which is in scope | ||||
if ((strlen(string) <= 4) && ((ix = LookupPhoneme(string, 0)) != -1)) { | |||||
if ((strlen(string) <= 4) && ((ix = LookupPhoneme(string, 0)) != -1)) | |||||
return &phoneme_tab2[ix]; | return &phoneme_tab2[ix]; | ||||
} | |||||
// no, treat the name as phonemetable/phoneme | // no, treat the name as phonemetable/phoneme | ||||
strcpy(buf, string); | strcpy(buf, string); | ||||
if ((phname = strchr(buf, '/')) != 0) { | |||||
if ((phname = strchr(buf, '/')) != 0) | |||||
*phname++ = 0; | *phname++ = 0; | ||||
} | |||||
phtab = FindPhonemeTable(buf); | phtab = FindPhonemeTable(buf); | ||||
if (phtab == NULL) { | |||||
if (phtab == NULL) | |||||
return NULL; // phoneme table not found | return NULL; // phoneme table not found | ||||
} | |||||
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]; | ||||
} | |||||
} | } | ||||
error("Phoneme reference not found: '%s'", string); | error("Phoneme reference not found: '%s'", string); | ||||
case i_SET_LENGTH: | case i_SET_LENGTH: | ||||
value = NextItemMax(511); | value = NextItemMax(511); | ||||
if (phoneme_out->type == phVOWEL) { | |||||
if (phoneme_out->type == phVOWEL) | |||||
value = (value * vowel_length_factor)/100; | value = (value * vowel_length_factor)/100; | ||||
} | |||||
if (after_if == 0) { | |||||
if (after_if == 0) | |||||
phoneme_out->std_length = value/2; | phoneme_out->std_length = value/2; | ||||
} else { | |||||
else { | |||||
*prog_out++ = (i_SET_LENGTH << 8) + value/2; | *prog_out++ = (i_SET_LENGTH << 8) + value/2; | ||||
DecThenCount(); | DecThenCount(); | ||||
} | } | ||||
value = strlen(&ipa_buf[start]); // number of UTF-8 bytes | value = strlen(&ipa_buf[start]); // number of UTF-8 bytes | ||||
*prog_out++ = (i_IPA_NAME << 8) + value; | *prog_out++ = (i_IPA_NAME << 8) + value; | ||||
for (ix = 0; ix < value; ix += 2) { | |||||
for (ix = 0; ix < value; ix += 2) | |||||
*prog_out++ = (ipa_buf[ix+start] << 8) + (ipa_buf[ix+start+1] & 0xff); | *prog_out++ = (ipa_buf[ix+start] << 8) + (ipa_buf[ix+start+1] & 0xff); | ||||
} | |||||
DecThenCount(); | DecThenCount(); | ||||
break; | break; | ||||
} | } | ||||
phcode = NextItem(tPHONEMEMNEM); | phcode = NextItem(tPHONEMEMNEM); | ||||
if (phcode == -1) | if (phcode == -1) | ||||
phcode = LookupPhoneme(item_string, 1); | phcode = LookupPhoneme(item_string, 1); | ||||
if (phoneme_out->type == phVOWEL) { | |||||
if (phoneme_out->type == phVOWEL) | |||||
phoneme_out->end_type = phcode; | phoneme_out->end_type = phcode; | ||||
} else { | |||||
if (phcode != phoneme_out->start_type) { | |||||
error("endtype must equal starttype for consonants", NULL); | |||||
} | |||||
} | |||||
else if (phcode != phoneme_out->start_type) | |||||
error("endtype must equal starttype for consonants", NULL); | |||||
break; | break; | ||||
case kVOICINGSWITCH: | case kVOICINGSWITCH: | ||||
case kENDPHONEME: | case kENDPHONEME: | ||||
case kENDPROCEDURE: | case kENDPROCEDURE: | ||||
endphoneme = 1; | endphoneme = 1; | ||||
if (if_level > 0) { | |||||
if (if_level > 0) | |||||
error("Missing ENDIF", NULL); | error("Missing ENDIF", NULL); | ||||
} | |||||
if ((prog_out > prog_buf) && (if_stack[0].returned == 0)) { | |||||
if ((prog_out > prog_buf) && (if_stack[0].returned == 0)) | |||||
*prog_out++ = i_RETURN; | *prog_out++ = i_RETURN; | ||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (endphoneme != 1) { | |||||
if (endphoneme != 1) | |||||
error("'endphoneme' not expected here", NULL); | error("'endphoneme' not expected here", NULL); | ||||
} | |||||
if (compile_phoneme) { | if (compile_phoneme) { | ||||
if (phoneme_out->type == phINVALID) { | if (phoneme_out->type == phINVALID) { | ||||
phoneme_out->phflags |= phLOCAL; // declared in this phoneme table | phoneme_out->phflags |= phLOCAL; // declared in this phoneme table | ||||
if (phoneme_out->type == phDELETED) { | |||||
if (phoneme_out->type == phDELETED) | |||||
phoneme_out->mnemonic = 0x01; // will not be recognised | phoneme_out->mnemonic = 0x01; // will not be recognised | ||||
} | |||||
} | } | ||||
DecompilePhoneme(f_errors, phoneme_out, compile_phoneme); | DecompilePhoneme(f_errors, phoneme_out, compile_phoneme); | ||||
fwrite(&phoneme_prog_log, 1, sizeof(phoneme_prog_log), f_prog_log); | fwrite(&phoneme_prog_log, 1, sizeof(phoneme_prog_log), f_prog_log); | ||||
} | } | ||||
if (compile_phoneme == 0) { | |||||
if (compile_phoneme == 0) | |||||
proc_addr[n_procs++] = ftell(f_phindex) / sizeof(USHORT); | proc_addr[n_procs++] = ftell(f_phindex) / sizeof(USHORT); | ||||
} | |||||
fwrite(prog_buf, sizeof(USHORT), prog_out - prog_buf, f_phindex); | fwrite(prog_buf, sizeof(USHORT), prog_out - prog_buf, f_phindex); | ||||
} | } | ||||
if (ix == n_phoneme_tabs) { | if (ix == n_phoneme_tabs) { | ||||
error("Can't find base phonemetable '%s'", item_string); | error("Can't find base phonemetable '%s'", item_string); | ||||
} | } | ||||
} else { | |||||
} else | |||||
ReservePhCodes(); | ReservePhCodes(); | ||||
} | |||||
n_phoneme_tabs++; | n_phoneme_tabs++; | ||||
} | } | ||||
strcpy(foreign_table_name, item_string); | strcpy(foreign_table_name, item_string); | ||||
if ((foreign_table = SelectPhonemeTableName(foreign_table_name)) < 0) { | if ((foreign_table = SelectPhonemeTableName(foreign_table_name)) < 0) { | ||||
if (strcmp(foreign_table_name, "NULL") != 0) { | |||||
if (strcmp(foreign_table_name, "NULL") != 0) | |||||
error("Unknown phoneme table '%s'", foreign_table_name); | error("Unknown phoneme table '%s'", foreign_table_name); | ||||
} | |||||
foreign_error = 1; | foreign_error = 1; | ||||
foreign_phoneme = 0; | foreign_phoneme = 0; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
for (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 | ||||
if ((n_names > 2) || (phcode[0] != phcode[1])) { | if ((n_names > 2) || (phcode[0] != phcode[1])) { | ||||
strncpy0(current_fname, item_string, sizeof(current_fname)); | strncpy0(current_fname, item_string, sizeof(current_fname)); | ||||
linenum = 1; | linenum = 1; | ||||
} else { | |||||
} else | |||||
error("Missing file: %s", item_string); | error("Missing file: %s", item_string); | ||||
} | |||||
break; | break; | ||||
case kPHONEMETABLE: | case kPHONEMETABLE: | ||||
sprintf(fname, "%s/%s", path_home, "phontab"); | sprintf(fname, "%s/%s", path_home, "phontab"); | ||||
f_phtab = fopen_log(f_errors, fname, "wb"); | f_phtab = fopen_log(f_errors, fname, "wb"); | ||||
if (f_phdata == NULL || f_phindex == NULL || f_phtab == NULL) { | |||||
if (f_phdata == NULL || f_phindex == NULL || f_phtab == NULL) | |||||
return ENE_WRITE_ERROR; | return ENE_WRITE_ERROR; | ||||
} | |||||
sprintf(fname, "%s/../phsource/compile_prog_log", path_home); | sprintf(fname, "%s/../phsource/compile_prog_log", path_home); | ||||
f_prog_log = fopen_log(f_errors, fname, "wb"); | f_prog_log = fopen_log(f_errors, fname, "wb"); | ||||
if (resample_count > 0) { | if (resample_count > 0) { | ||||
fprintf(f_errors, "\n%d WAV files resampled to %d Hz\n", resample_count, samplerate_native); | fprintf(f_errors, "\n%d WAV files resampled to %d Hz\n", resample_count, samplerate_native); | ||||
fprintf(log, "Compiled phonemes: %d errors, %d files resampled to %d Hz.\n", error_count, resample_count, samplerate_native); | fprintf(log, "Compiled phonemes: %d errors, %d files resampled to %d Hz.\n", error_count, resample_count, samplerate_native); | ||||
} else { | |||||
} else | |||||
fprintf(log, "Compiled phonemes: %d errors.\n", error_count); | fprintf(log, "Compiled phonemes: %d errors.\n", error_count); | ||||
} | |||||
if (f_errors != stderr) | if (f_errors != stderr) | ||||
fclose(f_errors); | fclose(f_errors); | ||||
while (isspace(*p)) p++; | while (isspace(*p)) p++; | ||||
ix = 0; | ix = 0; | ||||
while ((ix < (int)(sizeof(name) - 1)) && !isspace(*p)) { | |||||
while ((ix < (int)(sizeof(name) - 1)) && !isspace(*p)) | |||||
name[ix++] = *p++; | name[ix++] = *p++; | ||||
} | |||||
name[ix] = 0; | name[ix] = 0; | ||||
found = 0; | found = 0; | ||||
switch (keyword) | switch (keyword) | ||||
{ | { | ||||
case kTUNE: | case kTUNE: | ||||
if (compiling_tune) { | |||||
} | |||||
compiling_tune = 1; | compiling_tune = 1; | ||||
done_split = 0; | done_split = 0; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (found == 2) { | |||||
if (found == 2) | |||||
error("Duplicate tune name: '%s'", new_tune.name); | error("Duplicate tune name: '%s'", new_tune.name); | ||||
} | |||||
if (found == 0) { | |||||
if (found == 0) | |||||
error("Bad tune name: '%s;", new_tune.name); | error("Bad tune name: '%s;", new_tune.name); | ||||
} | |||||
break; | break; | ||||
case kENDTUNE: | case kENDTUNE: | ||||
} | } | ||||
for (ix = 0; ix < n_preset_tunes; ix++) { | for (ix = 0; ix < n_preset_tunes; ix++) { | ||||
if (tune_data[ix].name[0] == 0) { | |||||
if (tune_data[ix].name[0] == 0) | |||||
error("Tune '%s' not defined", preset_tune_names[ix]); | error("Tune '%s' not defined", preset_tune_names[ix]); | ||||
} | |||||
} | } | ||||
fwrite(tune_data, n_tune_names, sizeof(TUNE), f_out); | fwrite(tune_data, n_tune_names, sizeof(TUNE), f_out); | ||||
free(tune_data); | free(tune_data); |
match_type = 0; | match_type = 0; | ||||
buf_pre[0] = 0; | buf_pre[0] = 0; | ||||
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; | ||||
p = &buf[strlen(buf)]; | p = &buf[strlen(buf)]; | ||||
strcpy(p, suffix); | strcpy(p, suffix); | ||||
p += strlen(suffix); | p += strlen(suffix); | ||||
c = ' '; | c = ' '; | ||||
} else if (rb == RULE_LETTERGP) { | |||||
} else if (rb == RULE_LETTERGP) | |||||
c = symbols_lg[*rule++ - 'A']; | c = symbols_lg[*rule++ - 'A']; | ||||
} else if (rb == RULE_LETTERGP2) { | |||||
else if (rb == RULE_LETTERGP2) { | |||||
value = *rule++ - 'A'; | value = *rule++ - 'A'; | ||||
p[0] = 'L'; | p[0] = 'L'; | ||||
p[1] = (value / 10) + '0'; | p[1] = (value / 10) + '0'; | ||||
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; | ||||
} else if (flagnum == 201) { | |||||
else if (flagnum == 201) | |||||
text_mode = 0; | text_mode = 0; | ||||
} else if (flagnum == BITNUM_FLAG_TEXTMODE) { | |||||
else if (flagnum == BITNUM_FLAG_TEXTMODE) | |||||
text_not_phonemes = 1; | text_not_phonemes = 1; | ||||
} else { | |||||
else | |||||
flag_codes[n_flag_codes++] = flagnum; | flag_codes[n_flag_codes++] = flagnum; | ||||
} | |||||
} else { | } else { | ||||
fprintf(f_log, "%5d: Unknown keyword: %s\n", linenum, mnemptr); | fprintf(f_log, "%5d: Unknown keyword: %s\n", linenum, mnemptr); | ||||
error_count++; | error_count++; | ||||
case 1: | case 1: | ||||
if ((c == '-') && multiple_words) { | if ((c == '-') && multiple_words) { | ||||
if (IsDigit09(word[0])) { | |||||
if (IsDigit09(word[0])) | |||||
multiple_numeric_hyphen = 1; | multiple_numeric_hyphen = 1; | ||||
} | |||||
flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED; | flag_codes[n_flag_codes++] = BITNUM_FLAG_HYPHENATED; | ||||
c = ' '; | c = ' '; | ||||
} | } | ||||
if (multiple_words) { | if (multiple_words) { | ||||
multiple_string = multiple_string_end = p+1; | multiple_string = multiple_string_end = p+1; | ||||
step = 2; | step = 2; | ||||
} else { | |||||
} else | |||||
step = 3; | step = 3; | ||||
} | |||||
} else if (c == ')') { | } else if (c == ')') { | ||||
if (multiple_words) { | if (multiple_words) { | ||||
p[0] = 0; | p[0] = 0; | ||||
break; | break; | ||||
case 2: | case 2: | ||||
if (isspace2(c)) { | |||||
if (isspace2(c)) | |||||
multiple_words++; | multiple_words++; | ||||
} else if (c == ')') { | |||||
else if (c == ')') { | |||||
p[0] = ' '; // terminate extra string | p[0] = ' '; // terminate extra string | ||||
multiple_string_end = p+1; | multiple_string_end = p+1; | ||||
step = 3; | step = 3; | ||||
p++; | p++; | ||||
} | } | ||||
if (word[0] == 0) { | |||||
if (word[0] == 0) | |||||
return 0; /* blank line */ | return 0; /* blank line */ | ||||
} | |||||
if (text_mode) | if (text_mode) | ||||
text_not_phonemes = 1; | text_not_phonemes = 1; | ||||
error_need_dictionary++; | error_need_dictionary++; | ||||
fprintf(f_log, "%5d: Need to compile dictionary again\n", linenum); | fprintf(f_log, "%5d: Need to compile dictionary again\n", linenum); | ||||
} | } | ||||
} else { | |||||
} else | |||||
// 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 { | ||||
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) { | ||||
} | } | ||||
} | } | ||||
if (text_not_phonemes != translator->langopts.textmode) { | |||||
if (text_not_phonemes != translator->langopts.textmode) | |||||
flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE; | flag_codes[n_flag_codes++] = BITNUM_FLAG_TEXTMODE; | ||||
} | |||||
if (sscanf(word, "U+%x", &wc) == 1) { | if (sscanf(word, "U+%x", &wc) == 1) { | ||||
ix = utf8_in(&c2, p); | ix = utf8_in(&c2, p); | ||||
if (c2 == 0) | if (c2 == 0) | ||||
break; | break; | ||||
if (iswupper2(c2)) { | |||||
if (iswupper2(c2)) | |||||
utf8_out(towlower2(c2), p); | utf8_out(towlower2(c2), p); | ||||
} else { | |||||
else | |||||
all_upper_case = 0; | all_upper_case = 0; | ||||
} | |||||
p += ix; | p += ix; | ||||
} | } | ||||
if (all_upper_case) { | |||||
if (all_upper_case) | |||||
flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS; | flag_codes[n_flag_codes++] = BITNUM_FLAG_ALLCAPS; | ||||
} | |||||
} | } | ||||
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); | ||||
len_phonetic = strlen(encoded_ph); | len_phonetic = strlen(encoded_ph); | ||||
strcpy(&dict_line[(len_word)+2], encoded_ph); | strcpy(&dict_line[(len_word)+2], encoded_ph); | ||||
} | } | ||||
for (ix = 0; ix < n_flag_codes; ix++) { | |||||
for (ix = 0; ix < n_flag_codes; ix++) | |||||
dict_line[ix+length] = flag_codes[ix]; | dict_line[ix+length] = flag_codes[ix]; | ||||
} | |||||
length += n_flag_codes; | length += n_flag_codes; | ||||
if ((multiple_string != NULL) && (multiple_words > 0)) { | if ((multiple_string != NULL) && (multiple_words > 0)) { | ||||
} else { | } else { | ||||
dict_line[length++] = 80 + multiple_words; | dict_line[length++] = 80 + multiple_words; | ||||
ix = multiple_string_end - multiple_string; | ix = multiple_string_end - multiple_string; | ||||
if (multiple_numeric_hyphen) { | |||||
if (multiple_numeric_hyphen) | |||||
dict_line[length++] = ' '; // ??? | dict_line[length++] = ' '; // ??? | ||||
} | |||||
memcpy(&dict_line[length], multiple_string, ix); | memcpy(&dict_line[length], multiple_string, ix); | ||||
length += ix; | length += ix; | ||||
} | } | ||||
c = c2 * 16 + c3; | c = c2 * 16 + c3; | ||||
literal = 1; | literal = 1; | ||||
p++; | p++; | ||||
} else { | |||||
} else | |||||
hexdigit_input = 0; | hexdigit_input = 0; | ||||
} | |||||
} | } | ||||
if ((state == 1) || (state == 3)) { | if ((state == 1) || (state == 3)) { | ||||
// replace special characters (note: 'E' is reserved for a replaced silent 'e') | // replace special characters (note: 'E' is reserved for a replaced silent 'e') | ||||
// pre-rule, put the group number before the RULE_LETTERGP command | // pre-rule, put the group number before the RULE_LETTERGP command | ||||
output[ix++] = c; | output[ix++] = c; | ||||
c = RULE_LETTERGP2; | c = RULE_LETTERGP2; | ||||
} else { | |||||
} else | |||||
output[ix++] = RULE_LETTERGP2; | output[ix++] = RULE_LETTERGP2; | ||||
} | |||||
break; | break; | ||||
case '$': | case '$': | ||||
// 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; | ||||
start = 1; | start = 1; | ||||
} else { | |||||
} else | |||||
c = RULE_PRE; | c = RULE_PRE; | ||||
} | |||||
output[len++] = c; | output[len++] = c; | ||||
// output PRE string in reverse order | // output PRE string in reverse order | ||||
// group character is given as a character code (max 16 bits) | // group character is given as a character code (max 16 bits) | ||||
p = (unsigned char *)group_name; | p = (unsigned char *)group_name; | ||||
if (char_code > 0x100) { | |||||
if (char_code > 0x100) | |||||
*p++ = (char_code >> 8); | *p++ = (char_code >> 8); | ||||
} | |||||
*p++ = char_code; | *p++ = char_code; | ||||
*p = 0; | *p = 0; | ||||
} else { | } else { | ||||
if (translator->letter_bits_offset > 0) { | if (translator->letter_bits_offset > 0) { | ||||
utf8_in(&wc, group_name); | utf8_in(&wc, group_name); | ||||
if (((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128)) { | |||||
if (((ix = (wc - translator->letter_bits_offset)) >= 0) && (ix < 128)) | |||||
group3_ix = ix+1; // not zero | group3_ix = ix+1; // not zero | ||||
} | |||||
} | } | ||||
} | } | ||||
case 1: // .group | case 1: // .group | ||||
prule = compile_rule(buf); | prule = compile_rule(buf); | ||||
if (prule != NULL) { | if (prule != NULL) { | ||||
if (n_rules < N_RULES) { | |||||
if (n_rules < N_RULES) | |||||
rules[n_rules++] = prule; | rules[n_rules++] = prule; | ||||
} else { | |||||
else { | |||||
if (err_n_rules == 0) { | if (err_n_rules == 0) { | ||||
fprintf(stderr, "\nExceeded limit of rules (%d) in group '%s'\n", N_RULES, group_name); | fprintf(stderr, "\nExceeded limit of rules (%d) in group '%s'\n", N_RULES, group_name); | ||||
error_count++; | error_count++; | ||||
n_groups3++; | n_groups3++; | ||||
fputc(1, f_out); | fputc(1, f_out); | ||||
fputc(rgroup[gp].group3_ix, f_out); | fputc(rgroup[gp].group3_ix, f_out); | ||||
} else { | |||||
} else | |||||
fprintf(f_out, "%s", prev_rgroup_name = rgroup[gp].name); | fprintf(f_out, "%s", prev_rgroup_name = rgroup[gp].name); | ||||
} | |||||
fputc(0, f_out); | fputc(0, f_out); | ||||
} | } | ||||
c = fgetc(f_temp); | c = fgetc(f_temp); | ||||
fputc(c, f_out); | fputc(c, f_out); | ||||
} | } | ||||
if (different) { | |||||
} | |||||
} | } | ||||
fputc(RULE_GROUP_END, f_out); | fputc(RULE_GROUP_END, f_out); | ||||
fputc(0, f_out); | fputc(0, f_out); |
Write4Bytes(f_out, mbrola_ctrl); | Write4Bytes(f_out, mbrola_ctrl); | ||||
pw_end = (int *)(&data[count+1]); | pw_end = (int *)(&data[count+1]); | ||||
for (pw = (int *)data; pw < pw_end; pw++) { | |||||
for (pw = (int *)data; pw < pw_end; pw++) | |||||
Write4Bytes(f_out, *pw); | Write4Bytes(f_out, *pw); | ||||
} | |||||
fclose(f_out); | fclose(f_out); | ||||
fprintf(log, "Mbrola translation file: %s -- %d phonemes\n", buf, count); | fprintf(log, "Mbrola translation file: %s -- %d phonemes\n", buf, count); | ||||
return ENS_OK; | return ENS_OK; |
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | ||||
if (!fd_log) { | |||||
if (!fd_log) | |||||
debug_init(); | debug_init(); | ||||
} | |||||
if (fd_log) { | |||||
if (fd_log) | |||||
fprintf(fd_log, "%03d.%03dms > ENTER %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | fprintf(fd_log, "%03d.%03dms > ENTER %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | ||||
} | |||||
} | } | ||||
{ | { | ||||
va_list args; | va_list args; | ||||
va_start(args, format); | va_start(args, format); | ||||
if (!fd_log) { | |||||
if (!fd_log) | |||||
debug_init(); | debug_init(); | ||||
} | |||||
if (fd_log) { | |||||
if (fd_log) | |||||
vfprintf(fd_log, format, args); | vfprintf(fd_log, format, args); | ||||
} | |||||
va_end(args); | va_end(args); | ||||
} | } | ||||
gettimeofday(&tv, NULL); | gettimeofday(&tv, NULL); | ||||
if (!fd_log) { | |||||
if (!fd_log) | |||||
debug_init(); | debug_init(); | ||||
} | |||||
if (fd_log) { | |||||
if (fd_log) | |||||
fprintf(fd_log, "%03d.%03dms > %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | fprintf(fd_log, "%03d.%03dms > %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text); | ||||
} | |||||
} | } | ||||
#endif | #endif |
if (p[0] == RULE_REPLACEMENTS) { | if (p[0] == RULE_REPLACEMENTS) { | ||||
pw = (unsigned int *)(((intptr_t)p+4) & ~3); // advance to next word boundary | pw = (unsigned int *)(((intptr_t)p+4) & ~3); // advance to next word boundary | ||||
tr->langopts.replace_chars = pw; | tr->langopts.replace_chars = pw; | ||||
while (pw[0] != 0) { | |||||
while (pw[0] != 0) | |||||
pw += 2; // find the end of the replacement list, each entry is 2 words. | pw += 2; // find the end of the replacement list, each entry is 2 words. | ||||
} | |||||
p = (char *)(pw+1); | p = (char *)(pw+1); | ||||
#ifdef ARCH_BIG | #ifdef ARCH_BIG | ||||
if (p[0] == RULE_LETTERGP2) { | if (p[0] == RULE_LETTERGP2) { | ||||
ix = p[1] - 'A'; | ix = p[1] - 'A'; | ||||
p += 2; | p += 2; | ||||
if ((ix >= 0) && (ix < N_LETTER_GROUPS)) { | |||||
if ((ix >= 0) && (ix < N_LETTER_GROUPS)) | |||||
tr->letterGroups[ix] = p; | tr->letterGroups[ix] = p; | ||||
} | |||||
} else { | } else { | ||||
len = strlen(p); | len = strlen(p); | ||||
p_name = p; | p_name = p; | ||||
c2 = p_name[1]; | c2 = p_name[1]; | ||||
p += (len+1); | p += (len+1); | ||||
if (len == 1) { | |||||
if (len == 1) | |||||
tr->groups1[c] = p; | tr->groups1[c] = p; | ||||
} else if (len == 0) { | |||||
else if (len == 0) | |||||
tr->groups1[0] = p; | tr->groups1[0] = p; | ||||
} else if (c == 1) { | |||||
else if (c == 1) { | |||||
// index by offset from letter base | // index by offset from letter base | ||||
tr->groups3[c2 - 1] = p; | tr->groups3[c2 - 1] = p; | ||||
} else { | } else { | ||||
} | } | ||||
// skip over all the rules in this group | // skip over all the rules in this group | ||||
while (*p != RULE_GROUP_END) { | |||||
while (*p != RULE_GROUP_END) | |||||
p += (strlen(p) + 1); | p += (strlen(p) + 1); | ||||
} | |||||
p++; | p++; | ||||
} | } | ||||
f = fopen(fname, "rb"); | f = fopen(fname, "rb"); | ||||
if ((f == NULL) || (size <= 0)) { | if ((f == NULL) || (size <= 0)) { | ||||
if (no_error == 0) { | |||||
if (no_error == 0) | |||||
fprintf(stderr, "Can't read dictionary file: '%s'\n", fname); | fprintf(stderr, "Can't read dictionary file: '%s'\n", fname); | ||||
} | |||||
if (f != NULL) | if (f != NULL) | ||||
fclose(f); | fclose(f); | ||||
return 1; | return 1; | ||||
for (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; | ||||
} | |||||
p++; // skip over the zero which terminates the list for this hash value | p++; // skip over the zero which terminates the list for this hash value | ||||
} | } | ||||
if ((tr->dict_min_size > 0) && (size < (unsigned int)tr->dict_min_size)) { | |||||
if ((tr->dict_min_size > 0) && (size < (unsigned int)tr->dict_min_size)) | |||||
fprintf(stderr, "Full dictionary is not installed for '%s'\n", name); | fprintf(stderr, "Full dictionary is not installed for '%s'\n", name); | ||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
*bad_phoneme = 0; | *bad_phoneme = 0; | ||||
// skip initial blanks | // skip initial blanks | ||||
while (isspace(*p)) { | |||||
while (isspace(*p)) | |||||
p++; | p++; | ||||
} | |||||
while (((c = *p) != 0) && !isspace(c)) { | while (((c = *p) != 0) && !isspace(c)) { | ||||
consumed = 0; | consumed = 0; | ||||
if (max_ph == 0) { | if (max_ph == 0) { | ||||
// not recognised, report and ignore | // not recognised, report and ignore | ||||
if (bad_phoneme != NULL) { | |||||
if (bad_phoneme != NULL) | |||||
utf8_in(bad_phoneme, p); | utf8_in(bad_phoneme, p); | ||||
} | |||||
*outptr++ = 0; | *outptr++ = 0; | ||||
return p+1; | return p+1; | ||||
} | } | ||||
*p_lang = 0; // don't need "en", it's assumed by default | *p_lang = 0; // don't need "en", it's assumed by default | ||||
return p; | return p; | ||||
} | } | ||||
} else { | |||||
} else | |||||
*outptr++ = '|'; // more phonemes follow, terminate language string with separator | *outptr++ = '|'; // more phonemes follow, terminate language string with separator | ||||
} | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
mnem = mnem >> 8; | mnem = mnem >> 8; | ||||
} | } | ||||
if (phcode == phonSWITCH) { | if (phcode == phonSWITCH) { | ||||
while (isalpha(*inptr)) { | |||||
while (isalpha(*inptr)) | |||||
*outptr++ = *inptr++; | *outptr++ = *inptr++; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// has an ipa name been defined for this phoneme ? | // has an ipa name been defined for this phoneme ? | ||||
phdata.ipa_string[0] = 0; | phdata.ipa_string[0] = 0; | ||||
if (plist == NULL) { | |||||
if (plist == NULL) | |||||
InterpretPhoneme2(ph->code, &phdata); | InterpretPhoneme2(ph->code, &phdata); | ||||
} else { | |||||
else | |||||
InterpretPhoneme(NULL, 0, plist, &phdata, NULL); | InterpretPhoneme(NULL, 0, plist, &phdata, NULL); | ||||
} | |||||
p = phdata.ipa_string; | p = phdata.ipa_string; | ||||
if (*p == 0x20) { | if (*p == 0x20) { | ||||
c = ipa1[c-0x20]; | c = ipa1[c-0x20]; | ||||
ix += utf8_out(c, &phon_out[ix]); | ix += utf8_out(c, &phon_out[ix]); | ||||
} else { | |||||
} else | |||||
phon_out[ix++] = c; | phon_out[ix++] = c; | ||||
} | |||||
first = 0; | first = 0; | ||||
} | } | ||||
if ((!plist->newword) || (separate_phonemes == ' ')) { | if ((!plist->newword) || (separate_phonemes == ' ')) { | ||||
if ((separate_phonemes != 0) && (ix > 1)) { | if ((separate_phonemes != 0) && (ix > 1)) { | ||||
utf8_in(&c, phon_buf2); | utf8_in(&c, phon_buf2); | ||||
if ((c < 0x2b0) || (c > 0x36f)) { // not if the phoneme starts with a superscript letter | |||||
if ((c < 0x2b0) || (c > 0x36f)) // not if the phoneme starts with a superscript letter | |||||
buf += utf8_out(separate_phonemes, buf); | buf += utf8_out(separate_phonemes, buf); | ||||
} | |||||
} | } | ||||
} | } | ||||
c = 0x2cc; // ipa, secondary stress | c = 0x2cc; // ipa, secondary stress | ||||
if (stress > 3) | if (stress > 3) | ||||
c = 0x02c8; // ipa, primary stress | c = 0x02c8; // ipa, primary stress | ||||
} else { | |||||
} else | |||||
c = stress_chars[stress]; | c = stress_chars[stress]; | ||||
} | |||||
if (c != 0) { | |||||
if (c != 0) | |||||
buf += utf8_out(c, buf); | buf += utf8_out(c, buf); | ||||
} | |||||
} | } | ||||
} | } | ||||
p += utf8_in(&c, p); | p += utf8_in(&c, p); | ||||
if (use_tie != 0) { | if (use_tie != 0) { | ||||
// look for non-inital alphabetic character, but not diacritic, superscript etc. | // look for non-inital alphabetic character, but not diacritic, superscript etc. | ||||
if ((count > 0) && !(flags & (1 << (count-1))) && ((c < 0x2b0) || (c > 0x36f)) && iswalpha2(c)) { | |||||
if ((count > 0) && !(flags & (1 << (count-1))) && ((c < 0x2b0) || (c > 0x36f)) && iswalpha2(c)) | |||||
buf += utf8_out(use_tie, buf); | buf += utf8_out(use_tie, buf); | ||||
} | |||||
} | } | ||||
buf += utf8_out(c, buf); | buf += utf8_out(c, buf); | ||||
count++; | count++; | ||||
} | } | ||||
if (plist->ph->code != phonSWITCH) { | if (plist->ph->code != phonSWITCH) { | ||||
if (plist->synthflags & SFLAG_LENGTHEN) { | |||||
if (plist->synthflags & SFLAG_LENGTHEN) | |||||
buf = WritePhMnemonic(buf, phoneme_tab[phonLENGTHEN], NULL, use_ipa, NULL); | buf = WritePhMnemonic(buf, phoneme_tab[phonLENGTHEN], NULL, use_ipa, NULL); | ||||
} | |||||
if ((plist->synthflags & SFLAG_SYLLABLE) && (plist->type != phVOWEL)) { | if ((plist->synthflags & SFLAG_SYLLABLE) && (plist->type != phVOWEL)) { | ||||
// syllablic consonant | // syllablic consonant | ||||
buf = WritePhMnemonic(buf, phoneme_tab[phonSYLLABIC], NULL, use_ipa, NULL); | buf = WritePhMnemonic(buf, phoneme_tab[phonSYLLABIC], NULL, use_ipa, NULL); | ||||
} | } | ||||
if (plist->tone_ph > 0) { | |||||
if (plist->tone_ph > 0) | |||||
buf = WritePhMnemonic(buf, phoneme_tab[plist->tone_ph], NULL, use_ipa, NULL); | buf = WritePhMnemonic(buf, phoneme_tab[plist->tone_ph], NULL, use_ipa, NULL); | ||||
} | |||||
} | } | ||||
len = buf - phon_buf; | len = buf - phon_buf; | ||||
if (pre) { | if (pre) { | ||||
len = strlen(p); | len = strlen(p); | ||||
w = word - len + 1; | w = word - len + 1; | ||||
} else { | |||||
} else | |||||
w = word; | w = word; | ||||
} | |||||
while ((*p == *w) && (*w != 0)) { | while ((*p == *w) && (*w != 0)) { | ||||
w++; | w++; | ||||
p++; | p++; | ||||
} | } | ||||
max_stress = max_stress_input = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 1); | max_stress = max_stress_input = GetVowelStress(tr, phonetic, vowel_stress, &vowel_count, &stressed_syllable, 1); | ||||
if ((max_stress < 0) && dictionary_flags) { | |||||
if ((max_stress < 0) && dictionary_flags) | |||||
max_stress = 0; | max_stress = 0; | ||||
} | |||||
// heavy or light syllables | // heavy or light syllables | ||||
ix = 1; | ix = 1; | ||||
// stress on second syllable | // stress on second syllable | ||||
if ((stressed_syllable == 0) && (vowel_count > 2)) { | if ((stressed_syllable == 0) && (vowel_count > 2)) { | ||||
stressed_syllable = 2; | stressed_syllable = 2; | ||||
if (max_stress == 0) { | |||||
if (max_stress == 0) | |||||
vowel_stress[stressed_syllable] = 4; | vowel_stress[stressed_syllable] = 4; | ||||
} | |||||
max_stress = 4; | max_stress = 4; | ||||
} | } | ||||
break; | break; | ||||
} else { | } else { | ||||
if ((mnem == 's') && (phoneme_tab[final_ph2]->type == phNASAL)) { | if ((mnem == 's') && (phoneme_tab[final_ph2]->type == phNASAL)) { | ||||
// -ns stress remains on penultimate syllable | // -ns stress remains on penultimate syllable | ||||
} else if (((phoneme_tab[final_ph]->type != phNASAL) && (mnem != 's')) || (phoneme_tab[final_ph2]->type != phVOWEL)) { | |||||
} else if (((phoneme_tab[final_ph]->type != phNASAL) && (mnem != 's')) || (phoneme_tab[final_ph2]->type != phVOWEL)) | |||||
stressed_syllable = vowel_count - 1; | stressed_syllable = vowel_count - 1; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (stressflags & S_FINAL_LONG) { | if (stressflags & S_FINAL_LONG) { | ||||
// stress on last syllable if it has a long vowel, but previous syllable has a short vowel | // stress on last syllable if it has a long vowel, but previous syllable has a short vowel | ||||
if (vowel_length[vowel_count - 1] > vowel_length[vowel_count - 2]) { | |||||
if (vowel_length[vowel_count - 1] > vowel_length[vowel_count - 2]) | |||||
stressed_syllable = vowel_count - 1; | stressed_syllable = vowel_count - 1; | ||||
} | |||||
} | } | ||||
if ((vowel_stress[stressed_syllable] == 0) || (vowel_stress[stressed_syllable] == 1)) { | if ((vowel_stress[stressed_syllable] == 0) || (vowel_stress[stressed_syllable] == 1)) { | ||||
if (stressed_syllable < 1) | if (stressed_syllable < 1) | ||||
stressed_syllable = 1; | stressed_syllable = 1; | ||||
if (max_stress == 0) { | |||||
if (max_stress == 0) | |||||
vowel_stress[stressed_syllable] = 4; | vowel_stress[stressed_syllable] = 4; | ||||
} | |||||
max_stress = 4; | max_stress = 4; | ||||
} | } | ||||
break; | break; | ||||
if ((stressflags & S_INITIAL_2) && (vowel_stress[1] < 0)) { | if ((stressflags & S_INITIAL_2) && (vowel_stress[1] < 0)) { | ||||
// If there is only one syllable before the primary stress, give it a secondary stress | // If there is only one syllable before the primary stress, give it a secondary stress | ||||
if ((vowel_count > 3) && (vowel_stress[2] >= 4)) { | |||||
if ((vowel_count > 3) && (vowel_stress[2] >= 4)) | |||||
vowel_stress[1] = 3; | vowel_stress[1] = 3; | ||||
} | |||||
} | } | ||||
} | } | ||||
if ((tr->langopts.vowel_pause & 0x30) && (ph->type == phVOWEL)) { | if ((tr->langopts.vowel_pause & 0x30) && (ph->type == phVOWEL)) { | ||||
// word starts with a vowel | // word starts with a vowel | ||||
if ((tr->langopts.vowel_pause & 0x20) && (vowel_stress[1] >= 4)) { | |||||
if ((tr->langopts.vowel_pause & 0x20) && (vowel_stress[1] >= 4)) | |||||
*output++ = phonPAUSE_NOLINK; // not to be replaced by link | *output++ = phonPAUSE_NOLINK; // not to be replaced by link | ||||
} else { | |||||
else | |||||
*output++ = phonPAUSE_VSHORT; // break, but no pause | *output++ = phonPAUSE_VSHORT; // break, but no pause | ||||
} | |||||
} | } | ||||
} | } | ||||
if ((ph = phoneme_tab[phcode]) == NULL) | if ((ph = phoneme_tab[phcode]) == NULL) | ||||
continue; | continue; | ||||
if (ph->type == phPAUSE) { | |||||
if (ph->type == phPAUSE) | |||||
tr->prev_last_stress = 0; | tr->prev_last_stress = 0; | ||||
} else if (((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC)) || (*p == phonSYLLABIC)) { | |||||
else if (((ph->type == phVOWEL) && !(ph->phflags & phNONSYLLABIC)) || (*p == phonSYLLABIC)) { | |||||
// a vowel, or a consonant followed by a syllabic consonant marker | // a vowel, or a consonant followed by a syllabic consonant marker | ||||
v_stress = vowel_stress[v]; | v_stress = vowel_stress[v]; | ||||
*output++ = stress_phonemes[v_stress]; // mark stress of all vowels except 1 (unstressed) | *output++ = stress_phonemes[v_stress]; // mark stress of all vowels except 1 (unstressed) | ||||
if (vowel_stress[v] > max_stress) { | |||||
if (vowel_stress[v] > max_stress) | |||||
max_stress = vowel_stress[v]; | max_stress = vowel_stress[v]; | ||||
} | |||||
if ((*p == phonLENGTHEN) && ((opt_length = tr->langopts.param[LOPT_IT_LENGTHEN]) & 1)) { | if ((*p == phonLENGTHEN) && ((opt_length = tr->langopts.param[LOPT_IT_LENGTHEN]) & 1)) { | ||||
// remove lengthen indicator from non-stressed syllables | // remove lengthen indicator from non-stressed syllables | ||||
int length; | int length; | ||||
length = strlen(ph) + strlen(string); | length = strlen(ph) + strlen(string); | ||||
if (length >= size) { | |||||
if (length >= size) | |||||
return; | return; | ||||
} | |||||
/* any stressable vowel ? */ | /* any stressable vowel ? */ | ||||
unstress_mark = 0; | unstress_mark = 0; | ||||
if (rb == RULE_LINENUM) | if (rb == RULE_LINENUM) | ||||
match.phonemes += 2; // skip over line number | match.phonemes += 2; // skip over line number | ||||
} | } | ||||
} else { | |||||
} else | |||||
match.phonemes = ""; | match.phonemes = ""; | ||||
} | |||||
rule--; // so we are still pointing at the 0 | rule--; // so we are still pointing at the 0 | ||||
failed = 2; // matched OK | failed = 2; // matched OK | ||||
break; | break; | ||||
break; | break; | ||||
case RULE_NOTVOWEL: | case RULE_NOTVOWEL: | ||||
if (IsLetter(tr, letter_w, 0) || ((letter_w == ' ') && (word_flags & FLAG_SUFFIX_VOWEL))) { | |||||
if (IsLetter(tr, letter_w, 0) || ((letter_w == ' ') && (word_flags & FLAG_SUFFIX_VOWEL))) | |||||
failed = 1; | failed = 1; | ||||
} else { | |||||
else { | |||||
add_points = (20-distance_right); | add_points = (20-distance_right); | ||||
post_ptr += letter_xbytes; | post_ptr += letter_xbytes; | ||||
} | } | ||||
case RULE_DOLLAR: | case RULE_DOLLAR: | ||||
command = *rule++; | command = *rule++; | ||||
if (command == DOLLAR_UNPR) { | |||||
if (command == DOLLAR_UNPR) | |||||
match.end_type = SUFX_UNPRON; // $unpron | match.end_type = SUFX_UNPRON; // $unpron | ||||
} else if (command == DOLLAR_NOPREFIX) { // $noprefix | |||||
else if (command == DOLLAR_NOPREFIX) { // $noprefix | |||||
if (word_flags & FLAG_PREFIX_REMOVED) | if (word_flags & FLAG_PREFIX_REMOVED) | ||||
failed = 1; // a prefix has been removed | failed = 1; // a prefix has been removed | ||||
else | else | ||||
break; | break; | ||||
case '-': | case '-': | ||||
if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN_AFTER))) { | |||||
if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN_AFTER))) | |||||
add_points = (22-distance_right); // one point more than match against space | add_points = (22-distance_right); // one point more than match against space | ||||
} else | |||||
else | |||||
failed = 1; | failed = 1; | ||||
break; | break; | ||||
p2 = p; | p2 = p; | ||||
p += utf8_in(&letter_w, p); | p += utf8_in(&letter_w, p); | ||||
} | } | ||||
if (letter_w == rule_w) { | |||||
if (letter_w == rule_w) | |||||
post_ptr = p2; | post_ptr = p2; | ||||
} | |||||
} | } | ||||
break; | break; | ||||
break; | break; | ||||
case '-': | case '-': | ||||
if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN))) { | |||||
if ((letter == '-') || ((letter == ' ') && (word_flags & FLAG_HYPHEN))) | |||||
add_points = (22-distance_right); // one point more than match against space | add_points = (22-distance_right); // one point more than match against space | ||||
} else | |||||
else | |||||
failed = 1; | failed = 1; | ||||
break; | break; | ||||
if (letter == rb) { | if (letter == rb) { | ||||
if (letter == RULE_SPACE) | if (letter == RULE_SPACE) | ||||
add_points = 4; | add_points = 4; | ||||
else { | |||||
if ((letter & 0xc0) != 0x80) { | |||||
// not for non-initial UTF-8 bytes | |||||
add_points = (21-distance_left); | |||||
} | |||||
else if ((letter & 0xc0) != 0x80) { | |||||
// not for non-initial UTF-8 bytes | |||||
add_points = (21-distance_left); | |||||
} | } | ||||
} else | } else | ||||
failed = 1; | failed = 1; | ||||
char wordbuf[120]; | char wordbuf[120]; | ||||
unsigned int ix; | unsigned int ix; | ||||
for (ix = 0; ((c = p_start[ix]) != ' ') && (c != 0) && (ix < (sizeof(wordbuf)-1)); ix++) { | |||||
for (ix = 0; ((c = p_start[ix]) != ' ') && (c != 0) && (ix < (sizeof(wordbuf)-1)); ix++) | |||||
wordbuf[ix] = c; | wordbuf[ix] = c; | ||||
} | |||||
wordbuf[ix] = 0; | wordbuf[ix] = 0; | ||||
if (word_flags & FLAG_UNPRON_TEST) | if (word_flags & FLAG_UNPRON_TEST) | ||||
fprintf(f_trans, "Unpronouncable? '%s'\n", wordbuf); | fprintf(f_trans, "Unpronouncable? '%s'\n", wordbuf); | ||||
return 0; | return 0; | ||||
} | } | ||||
if ((option_phonemes & espeakPHONEMES_TRACE) && ((word_flags & FLAG_NO_TRACE) == 0)) { | |||||
if ((option_phonemes & espeakPHONEMES_TRACE) && ((word_flags & FLAG_NO_TRACE) == 0)) | |||||
fprintf(f_trans, "\n"); | fprintf(f_trans, "\n"); | ||||
} | |||||
match1.end_type &= ~SUFX_UNPRON; | match1.end_type &= ~SUFX_UNPRON; | ||||
p += utf8_in(&c, p); | p += utf8_in(&c, p); | ||||
if (c != 0) { | if (c != 0) { | ||||
if ((c >= min) && (c <= max)) { | if ((c >= min) && (c <= max)) { | ||||
if (map == NULL) { | |||||
if (map == NULL) | |||||
buf[bufix++] = c - offset; | buf[bufix++] = c - offset; | ||||
} else { | |||||
else { | |||||
// get the code from the transpose map | // get the code from the transpose map | ||||
if (map[c - min] > 0) { | |||||
if (map[c - min] > 0) | |||||
buf[bufix++] = map[c - min]; | buf[bufix++] = map[c - min]; | ||||
} else { | |||||
else { | |||||
all_alpha = 0; | all_alpha = 0; | ||||
break; | break; | ||||
} | } | ||||
ix = p2 - buf; | ix = p2 - buf; | ||||
memcpy(text, buf, ix); | memcpy(text, buf, ix); | ||||
return ix | 0x40; // bit 6 indicates compressed characters | return ix | 0x40; // bit 6 indicates compressed characters | ||||
} else { | |||||
return strlen(text); | |||||
} | } | ||||
return strlen(text); | |||||
} | } | ||||
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; | ||||
} | |||||
lookup_symbol = flags[1] & FLAG_LOOKUP_SYMBOL; | lookup_symbol = flags[1] & FLAG_LOOKUP_SYMBOL; | ||||
word1 = word; | word1 = word; | ||||
strncpy0(word_buf, word, N_WORD_BYTES); | strncpy0(word_buf, word, N_WORD_BYTES); | ||||
wlen = TransposeAlphabet(tr, word_buf); // bit 6 indicates compressed characters | wlen = TransposeAlphabet(tr, word_buf); // bit 6 indicates compressed characters | ||||
word = word_buf; | word = word_buf; | ||||
} else { | |||||
} else | |||||
wlen = strlen(word); | wlen = strlen(word); | ||||
} | |||||
hash = HashDictionary(word); | hash = HashDictionary(word); | ||||
p = tr->dict_hashtab[hash]; | p = tr->dict_hashtab[hash]; | ||||
// or has an embedded command, such as MARK | // or has an embedded command, such as MARK | ||||
if (wtab != NULL) { | if (wtab != NULL) { | ||||
for (ix = 0; ix <= skipwords; ix++) { | for (ix = 0; ix <= skipwords; ix++) { | ||||
if (wtab[ix].flags & FLAG_EMPHASIZED2) { | |||||
if (wtab[ix].flags & FLAG_EMPHASIZED2) | |||||
condition_failed = 1; | condition_failed = 1; | ||||
} | |||||
} | } | ||||
} | } | ||||
dictionary_flags = (dictionary_flags & ~0xf) | (flag & 0xf); | dictionary_flags = (dictionary_flags & ~0xf) | (flag & 0xf); | ||||
if ((flag & 0xc) == 0xc) | if ((flag & 0xc) == 0xc) | ||||
dictionary_flags |= FLAG_STRESS_END; | dictionary_flags |= FLAG_STRESS_END; | ||||
} else if (flag >= 32) { | |||||
} else if (flag >= 32) | |||||
dictionary_flags2 |= (1L << (flag-32)); | dictionary_flags2 |= (1L << (flag-32)); | ||||
} else { | |||||
else | |||||
dictionary_flags |= (1L << flag); | dictionary_flags |= (1L << flag); | ||||
} | |||||
} | } | ||||
if (condition_failed) { | if (condition_failed) { | ||||
} | } | ||||
if (dictionary_flags2 & FLAG_HYPHENATED) { | if (dictionary_flags2 & FLAG_HYPHENATED) { | ||||
if (!(wflags & FLAG_HYPHEN_AFTER)) { | |||||
if (!(wflags & FLAG_HYPHEN_AFTER)) | |||||
continue; | continue; | ||||
} | |||||
} | } | ||||
if (dictionary_flags2 & FLAG_CAPITAL) { | if (dictionary_flags2 & FLAG_CAPITAL) { | ||||
if (!(wflags & FLAG_FIRST_UPPER)) { | |||||
if (!(wflags & FLAG_FIRST_UPPER)) | |||||
continue; | continue; | ||||
} | |||||
} | } | ||||
if (dictionary_flags2 & FLAG_ALLCAPS) { | if (dictionary_flags2 & FLAG_ALLCAPS) { | ||||
if (!(wflags & FLAG_ALL_UPPER)) { | |||||
if (!(wflags & FLAG_ALL_UPPER)) | |||||
continue; | continue; | ||||
} | |||||
} | } | ||||
if (dictionary_flags & FLAG_NEEDS_DOT) { | if (dictionary_flags & FLAG_NEEDS_DOT) { | ||||
if (!(wflags & FLAG_HAS_DOT)) | if (!(wflags & FLAG_HAS_DOT)) | ||||
memcpy(word_buf, word2, word_end-word2); | memcpy(word_buf, word2, word_end-word2); | ||||
word_buf[word_end-word2-1] = 0; | word_buf[word_end-word2-1] = 0; | ||||
fprintf(f_trans, "Found: '%s %s\n", word1, word_buf); | fprintf(f_trans, "Found: '%s %s\n", word1, word_buf); | ||||
} else { | |||||
} else | |||||
fprintf(f_trans, "Found: '%s", word1); | fprintf(f_trans, "Found: '%s", word1); | ||||
} | |||||
print_dictionary_flags(flags, dict_flags_buf, sizeof(dict_flags_buf)); | print_dictionary_flags(flags, dict_flags_buf, sizeof(dict_flags_buf)); | ||||
fprintf(f_trans, "' [%s] %s\n", ph_decoded, dict_flags_buf); | fprintf(f_trans, "' [%s] %s\n", ph_decoded, dict_flags_buf); | ||||
} | } | ||||
} | } | ||||
ix = utf8_in(&c, word); | ix = utf8_in(&c, word); | ||||
if ((word[ix] == 0) && !IsAlpha(c)) { | |||||
if ((word[ix] == 0) && !IsAlpha(c)) | |||||
flags[0] |= FLAG_MAX3; | flags[0] |= FLAG_MAX3; | ||||
} | |||||
return word_end; | return word_end; | ||||
} | } | ||||
if (length > 0) { | if (length > 0) { | ||||
// found an abbreviation containing dots | // found an abbreviation containing dots | ||||
nbytes = 0; | nbytes = 0; | ||||
while (((c = word2[nbytes]) != 0) && (c != ' ')) { | |||||
while (((c = word2[nbytes]) != 0) && (c != ' ')) | |||||
nbytes++; | nbytes++; | ||||
} | |||||
memcpy(&word[length], word2, nbytes); | memcpy(&word[length], word2, nbytes); | ||||
word[length+nbytes] = 0; | word[length+nbytes] = 0; | ||||
found = LookupDict2(tr, word, word2, ph_out, flags, end_flags, wtab); | found = LookupDict2(tr, word, word2, ph_out, flags, end_flags, wtab); | ||||
if (flags[0] & FLAG_MAX3) { | if (flags[0] & FLAG_MAX3) { | ||||
if (strcmp(ph_out, tr->phonemes_repeat) == 0) { | if (strcmp(ph_out, tr->phonemes_repeat) == 0) { | ||||
tr->phonemes_repeat_count++; | tr->phonemes_repeat_count++; | ||||
if (tr->phonemes_repeat_count > 3) { | |||||
if (tr->phonemes_repeat_count > 3) | |||||
ph_out[0] = 0; | ph_out[0] = 0; | ||||
} | |||||
} else { | } else { | ||||
strncpy0(tr->phonemes_repeat, ph_out, sizeof(tr->phonemes_repeat)); | strncpy0(tr->phonemes_repeat, ph_out, sizeof(tr->phonemes_repeat)); | ||||
tr->phonemes_repeat_count = 1; | tr->phonemes_repeat_count = 1; | ||||
} | } | ||||
} else { | |||||
} else | |||||
tr->phonemes_repeat_count = 0; | tr->phonemes_repeat_count = 0; | ||||
} | |||||
if ((found == 0) && (flags[1] & FLAG_ACCENT)) { | if ((found == 0) && (flags[1] & FLAG_ACCENT)) { | ||||
flags[0] = 0; | flags[0] = 0; | ||||
flags[1] = FLAG_LOOKUP_SYMBOL; | flags[1] = FLAG_LOOKUP_SYMBOL; | ||||
if ((flags0 = LookupDictList(tr, &word1, ph_out, flags, FLAG_ALLOW_TEXTMODE, NULL)) != 0) { | |||||
if ((flags0 = LookupDictList(tr, &word1, ph_out, flags, FLAG_ALLOW_TEXTMODE, NULL)) != 0) | |||||
flags0 = flags[0]; | flags0 = flags[0]; | ||||
} | |||||
if (flags[0] & FLAG_TEXTMODE) { | if (flags[0] & FLAG_TEXTMODE) { | ||||
say_as = option_sayas; | say_as = option_sayas; | ||||
for (i = 0; (p = add_e_exceptions[i]) != NULL; i++) { | for (i = 0; (p = add_e_exceptions[i]) != NULL; i++) { | ||||
len = strlen(p); | len = strlen(p); | ||||
if (memcmp(p, &word_end[1-len], len) == 0) { | |||||
if (memcmp(p, &word_end[1-len], len) == 0) | |||||
break; | break; | ||||
} | |||||
} | } | ||||
if (p == NULL) | if (p == NULL) | ||||
end_flags |= FLAG_SUFX_E_ADDED; // no exception found | end_flags |= FLAG_SUFX_E_ADDED; // no exception found | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} else if (tr->langopts.suffix_add_e != 0) { | |||||
} else if (tr->langopts.suffix_add_e != 0) | |||||
end_flags |= FLAG_SUFX_E_ADDED; | end_flags |= FLAG_SUFX_E_ADDED; | ||||
} | |||||
if (end_flags & FLAG_SUFX_E_ADDED) { | if (end_flags & FLAG_SUFX_E_ADDED) { | ||||
utf8_out(tr->langopts.suffix_add_e, &word_end[1]); | utf8_out(tr->langopts.suffix_add_e, &word_end[1]); | ||||
if (option_phonemes & espeakPHONEMES_TRACE) { | |||||
if (option_phonemes & espeakPHONEMES_TRACE) | |||||
fprintf(f_trans, "add e\n"); | fprintf(f_trans, "add e\n"); | ||||
} | |||||
} | } | ||||
} | } | ||||
t_espeak_text *data = NULL; | t_espeak_text *data = NULL; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!text || !size || !a_command) { | |||||
if (!text || !size || !a_command) | |||||
goto text_error; | goto text_error; | ||||
} | |||||
a_text = malloc(size+1); | a_text = malloc(size+1); | ||||
if (!a_text) { | |||||
if (!a_text) | |||||
goto text_error; | goto text_error; | ||||
} | |||||
memcpy(a_text, text, size); | memcpy(a_text, text, size); | ||||
a_command->type = ET_TEXT; | a_command->type = ET_TEXT; | ||||
text_error: | text_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_text) { | |||||
if (a_text) | |||||
free(a_text); | free(a_text); | ||||
} | |||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
t_espeak_terminated_msg *data = NULL; | t_espeak_terminated_msg *data = NULL; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!a_command) { | |||||
if (!a_command) | |||||
goto msg_error; | goto msg_error; | ||||
} | |||||
a_command->type = ET_TERMINATED_MSG; | a_command->type = ET_TERMINATED_MSG; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
msg_error: | msg_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
t_espeak_mark *data = NULL; | t_espeak_mark *data = NULL; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!text || !size || !index_mark || !a_command) { | |||||
if (!text || !size || !index_mark || !a_command) | |||||
goto mark_error; | goto mark_error; | ||||
} | |||||
a_text = malloc(size); | a_text = malloc(size); | ||||
if (!a_text) { | |||||
if (!a_text) | |||||
goto mark_error; | goto mark_error; | ||||
} | |||||
memcpy(a_text, text, size); | memcpy(a_text, text, size); | ||||
a_index_mark = strdup(index_mark); | a_index_mark = strdup(index_mark); | ||||
mark_error: | mark_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_text) { | |||||
if (a_text) | |||||
free(a_text); | free(a_text); | ||||
} | |||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
if (a_index_mark) { | |||||
if (a_index_mark) | |||||
free(a_index_mark); | free(a_index_mark); | ||||
} | |||||
} | } | ||||
SHOW("ET_MARK malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier); | SHOW("ET_MARK malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier); | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!key_name || !a_command) { | |||||
if (!key_name || !a_command) | |||||
goto key_error; | goto key_error; | ||||
} | |||||
a_command->type = ET_KEY; | a_command->type = ET_KEY; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
key_error: | key_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
ENTER("create_espeak_char"); | ENTER("create_espeak_char"); | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!a_command) { | |||||
if (!a_command) | |||||
goto char_error; | goto char_error; | ||||
} | |||||
a_command->type = ET_CHAR; | a_command->type = ET_CHAR; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
char_error: | char_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_parameter *data = NULL; | t_espeak_parameter *data = NULL; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!a_command) { | |||||
if (!a_command) | |||||
goto param_error; | goto param_error; | ||||
} | |||||
a_command->type = ET_PARAMETER; | a_command->type = ET_PARAMETER; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
param_error: | param_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!punctlist || !a_command) { | |||||
if (!punctlist || !a_command) | |||||
goto list_error; | goto list_error; | ||||
} | |||||
a_command->type = ET_PUNCTUATION_LIST; | a_command->type = ET_PUNCTUATION_LIST; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
{ | |||||
size_t len = (wcslen(punctlist) + 1)*sizeof(wchar_t); | |||||
wchar_t *a_list = (wchar_t *)malloc(len); | |||||
memcpy(a_list, punctlist, len); | |||||
a_command->u.my_punctuation_list = a_list; | |||||
} | |||||
size_t len = (wcslen(punctlist) + 1)*sizeof(wchar_t); | |||||
wchar_t *a_list = (wchar_t *)malloc(len); | |||||
memcpy(a_list, punctlist, len); | |||||
a_command->u.my_punctuation_list = a_list; | |||||
a_error = 0; | a_error = 0; | ||||
list_error: | list_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!name || !a_command) { | |||||
if (!name || !a_command) | |||||
goto name_error; | goto name_error; | ||||
} | |||||
a_command->type = ET_VOICE_NAME; | a_command->type = ET_VOICE_NAME; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
name_error: | name_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
int a_error = 1; | int a_error = 1; | ||||
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); | ||||
if (!voice || !a_command) { | |||||
if (!voice || !a_command) | |||||
goto spec_error; | goto spec_error; | ||||
} | |||||
a_command->type = ET_VOICE_SPEC; | a_command->type = ET_VOICE_SPEC; | ||||
a_command->state = CS_UNDEFINED; | a_command->state = CS_UNDEFINED; | ||||
{ | |||||
espeak_VOICE *data = &(a_command->u.my_voice_spec); | |||||
memcpy(data, voice, sizeof(espeak_VOICE)); | |||||
if (voice->name) { | |||||
data->name = strdup(voice->name); | |||||
} | |||||
espeak_VOICE *data = &(a_command->u.my_voice_spec); | |||||
memcpy(data, voice, sizeof(espeak_VOICE)); | |||||
if (voice->languages) { | |||||
data->languages = strdup(voice->languages); | |||||
} | |||||
if (voice->name) | |||||
data->name = strdup(voice->name); | |||||
if (voice->identifier) { | |||||
data->identifier = strdup(voice->identifier); | |||||
} | |||||
if (voice->languages) | |||||
data->languages = strdup(voice->languages); | |||||
a_error = 0; | |||||
} | |||||
if (voice->identifier) | |||||
data->identifier = strdup(voice->identifier); | |||||
a_error = 0; | |||||
spec_error: | spec_error: | ||||
if (a_error) { | if (a_error) { | ||||
if (a_command) { | |||||
if (a_command) | |||||
free(a_command); | free(a_command); | ||||
} | |||||
a_command = NULL; | a_command = NULL; | ||||
} | } | ||||
break; | break; | ||||
case ET_MARK: | case ET_MARK: | ||||
if (the_command->u.my_mark.text) { | |||||
if (the_command->u.my_mark.text) | |||||
free(the_command->u.my_mark.text); | free(the_command->u.my_mark.text); | ||||
} | |||||
if (the_command->u.my_mark.index_mark) { | |||||
if (the_command->u.my_mark.index_mark) | |||||
free((void *)(the_command->u.my_mark.index_mark)); | free((void *)(the_command->u.my_mark.index_mark)); | ||||
} | |||||
break; | break; | ||||
case ET_TERMINATED_MSG: | case ET_TERMINATED_MSG: | ||||
break; | break; | ||||
case ET_KEY: | case ET_KEY: | ||||
if (the_command->u.my_key.key_name) { | |||||
if (the_command->u.my_key.key_name) | |||||
free((void *)(the_command->u.my_key.key_name)); | free((void *)(the_command->u.my_key.key_name)); | ||||
} | |||||
break; | break; | ||||
case ET_CHAR: | case ET_CHAR: | ||||
break; | break; | ||||
case ET_PUNCTUATION_LIST: | case ET_PUNCTUATION_LIST: | ||||
if (the_command->u.my_punctuation_list) { | |||||
if (the_command->u.my_punctuation_list) | |||||
free((void *)(the_command->u.my_punctuation_list)); | free((void *)(the_command->u.my_punctuation_list)); | ||||
} | |||||
break; | break; | ||||
case ET_VOICE_NAME: | case ET_VOICE_NAME: | ||||
if (the_command->u.my_voice_name) { | |||||
if (the_command->u.my_voice_name) | |||||
free((void *)(the_command->u.my_voice_name)); | free((void *)(the_command->u.my_voice_name)); | ||||
} | |||||
break; | break; | ||||
case ET_VOICE_SPEC: | case ET_VOICE_SPEC: | ||||
{ | { | ||||
espeak_VOICE *data = &(the_command->u.my_voice_spec); | espeak_VOICE *data = &(the_command->u.my_voice_spec); | ||||
if (data->name) { | |||||
if (data->name) | |||||
free((void *)data->name); | free((void *)data->name); | ||||
} | |||||
if (data->languages) { | |||||
if (data->languages) | |||||
free((void *)data->languages); | free((void *)data->languages); | ||||
} | |||||
if (data->identifier) { | |||||
if (data->identifier) | |||||
free((void *)data->identifier); | free((void *)data->identifier); | ||||
} | |||||
} | } | ||||
break; | break; | ||||
SHOW("command=0x%x\n", the_command); | SHOW("command=0x%x\n", the_command); | ||||
if (the_command == NULL) { | |||||
if (the_command == NULL) | |||||
return; | return; | ||||
} | |||||
the_command->state = CS_PROCESSED; | the_command->state = CS_PROCESSED; | ||||
ENTER("event_display"); | ENTER("event_display"); | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
if (event == NULL) { | |||||
if (event == NULL) | |||||
SHOW("event_display > event=%s\n", "NULL"); | SHOW("event_display > event=%s\n", "NULL"); | ||||
} else { | |||||
else { | |||||
static const char *label[] = { | static const char *label[] = { | ||||
"LIST_TERMINATED", | "LIST_TERMINATED", | ||||
"WORD", | "WORD", | ||||
{ | { | ||||
ENTER("event_copy"); | ENTER("event_copy"); | ||||
if (event == NULL) { | |||||
if (event == NULL) | |||||
return NULL; | return NULL; | ||||
} | |||||
espeak_EVENT *a_event = (espeak_EVENT *)malloc(sizeof(espeak_EVENT)); | espeak_EVENT *a_event = (espeak_EVENT *)malloc(sizeof(espeak_EVENT)); | ||||
if (a_event) { | if (a_event) { | ||||
{ | { | ||||
case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
if (event->id.name) { | |||||
if (event->id.name) | |||||
a_event->id.name = strdup(event->id.name); | a_event->id.name = strdup(event->id.name); | ||||
} | |||||
break; | break; | ||||
default: | default: | ||||
event_display(event); | event_display(event); | ||||
if (event == NULL) { | |||||
if (event == NULL) | |||||
return 0; | return 0; | ||||
} | |||||
switch (event->type) | switch (event->type) | ||||
{ | { | ||||
case espeakEVENT_MARK: | case espeakEVENT_MARK: | ||||
case espeakEVENT_PLAY: | case espeakEVENT_PLAY: | ||||
if (event->id.name) { | |||||
if (event->id.name) | |||||
free((void *)(event->id.name)); | free((void *)(event->id.name)); | ||||
} | |||||
break; | break; | ||||
default: | default: | ||||
event_display(event); | event_display(event); | ||||
if (!event) { | |||||
if (!event) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
SHOW_TIME("event_declare > locked\n"); | SHOW_TIME("event_declare > locked\n"); | ||||
espeak_EVENT *a_event = event_copy(event); | espeak_EVENT *a_event = event_copy(event); | ||||
a_error = push(a_event); | a_error = push(a_event); | ||||
if (a_error != EE_OK) { | |||||
if (a_error != EE_OK) | |||||
event_delete(a_event); | event_delete(a_event); | ||||
} | |||||
SHOW_TIME("event_declare > unlocking\n"); | SHOW_TIME("event_declare > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
} | } | ||||
SHOW_TIME("event_declare > post my_sem_start_is_required\n"); | SHOW_TIME("event_declare > post my_sem_start_is_required\n"); | ||||
sem_post(&my_sem_start_is_required); | sem_post(&my_sem_start_is_required); | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | |||||
return a_error; | return a_error; | ||||
} | } | ||||
int a_event_is_running = 0; | int a_event_is_running = 0; | ||||
SHOW_TIME("event_stop > locked\n"); | SHOW_TIME("event_stop > locked\n"); | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (my_event_is_running) { | if (my_event_is_running) { | ||||
SHOW_TIME("event_stop > post my_sem_stop_is_required\n"); | SHOW_TIME("event_stop > post my_sem_stop_is_required\n"); | ||||
sem_post(&my_sem_stop_is_required); | sem_post(&my_sem_stop_is_required); | ||||
a_event_is_running = 1; | a_event_is_running = 1; | ||||
} else { | |||||
} else | |||||
init(); // clear pending events | init(); // clear pending events | ||||
} | |||||
SHOW_TIME("event_stop > unlocking\n"); | SHOW_TIME("event_stop > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (a_event_is_running) { | if (a_event_is_running) { | ||||
SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n"); | ||||
} | } | ||||
ts.tv_sec, ts.tv_nsec); | ts.tv_sec, ts.tv_nsec); | ||||
while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 | while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 | ||||
&& errno == EINTR) { | |||||
&& errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
assert(gettimeofday(&tv, NULL) != -1); | assert(gettimeofday(&tv, NULL) != -1); | ||||
SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", | SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n", | ||||
SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n"); | SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n"); | ||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) { | |||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
SHOW_TIME("polling_thread > get my_sem_start_is_required\n"); | SHOW_TIME("polling_thread > get my_sem_start_is_required\n"); | ||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | while (0 == sem_trywait(&my_sem_stop_is_required)) { | ||||
} | } | ||||
; | ; | ||||
} else { | |||||
} else | |||||
a_stop_is_required = 0; | a_stop_is_required = 0; | ||||
} | |||||
// In this loop, my_event_is_running = 1 | // In this loop, my_event_is_running = 1 | ||||
while (head && (a_stop_is_required <= 0)) { | while (head && (a_stop_is_required <= 0)) { | ||||
SHOW_TIME("polling_thread > check head\n"); | SHOW_TIME("polling_thread > check head\n"); | ||||
while (0 == sem_trywait(&my_sem_start_is_required)) { | while (0 == sem_trywait(&my_sem_start_is_required)) { | ||||
} | } | ||||
; | |||||
espeak_EVENT *event = (espeak_EVENT *)(head->data); | espeak_EVENT *event = (espeak_EVENT *)(head->data); | ||||
assert(event); | assert(event); | ||||
int err = get_remaining_time((uint32_t)event->sample, | int err = get_remaining_time((uint32_t)event->sample, | ||||
&time_in_ms, | &time_in_ms, | ||||
&a_stop_is_required); | &a_stop_is_required); | ||||
if (a_stop_is_required > 0) { | |||||
if (a_stop_is_required > 0) | |||||
break; | break; | ||||
} else if (err != 0) { | |||||
else if (err != 0) { | |||||
// No available time: the event is deleted. | // No available time: the event is deleted. | ||||
SHOW("polling_thread > %s\n", "audio device down"); | SHOW("polling_thread > %s\n", "audio device down"); | ||||
a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW("polling_thread > stop required (%d)\n", __LINE__); | SHOW("polling_thread > stop required (%d)\n", __LINE__); | ||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | while (0 == sem_trywait(&my_sem_stop_is_required)) { | ||||
} | } | ||||
; | |||||
} else { | |||||
} else | |||||
a_stop_is_required = 0; | a_stop_is_required = 0; | ||||
} | |||||
} else { // The event will be notified soon: sleep until timeout or stop request | |||||
} else // The event will be notified soon: sleep until timeout or stop request | |||||
a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); | a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); | ||||
} | |||||
} | } | ||||
a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW("polling_thread > stop required (%d)\n", __LINE__); | SHOW("polling_thread > stop required (%d)\n", __LINE__); | ||||
while (0 == sem_trywait(&my_sem_stop_is_required)) { | while (0 == sem_trywait(&my_sem_stop_is_required)) { | ||||
} | } | ||||
; | |||||
} else { | |||||
} else | |||||
a_stop_is_required = 0; | a_stop_is_required = 0; | ||||
} | |||||
} | } | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
} | } | ||||
node *n = (node *)malloc(sizeof(node)); | node *n = (node *)malloc(sizeof(node)); | ||||
if (n == NULL) { | |||||
if (n == NULL) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (head == NULL) { | if (head == NULL) { | ||||
head = n; | head = n; | ||||
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n", the_data, node_counter, ((espeak_EVENT *)the_data)->unique_identifier); | SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n", the_data, node_counter, ((espeak_EVENT *)the_data)->unique_identifier); | ||||
} | } | ||||
if (head == NULL) { | |||||
if (head == NULL) | |||||
tail = NULL; | tail = NULL; | ||||
} | |||||
return the_data; | return the_data; | ||||
} | } |
// leave once the thread is actually started | // leave once the thread is actually started | ||||
SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | |||||
SHOW_TIME("LEAVE fifo_add_command"); | SHOW_TIME("LEAVE fifo_add_command"); | ||||
return a_error; | return a_error; | ||||
} | } | ||||
} | } | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
a_error = EE_INTERNAL_ERROR; | a_error = EE_INTERNAL_ERROR; | ||||
} | |||||
SHOW_TIME("LEAVE fifo_add_commands"); | SHOW_TIME("LEAVE fifo_add_commands"); | ||||
return a_error; | return a_error; | ||||
int a_command_is_running = 0; | int a_command_is_running = 0; | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
SHOW_TIME("fifo_stop > locked\n"); | SHOW_TIME("fifo_stop > locked\n"); | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (my_command_is_running) { | if (my_command_is_running) { | ||||
a_command_is_running = 1; | a_command_is_running = 1; | ||||
} | } | ||||
SHOW_TIME("fifo_stop > unlocking\n"); | SHOW_TIME("fifo_stop > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (a_status != 0) { | |||||
if (a_status != 0) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (a_command_is_running) { | if (a_command_is_running) { | ||||
SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n"); | ||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) { | |||||
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n"); | SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n"); | ||||
} | } | ||||
// | // | ||||
int i = 0; | int i = 0; | ||||
while ((i <= MAX_INACTIVITY_CHECK) && !a_start_is_required) { | while ((i <= MAX_INACTIVITY_CHECK) && !a_start_is_required) { | ||||
if (wave_is_busy(NULL)) { | |||||
if (wave_is_busy(NULL)) | |||||
i = 0; | i = 0; | ||||
} else { | |||||
else | |||||
i++; | i++; | ||||
} | |||||
int err = 0; | int err = 0; | ||||
struct timespec ts; | struct timespec ts; | ||||
ts.tv_sec, ts.tv_nsec); | ts.tv_sec, ts.tv_nsec); | ||||
while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 | while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 | ||||
&& errno == EINTR) { | |||||
&& errno == EINTR) | |||||
continue; | continue; | ||||
} | |||||
assert(gettimeofday(&tv, NULL) != -1); | assert(gettimeofday(&tv, NULL) != -1); | ||||
SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err, | SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err, | ||||
tv.tv_sec, tv.tv_usec*1000); | tv.tv_sec, tv.tv_usec*1000); | ||||
if (err == 0) { | |||||
if (err == 0) | |||||
a_start_is_required = 1; | a_start_is_required = 1; | ||||
} | |||||
} | } | ||||
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE"); | SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE"); | ||||
return a_start_is_required; | return a_start_is_required; | ||||
int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
assert(!a_status); | assert(!a_status); | ||||
int a_stop_is_required = my_stop_is_required; | int a_stop_is_required = my_stop_is_required; | ||||
if (!a_stop_is_required) { | |||||
if (!a_stop_is_required) | |||||
my_command_is_running = 1; | my_command_is_running = 1; | ||||
} | |||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (!a_stop_is_required) { | if (!a_stop_is_required) { | ||||
int a_start_is_required = 0; | int a_start_is_required = 0; | ||||
if (look_for_inactivity) { | if (look_for_inactivity) { | ||||
a_start_is_required = sleep_until_start_request_or_inactivity(); | a_start_is_required = sleep_until_start_request_or_inactivity(); | ||||
if (!a_start_is_required) { | |||||
if (!a_start_is_required) | |||||
close_stream(); | close_stream(); | ||||
} | |||||
} | } | ||||
look_for_inactivity = 1; | look_for_inactivity = 1; | ||||
if (!a_start_is_required) { | if (!a_start_is_required) { | ||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) { | |||||
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) | |||||
continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
} | |||||
} | } | ||||
SHOW_TIME("say_thread > get my_sem_start_is_required\n"); | SHOW_TIME("say_thread > get my_sem_start_is_required\n"); | ||||
SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); | SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); | ||||
while (0 == sem_trywait(&my_sem_start_is_required)) { | while (0 == sem_trywait(&my_sem_start_is_required)) { | ||||
} | } | ||||
; | |||||
if (my_stop_is_required) { | if (my_stop_is_required) { | ||||
SHOW_TIME("say_thread > my_command_is_running = 0\n"); | SHOW_TIME("say_thread > my_command_is_running = 0\n"); | ||||
SHOW_TIME("say_thread > unlocking\n"); | SHOW_TIME("say_thread > unlocking\n"); | ||||
a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
if (my_command_is_running) { | |||||
if (my_command_is_running) | |||||
process_espeak_command(a_command); | process_espeak_command(a_command); | ||||
} | |||||
delete_espeak_command(a_command); | delete_espeak_command(a_command); | ||||
} | } | ||||
} | } | ||||
SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); | SHOW_TIME("say_thread > purge my_sem_start_is_required\n"); | ||||
while (0 == sem_trywait(&my_sem_start_is_required)) { | while (0 == sem_trywait(&my_sem_start_is_required)) { | ||||
} | } | ||||
; | |||||
// acknowledge the stop request | // acknowledge the stop request | ||||
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n"); | SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n"); | ||||
} | } | ||||
node *n = (node *)malloc(sizeof(node)); | node *n = (node *)malloc(sizeof(node)); | ||||
if (n == NULL) { | |||||
if (n == NULL) | |||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
} | |||||
if (head == NULL) { | if (head == NULL) { | ||||
head = n; | head = n; | ||||
SHOW("pop > command=0x%x (counter=%d)\n", the_command, node_counter); | SHOW("pop > command=0x%x (counter=%d)\n", the_command, node_counter); | ||||
} | } | ||||
if (head == NULL) { | |||||
if (head == NULL) | |||||
tail = NULL; | tail = NULL; | ||||
} | |||||
display_espeak_command(the_command); | display_espeak_command(the_command); | ||||
ENTER("fifo > init"); | ENTER("fifo > init"); | ||||
c = pop(); | c = pop(); | ||||
while (c != NULL) { | while (c != NULL) { | ||||
if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC)) { | |||||
if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC)) | |||||
process_espeak_command(c); | process_espeak_command(c); | ||||
} | |||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
c = pop(); | c = pop(); | ||||
} | } |
stress = syllable_tab[ix].stress; /* marked stress level */ | stress = syllable_tab[ix].stress; /* marked stress level */ | ||||
if (stress >= max_stress) { | if (stress >= max_stress) { | ||||
if (stress > max_stress) { | |||||
if (stress > max_stress) | |||||
max_stress_posn2 = ix; | max_stress_posn2 = ix; | ||||
} else { | |||||
else | |||||
max_stress_posn2 = max_stress_posn; | max_stress_posn2 = max_stress_posn; | ||||
} | |||||
max_stress_posn = ix; | max_stress_posn = ix; | ||||
max_stress = stress; | max_stress = stress; | ||||
} | } | ||||
if (no_tonic) { | if (no_tonic) { | ||||
tone_posn = tone_posn2 = end; // next position after the end of the truncated clause | tone_posn = tone_posn2 = end; // next position after the end of the truncated clause | ||||
} else if (last_primary >= 0) { | } else if (last_primary >= 0) { | ||||
if (end == clause_end) { | |||||
if (end == clause_end) | |||||
syllable_tab[last_primary].stress = PRIMARY_LAST; | syllable_tab[last_primary].stress = PRIMARY_LAST; | ||||
} | |||||
} else { | } else { | ||||
// no primary stress. Use the highest stress | // no primary stress. Use the highest stress | ||||
syllable_tab[tone_posn].stress = PRIMARY_LAST; | syllable_tab[tone_posn].stress = PRIMARY_LAST; | ||||
if (n_steps > tune->head_max_steps) | if (n_steps > tune->head_max_steps) | ||||
n_steps = tune->head_max_steps; | n_steps = tune->head_max_steps; | ||||
if (n_steps > 1) { | |||||
if (n_steps > 1) | |||||
increment = pitch_range / (n_steps -1); | increment = pitch_range / (n_steps -1); | ||||
} else | |||||
else | |||||
increment = 0; | increment = 0; | ||||
} else if (syl_ix == head_final) { | } else if (syl_ix == head_final) { | ||||
pitch += increment; | pitch += increment; | ||||
else { | else { | ||||
pitch = (tune->head_end << 8) + (pitch_range_abs * tune->head_extend[overflow_ix++])/64; | pitch = (tune->head_end << 8) + (pitch_range_abs * tune->head_extend[overflow_ix++])/64; | ||||
if (overflow_ix >= tune->n_head_extend) { | |||||
if (overflow_ix >= tune->n_head_extend) | |||||
overflow_ix = 0; | overflow_ix = 0; | ||||
} | |||||
} | } | ||||
} | } | ||||
if (n_steps > th->body_max_steps) | if (n_steps > th->body_max_steps) | ||||
n_steps = th->body_max_steps; | n_steps = th->body_max_steps; | ||||
if (n_steps > 1) { | |||||
if (n_steps > 1) | |||||
increment = pitch_range / (n_steps -1); | increment = pitch_range / (n_steps -1); | ||||
} else | |||||
else | |||||
increment = 0; | increment = 0; | ||||
pitch = th->body_start << 8; | pitch = th->body_start << 8; | ||||
n_steps--; | n_steps--; | ||||
n_primary--; | n_primary--; | ||||
if ((tn->backwards) && (n_primary < 2)) { | |||||
if ((tn->backwards) && (n_primary < 2)) | |||||
pitch = tn->backwards[n_primary] << 8; | pitch = tn->backwards[n_primary] << 8; | ||||
} | |||||
} | } | ||||
if (stress >= PRIMARY) { | if (stress >= PRIMARY) { | ||||
syl->stress = PRIMARY_STRESSED; | syl->stress = PRIMARY_STRESSED; | ||||
set_pitch(syl, (pitch >> 8), drops[stress]); | set_pitch(syl, (pitch >> 8), drops[stress]); | ||||
} else if (stress >= SECONDARY) { | |||||
} else if (stress >= SECONDARY) | |||||
set_pitch(syl, (pitch >> 8), drops[stress]); | set_pitch(syl, (pitch >> 8), drops[stress]); | ||||
} else { | |||||
else { | |||||
/* unstressed, drop pitch if preceded by PRIMARY */ | /* unstressed, drop pitch if preceded by PRIMARY */ | ||||
if ((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY) | if ((syllable_tab[ix-1].stress & 0x3f) >= SECONDARY) | ||||
set_pitch(syl, (pitch >> 8) - th->body_lower_u, drops[stress]); | set_pitch(syl, (pitch >> 8) - th->body_lower_u, drops[stress]); | ||||
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; | ||||
} | |||||
pitch = start_pitch << 8; | pitch = start_pitch << 8; | ||||
/* body of tonic segment */ | /* body of tonic segment */ | ||||
/*************************/ | /*************************/ | ||||
if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) { | |||||
if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) | |||||
tone_posn = tone_posn2; // put tone on the penultimate stressed word | tone_posn = tone_posn2; // put tone on the penultimate stressed word | ||||
} | |||||
ix = SetHeadIntonation(tune, ix, tone_posn, 0); | ix = SetHeadIntonation(tune, ix, tone_posn, 0); | ||||
if (no_tonic) | if (no_tonic) | ||||
int drop; | int drop; | ||||
int continuing = 0; | int continuing = 0; | ||||
if (control == 0) { | |||||
if (control == 0) | |||||
return calc_pitches2(start, end, tune_number); | return calc_pitches2(start, end, tune_number); | ||||
} | |||||
if (start > 0) | if (start > 0) | ||||
continuing = 1; | continuing = 1; | ||||
/* body of tonic segment */ | /* body of tonic segment */ | ||||
/*************************/ | /*************************/ | ||||
if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) { | |||||
if (option_tone_flags & OPTION_EMPHASIZE_PENULTIMATE) | |||||
tone_posn = tone_posn2; // put tone on the penultimate stressed word | tone_posn = tone_posn2; // put tone on the penultimate stressed word | ||||
} | |||||
ix = calc_pitch_segment(ix, tone_posn, th, tn, PRIMARY, continuing); | ix = calc_pitch_segment(ix, tone_posn, th, tn, PRIMARY, continuing); | ||||
if (no_tonic) | if (no_tonic) | ||||
prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone | prevw_tph = phoneme_tab[phonPAUSE]; // forget previous tone | ||||
} | } | ||||
if (p->newword) { | |||||
if (p->newword) | |||||
prev_tph = phoneme_tab[phonPAUSE]; // forget across word boundaries | prev_tph = phoneme_tab[phonPAUSE]; // forget across word boundaries | ||||
} | |||||
if (p->synthflags & SFLAG_SYLLABLE) { | if (p->synthflags & SFLAG_SYLLABLE) { | ||||
tone_ph = p->tone_ph; | tone_ph = p->tone_ph; | ||||
if (pause || tone_promoted) { | if (pause || tone_promoted) { | ||||
tone_ph = PhonemeCode2('5', '5'); // no previous vowel, use tone 1 | tone_ph = PhonemeCode2('5', '5'); // no previous vowel, use tone 1 | ||||
tone_promoted = 1; | tone_promoted = 1; | ||||
} else { | |||||
} else | |||||
tone_ph = PhonemeCode2('1', '1'); // default tone 5 | tone_ph = PhonemeCode2('1', '1'); // default tone 5 | ||||
} | |||||
p->tone_ph = tone_ph; | p->tone_ph = tone_ph; | ||||
tph = phoneme_tab[tone_ph]; | tph = phoneme_tab[tone_ph]; | ||||
} else { | |||||
} else | |||||
tone_promoted = 0; | tone_promoted = 0; | ||||
} | |||||
if (ix == final_stressed) { | if (ix == final_stressed) { | ||||
if ((tph->mnemonic == 0x3535 ) || (tph->mnemonic == 0x3135)) { | if ((tph->mnemonic == 0x3535 ) || (tph->mnemonic == 0x3135)) { | ||||
else | else | ||||
prev_p->tone_ph = PhonemeCode2('2', '1'); | prev_p->tone_ph = PhonemeCode2('2', '1'); | ||||
} | } | ||||
if ((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) { // [51] + [51] | |||||
if ((prev_tph->mnemonic == 0x3135) && (tph->mnemonic == 0x3135)) // [51] + [51] | |||||
prev_p->tone_ph = PhonemeCode2('5', '3'); | prev_p->tone_ph = PhonemeCode2('5', '3'); | ||||
} | |||||
if (tph->mnemonic == 0x3131) { // [11] Tone 5 | if (tph->mnemonic == 0x3131) { // [11] Tone 5 | ||||
// tone 5, change its level depending on the previous tone (across word boundaries) | // tone 5, change its level depending on the previous tone (across word boundaries) | ||||
if (p->stresslevel >= 4) | if (p->stresslevel >= 4) | ||||
n_primary++; | n_primary++; | ||||
} else if ((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0)) { | |||||
} else if ((p->ph->code == phonPAUSE_CLAUSE) && (n_st > 0)) | |||||
syllable_tab[n_st-1].flags |= SYL_END_CLAUSE; | syllable_tab[n_st-1].flags |= SYL_END_CLAUSE; | ||||
} | |||||
} | } | ||||
syllable_tab[n_st].stress = 0; // extra 0 entry at the end | syllable_tab[n_st].stress = 0; // extra 0 entry at the end | ||||
p->pitch2 = syl->pitch2; | p->pitch2 = syl->pitch2; | ||||
p->env = PITCHfall; | p->env = PITCHfall; | ||||
if (syl->flags & SYL_RISE) { | |||||
if (syl->flags & SYL_RISE) | |||||
p->env = PITCHrise; | p->env = PITCHrise; | ||||
} else if (p->stresslevel > 5) | |||||
else if (p->stresslevel > 5) | |||||
p->env = syl->env; | p->env = syl->env; | ||||
if (p->pitch1 > p->pitch2) { | if (p->pitch1 > p->pitch2) { | ||||
p->pitch1 = x + ph->start_type; | p->pitch1 = x + ph->start_type; | ||||
} | } | ||||
if (syl->flags & SYL_EMPHASIS) { | |||||
if (syl->flags & SYL_EMPHASIS) | |||||
p->stresslevel |= 8; // emphasized | p->stresslevel |= 8; // emphasized | ||||
} | |||||
st_ix++; | st_ix++; | ||||
} | } |
result = samples[itemp] + diff_value; | result = samples[itemp] + diff_value; | ||||
result = result * kt_globals.sample_factor; | result = result * kt_globals.sample_factor; | ||||
} else { | |||||
} else | |||||
result = 0; | result = 0; | ||||
} | |||||
return result; | return result; | ||||
} | } | ||||
second half of glottal period) if voicing simultaneously present. | second half of glottal period) if voicing simultaneously present. | ||||
*/ | */ | ||||
if (kt_globals.nper > kt_globals.nmod) { | |||||
if (kt_globals.nper > kt_globals.nmod) | |||||
noise *= (double)0.5; | noise *= (double)0.5; | ||||
} | |||||
/* Compute frication noise */ | /* Compute frication noise */ | ||||
frics = kt_globals.amp_frica * noise; | frics = kt_globals.amp_frica * noise; | ||||
*/ | */ | ||||
if (kt_globals.nper < kt_globals.nopen) { | |||||
if (kt_globals.nper < kt_globals.nopen) | |||||
voice += kt_globals.amp_breth * kt_globals.nrand; | voice += kt_globals.amp_breth * kt_globals.nrand; | ||||
} | |||||
/* Set amplitude of voicing */ | /* Set amplitude of voicing */ | ||||
glotout = kt_globals.amp_voice * voice; | glotout = kt_globals.amp_voice * voice; | ||||
sourc = frics + par_glotout - glotlast; | sourc = frics + par_glotout - glotlast; | ||||
glotlast = par_glotout; | glotlast = par_glotout; | ||||
for (ix = R2p; ix <= R6p; ix++) { | |||||
for (ix = R2p; ix <= R6p; ix++) | |||||
out = resonator(&(kt_globals.rsn[ix]), sourc) - out; | out = resonator(&(kt_globals.rsn[ix]), sourc) - out; | ||||
} | |||||
outbypas = kt_globals.amp_bypas * sourc; | outbypas = kt_globals.amp_bypas * sourc; | ||||
if (echo_tail >= N_ECHO_BUF) | if (echo_tail >= N_ECHO_BUF) | ||||
echo_tail = 0; | echo_tail = 0; | ||||
if (value < -32768) { | |||||
if (value < -32768) | |||||
value = -32768; | value = -32768; | ||||
} | |||||
if (value > 32767) { | |||||
if (value > 32767) | |||||
value = 32767; | value = 32767; | ||||
} | |||||
*out_ptr++ = value; | *out_ptr++ = value; | ||||
*out_ptr++ = value >> 8; | *out_ptr++ = value >> 8; | ||||
echo_head = 0; | echo_head = 0; | ||||
sample_count++; | sample_count++; | ||||
if (out_ptr >= out_end) { | |||||
if (out_ptr >= out_end) | |||||
return 1; | return 1; | ||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
kt_globals.original_f0 = frame->F0hz10 / 10; | kt_globals.original_f0 = frame->F0hz10 / 10; | ||||
frame->AVdb_tmp = frame->AVdb - 7; | frame->AVdb_tmp = frame->AVdb - 7; | ||||
if (frame->AVdb_tmp < 0) { | |||||
if (frame->AVdb_tmp < 0) | |||||
frame->AVdb_tmp = 0; | frame->AVdb_tmp = 0; | ||||
} | |||||
kt_globals.amp_aspir = DBtoLIN(frame->ASP) * 0.05; | kt_globals.amp_aspir = DBtoLIN(frame->ASP) * 0.05; | ||||
kt_globals.amp_frica = DBtoLIN(frame->AF) * 0.25; | kt_globals.amp_frica = DBtoLIN(frame->AF) * 0.25; | ||||
} | } | ||||
Gain0_tmp = frame->Gain0 - 3; | Gain0_tmp = frame->Gain0 - 3; | ||||
if (Gain0_tmp <= 0) { | |||||
if (Gain0_tmp <= 0) | |||||
Gain0_tmp = 57; | Gain0_tmp = 57; | ||||
} | |||||
kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav; | kt_globals.amp_gain0 = DBtoLIN(Gain0_tmp) / kt_globals.scale_wav; | ||||
/* Set coefficients of variable cascade resonators */ | /* Set coefficients of variable cascade resonators */ | ||||
static double doublet[] = { 0.0, 13000000.0, -13000000.0 }; | static double doublet[] = { 0.0, 13000000.0, -13000000.0 }; | ||||
static double vwave; | static double vwave; | ||||
if (kt_globals.nper < 3) { | |||||
if (kt_globals.nper < 3) | |||||
vwave = doublet[kt_globals.nper]; | vwave = doublet[kt_globals.nper]; | ||||
} else { | |||||
else | |||||
vwave = 0.0; | vwave = 0.0; | ||||
} | |||||
return resonator(&(kt_globals.rsn[RGL]), vwave); | return resonator(&(kt_globals.rsn[RGL]), vwave); | ||||
} | } | ||||
lgtemp = vwave * 0.028; | lgtemp = vwave * 0.028; | ||||
return lgtemp; | return lgtemp; | ||||
} else { | |||||
vwave = 0.0; | |||||
return 0.0; | |||||
} | } | ||||
vwave = 0.0; | |||||
return 0.0; | |||||
} | } | ||||
/* Duration of period before amplitude modulation */ | /* Duration of period before amplitude modulation */ | ||||
kt_globals.nmod = kt_globals.T0; | kt_globals.nmod = kt_globals.T0; | ||||
if (frame->AVdb_tmp > 0) { | |||||
if (frame->AVdb_tmp > 0) | |||||
kt_globals.nmod >>= 1; | kt_globals.nmod >>= 1; | ||||
} | |||||
/* Breathiness of voicing waveform */ | /* Breathiness of voicing waveform */ | ||||
kt_globals.nopen = 4 * frame->Kopen; | kt_globals.nopen = 4 * frame->Kopen; | ||||
if ((kt_globals.glsource == IMPULSIVE) && (kt_globals.nopen > 263)) { | |||||
if ((kt_globals.glsource == IMPULSIVE) && (kt_globals.nopen > 263)) | |||||
kt_globals.nopen = 263; | kt_globals.nopen = 263; | ||||
} | |||||
if (kt_globals.nopen >= (kt_globals.T0-1)) { | |||||
if (kt_globals.nopen >= (kt_globals.T0-1)) | |||||
kt_globals.nopen = kt_globals.T0 - 2; | kt_globals.nopen = kt_globals.T0 - 2; | ||||
} | |||||
if (kt_globals.nopen < 40) { | if (kt_globals.nopen < 40) { | ||||
/* F0 max = 1000 Hz */ | /* F0 max = 1000 Hz */ | ||||
temp = kt_globals.T0 - kt_globals.nopen; | temp = kt_globals.T0 - kt_globals.nopen; | ||||
if (frame->Kskew > temp) { | |||||
if (frame->Kskew > temp) | |||||
frame->Kskew = temp; | frame->Kskew = temp; | ||||
} | |||||
if (skew >= 0) { | |||||
if (skew >= 0) | |||||
skew = frame->Kskew; | skew = frame->Kskew; | ||||
} else { | |||||
else | |||||
skew = -frame->Kskew; | skew = -frame->Kskew; | ||||
} | |||||
/* Add skewness to closed portion of voicing period */ | /* Add skewness to closed portion of voicing period */ | ||||
kt_globals.T0 = kt_globals.T0 + skew; | kt_globals.T0 = kt_globals.T0 + skew; | ||||
kt_globals.decay = (0.033 * frame->TLTdb); | kt_globals.decay = (0.033 * frame->TLTdb); | ||||
if (kt_globals.decay > 0.0) { | |||||
if (kt_globals.decay > 0.0) | |||||
kt_globals.onemd = 1.0 - kt_globals.decay; | kt_globals.onemd = 1.0 - kt_globals.decay; | ||||
} else { | |||||
else | |||||
kt_globals.onemd = 1.0; | kt_globals.onemd = 1.0; | ||||
} | |||||
} | } | ||||
} | } | ||||
26214, 29491, 32767 | 26214, 29491, 32767 | ||||
}; | }; | ||||
if ((dB < 0) || (dB > 87)) { | |||||
if ((dB < 0) || (dB > 87)) | |||||
return 0; | return 0; | ||||
} | |||||
return (double)(amptable[dB]) * 0.001; | return (double)(amptable[dB]) * 0.001; | ||||
} | } | ||||
int ix; | int ix; | ||||
int fade; | int fade; | ||||
if (resume == 0) { | |||||
if (resume == 0) | |||||
sample_count = 0; | sample_count = 0; | ||||
} | |||||
while (sample_count < nsamples) { | while (sample_count < nsamples) { | ||||
kt_frame.F0hz10 = (wdata.pitch * 10) / 4096; | kt_frame.F0hz10 = (wdata.pitch * 10) / 4096; | ||||
// F0 is used for the nasal zero | // F0 is used for the nasal zero | ||||
for (ix = 0; ix < 6; ix++) { | for (ix = 0; ix < 6; ix++) { | ||||
kt_frame.Fhz[ix] = peaks[ix].freq; | kt_frame.Fhz[ix] = peaks[ix].freq; | ||||
if (ix < 4) { | |||||
if (ix < 4) | |||||
kt_frame.Bhz[ix] = peaks[ix].bw; | kt_frame.Bhz[ix] = peaks[ix].bw; | ||||
} | |||||
} | } | ||||
for (ix = 1; ix < 7; ix++) { | |||||
for (ix = 1; ix < 7; ix++) | |||||
kt_frame.Ap[ix] = peaks[ix].ap; | kt_frame.Ap[ix] = peaks[ix].ap; | ||||
} | |||||
kt_frame.AVdb = klattp[KLATT_AV]; | kt_frame.AVdb = klattp[KLATT_AV]; | ||||
kt_frame.AVpdb = klattp[KLATT_AVp]; | kt_frame.AVpdb = klattp[KLATT_AVp]; | ||||
for (ix = 0; ix <= 6; ix++) { | for (ix = 0; ix <= 6; ix++) { | ||||
kt_frame.Fhz_next[ix] = peaks[ix].freq; | kt_frame.Fhz_next[ix] = peaks[ix].freq; | ||||
if (ix < 4) { | |||||
if (ix < 4) | |||||
kt_frame.Bhz_next[ix] = peaks[ix].bw; | kt_frame.Bhz_next[ix] = peaks[ix].bw; | ||||
} | |||||
} | } | ||||
// advance the pitch | // advance the pitch | ||||
frame_init(&kt_frame); /* get parameters for next frame of speech */ | frame_init(&kt_frame); /* get parameters for next frame of speech */ | ||||
if (parwave(&kt_frame) == 1) { | |||||
if (parwave(&kt_frame) == 1) | |||||
return 1; // output buffer is full | return 1; // output buffer is full | ||||
} | |||||
} | } | ||||
if (end_wave > 0) { | if (end_wave > 0) { | ||||
end_wave = 0; | end_wave = 0; | ||||
sample_count -= fade; | sample_count -= fade; | ||||
kt_globals.nspfr = fade; | kt_globals.nspfr = fade; | ||||
if (parwave(&kt_frame) == 1) { | |||||
if (parwave(&kt_frame) == 1) | |||||
return 1; // output buffer is full | return 1; // output buffer is full | ||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
end_wave = 0; | end_wave = 0; | ||||
if (control & 2) { | |||||
if (control & 2) | |||||
end_wave = 1; // fadeout at the end | end_wave = 1; // fadeout at the end | ||||
} | |||||
if (control & 1) { | if (control & 1) { | ||||
end_wave = 1; | end_wave = 1; | ||||
for (qix = wcmdq_head+1;; qix++) { | for (qix = wcmdq_head+1;; qix++) { |
if (pipe(p1) != -1) { | if (pipe(p1) != -1) { | ||||
if (pipe(p2) != -1) { | if (pipe(p2) != -1) { | ||||
if (pipe(p3) != -1) { | |||||
if (pipe(p3) != -1) | |||||
return 0; | return 0; | ||||
} else | |||||
else | |||||
error = errno; | error = errno; | ||||
close(p2[0]); | close(p2[0]); | ||||
close(p2[1]); | close(p2[1]); | ||||
char msgbuf[80]; | char msgbuf[80]; | ||||
pid = waitpid(mbr_pid, &status, WNOHANG); | pid = waitpid(mbr_pid, &status, WNOHANG); | ||||
if (!pid) { | |||||
if (!pid) | |||||
msg = "mbrola closed stderr and did not exit"; | msg = "mbrola closed stderr and did not exit"; | ||||
} else if (pid != mbr_pid) { | |||||
else if (pid != mbr_pid) | |||||
msg = "waitpid() is confused"; | msg = "waitpid() is confused"; | ||||
} else { | |||||
else { | |||||
mbr_pid = 0; | mbr_pid = 0; | ||||
if (WIFSIGNALED(status)) { | if (WIFSIGNALED(status)) { | ||||
int sig = WTERMSIG(status); | int sig = WTERMSIG(status); | ||||
snprintf(msgbuf, sizeof(msgbuf), | snprintf(msgbuf, sizeof(msgbuf), | ||||
"mbrola exited with status %d", exst); | "mbrola exited with status %d", exst); | ||||
msg = msgbuf; | msg = msgbuf; | ||||
} else { | |||||
} else | |||||
msg = "mbrola died and wait status is weird"; | msg = "mbrola died and wait status is weird"; | ||||
} | |||||
} | } | ||||
log("mbrowrap error: %s", msg); | log("mbrowrap error: %s", msg); | ||||
if (result == -1) { | if (result == -1) { | ||||
int error = errno; | int error = errno; | ||||
if (error == EPIPE && mbrola_has_errors()) { | |||||
if (error == EPIPE && mbrola_has_errors()) | |||||
return -1; | return -1; | ||||
} else if (error == EAGAIN) { | |||||
else if (error == EAGAIN) | |||||
result = 0; | result = 0; | ||||
} else { | |||||
else { | |||||
err("write(): %s", strerror(error)); | err("write(): %s", strerror(error)); | ||||
return -1; | return -1; | ||||
} | } | ||||
err("write(): %s", strerror(error)); | err("write(): %s", strerror(error)); | ||||
return -1; | return -1; | ||||
} | } | ||||
if (result != left) { | |||||
if (result != left) | |||||
head->done += result; | head->done += result; | ||||
} else { | |||||
else { | |||||
mbr_pending_data_head = head->next; | mbr_pending_data_head = head->next; | ||||
free(head); | free(head); | ||||
if (!mbr_pending_data_head) | if (!mbr_pending_data_head) |
if (Lookup(tr, &single_letter[1], ph_buf) == 0) { | if (Lookup(tr, &single_letter[1], ph_buf) == 0) { | ||||
single_letter[1] = ' '; | single_letter[1] = ' '; | ||||
if (Lookup(tr, &single_letter[2], ph_buf) == 0) { | |||||
if (Lookup(tr, &single_letter[2], ph_buf) == 0) | |||||
TranslateRules(tr, &single_letter[2], ph_buf, 20, NULL, 0, NULL); | TranslateRules(tr, &single_letter[2], ph_buf, 20, NULL, 0, NULL); | ||||
} | |||||
} | } | ||||
return ph_buf[0]; | return ph_buf[0]; | ||||
} | } | ||||
ph_accent2[0] = 0; | ph_accent2[0] = 0; | ||||
if ((letter >= 0xe0) && (letter < 0x17f)) { | |||||
if ((letter >= 0xe0) && (letter < 0x17f)) | |||||
accent_data = letter_accents_0e0[letter - 0xe0]; | accent_data = letter_accents_0e0[letter - 0xe0]; | ||||
} else if ((letter >= 0x250) && (letter <= 0x2a8)) { | |||||
else if ((letter >= 0x250) && (letter <= 0x2a8)) | |||||
accent_data = letter_accents_250[letter - 0x250]; | accent_data = letter_accents_250[letter - 0x250]; | ||||
} | |||||
if (accent_data != 0) { | if (accent_data != 0) { | ||||
basic_letter = (accent_data & 0x3f) + 59; | basic_letter = (accent_data & 0x3f) + 59; | ||||
if (Lookup(tr, &single_letter[1], ph_buf3) == 0) { | if (Lookup(tr, &single_letter[1], ph_buf3) == 0) { | ||||
single_letter[1] = ' '; | single_letter[1] = ' '; | ||||
if (Lookup(tr, &single_letter[2], ph_buf3) == 0) { | |||||
if (Lookup(tr, &single_letter[2], ph_buf3) == 0) | |||||
TranslateRules(tr, &single_letter[2], ph_buf3, sizeof(ph_buf3), NULL, FLAG_NO_TRACE, NULL); | TranslateRules(tr, &single_letter[2], ph_buf3, sizeof(ph_buf3), NULL, FLAG_NO_TRACE, NULL); | ||||
} | |||||
} | } | ||||
if (ph_buf3[0] == 0) { | |||||
if (ph_buf3[0] == 0) | |||||
LookupAccentedLetter(tr, letter, ph_buf3); | LookupAccentedLetter(tr, letter, ph_buf3); | ||||
} | |||||
strcpy(ph_buf1, ph_buf3); | strcpy(ph_buf1, ph_buf3); | ||||
if ((ph_buf1[0] == 0) || (ph_buf1[0] == phonSWITCH)) { | |||||
if ((ph_buf1[0] == 0) || (ph_buf1[0] == phonSWITCH)) | |||||
return; | return; | ||||
} | |||||
dict_flags[0] = 0; | dict_flags[0] = 0; | ||||
dict_flags[1] = 0; | dict_flags[1] = 0; | ||||
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 | ||||
} | |||||
if (control & 2) { | if (control & 2) { | ||||
// include CAPITAL information | // include CAPITAL information | ||||
if (iswupper2(letter)) { | |||||
if (iswupper2(letter)) | |||||
Lookup(tr, "_cap", capital); | Lookup(tr, "_cap", capital); | ||||
} | |||||
} | } | ||||
letter = towlower2(letter); | letter = towlower2(letter); | ||||
LookupLetter(tr, letter, word[n_bytes], ph_buf, control & 1); | LookupLetter(tr, letter, word[n_bytes], ph_buf, control & 1); | ||||
ph_buf[3] = 0; | ph_buf[3] = 0; | ||||
TranslateRules(translator2, &hangul_buf[1], &ph_buf[3], sizeof(ph_buf)-3, NULL, 0, NULL); | TranslateRules(translator2, &hangul_buf[1], &ph_buf[3], sizeof(ph_buf)-3, NULL, 0, NULL); | ||||
SetWordStress(translator2, &ph_buf[3], NULL, -1, 0); | SetWordStress(translator2, &ph_buf[3], NULL, -1, 0); | ||||
} else { | |||||
} else | |||||
LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1); | LookupLetter(translator2, letter, word[n_bytes], &ph_buf[3], control & 1); | ||||
} | |||||
if (ph_buf[3] == phonSWITCH) { | if (ph_buf[3] == phonSWITCH) { | ||||
// another level of language change | // another level of language change | ||||
if ((ph_buf[0] == 0) && !iswspace(letter)) | if ((ph_buf[0] == 0) && !iswspace(letter)) | ||||
Lookup(translator, "_??", ph_buf); | Lookup(translator, "_??", ph_buf); | ||||
if (ph_buf[0] == 0) { | |||||
if (ph_buf[0] == 0) | |||||
EncodePhonemes("l'et@", ph_buf, NULL); | EncodePhonemes("l'et@", ph_buf, NULL); | ||||
} | |||||
} | } | ||||
if (!(control & 4) && (al_flags & AL_NOT_CODE)) { | if (!(control & 4) && (al_flags & AL_NOT_CODE)) { | ||||
// braille dots symbol, list the numbered dots | // braille dots symbol, list the numbered dots | ||||
p2 = hexbuf; | p2 = hexbuf; | ||||
for (ix = 0; ix < 8; ix++) { | for (ix = 0; ix < 8; ix++) { | ||||
if (letter & (1 << ix)) { | |||||
if (letter & (1 << ix)) | |||||
*p2++ = '1'+ix; | *p2++ = '1'+ix; | ||||
} | |||||
} | } | ||||
*p2 = 0; | *p2 = 0; | ||||
} else { | } else { | ||||
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++) { | ||||
if ((c == phonSTRESS_P) && (prev != phonSWITCH)) { | |||||
if ((c == phonSTRESS_P) && (prev != phonSWITCH)) | |||||
n_stress++; | n_stress++; | ||||
} | |||||
buf[ix] = prev = c; | buf[ix] = prev = c; | ||||
} | } | ||||
buf[ix] = 0; | buf[ix] = 0; | ||||
if ((roman == 0) && (tr->translator_name == L('h', 'u'))) { | if ((roman == 0) && (tr->translator_name == L('h', 'u'))) { | ||||
// lang=hu don't treat dot as ordinal indicator if the next word is a month name ($alt). It may have a suffix. | // lang=hu don't treat dot as ordinal indicator if the next word is a month name ($alt). It may have a suffix. | ||||
nextflags = 0; | nextflags = 0; | ||||
if (IsAlpha(c2)) { | |||||
if (IsAlpha(c2)) | |||||
nextflags = TranslateWord(tr, &word_end[2], 0, NULL, NULL); | nextflags = TranslateWord(tr, &word_end[2], 0, NULL, NULL); | ||||
} | |||||
if ((tr->prev_dict_flags[0] & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || iswdigit(c2))) | if ((tr->prev_dict_flags[0] & FLAG_ALT_TRANS) && ((c2 == 0) || (wtab[0].flags & FLAG_COMMA_AFTER) || iswdigit(c2))) | ||||
ordinal = 0; // TEST 09.02.10 | ordinal = 0; // TEST 09.02.10 | ||||
} else | } else | ||||
return 0; | return 0; | ||||
} | } | ||||
} else { | |||||
} else | |||||
wtab[0].flags |= FLAG_ORDINAL; | wtab[0].flags |= FLAG_ORDINAL; | ||||
} | |||||
} | } | ||||
tr->prev_dict_flags[0] = 0; | tr->prev_dict_flags[0] = 0; | ||||
} | } | ||||
if (found_value == 0) { | if (found_value == 0) { | ||||
if ((value % 100) >= 20) { | |||||
if ((value % 100) >= 20) | |||||
Lookup(tr, "_0of", ph_of); | Lookup(tr, "_0of", ph_of); | ||||
} | |||||
found = 0; | found = 0; | ||||
if (thousands_exact & 1) { | if (thousands_exact & 1) { | ||||
ph_digits[0] = 0; | ph_digits[0] = 0; | ||||
ph_and[0] = 0; | ph_and[0] = 0; | ||||
if (control & 0x20) { | |||||
if (control & 0x20) | |||||
ord_type = 'q'; | ord_type = 'q'; | ||||
} | |||||
is_ordinal = control & 1; | is_ordinal = control & 1; | ||||
if ((ix > 0) && (phoneme_tab[(unsigned char)(ph_out[ix-1])]->type == phVOWEL)) | if ((ix > 0) && (phoneme_tab[(unsigned char)(ph_out[ix-1])]->type == phVOWEL)) | ||||
ix--; | ix--; | ||||
sprintf(&ph_out[ix], "%s", ph_ordinal); | sprintf(&ph_out[ix], "%s", ph_ordinal); | ||||
} else { | |||||
} else | |||||
sprintf(ph_out, "%s%s%s", ph_tens, ph_digits, ph_ordinal); | sprintf(ph_out, "%s%s%s", ph_tens, ph_digits, ph_ordinal); | ||||
} | |||||
} | } | ||||
} | } | ||||
// special form for exact hundreds? | // special form for exact hundreds? | ||||
found = Lookup(tr, "_0C0", ph_100); | found = Lookup(tr, "_0C0", ph_100); | ||||
} | } | ||||
if (!found) { | |||||
if (!found) | |||||
Lookup(tr, "_0C", ph_100); | Lookup(tr, "_0C", ph_100); | ||||
} | |||||
} | } | ||||
if (((tr->langopts.numbers & NUM_1900) != 0) && (hundreds == 19)) { | if (((tr->langopts.numbers & NUM_1900) != 0) && (hundreds == 19)) { | ||||
exact = 1; | exact = 1; | ||||
tplex = thousandplex+1; | tplex = thousandplex+1; | ||||
if (tr->langopts.numbers2 & NUM2_MYRIADS) { | |||||
if (tr->langopts.numbers2 & NUM2_MYRIADS) | |||||
tplex = 0; | tplex = 0; | ||||
} | |||||
if (LookupThousands(tr, hundreds / 10, tplex, exact | ordinal, ph_10T) == 0) { | if (LookupThousands(tr, hundreds / 10, tplex, exact | ordinal, ph_10T) == 0) { | ||||
x = 0; | x = 0; | ||||
ph_digits[0] = 0; | ph_digits[0] = 0; | ||||
if ((hundreds > 0) || say_zero_hundred) { | if ((hundreds > 0) || say_zero_hundred) { | ||||
if ((tr->langopts.numbers & NUM_AND_HUNDRED) && ((control & 1) || (ph_thousands[0] != 0))) { | |||||
if ((tr->langopts.numbers & NUM_AND_HUNDRED) && ((control & 1) || (ph_thousands[0] != 0))) | |||||
Lookup(tr, "_0and", ph_thousand_and); | Lookup(tr, "_0and", ph_thousand_and); | ||||
} | |||||
suppress_null = 1; | suppress_null = 1; | ||||
} | } | ||||
} | } | ||||
if ((hundreds == 0) && say_zero_hundred) { | |||||
if ((hundreds == 0) && say_zero_hundred) | |||||
Lookup(tr, "_0", ph_digits); | Lookup(tr, "_0", ph_digits); | ||||
} else { | |||||
else { | |||||
if ((hundreds == 1) && (tr->langopts.numbers2 & NUM2_OMIT_1_HUNDRED_ONLY) && ((control & 1) == 0)) { | if ((hundreds == 1) && (tr->langopts.numbers2 & NUM2_OMIT_1_HUNDRED_ONLY) && ((control & 1) == 0)) { | ||||
// only look for special 100 if there are previous thousands | // only look for special 100 if there are previous thousands | ||||
} else { | } else { | ||||
} | } | ||||
} | } | ||||
if (found) { | |||||
if (found) | |||||
ph_100[0] = 0; | ph_100[0] = 0; | ||||
} else { | |||||
else { | |||||
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; | ||||
} | } | ||||
if (say_one_hundred != 0) { | |||||
if (say_one_hundred != 0) | |||||
LookupNum2(tr, hundreds, thousandplex, 0, ph_digits); | LookupNum2(tr, hundreds, thousandplex, 0, ph_digits); | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// Don't use "and" if we apply ordinal to both hundreds and units | // Don't use "and" if we apply ordinal to both hundreds and units | ||||
} else { | } else { | ||||
if ((value > 100) || ((control & 1) && (thousandplex == 0))) { | if ((value > 100) || ((control & 1) && (thousandplex == 0))) { | ||||
if ((tr->langopts.numbers & NUM_HUNDRED_AND) || ((tr->langopts.numbers & NUM_HUNDRED_AND_DIGIT) && (tensunits < 10))) { | |||||
if ((tr->langopts.numbers & NUM_HUNDRED_AND) || ((tr->langopts.numbers & NUM_HUNDRED_AND_DIGIT) && (tensunits < 10))) | |||||
Lookup(tr, "_0and", ph_hundred_and); | Lookup(tr, "_0and", ph_hundred_and); | ||||
} | |||||
} | } | ||||
if ((tr->langopts.numbers & NUM_THOUSAND_AND) && (hundreds == 0) && ((control & 1) || (ph_thousands[0] != 0))) { | |||||
if ((tr->langopts.numbers & NUM_THOUSAND_AND) && (hundreds == 0) && ((control & 1) || (ph_thousands[0] != 0))) | |||||
Lookup(tr, "_0and", ph_hundred_and); | Lookup(tr, "_0and", ph_hundred_and); | ||||
} | |||||
} | } | ||||
} | } | ||||
x |= 4; // tens and units only, no higher digits | x |= 4; // tens and units only, no higher digits | ||||
if (ordinal & 0x20) | if (ordinal & 0x20) | ||||
x |= 0x20; // variant form of ordinal number | x |= 0x20; // variant form of ordinal number | ||||
} else { | |||||
if (tr->langopts.numbers2 & (1 << thousandplex)) | |||||
x = 8; // use variant (feminine) for before thousands and millions | |||||
} | |||||
} else if (tr->langopts.numbers2 & (1 << thousandplex)) | |||||
x = 8; // use variant (feminine) for before thousands and millions | |||||
if ((tr->translator_name == L('m', 'l')) && (thousandplex == 1)) { | |||||
if ((tr->translator_name == L('m', 'l')) && (thousandplex == 1)) | |||||
x |= 0x208; // use #f form for both tens and units | x |= 0x208; // use #f form for both tens and units | ||||
} | |||||
if ((tr->langopts.numbers2 & NUM2_ZERO_TENS) && ((control & 1) || (hundreds > 0))) { | if ((tr->langopts.numbers2 & NUM2_ZERO_TENS) && ((control & 1) || (hundreds > 0))) { | ||||
// LANG=zh, | // LANG=zh, | ||||
prev_thousands = 1; | prev_thousands = 1; | ||||
} else if ((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & NUM_ALLOW_SPACE)) { | } else if ((tr->langopts.thousands_sep == ' ') || (tr->langopts.numbers & NUM_ALLOW_SPACE)) { | ||||
// thousands groups can be separated by spaces | // thousands groups can be separated by spaces | ||||
if ((n_digits == 3) && !(wtab->flags & FLAG_MULTIPLE_SPACES) && IsDigit09(word[-2])) { | |||||
if ((n_digits == 3) && !(wtab->flags & FLAG_MULTIPLE_SPACES) && IsDigit09(word[-2])) | |||||
prev_thousands = 1; | prev_thousands = 1; | ||||
} | |||||
} | } | ||||
if (prev_thousands == 0) { | |||||
if (prev_thousands == 0) | |||||
speak_missing_thousands = 0; | speak_missing_thousands = 0; | ||||
} | |||||
ph_ordinal2[0] = 0; | ph_ordinal2[0] = 0; | ||||
ph_zeros[0] = 0; | ph_zeros[0] = 0; | ||||
*p++ = '-'; | *p++ = '-'; | ||||
ix++; | ix++; | ||||
} | } | ||||
while ((word[ix] != 0) && (word[ix] != ' ') && (ix < (int)(sizeof(suffix)-1))) { | |||||
while ((word[ix] != 0) && (word[ix] != ' ') && (ix < (int)(sizeof(suffix)-1))) | |||||
*p++ = word[ix++]; | *p++ = word[ix++]; | ||||
} | |||||
*p = 0; | *p = 0; | ||||
if (suffix[0] != 0) { | if (suffix[0] != 0) { | ||||
if ((tr->langopts.ordinal_indicator != NULL) && (strcmp(suffix, tr->langopts.ordinal_indicator) == 0)) { | |||||
if ((tr->langopts.ordinal_indicator != NULL) && (strcmp(suffix, tr->langopts.ordinal_indicator) == 0)) | |||||
ordinal = 2; | ordinal = 2; | ||||
} else if (!IsDigit09(suffix[0])) { // not _#9 (tab) | |||||
else if (!IsDigit09(suffix[0])) { // not _#9 (tab) | |||||
sprintf(string, "_#%s", suffix); | sprintf(string, "_#%s", suffix); | ||||
if (Lookup(tr, string, ph_ordinal2)) { | if (Lookup(tr, string, ph_ordinal2)) { | ||||
// this is an ordinal suffix | // this is an ordinal suffix | ||||
} | } | ||||
// speak leading zeros | // speak leading zeros | ||||
for (ix = 0; (word[ix] == '0') && (ix < (n_digits-1)); ix++) { | |||||
for (ix = 0; (word[ix] == '0') && (ix < (n_digits-1)); ix++) | |||||
Lookup(tr, "_0", &ph_zeros[strlen(ph_zeros)]); | Lookup(tr, "_0", &ph_zeros[strlen(ph_zeros)]); | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if ((value == 0) && prev_thousands) { | |||||
if ((value == 0) && prev_thousands) | |||||
suppress_null = 1; | suppress_null = 1; | ||||
} | |||||
if (tr->translator_name == L('h', 'u')) { | if (tr->translator_name == L('h', 'u')) { | ||||
// variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt | // variant form of numbers when followed by hyphen and a suffix starting with 'a' or 'e' (but not a, e, az, ez, azt, ezt | ||||
if ((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact == 1) && hu_number_e(&word[suffix_ix], thousandplex, value)) { | |||||
if ((wtab[thousandplex].flags & FLAG_HYPHEN_AFTER) && (thousands_exact == 1) && hu_number_e(&word[suffix_ix], thousandplex, value)) | |||||
number_control |= 1; // use _1e variant of number | number_control |= 1; // use _1e variant of number | ||||
} | |||||
} | } | ||||
if ((word[n_digits] == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) { | if ((word[n_digits] == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) { | ||||
} | } | ||||
} | } | ||||
if ((ph_append[0] == 0) && (word[n_digits] == '.') && (thousandplex == 0)) { | |||||
if ((ph_append[0] == 0) && (word[n_digits] == '.') && (thousandplex == 0)) | |||||
Lookup(tr, "_.", ph_append); | Lookup(tr, "_.", ph_append); | ||||
} | |||||
if (thousandplex == 0) { | if (thousandplex == 0) { | ||||
char *p2; | char *p2; | ||||
while (IsDigit09(p[1])) p++; // just use the last digit | while (IsDigit09(p[1])) p++; // just use the last digit | ||||
if (IsDigit09(p[-1])) { | if (IsDigit09(p[-1])) { | ||||
p2 = p - 1; | p2 = p - 1; | ||||
if (LookupDictList(tr, &p2, buf_digit_lookup, flags, FLAG_SUFX, wtab)) { // lookup 2 digits | |||||
if (LookupDictList(tr, &p2, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // lookup 2 digits | |||||
n_digit_lookup = 2; | n_digit_lookup = 2; | ||||
} | |||||
} | } | ||||
if ((buf_digit_lookup[0] == 0) && (*p != '0')) { | if ((buf_digit_lookup[0] == 0) && (*p != '0')) { | ||||
// LANG=hu ? | // LANG=hu ? | ||||
// not found, lookup only the last digit (?? but not if dot-ordinal has been found) | // not found, lookup only the last digit (?? but not if dot-ordinal has been found) | ||||
if (LookupDictList(tr, &p, buf_digit_lookup, flags, FLAG_SUFX, wtab)) { // don't match '0', or entries with $only | |||||
if (LookupDictList(tr, &p, buf_digit_lookup, flags, FLAG_SUFX, wtab)) // don't match '0', or entries with $only | |||||
n_digit_lookup = 1; | n_digit_lookup = 1; | ||||
} | |||||
} | } | ||||
if (prev_thousands == 0) { | if (prev_thousands == 0) { | ||||
if ((decimal_point == 0) && (ordinal == 0)) { | if ((decimal_point == 0) && (ordinal == 0)) { | ||||
// Look for special pronunciation for this number in isolation (LANG=kl) | // Look for special pronunciation for this number in isolation (LANG=kl) | ||||
sprintf(string, "_%dn", value); | sprintf(string, "_%dn", value); | ||||
if (Lookup(tr, string, ph_out)) { | |||||
if (Lookup(tr, string, ph_out)) | |||||
return 1; | return 1; | ||||
} | |||||
} | } | ||||
if (tr->langopts.numbers2 & NUM2_PERCENT_BEFORE) { | if (tr->langopts.numbers2 & NUM2_PERCENT_BEFORE) { | ||||
// LANG=si, say "percent" before the number | // LANG=si, say "percent" before the number | ||||
p2 = word; | p2 = word; | ||||
while ((*p2 != ' ') && (*p2 != 0)) { | |||||
while ((*p2 != ' ') && (*p2 != 0)) | |||||
p2++; | p2++; | ||||
} | |||||
if (p2[1] == '%') { | if (p2[1] == '%') { | ||||
Lookup(tr, "%", ph_out); | Lookup(tr, "%", ph_out); | ||||
ph_out += strlen(ph_out); | ph_out += strlen(ph_out); | ||||
if ((c == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) { | if ((c == tr->langopts.decimal_sep) && IsDigit09(word[n_digits+1])) { | ||||
Lookup(tr, "_dpt", buf1); | Lookup(tr, "_dpt", buf1); | ||||
strcat(ph_out, buf1); | strcat(ph_out, buf1); | ||||
} else { | |||||
} else | |||||
decimal_point = 0; | decimal_point = 0; | ||||
} | |||||
} | } | ||||
if ((ph_out[0] != 0) && (ph_out[0] != phonSWITCH)) { | if ((ph_out[0] != 0) && (ph_out[0] != phonSWITCH)) { | ||||
int next_char; | int next_char; | ||||
if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS)) | if ((option_sayas == SAYAS_DIGITS1) || (wtab[0].flags & FLAG_INDIVIDUAL_DIGITS)) | ||||
return 0; // speak digits individually | return 0; // speak digits individually | ||||
if (tr->langopts.numbers != 0) { | |||||
if (tr->langopts.numbers != 0) | |||||
return TranslateNumber_1(tr, word1, ph_out, flags, wtab, control); | return TranslateNumber_1(tr, word1, ph_out, flags, wtab, control); | ||||
} | |||||
return 0; | return 0; | ||||
} | } |
} | } | ||||
} | } | ||||
if (plist2->phcode == 0) { | |||||
if (plist2->phcode == 0) | |||||
continue; // phoneme has been replaced by NULL, so don't copy it | continue; // phoneme has been replaced by NULL, so don't copy it | ||||
} | |||||
} | } | ||||
// copy phoneme into the output list | // copy phoneme into the output list | ||||
delete_count = 0; | delete_count = 0; | ||||
current_phoneme_tab = tr->phoneme_tab_ix; | 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; | ||||
} | |||||
if (delete_count > 0) { | |||||
if (delete_count > 0) | |||||
memcpy(&plist2[j-delete_count], &plist2[j], sizeof(plist2[0])); | memcpy(&plist2[j-delete_count], &plist2[j], sizeof(plist2[0])); | ||||
} | |||||
if (plist2[j].phcode == phonSWITCH) { | if (plist2[j].phcode == phonSWITCH) { | ||||
if ((!(plist2[j].synthflags & SFLAG_EMBEDDED)) && ( | if ((!(plist2[j].synthflags & SFLAG_EMBEDDED)) && ( | ||||
// delete this phonSWITCH if it's switching to the current phoneme table, or | // delete this phonSWITCH if it's switching to the current phoneme table, or | ||||
// delete this phonSWITCH if its followed by another phonSWITCH | // delete this phonSWITCH if its followed by another phonSWITCH | ||||
delete_count++; | delete_count++; | ||||
} else { | |||||
} else | |||||
current_phoneme_tab = plist2[j].tone_ph; | current_phoneme_tab = plist2[j].tone_ph; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if ((type == phSTOP) || type == (phFRICATIVE)) { | if ((type == phSTOP) || type == (phFRICATIVE)) { | ||||
if ((voicing == 0) && (regression & 0xf)) { | |||||
if ((voicing == 0) && (regression & 0xf)) | |||||
voicing = 1; | voicing = 1; | ||||
} else if ((voicing == 2) && (ph->end_type != 0)) { // use end_type field for voicing_switch for consonants | |||||
else if ((voicing == 2) && (ph->end_type != 0)) // use end_type field for voicing_switch for consonants | |||||
plist2[j].phcode = ph->end_type; // change to voiced equivalent | plist2[j].phcode = ph->end_type; // change to voiced equivalent | ||||
} | |||||
} else if ((type == phVSTOP) || type == (phVFRICATIVE)) { | } else if ((type == phVSTOP) || type == (phVFRICATIVE)) { | ||||
if ((voicing == 0) && (regression & 0xf)) { | |||||
if ((voicing == 0) && (regression & 0xf)) | |||||
voicing = 2; | voicing = 2; | ||||
} else if ((voicing == 1) && (ph->end_type != 0)) { | |||||
else if ((voicing == 1) && (ph->end_type != 0)) | |||||
plist2[j].phcode = ph->end_type; // change to unvoiced equivalent | plist2[j].phcode = ph->end_type; // change to unvoiced equivalent | ||||
} | |||||
} else { | } else { | ||||
if (regression & 0x8) { | if (regression & 0x8) { | ||||
// LANG=Polish, propagate through liquids and nasals | // LANG=Polish, propagate through liquids and nasals | ||||
if ((type == phPAUSE) || (type == phVOWEL)) | if ((type == phPAUSE) || (type == phVOWEL)) | ||||
voicing = 0; | voicing = 0; | ||||
} else { | |||||
} else | |||||
voicing = 0; | voicing = 0; | ||||
} | |||||
} | } | ||||
if (stop_propagation) { | if (stop_propagation) { | ||||
voicing = 0; | voicing = 0; | ||||
} | } | ||||
if (regression & 0x100) { | if (regression & 0x100) { | ||||
// devoice word-final consonants, unless propagating voiced | // devoice word-final consonants, unless propagating voiced | ||||
if (voicing == 0) { | |||||
if (voicing == 0) | |||||
voicing = 1; | voicing = 1; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (ph_list3[nextw].sourceix) | if (ph_list3[nextw].sourceix) | ||||
break; // start of the next word | break; // start of the next word | ||||
} | } | ||||
for (k = j; k < nextw; k++) { | |||||
for (k = j; k < nextw; k++) | |||||
ph_list3[k].wordstress = word_stress; | ph_list3[k].wordstress = word_stress; | ||||
} | |||||
j = nextw; | j = nextw; | ||||
} else { | |||||
} else | |||||
j++; | j++; | ||||
} | |||||
} | } | ||||
// transfer all the phonemes of the clause into phoneme_list | // transfer all the phonemes of the clause into phoneme_list | ||||
if (word_start > 0) { | if (word_start > 0) { | ||||
k = word_start; | k = word_start; | ||||
word_start--; | word_start--; | ||||
} else { | |||||
} else | |||||
k = 2; // No more space, don't loose the start of word mark at ph_list2[word_start] | k = 2; // No more space, don't loose the start of word mark at ph_list2[word_start] | ||||
} | |||||
for (; k <= j; k++) | for (; k <= j; k++) | ||||
memcpy(&ph_list3[k-1], &ph_list3[k], sizeof(*plist3)); | memcpy(&ph_list3[k-1], &ph_list3[k], sizeof(*plist3)); | ||||
} | } | ||||
plist3->ph = ph; | plist3->ph = ph; | ||||
plist3->phcode = alternative; | plist3->phcode = alternative; | ||||
if (alternative == 1) { | |||||
if (alternative == 1) | |||||
deleted = 1; // NULL phoneme, discard | deleted = 1; // NULL phoneme, discard | ||||
} else { | |||||
else { | |||||
if (ph->type == phVOWEL) { | if (ph->type == phVOWEL) { | ||||
plist3->synthflags |= SFLAG_SYLLABLE; | plist3->synthflags |= SFLAG_SYLLABLE; | ||||
if (ph2->type != phVOWEL) | if (ph2->type != phVOWEL) | ||||
if ((tr->langopts.stress_flags & S_NO_DIM) || ((word_stress > 3) && ((plist3+1)->sourceix != 0))) { | if ((tr->langopts.stress_flags & S_NO_DIM) || ((word_stress > 3) && ((plist3+1)->sourceix != 0))) { | ||||
// An unstressed final vowel of a stressed word | // An unstressed final vowel of a stressed word | ||||
unstress_count = 1; // try again for next syllable | unstress_count = 1; // try again for next syllable | ||||
} else { | |||||
} else | |||||
plist3->stresslevel = 0; // change stress to 'diminished' | plist3->stresslevel = 0; // change stress to 'diminished' | ||||
} | |||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
insert_ph = pause_phonemes[x]; | insert_ph = pause_phonemes[x]; | ||||
} | } | ||||
} | } | ||||
if (option_wordgap > 0) { | |||||
if (option_wordgap > 0) | |||||
insert_ph = phonPAUSE_LONG; | insert_ph = phonPAUSE_LONG; | ||||
} | |||||
} | } | ||||
} | } | ||||
next2 = phoneme_tab[plist3[2].phcode]; | next2 = phoneme_tab[plist3[2].phcode]; | ||||
plist3[2].ph = next2; | plist3[2].ph = next2; | ||||
if ((insert_ph == 0) && (phdata.pd_param[pd_APPENDPHONEME] != 0)) { | |||||
if ((insert_ph == 0) && (phdata.pd_param[pd_APPENDPHONEME] != 0)) | |||||
insert_ph = phdata.pd_param[pd_APPENDPHONEME]; | insert_ph = phdata.pd_param[pd_APPENDPHONEME]; | ||||
} | |||||
if (deleted == 0) { | if (deleted == 0) { | ||||
phlist[ix].ph = ph; | phlist[ix].ph = ph; | ||||
phlist[ix].newword = 5; // start of sentence + start of word | phlist[ix].newword = 5; // start of sentence + start of word | ||||
start_sentence = 0; | start_sentence = 0; | ||||
} | } | ||||
} else { | |||||
} else | |||||
phlist[ix].newword = 0; | phlist[ix].newword = 0; | ||||
} | |||||
phlist[ix].length = phdata.pd_param[i_SET_LENGTH]*2; | phlist[ix].length = phdata.pd_param[i_SET_LENGTH]*2; | ||||
if ((ph->code == phonPAUSE_LONG) && (option_wordgap > 0) && (plist3[1].sourceix != 0)) { | if ((ph->code == phonPAUSE_LONG) && (option_wordgap > 0) && (plist3[1].sourceix != 0)) { |
{ | { | ||||
int ix = 0; | int ix = 0; | ||||
while (*str != 0) { | |||||
while (*str != 0) | |||||
ix++; | ix++; | ||||
} | |||||
return ix; | return ix; | ||||
} | } | ||||
#endif | #endif | ||||
// 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 (translator->langopts.dotless_i) { | |||||
if (translator->langopts.dotless_i) | |||||
c = 0x131; // I -> ı | c = 0x131; // I -> ı | ||||
} | |||||
} | } | ||||
if (c < 0x80) | if (c < 0x80) | ||||
if (ungot2 != 0) { | if (ungot2 != 0) { | ||||
c1 = ungot2; | c1 = ungot2; | ||||
ungot2 = 0; | ungot2 = 0; | ||||
} else { | |||||
} else | |||||
c1 = GetC_get(); | c1 = GetC_get(); | ||||
} | |||||
if ((option_multibyte == espeakCHARS_WCHAR) || (option_multibyte == espeakCHARS_16BIT)) { | if ((option_multibyte == espeakCHARS_WCHAR) || (option_multibyte == espeakCHARS_16BIT)) { | ||||
count_characters++; | count_characters++; | ||||
string = &single_letter[2]; | string = &single_letter[2]; | ||||
LookupDictList(translator2, &string, phonemes, flags, 0, NULL); | LookupDictList(translator2, &string, phonemes, flags, 0, NULL); | ||||
} | } | ||||
if (phonemes[0]) { | |||||
if (phonemes[0]) | |||||
lang_name = "en"; | lang_name = "en"; | ||||
} else { | |||||
else | |||||
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table | SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table | ||||
} | |||||
} | } | ||||
if (phonemes[0]) { | if (phonemes[0]) { | ||||
DecodePhonemes(phonemes, phonemes2); | DecodePhonemes(phonemes, phonemes2); | ||||
sprintf(buf, "[\002%s]] ", phonemes2); | sprintf(buf, "[\002%s]] ", phonemes2); | ||||
} | } | ||||
} else if (only == 0) { | |||||
} else if (only == 0) | |||||
strcpy(buf, "[\002(X1)(X1)(X1)]]"); | strcpy(buf, "[\002(X1)(X1)(X1)]]"); | ||||
} | |||||
return buf; | return buf; | ||||
} | } | ||||
if ((fd_temp = mkstemp(fname_temp)) >= 0) { | if ((fd_temp = mkstemp(fname_temp)) >= 0) { | ||||
close(fd_temp); | close(fd_temp); | ||||
sprintf(command, "sox \"%s\" -r %d -c1 -t wav %s\n", fname, samplerate, fname_temp); | sprintf(command, "sox \"%s\" -r %d -c1 -t wav %s\n", fname, samplerate, fname_temp); | ||||
if (system(command) == 0) { | |||||
if (system(command) == 0) | |||||
fname = fname_temp; | fname = fname_temp; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
UngetC(c2); | UngetC(c2); | ||||
} else { | } else { | ||||
if ((c1 == '.') && (end_clause) && (c2 != '.')) { | if ((c1 == '.') && (end_clause) && (c2 != '.')) { | ||||
if (LookupSpecial(tr, "_.p", ph_buf)) { | |||||
if (LookupSpecial(tr, "_.p", ph_buf)) | |||||
punctname = ph_buf; // use word for 'period' instead of 'dot' | punctname = ph_buf; // use word for 'period' instead of 'dot' | ||||
} | |||||
} | } | ||||
if (punctname == NULL) { | |||||
if (punctname == NULL) | |||||
punctname = LookupCharName(tr, c1, 0); | punctname = LookupCharName(tr, c1, 0); | ||||
} | |||||
if (punctname == NULL) | if (punctname == NULL) | ||||
return -1; | return -1; | ||||
c2 = GetC(); | c2 = GetC(); | ||||
} | } | ||||
*c2_ptr = c2; | *c2_ptr = c2; | ||||
if (end_clause) { | |||||
if (end_clause) | |||||
UngetC(c2); | UngetC(c2); | ||||
} | |||||
if (punct_count == 1) { | |||||
if (punct_count == 1) | |||||
sprintf(buf, " %s", punctname); // we need the space before punctname, to ensure it doesn't merge with the previous word (eg. "2.-a") | sprintf(buf, " %s", punctname); // we need the space before punctname, to ensure it doesn't merge with the previous word (eg. "2.-a") | ||||
} else if (punct_count < 4) { | |||||
else if (punct_count < 4) { | |||||
buf[0] = 0; | buf[0] = 0; | ||||
if (embedded_value[EMBED_S] < 300) | if (embedded_value[EMBED_S] < 300) | ||||
sprintf(buf, "\001+10S"); // Speak punctuation name faster, unless we are already speaking fast. It would upset Sonic SpeedUp | sprintf(buf, "\001+10S"); // Speak punctuation name faster, unless we are already speaking fast. It would upset Sonic SpeedUp | ||||
sprintf(buf2, " \001-10S"); | sprintf(buf2, " \001-10S"); | ||||
strcat(buf, buf2); | strcat(buf, buf2); | ||||
} | } | ||||
} else { | |||||
} else | |||||
sprintf(buf, " %s %d %s", | sprintf(buf, " %s %d %s", | ||||
punctname, punct_count, punctname); | punctname, punct_count, punctname); | ||||
} | |||||
} else { | } else { | ||||
// end the clause now and pick up the punctuation next time | // end the clause now and pick up the punctuation next time | ||||
UngetC(c2); | UngetC(c2); | ||||
if (voice_name_specified == 0) | if (voice_name_specified == 0) | ||||
voice_name[0] = 0; // forget a previous voice name if a language is specified | voice_name[0] = 0; // forget a previous voice name if a language is specified | ||||
} | } | ||||
if (sp->voice_gender != 0) { | |||||
if (sp->voice_gender != 0) | |||||
voice_select.gender = sp->voice_gender; | voice_select.gender = sp->voice_gender; | ||||
} | |||||
if (sp->voice_age != 0) | if (sp->voice_age != 0) | ||||
voice_select.age = sp->voice_age; | voice_select.age = sp->voice_age; | ||||
n_param_stack++; | n_param_stack++; | ||||
sp->type = tag_type; | sp->type = tag_type; | ||||
for (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; | ||||
} | } | ||||
tag_type -= SSML_CLOSE; | tag_type -= SSML_CLOSE; | ||||
for (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; | ||||
} | |||||
} | } | ||||
if (top > 0) { | |||||
if (top > 0) | |||||
n_param_stack = top; | n_param_stack = top; | ||||
} | |||||
ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
} | } | ||||
if ((pw == NULL) || !IsDigit09(*pw)) | if ((pw == NULL) || !IsDigit09(*pw)) | ||||
return default_value; | return default_value; | ||||
while (IsDigit09(*pw)) { | |||||
while (IsDigit09(*pw)) | |||||
value = value*10 + *pw++ - '0'; | value = value*10 + *pw++ - '0'; | ||||
} | |||||
if ((type == 1) && (towlower(*pw) == 's')) { | if ((type == 1) && (towlower(*pw) == 's')) { | ||||
// time: seconds rather than ms | // time: seconds rather than ms | ||||
value *= 1000; | value *= 1000; | ||||
if (wide) { | if (wide) { | ||||
len = (wcslen((const wchar_t *)name)+1)*sizeof(wchar_t); | len = (wcslen((const wchar_t *)name)+1)*sizeof(wchar_t); | ||||
n_namedata = (n_namedata + sizeof(wchar_t) - 1) % sizeof(wchar_t); // round to wchar_t boundary | n_namedata = (n_namedata + sizeof(wchar_t) - 1) % sizeof(wchar_t); // round to wchar_t boundary | ||||
} else { | |||||
} else | |||||
len = strlen(name)+1; | len = strlen(name)+1; | ||||
} | |||||
if (namedata_ix+len >= n_namedata) { | if (namedata_ix+len >= n_namedata) { | ||||
// allocate more space for marker names | // allocate more space for marker names | ||||
if (tag_type & SSML_CLOSE) { | if (tag_type & SSML_CLOSE) { | ||||
// delete a stack frame | // delete a stack frame | ||||
if (n_ssml_stack > 1) { | |||||
if (n_ssml_stack > 1) | |||||
n_ssml_stack--; | n_ssml_stack--; | ||||
} | |||||
} else { | } else { | ||||
// add a stack frame if any voice details are specified | // add a stack frame if any voice details are specified | ||||
lang = GetSsmlAttribute(pw, "xml:lang"); | lang = GetSsmlAttribute(pw, "xml:lang"); | ||||
if (tag_name[0] == '/') { | if (tag_name[0] == '/') { | ||||
// closing tag | // closing tag | ||||
if ((tag_type = LookupMnem(ssmltags, &tag_name[1])) != HTML_NOSPACE) { | |||||
if ((tag_type = LookupMnem(ssmltags, &tag_name[1])) != HTML_NOSPACE) | |||||
outbuf[(*outix)++] = ' '; | outbuf[(*outix)++] = ' '; | ||||
} | |||||
tag_type += SSML_CLOSE; | tag_type += SSML_CLOSE; | ||||
} else { | } else { | ||||
if ((tag_type = LookupMnem(ssmltags, tag_name)) != HTML_NOSPACE) { | if ((tag_type = LookupMnem(ssmltags, tag_name)) != HTML_NOSPACE) { | ||||
// look for attributes: rate, volume, pitch, range | // look for attributes: rate, volume, pitch, range | ||||
for (param_type = espeakRATE; param_type <= espeakRANGE; param_type++) { | for (param_type = espeakRATE; param_type <= espeakRANGE; param_type++) { | ||||
if ((attr1 = GetSsmlAttribute(px, prosody_attr[param_type])) != NULL) { | |||||
if ((attr1 = GetSsmlAttribute(px, prosody_attr[param_type])) != NULL) | |||||
SetProsodyParameter(param_type, attr1, sp); | SetProsodyParameter(param_type, attr1, sp); | ||||
} | |||||
} | } | ||||
ProcessParamStack(outbuf, outix); | ProcessParamStack(outbuf, outix); | ||||
case SSML_EMPHASIS: | case SSML_EMPHASIS: | ||||
sp = PushParamStack(tag_type); | sp = PushParamStack(tag_type); | ||||
value = 3; // default is "moderate" | value = 3; // default is "moderate" | ||||
if ((attr1 = GetSsmlAttribute(px, "level")) != NULL) { | |||||
if ((attr1 = GetSsmlAttribute(px, "level")) != NULL) | |||||
value = attrlookup(attr1, mnem_emphasis); | value = attrlookup(attr1, mnem_emphasis); | ||||
} | |||||
if (translator->langopts.tone_language == 1) { | if (translator->langopts.tone_language == 1) { | ||||
static unsigned char emphasis_to_pitch_range[] = { 50, 50, 40, 70, 90, 100 }; | static unsigned char emphasis_to_pitch_range[] = { 50, 50, 40, 70, 90, 100 }; | ||||
if ((xmlbase != NULL) && (buf[0] != '/')) { | if ((xmlbase != NULL) && (buf[0] != '/')) { | ||||
sprintf(fname, "%s/%s", xmlbase, buf); | sprintf(fname, "%s/%s", xmlbase, buf); | ||||
index = LoadSoundFile2(fname); | index = LoadSoundFile2(fname); | ||||
} else { | |||||
} else | |||||
index = LoadSoundFile2(buf); | index = LoadSoundFile2(buf); | ||||
} | |||||
if (index >= 0) { | if (index >= 0) { | ||||
sprintf(buf, "%c%dI", CTRL_EMBEDDED, index); | sprintf(buf, "%c%dI", CTRL_EMBEDDED, index); | ||||
strcpy(&outbuf[*outix], buf); | strcpy(&outbuf[*outix], buf); | ||||
case SSML_SPEAK: | case SSML_SPEAK: | ||||
if ((attr1 = GetSsmlAttribute(px, "xml:base")) != NULL) { | if ((attr1 = GetSsmlAttribute(px, "xml:base")) != NULL) { | ||||
attrcopy_utf8(buf, attr1, sizeof(buf)); | attrcopy_utf8(buf, attr1, sizeof(buf)); | ||||
if ((index = AddNameData(buf, 0)) >= 0) { | |||||
if ((index = AddNameData(buf, 0)) >= 0) | |||||
xmlbase = &namedata[index]; | xmlbase = &namedata[index]; | ||||
} | |||||
} | } | ||||
if (GetVoiceAttributes(px, tag_type) == 0) | if (GetVoiceAttributes(px, tag_type) == 0) | ||||
return 0; // no voice change | return 0; // no voice change | ||||
case SSML_SPEAK + SSML_CLOSE: | case SSML_SPEAK + SSML_CLOSE: | ||||
// unwind stack until the previous <voice> or <speak> tag | // unwind stack until the previous <voice> or <speak> tag | ||||
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK)) { | |||||
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK)) | |||||
n_ssml_stack--; | n_ssml_stack--; | ||||
} | |||||
return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type); | return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type); | ||||
case SSML_VOICE + SSML_CLOSE: | case SSML_VOICE + SSML_CLOSE: | ||||
// unwind stack until the previous <voice> or <speak> tag | // unwind stack until the previous <voice> or <speak> tag | ||||
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE)) { | |||||
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE)) | |||||
n_ssml_stack--; | n_ssml_stack--; | ||||
} | |||||
terminator = 0; // ?? Sentence intonation, but no pause ?? | terminator = 0; // ?? Sentence intonation, but no pause ?? | ||||
return terminator + GetVoiceAttributes(px, tag_type); | return terminator + GetVoiceAttributes(px, tag_type); | ||||
ungot_word = NULL; | ungot_word = NULL; | ||||
} | } | ||||
if (ungot_char2 != 0) { | |||||
if (ungot_char2 != 0) | |||||
c2 = ungot_char2; | c2 = ungot_char2; | ||||
} else { | |||||
else | |||||
c2 = GetC(); | c2 = GetC(); | ||||
} | |||||
while (!Eof() || (ungot_char != 0) || (ungot_char2 != 0) || (ungot_string_ix >= 0)) { | while (!Eof() || (ungot_char != 0) || (ungot_char2 != 0) || (ungot_string_ix >= 0)) { | ||||
if (!iswalnum(c1)) { | if (!iswalnum(c1)) { | ||||
ungot_string_ix = -1; | ungot_string_ix = -1; | ||||
} | } | ||||
if ((ungot_string_ix == 0) && (ungot_char2 == 0)) { | |||||
if ((ungot_string_ix == 0) && (ungot_char2 == 0)) | |||||
c1 = ungot_string[ungot_string_ix++]; | c1 = ungot_string[ungot_string_ix++]; | ||||
} | |||||
if (ungot_string_ix >= 0) { | |||||
if (ungot_string_ix >= 0) | |||||
c2 = ungot_string[ungot_string_ix++]; | c2 = ungot_string[ungot_string_ix++]; | ||||
} else { | |||||
else { | |||||
c2 = GetC(); | c2 = GetC(); | ||||
if (Eof()) { | |||||
if (Eof()) | |||||
c2 = ' '; | c2 = ' '; | ||||
} | |||||
} | } | ||||
ungot_char2 = 0; | ungot_char2 = 0; | ||||
c2 = ' '; | c2 = ' '; | ||||
} | } | ||||
} | } | ||||
} else { | |||||
} else | |||||
found = -1; | found = -1; | ||||
} | |||||
if (found <= 0) { | if (found <= 0) { | ||||
ungot_string_ix = 0; | ungot_string_ix = 0; | ||||
} else if ((c1 == '<') && (ssml_ignore_l_angle != '<')) { | } else if ((c1 == '<') && (ssml_ignore_l_angle != '<')) { | ||||
if ((c2 == '!') || (c2 == '?')) { | if ((c2 == '!') || (c2 == '?')) { | ||||
// a comment, ignore until closing '<' (or <?xml tag ) | // a comment, ignore until closing '<' (or <?xml tag ) | ||||
while (!Eof() && (c1 != '>')) { | |||||
while (!Eof() && (c1 != '>')) | |||||
c1 = GetC(); | c1 = GetC(); | ||||
} | |||||
c2 = ' '; | c2 = ' '; | ||||
} else if ((c2 == '/') || iswalpha2(c2)) { | } else if ((c2 == '/') || iswalpha2(c2)) { | ||||
// check for space in the output buffer for embedded commands produced by the SSML tag | // check for space in the output buffer for embedded commands produced by the SSML tag | ||||
buf[ix] = ' '; | buf[ix] = ' '; | ||||
buf[ix++] = 0; | buf[ix++] = 0; | ||||
if (terminator & CLAUSE_BIT_VOICE) { | |||||
if (terminator & CLAUSE_BIT_VOICE) | |||||
strcpy(voice_change, current_voice_id); | strcpy(voice_change, current_voice_id); | ||||
} | |||||
return terminator; | return terminator; | ||||
} | } | ||||
c1 = ' '; | c1 = ' '; | ||||
*charix_top = ix; | *charix_top = ix; | ||||
ix += utf8_out(c1, &buf[ix]); | ix += utf8_out(c1, &buf[ix]); | ||||
terminator = CLAUSE_PERIOD; // line doesn't end in punctuation, assume period | terminator = CLAUSE_PERIOD; // line doesn't end in punctuation, assume period | ||||
} else { | |||||
} else | |||||
terminator = punct_attributes[punct]; | terminator = punct_attributes[punct]; | ||||
} | |||||
buf[ix] = ' '; | buf[ix] = ' '; | ||||
buf[ix+1] = 0; | buf[ix+1] = 0; | ||||
return terminator; | return terminator; | ||||
if (c1 == 0xd4d) { | if (c1 == 0xd4d) { | ||||
// Malayalam virama, check if next character is Zero-width-joiner | // Malayalam virama, check if next character is Zero-width-joiner | ||||
if (c2 == 0x200d) { | |||||
if (c2 == 0x200d) | |||||
c1 = 0xd4e; // use this unofficial code for chillu-virama | c1 = 0xd4e; // use this unofficial code for chillu-virama | ||||
} | |||||
} | } | ||||
} | } | ||||
// 2nd newline, assume paragraph | // 2nd newline, assume paragraph | ||||
UngetC(c2); | UngetC(c2); | ||||
if (end_clause_after_tag) { | |||||
if (end_clause_after_tag) | |||||
RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation | RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation | ||||
} | |||||
buf[ix] = ' '; | buf[ix] = ' '; | ||||
buf[ix+1] = 0; | buf[ix+1] = 0; | ||||
if (parag > 3) | if (parag > 3) | ||||
} | } | ||||
} | } | ||||
if ((c1 == '.') && (nl_count < 2)) { | |||||
if ((c1 == '.') && (nl_count < 2)) | |||||
punct_data |= CLAUSE_DOT; | punct_data |= CLAUSE_DOT; | ||||
} | |||||
if (nl_count == 0) { | if (nl_count == 0) { | ||||
if ((c1 == ',') && (cprev == '.') && (tr->translator_name == L('h', 'u')) && iswdigit(cprev2) && (iswdigit(c_next) || (iswlower2(c_next)))) { | if ((c1 == ',') && (cprev == '.') && (tr->translator_name == L('h', 'u')) && iswdigit(cprev2) && (iswdigit(c_next) || (iswlower2(c_next)))) { | ||||
if ((tr->langopts.numbers & NUM_ORDINAL_DOT) && | if ((tr->langopts.numbers & NUM_ORDINAL_DOT) && | ||||
(iswdigit(cprev) || (IsRomanU(cprev) && (IsRomanU(cprev2) || iswspace(cprev2))))) { // lang=hu | (iswdigit(cprev) || (IsRomanU(cprev) && (IsRomanU(cprev2) || iswspace(cprev2))))) { // lang=hu | ||||
// dot after a number indicates an ordinal number | // dot after a number indicates an ordinal number | ||||
if (!iswdigit(cprev)) { | |||||
if (!iswdigit(cprev)) | |||||
is_end_clause = 0; // Roman number followed by dot | is_end_clause = 0; // Roman number followed by dot | ||||
} else { | |||||
else { | |||||
if (iswlower2(c_next) || (c_next == '-')) // hyphen is needed for lang-hu (eg. 2.-kal) | if (iswlower2(c_next) || (c_next == '-')) // hyphen is needed for lang-hu (eg. 2.-kal) | ||||
is_end_clause = 0; // only if followed by lower-case, (or if there is a XML tag) | is_end_clause = 0; // only if followed by lower-case, (or if there is a XML tag) | ||||
} | } | ||||
} else if (c_next == '\'') { | |||||
} else if (c_next == '\'') | |||||
is_end_clause = 0; // eg. u.s.a.'s | is_end_clause = 0; // eg. u.s.a.'s | ||||
} | |||||
if (iswlower2(c_next)) { | if (iswlower2(c_next)) { | ||||
// next word has no capital letter, this dot is probably from an abbreviation | // next word has no capital letter, this dot is probably from an abbreviation | ||||
is_end_clause = 0; | is_end_clause = 0; | ||||
buf[ix] = ' '; | buf[ix] = ' '; | ||||
buf[ix+1] = 0; | buf[ix+1] = 0; | ||||
if (iswdigit(cprev) && !IsAlpha(c_next)) { // ???? | |||||
if (iswdigit(cprev) && !IsAlpha(c_next)) // ???? | |||||
punct_data &= ~CLAUSE_DOT; | punct_data &= ~CLAUSE_DOT; | ||||
} | |||||
if (nl_count > 1) { | if (nl_count > 1) { | ||||
if ((punct_data == CLAUSE_QUESTION) || (punct_data == CLAUSE_EXCLAMATION)) | if ((punct_data == CLAUSE_QUESTION) || (punct_data == CLAUSE_EXCLAMATION)) | ||||
return punct_data + 35; // with a longer pause | return punct_data + 35; // with a longer pause | ||||
if (c1 == announced_punctuation) { | if (c1 == announced_punctuation) { | ||||
// This character has already been announced, so delete it so that it isn't spoken a second time. | // This character has already been announced, so delete it so that it isn't spoken a second time. | ||||
// Unless it's a hyphen or apostrophe (which is used by TranslateClause() ) | // Unless it's a hyphen or apostrophe (which is used by TranslateClause() ) | ||||
if (IsBracket(c1)) { | |||||
if (IsBracket(c1)) | |||||
c1 = 0xe000 + '('; // Unicode private useage area. So TranslateRules() knows the bracket name has been spoken | c1 = 0xe000 + '('; // Unicode private useage area. So TranslateRules() knows the bracket name has been spoken | ||||
} else if (c1 != '-') { | |||||
else if (c1 != '-') | |||||
c1 = ' '; | c1 = ' '; | ||||
} | |||||
} | } | ||||
j = ix+1; | j = ix+1; | ||||
} | } | ||||
} | } | ||||
if (stressed_word) { | |||||
if (stressed_word) | |||||
ix += utf8_out(CHAR_EMPHASIS, &buf[ix]); | ix += utf8_out(CHAR_EMPHASIS, &buf[ix]); | ||||
} | |||||
if (end_clause_after_tag) { | |||||
if (end_clause_after_tag) | |||||
RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation | RemoveChar(&buf[end_clause_index]); // delete clause-end punctiation | ||||
} | |||||
buf[ix] = ' '; | buf[ix] = ' '; | ||||
buf[ix+1] = 0; | buf[ix+1] = 0; | ||||
return CLAUSE_EOF; // end of file | return CLAUSE_EOF; // end of file |
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; | ||||
} | |||||
if (control & 2) { | |||||
if (control & 2) | |||||
DoSonicSpeed(1 * 1024); | DoSonicSpeed(1 * 1024); | ||||
} | |||||
if ((wpm_value >= 450) || ((wpm_value > speed.fast_settings[0]) && (wpm > 350))) { | if ((wpm_value >= 450) || ((wpm_value > speed.fast_settings[0]) && (wpm > 350))) { | ||||
wpm2 = wpm; | wpm2 = wpm; | ||||
wpm = 175; | wpm = 175; | ||||
if (wpm > 450) | if (wpm > 450) | ||||
wpm = 450; | wpm = 450; | ||||
if (wpm > 360) { | |||||
if (wpm > 360) | |||||
speed.loud_consonants = (wpm - 360) / 8; | speed.loud_consonants = (wpm - 360) / 8; | ||||
} | |||||
wpm2 = wpm; | wpm2 = wpm; | ||||
if (wpm > 359) wpm2 = 359; | if (wpm > 359) wpm2 = 359; | ||||
else | else | ||||
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm | speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm | ||||
if (wpm >= 350) { | |||||
if (wpm >= 350) | |||||
speed.wav_factor = wav_factor_350[wpm-350]; | speed.wav_factor = wav_factor_350[wpm-350]; | ||||
} | |||||
if (wpm >= 390) { | if (wpm >= 390) { | ||||
speed.min_sample_len = 450 - (wpm - 400)/2; | speed.min_sample_len = 450 - (wpm - 400)/2; | ||||
speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length | speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length | ||||
speed.clause_pause_factor = 0; | speed.clause_pause_factor = 0; | ||||
if (wpm > 430) { | |||||
if (wpm > 430) | |||||
speed.pause_factor = 12; | speed.pause_factor = 12; | ||||
} else if (wpm > 400) { | |||||
else if (wpm > 400) | |||||
speed.pause_factor = 13; | speed.pause_factor = 13; | ||||
} else if (wpm > 374) { | |||||
else if (wpm > 374) | |||||
speed.pause_factor = 14; | speed.pause_factor = 14; | ||||
} else if (wpm > 350) { | |||||
else if (wpm > 350) | |||||
speed.pause_factor = pause_factor_350[wpm - 350]; | speed.pause_factor = pause_factor_350[wpm - 350]; | ||||
} | |||||
if (speed.clause_pause_factor == 0) { | if (speed.clause_pause_factor == 0) { | ||||
// restrict the reduction of pauses between clauses | // restrict the reduction of pauses between clauses | ||||
if (control == 2) | if (control == 2) | ||||
wpm = embedded_value[EMBED_S2]; | wpm = embedded_value[EMBED_S2]; | ||||
if (voice->speed_percent > 0) { | |||||
if (voice->speed_percent > 0) | |||||
wpm = (wpm * voice->speed_percent)/100; | wpm = (wpm * voice->speed_percent)/100; | ||||
} | |||||
if (wpm > 450) | if (wpm > 450) | ||||
wpm = 450; | wpm = 450; | ||||
if (wpm > 360) { | |||||
if (wpm > 360) | |||||
speed.loud_consonants = (wpm - 360) / 8; | speed.loud_consonants = (wpm - 360) / 8; | ||||
} | |||||
wpm2 = wpm; | wpm2 = wpm; | ||||
if (wpm > 359) wpm2 = 359; | if (wpm > 359) wpm2 = 359; | ||||
else | else | ||||
speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm | speed.wav_factor = 128 + (128*s1)/130; // = 215 at 170 wpm | ||||
if (wpm >= 350) { | |||||
if (wpm >= 350) | |||||
speed.wav_factor = wav_factor_350[wpm-350]; | speed.wav_factor = wav_factor_350[wpm-350]; | ||||
} | |||||
if (wpm >= 390) { | if (wpm >= 390) { | ||||
speed.min_sample_len = 450 - (wpm - 400)/2; | speed.min_sample_len = 450 - (wpm - 400)/2; | ||||
speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length | speed.pause_factor = (256 * s1)/115; // full speed adjustment, used for pause length | ||||
speed.clause_pause_factor = 0; | speed.clause_pause_factor = 0; | ||||
if (wpm > 430) { | |||||
if (wpm > 430) | |||||
speed.pause_factor = 12; | speed.pause_factor = 12; | ||||
} else if (wpm > 400) { | |||||
else if (wpm > 400) | |||||
speed.pause_factor = 13; | speed.pause_factor = 13; | ||||
} else if (wpm > 374) { | |||||
else if (wpm > 374) | |||||
speed.pause_factor = 14; | speed.pause_factor = 14; | ||||
} else if (wpm > 350) { | |||||
else if (wpm > 350) | |||||
speed.pause_factor = pause_factor_350[wpm - 350]; | speed.pause_factor = pause_factor_350[wpm - 350]; | ||||
} | |||||
if (speed.clause_pause_factor == 0) { | if (speed.clause_pause_factor == 0) { | ||||
// restrict the reduction of pauses between clauses | // restrict the reduction of pauses between clauses | ||||
next = &phoneme_list[ix+1]; | next = &phoneme_list[ix+1]; | ||||
if (p->synthflags & SFLAG_EMBEDDED) { | |||||
if (p->synthflags & SFLAG_EMBEDDED) | |||||
DoEmbedded2(&embedded_ix); | DoEmbedded2(&embedded_ix); | ||||
} | |||||
type = p->type; | type = p->type; | ||||
if (p->synthflags & SFLAG_SYLLABLE) | if (p->synthflags & SFLAG_SYLLABLE) | ||||
case phFRICATIVE: | case phFRICATIVE: | ||||
if (p->newword) { | if (p->newword) { | ||||
if ((prev->type == phVOWEL) && (p->ph->phflags & phNOPAUSE)) { | if ((prev->type == phVOWEL) && (p->ph->phflags & phNOPAUSE)) { | ||||
} else { | |||||
} else | |||||
p->prepause = 15; | p->prepause = 15; | ||||
} | |||||
} | } | ||||
if (next->type == phPAUSE && prev->type == phNASAL && !(p->ph->phflags&phFORTIS)) | if (next->type == phPAUSE && prev->type == phNASAL && !(p->ph->phflags&phFORTIS)) | ||||
p->length = 256; | p->length = 256; | ||||
if (type == phVFRICATIVE) { | if (type == phVFRICATIVE) { | ||||
if (next->type == phVOWEL) { | |||||
if (next->type == phVOWEL) | |||||
pre_voiced = 1; | pre_voiced = 1; | ||||
} | |||||
if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) { | |||||
if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) | |||||
p->length = (255 + prev->length)/2; | p->length = (255 + prev->length)/2; | ||||
} | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (next->type == phVOWEL) { | |||||
if (next->type == phVOWEL) | |||||
pre_sonorant = 1; | pre_sonorant = 1; | ||||
} else { | |||||
else { | |||||
p->pitch2 = last_pitch; | p->pitch2 = last_pitch; | ||||
if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) { | if ((prev->type == phVOWEL) || (prev->type == phLIQUID)) { | ||||
p->length = prev->length; | p->length = prev->length; | ||||
if (p->type == phLIQUID) { | |||||
if (p->type == phLIQUID) | |||||
p->length = speed1; | p->length = speed1; | ||||
} | |||||
if (next->type == phVSTOP) { | |||||
if (next->type == phVSTOP) | |||||
p->length = (p->length * 160)/100; | p->length = (p->length * 160)/100; | ||||
} | |||||
if (next->type == phVFRICATIVE) { | |||||
if (next->type == phVFRICATIVE) | |||||
p->length = (p->length * 120)/100; | p->length = (p->length * 120)/100; | ||||
} | |||||
} else { | } else { | ||||
for (ix2 = ix; ix2 < n_phoneme_list; ix2++) { | for (ix2 = ix; ix2 < n_phoneme_list; ix2++) { | ||||
if (phoneme_list[ix2].type == phVOWEL) { | if (phoneme_list[ix2].type == phVOWEL) { | ||||
} | } | ||||
p->pitch1 = p->pitch2-16; | p->pitch1 = p->pitch2-16; | ||||
if (p->pitch2 < 16) { | |||||
if (p->pitch2 < 16) | |||||
p->pitch1 = 0; | p->pitch1 = 0; | ||||
} | |||||
p->env = PITCHfall; | p->env = PITCHfall; | ||||
pre_voiced = 0; | pre_voiced = 0; | ||||
} | } | ||||
if (stress > 7) stress = 7; | if (stress > 7) stress = 7; | ||||
if (stress <= 1) { | |||||
if (stress <= 1) | |||||
stress = stress ^ 1; // swap diminished and unstressed (until we swap stress_amps,stress_lengths in tr_languages) | stress = stress ^ 1; // swap diminished and unstressed (until we swap stress_amps,stress_lengths in tr_languages) | ||||
} | |||||
if (pre_sonorant) | if (pre_sonorant) | ||||
p->amp = tr->stress_amps[stress]-1; | p->amp = tr->stress_amps[stress]-1; | ||||
else | else | ||||
if (p2->ph->code == phonPAUSE_CLAUSE) | if (p2->ph->code == phonPAUSE_CLAUSE) | ||||
end_of_clause = 2; | end_of_clause = 2; | ||||
if ((p2->newword & 2) && (more_syllables == 0)) { | |||||
if ((p2->newword & 2) && (more_syllables == 0)) | |||||
end_of_clause = 2; | end_of_clause = 2; | ||||
} | |||||
// calc length modifier | // calc length modifier | ||||
if ((next->ph->code == phonPAUSE_VSHORT) && (next2->type == phPAUSE)) { | if ((next->ph->code == phonPAUSE_VSHORT) && (next2->type == phPAUSE)) { | ||||
length_mod += tr->langopts.lengthen_tonic; | length_mod += tr->langopts.lengthen_tonic; | ||||
if (emphasized) | if (emphasized) | ||||
length_mod += (tr->langopts.lengthen_tonic/2); | length_mod += (tr->langopts.lengthen_tonic/2); | ||||
} else if (emphasized) { | |||||
} else if (emphasized) | |||||
length_mod += tr->langopts.lengthen_tonic; | length_mod += tr->langopts.lengthen_tonic; | ||||
} | |||||
if ((len = tr->stress_lengths[stress]) == 0) | if ((len = tr->stress_lengths[stress]) == 0) | ||||
len = tr->stress_lengths[6]; | len = tr->stress_lengths[6]; | ||||
if (p->tone_ph != 0) { | if (p->tone_ph != 0) { | ||||
InterpretPhoneme2(p->tone_ph, &phdata_tone); | InterpretPhoneme2(p->tone_ph, &phdata_tone); | ||||
pitch_env = GetEnvelope(phdata_tone.pitch_env); | pitch_env = GetEnvelope(phdata_tone.pitch_env); | ||||
} else { | |||||
} else | |||||
pitch_env = envelope_data[env2]; | pitch_env = envelope_data[env2]; | ||||
} | |||||
pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256; | pitch_start = p->pitch1 + ((p->pitch2-p->pitch1)*pitch_env[0])/256; | ||||
if (last_pitch < pitch_start) { | if (last_pitch < pitch_start) { | ||||
prev->env = PITCHrise; | prev->env = PITCHrise; | ||||
p->env = env2; | p->env = env2; | ||||
} else { | |||||
} else | |||||
prev->env = PITCHfall; | prev->env = PITCHfall; | ||||
} | |||||
prev->length = length_mod; | prev->length = length_mod; | ||||
if (next->type == phLIQUID) { | if (next->type == phLIQUID) { | ||||
next->synthflags |= SFLAG_SEQCONTINUE; | next->synthflags |= SFLAG_SEQCONTINUE; | ||||
if (next2->type == phVOWEL) { | |||||
if (next2->type == phVOWEL) | |||||
next->synthflags &= ~SFLAG_SEQCONTINUE; | next->synthflags &= ~SFLAG_SEQCONTINUE; | ||||
} | |||||
if (next2->type != phVOWEL) { | if (next2->type != phVOWEL) { | ||||
if (next->ph->mnemonic == ('/'*256+'r')) { | |||||
if (next->ph->mnemonic == ('/'*256+'r')) | |||||
next->synthflags &= ~SFLAG_SEQCONTINUE; | next->synthflags &= ~SFLAG_SEQCONTINUE; | ||||
} | |||||
} | } | ||||
} | } | ||||
case AUDIO_OUTPUT_PLAYBACK: | case AUDIO_OUTPUT_PLAYBACK: | ||||
{ | { | ||||
int event_type = 0; | int event_type = 0; | ||||
if (event) { | |||||
if (event) | |||||
event_type = event->type; | event_type = event->type; | ||||
} | |||||
if (event_type == espeakEVENT_SAMPLERATE) { | if (event_type == espeakEVENT_SAMPLERATE) { | ||||
voice_samplerate = event->id.number; | voice_samplerate = event->id.number; | ||||
// TBD: For example sentence "or ALT)." returns three words | // TBD: For example sentence "or ALT)." returns three words | ||||
// "or", "ALT" and "". | // "or", "ALT" and "". | ||||
// TBD: the last one has its size=0. | // TBD: the last one has its size=0. | ||||
if (event && (event->type == espeakEVENT_WORD) && (event->length == 0)) { | |||||
if (event && (event->type == espeakEVENT_WORD) && (event->length == 0)) | |||||
break; | break; | ||||
} | |||||
espeak_ERROR a_error = event_declare(event); | espeak_ERROR a_error = event_declare(event); | ||||
if (a_error != EE_BUFFER_FULL) { | |||||
if (a_error != EE_BUFFER_FULL) | |||||
break; | break; | ||||
} | |||||
SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n"); | SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n"); | ||||
usleep(10000); | usleep(10000); | ||||
a_wave_can_be_played = fifo_is_command_enabled(); | a_wave_can_be_played = fifo_is_command_enabled(); | ||||
break; | break; | ||||
case AUDIO_OUTPUT_RETRIEVAL: | case AUDIO_OUTPUT_RETRIEVAL: | ||||
if (synth_callback) { | |||||
if (synth_callback) | |||||
synth_callback(outbuf, length, event); | synth_callback(outbuf, length, event); | ||||
} | |||||
break; | break; | ||||
case AUDIO_OUTPUT_SYNCHRONOUS: | case AUDIO_OUTPUT_SYNCHRONOUS: | ||||
break; | break; | ||||
} | } | ||||
if (!a_wave_can_be_played) { | |||||
if (!a_wave_can_be_played) | |||||
SHOW_TIME("dispatch_audio > synth must be stopped!\n"); | SHOW_TIME("dispatch_audio > synth must be stopped!\n"); | ||||
} | |||||
SHOW_TIME("LEAVE dispatch_audio\n"); | SHOW_TIME("LEAVE dispatch_audio\n"); | ||||
do { // for each event | do { // for each event | ||||
espeak_EVENT *event; | espeak_EVENT *event; | ||||
if (event_list_ix == 0) { | |||||
if (event_list_ix == 0) | |||||
event = NULL; | event = NULL; | ||||
} else { | |||||
else { | |||||
event = event_list + i; | event = event_list + i; | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos); | SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos); | ||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | ||||
while (1) { | while (1) { | ||||
espeak_ERROR a_error = event_declare(event_list); | espeak_ERROR a_error = event_declare(event_list); | ||||
if (a_error != EE_BUFFER_FULL) { | |||||
if (a_error != EE_BUFFER_FULL) | |||||
break; | break; | ||||
} | |||||
SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n"); | SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n"); | ||||
usleep(10000); | usleep(10000); | ||||
} | } | ||||
} else { | } else { | ||||
if (synth_callback) { | |||||
if (synth_callback) | |||||
finished = synth_callback(NULL, 0, event_list); | finished = synth_callback(NULL, 0, event_list); | ||||
} | |||||
} | } | ||||
return finished; | return finished; | ||||
} | } | ||||
} | } | ||||
snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); | snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); | ||||
if (access(path_home, R_OK) != 0) { | |||||
if (access(path_home, R_OK) != 0) | |||||
strcpy(path_home, PATH_ESPEAK_DATA); | strcpy(path_home, PATH_ESPEAK_DATA); | ||||
} | |||||
#endif | #endif | ||||
} | } | ||||
if ((result = LoadPhData(&srate)) != 1) { // reads sample rate from espeak-data/phontab | if ((result = LoadPhData(&srate)) != 1) { // reads sample rate from espeak-data/phontab | ||||
if (result == -1) { | if (result == -1) { | ||||
fprintf(stderr, "Failed to load espeak-data\n"); | fprintf(stderr, "Failed to load espeak-data\n"); | ||||
if ((control & espeakINITIALIZE_DONT_EXIT) == 0) { | |||||
if ((control & espeakINITIALIZE_DONT_EXIT) == 0) | |||||
exit(1); | exit(1); | ||||
} | |||||
} else | } else | ||||
fprintf(stderr, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n", result, version_phdata, path_home); | fprintf(stderr, "Wrong version of espeak-data 0x%x (expects 0x%x) at %s\n", result, version_phdata, path_home); | ||||
} | } | ||||
#ifdef DEBUG_ENABLED | #ifdef DEBUG_ENABLED | ||||
ENTER("Synthesize"); | ENTER("Synthesize"); | ||||
if (text) { | |||||
if (text) | |||||
SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text); | SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text); | ||||
} | |||||
#endif | #endif | ||||
if ((outbuf == NULL) || (event_list == NULL)) | if ((outbuf == NULL) || (event_list == NULL)) | ||||
count_samples = 0; | count_samples = 0; | ||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | |||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) | |||||
a_write_pos = wave_get_write_position(my_audio); | a_write_pos = wave_get_write_position(my_audio); | ||||
} | |||||
#endif | #endif | ||||
if (translator == NULL) { | |||||
if (translator == NULL) | |||||
SetVoiceByName("default"); | SetVoiceByName("default"); | ||||
} | |||||
SpeakNextClause(NULL, text, 0); | SpeakNextClause(NULL, text, 0); | ||||
return EE_INTERNAL_ERROR; | return EE_INTERNAL_ERROR; | ||||
length = 0; // the wave data are played once. | length = 0; // the wave data are played once. | ||||
#endif | #endif | ||||
} else { | |||||
} else | |||||
finished = synth_callback((short *)outbuf, length, event_list); | finished = synth_callback((short *)outbuf, length, event_list); | ||||
} | |||||
if (finished) { | if (finished) { | ||||
SpeakNextClause(NULL, 0, 2); // stop | SpeakNextClause(NULL, 0, 2); // stop | ||||
break; | break; | ||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | ||||
if (dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case | if (dispatch_audio(NULL, 0, NULL) < 0) // TBD: test case | ||||
return err = EE_INTERNAL_ERROR; | return err = EE_INTERNAL_ERROR; | ||||
} else { | |||||
} else | |||||
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data | synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data | ||||
} | |||||
#else | #else | ||||
synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data | synth_callback(NULL, 0, event_list); // NULL buffer ptr indicates end of data | ||||
#endif | #endif | ||||
p = (int *)(ep->id.string); | p = (int *)(ep->id.string); | ||||
p[0] = value; | p[0] = value; | ||||
p[1] = value2; | p[1] = value2; | ||||
} else { | |||||
} else | |||||
ep->id.number = value; | ep->id.number = value; | ||||
} | |||||
} | } | ||||
initialise(options); | initialise(options); | ||||
select_output(output_type); | select_output(output_type); | ||||
if (f_logespeak) { | |||||
if (f_logespeak) | |||||
fprintf(f_logespeak, "INIT mode %d options 0x%x\n", output_type, options); | fprintf(f_logespeak, "INIT mode %d options 0x%x\n", output_type, options); | ||||
} | |||||
// buflength is in mS, allocate 2 bytes per sample | // buflength is in mS, allocate 2 bytes per sample | ||||
if ((buf_length == 0) || (output_type == AUDIO_OUTPUT_PLAYBACK) || (output_type == AUDIO_OUTPUT_SYNCH_PLAYBACK)) | if ((buf_length == 0) || (output_type == AUDIO_OUTPUT_PLAYBACK) || (output_type == AUDIO_OUTPUT_SYNCH_PLAYBACK)) | ||||
espeak_ERROR a_error = EE_INTERNAL_ERROR; | espeak_ERROR a_error = EE_INTERNAL_ERROR; | ||||
static unsigned int temp_identifier; | static unsigned int temp_identifier; | ||||
if (unique_identifier == NULL) { | |||||
if (unique_identifier == NULL) | |||||
unique_identifier = &temp_identifier; | unique_identifier = &temp_identifier; | ||||
} | |||||
*unique_identifier = 0; | *unique_identifier = 0; | ||||
if (synchronous_mode) { | |||||
if (synchronous_mode) | |||||
return sync_espeak_Synth(0, text, size, position, position_type, end_position, flags, user_data); | return sync_espeak_Synth(0, text, size, position, position_type, end_position, flags, user_data); | ||||
} | |||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
// Create the text command | // Create the text command | ||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
static unsigned int temp_identifier; | static unsigned int temp_identifier; | ||||
if (f_logespeak) { | |||||
if (f_logespeak) | |||||
fprintf(f_logespeak, "\nSYNTH MARK %s posn %d flags 0x%x\n%s\n", index_mark, end_position, flags, (const char *)text); | fprintf(f_logespeak, "\nSYNTH MARK %s posn %d flags 0x%x\n%s\n", index_mark, end_position, flags, (const char *)text); | ||||
} | |||||
if (unique_identifier == NULL) { | |||||
if (unique_identifier == NULL) | |||||
unique_identifier = &temp_identifier; | unique_identifier = &temp_identifier; | ||||
} | |||||
*unique_identifier = 0; | *unique_identifier = 0; | ||||
if (synchronous_mode) { | |||||
if (synchronous_mode) | |||||
return sync_espeak_Synth_Mark(0, text, size, index_mark, end_position, flags, user_data); | return sync_espeak_Synth_Mark(0, text, size, index_mark, end_position, flags, user_data); | ||||
} | |||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
// Create the mark command | // Create the mark command | ||||
ENTER("espeak_Key"); | ENTER("espeak_Key"); | ||||
// symbolic name, symbolicname_character - is there a system resource of symbolicnames per language | // symbolic name, symbolicname_character - is there a system resource of symbolicnames per language | ||||
if (f_logespeak) { | |||||
if (f_logespeak) | |||||
fprintf(f_logespeak, "\nKEY %s\n", key); | fprintf(f_logespeak, "\nKEY %s\n", key); | ||||
} | |||||
espeak_ERROR a_error = EE_OK; | espeak_ERROR a_error = EE_OK; | ||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
t_espeak_command *c = create_espeak_key(key, NULL); | t_espeak_command *c = create_espeak_key(key, NULL); | ||||
a_error = fifo_add_command(c); | a_error = fifo_add_command(c); | ||||
if (a_error != EE_OK) { | |||||
if (a_error != EE_OK) | |||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
} | |||||
#endif | #endif | ||||
return a_error; | return a_error; | ||||
ENTER("espeak_Char"); | ENTER("espeak_Char"); | ||||
// is there a system resource of character names per language? | // is there a system resource of character names per language? | ||||
if (f_logespeak) { | |||||
if (f_logespeak) | |||||
fprintf(f_logespeak, "\nCHAR U+%x\n", character); | fprintf(f_logespeak, "\nCHAR U+%x\n", character); | ||||
} | |||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
espeak_ERROR a_error; | espeak_ERROR a_error; | ||||
t_espeak_command *c = create_espeak_char(character, NULL); | t_espeak_command *c = create_espeak_char(character, NULL); | ||||
a_error = fifo_add_command(c); | a_error = fifo_add_command(c); | ||||
if (a_error != EE_OK) { | |||||
if (a_error != EE_OK) | |||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
} | |||||
return a_error; | return a_error; | ||||
#else | #else | ||||
sync_espeak_Char(character); | sync_espeak_Char(character); | ||||
{ | { | ||||
ENTER("espeak_GetParameter"); | ENTER("espeak_GetParameter"); | ||||
// current: 0=default value, 1=current value | // current: 0=default value, 1=current value | ||||
if (current) { | |||||
if (current) | |||||
return param_stack[0].parameter[parameter]; | return param_stack[0].parameter[parameter]; | ||||
} else { | |||||
return param_defaults[parameter]; | |||||
} | |||||
return param_defaults[parameter]; | |||||
} | } | ||||
{ | { | ||||
ENTER("espeak_SetParameter"); | ENTER("espeak_SetParameter"); | ||||
if (f_logespeak) { | |||||
if (f_logespeak) | |||||
fprintf(f_logespeak, "SETPARAM %d %d %d\n", parameter, value, relative); | fprintf(f_logespeak, "SETPARAM %d %d %d\n", parameter, value, relative); | ||||
} | |||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
espeak_ERROR a_error; | espeak_ERROR a_error; | ||||
t_espeak_command *c = create_espeak_parameter(parameter, value, relative); | t_espeak_command *c = create_espeak_parameter(parameter, value, relative); | ||||
a_error = fifo_add_command(c); | a_error = fifo_add_command(c); | ||||
if (a_error != EE_OK) { | |||||
if (a_error != EE_OK) | |||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
} | |||||
return a_error; | return a_error; | ||||
#else | #else | ||||
SetParameter(parameter, value, relative); | SetParameter(parameter, value, relative); | ||||
t_espeak_command *c = create_espeak_punctuation_list(punctlist); | t_espeak_command *c = create_espeak_punctuation_list(punctlist); | ||||
a_error = fifo_add_command(c); | a_error = fifo_add_command(c); | ||||
if (a_error != EE_OK) { | |||||
if (a_error != EE_OK) | |||||
delete_espeak_command(c); | delete_espeak_command(c); | ||||
} | |||||
return a_error; | return a_error; | ||||
#else | #else | ||||
sync_espeak_SetPunctuationList(punctlist); | sync_espeak_SetPunctuationList(punctlist); | ||||
fifo_stop(); | fifo_stop(); | ||||
event_clear_all(); | event_clear_all(); | ||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) { | |||||
if (my_mode == AUDIO_OUTPUT_PLAYBACK) | |||||
wave_close(my_audio); | wave_close(my_audio); | ||||
} | |||||
SHOW_TIME("espeak_Cancel > LEAVE"); | SHOW_TIME("espeak_Cancel > LEAVE"); | ||||
#endif | #endif | ||||
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements | embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements | ||||
espeak_ERROR berr = err; | espeak_ERROR berr = err; | ||||
#ifdef USE_ASYNC | #ifdef USE_ASYNC | ||||
SHOW_TIME("espeak_Synchronize > ENTER"); | SHOW_TIME("espeak_Synchronize > ENTER"); | ||||
while (espeak_IsPlaying()) { | |||||
while (espeak_IsPlaying()) | |||||
usleep(20000); | usleep(20000); | ||||
} | |||||
#endif | #endif | ||||
err = EE_OK; | err = EE_OK; | ||||
SHOW_TIME("espeak_Synchronize > LEAVE"); | SHOW_TIME("espeak_Synchronize > LEAVE"); | ||||
ESPEAK_API const char *espeak_Info(const char **ptr) | ESPEAK_API const char *espeak_Info(const char **ptr) | ||||
{ | { | ||||
if (ptr != NULL) { | |||||
if (ptr != NULL) | |||||
*ptr = path_home; | *ptr = path_home; | ||||
} | |||||
return version_string; | return version_string; | ||||
} | } | ||||
ho = xa[i]-x; | ho = xa[i]-x; | ||||
hp = xa[i+m]-x; | hp = xa[i+m]-x; | ||||
w = c[i+1]-d[i]; | w = c[i+1]-d[i]; | ||||
if ((den = ho-hp) == 0.0) { | |||||
if ((den = ho-hp) == 0.0) | |||||
return ya[2]; // two input xa are identical | return ya[2]; // two input xa are identical | ||||
} | |||||
den = w/den; | den = w/den; | ||||
d[i] = hp*den; | d[i] = hp*den; | ||||
c[i] = ho*den; | c[i] = ho*den; | ||||
} | } | ||||
if (file_format_type > 0) { | if (file_format_type > 0) { | ||||
for (ix = 0; ix < N_KLATTP2; ix++) { | |||||
for (ix = 0; ix < N_KLATTP2; ix++) | |||||
fread(frame->klatt_param + ix, sizeof(short), 1, stream); | fread(frame->klatt_param + ix, sizeof(short), 1, stream); | ||||
} | |||||
} | } | ||||
spect_data = malloc(sizeof(USHORT) * frame->nx); | spect_data = malloc(sizeof(USHORT) * frame->nx); | ||||
} | } | ||||
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; | ||||
return frame->rms; | return frame->rms; | ||||
} | } | ||||
fread(&id1, sizeof(uint32_t), 1, stream); | fread(&id1, sizeof(uint32_t), 1, stream); | ||||
fread(&id2, sizeof(uint32_t), 1, stream); | fread(&id2, sizeof(uint32_t), 1, stream); | ||||
if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEQ)) { | |||||
if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEQ)) | |||||
spect->file_format = 0; // eSpeak formants | spect->file_format = 0; // eSpeak formants | ||||
} else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEK)) { | |||||
else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSEK)) | |||||
spect->file_format = 1; // formants for Klatt synthesizer | spect->file_format = 1; // formants for Klatt synthesizer | ||||
} else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSQ2)) { | |||||
else if ((id1 == FILEID1_SPECTSEQ) && (id2 == FILEID2_SPECTSQ2)) | |||||
spect->file_format = 2; // formants for Klatt synthesizer | spect->file_format = 2; // formants for Klatt synthesizer | ||||
} else { | |||||
else { | |||||
fprintf(stderr, "Unsupported spectral file format.\n"); | fprintf(stderr, "Unsupported spectral file format.\n"); | ||||
fclose(stream); | fclose(stream); | ||||
return 1; | return 1; |
if (GetFileLength(path) <= 0) { | if (GetFileLength(path) <= 0) { | ||||
sprintf(path, "/usr/share/mbrola/%s/%s", mbrola_voice, mbrola_voice); | sprintf(path, "/usr/share/mbrola/%s/%s", mbrola_voice, mbrola_voice); | ||||
if (GetFileLength(path) <= 0) { | |||||
if (GetFileLength(path) <= 0) | |||||
sprintf(path, "/usr/share/mbrola/voices/%s", mbrola_voice); | sprintf(path, "/usr/share/mbrola/voices/%s", mbrola_voice); | ||||
} | |||||
} | } | ||||
} | } | ||||
close_MBR(); | close_MBR(); | ||||
mbrola_control = Read4Bytes(f_in); | mbrola_control = Read4Bytes(f_in); | ||||
pw = (int *)mbrola_tab; | pw = (int *)mbrola_tab; | ||||
for (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); | ||||
if (mnem == pr->name) { | if (mnem == pr->name) { | ||||
if (pr->next_phoneme == 0) | if (pr->next_phoneme == 0) | ||||
found = 1; | found = 1; | ||||
else if ((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN)) { | |||||
else if ((pr->next_phoneme == ':') && (plist->synthflags & SFLAG_LENGTHEN)) | |||||
found = 1; | found = 1; | ||||
} else { | |||||
else { | |||||
if (pr->control & 2) | if (pr->control & 2) | ||||
other_ph = ph_prev; | other_ph = ph_prev; | ||||
else if ((pr->control & 8) && ((plist+1)->newword)) | else if ((pr->control & 8) && ((plist+1)->newword)) | ||||
if ((pr->next_phoneme == other_ph->mnemonic) || | if ((pr->next_phoneme == other_ph->mnemonic) || | ||||
((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) || | ((pr->next_phoneme == 2) && (other_ph->type == phVOWEL)) || | ||||
((pr->next_phoneme == '_') && (other_ph->type == phPAUSE))) { | |||||
((pr->next_phoneme == '_') && (other_ph->type == phPAUSE))) | |||||
found = 1; | found = 1; | ||||
} | |||||
} | } | ||||
if ((pr->control & 4) && (plist->newword == 0)) // only at start of word | if ((pr->control & 4) && (plist->newword == 0)) // only at start of word | ||||
pr++; | pr++; | ||||
} | } | ||||
if (mbr_name_prefix != 0) { | |||||
if (mbr_name_prefix != 0) | |||||
mnem = (mnem << 8) | (mbr_name_prefix & 0xff); | mnem = (mnem << 8) | (mbr_name_prefix & 0xff); | ||||
} | |||||
mbr_name_prefix = 0; | mbr_name_prefix = 0; | ||||
return mnem; | return mnem; | ||||
} | } | ||||
// set an additional pitch point half way through the phoneme. | // set an additional pitch point half way through the phoneme. | ||||
// but look for a maximum or a minimum and use that instead | // but look for a maximum or a minimum and use that instead | ||||
y[2] = 64; | y[2] = 64; | ||||
if ((y_max > 0) && (y_max < 127)) { | |||||
if ((y_max > 0) && (y_max < 127)) | |||||
y[2] = y_max; | y[2] = y_max; | ||||
} | |||||
if ((y_min > 0) && (y_min < 127)) { | |||||
if ((y_min > 0) && (y_min < 127)) | |||||
y[2] = y_min; | y[2] = y_min; | ||||
} | |||||
y[1] = y[2] / 2; | y[1] = y[2] / 2; | ||||
y[3] = y[2] + (127 - y[2])/2; | y[3] = y[2] + (127 - y[2])/2; | ||||
for (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) | |||||
y2 = (y[ix] * env100)/env_split; | y2 = (y[ix] * env100)/env_split; | ||||
} else if (split < 0) { | |||||
else if (split < 0) | |||||
y2 = ((y[ix]-env_split) * env100)/env_split; | y2 = ((y[ix]-env_split) * env100)/env_split; | ||||
} else { | |||||
else | |||||
y2 = (y[ix] * env100)/128; | y2 = (y[ix] * env100)/128; | ||||
} | |||||
if ((y2 > 0) && (y2 <= env100)) { | if ((y2 > 0) && (y2 <= env100)) { | ||||
sprintf(buf, " %d %d", y2, p2/4096); | sprintf(buf, " %d %d", y2, p2/4096); | ||||
strcat(output, buf); | strcat(output, buf); | ||||
ph_prev = plist[phix-1].ph; | ph_prev = plist[phix-1].ph; | ||||
ph_next = plist[phix+1].ph; | ph_next = plist[phix+1].ph; | ||||
if (p->synthflags & SFLAG_EMBEDDED) { | |||||
if (p->synthflags & SFLAG_EMBEDDED) | |||||
DoEmbedded(&embedded_ix, p->sourceix); | DoEmbedded(&embedded_ix, p->sourceix); | ||||
} | |||||
if (p->newword & 4) | if (p->newword & 4) | ||||
DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences); | DoMarker(espeakEVENT_SENTENCE, (p->sourceix & 0x7ff) + clause_start_char, 0, count_sentences); | ||||
pause = 0; | pause = 0; | ||||
} | } | ||||
if (f_mbrola) { | |||||
if (f_mbrola) | |||||
fwrite(mbr_buf, 1, (ptr-mbr_buf), f_mbrola); // write .pho to a file | fwrite(mbr_buf, 1, (ptr-mbr_buf), f_mbrola); // write .pho to a file | ||||
} else { | |||||
else { | |||||
int res = write_MBR(mbr_buf); | int res = write_MBR(mbr_buf); | ||||
if (res < 0) | if (res < 0) | ||||
return 0; /* don't get stuck on error */ | return 0; /* don't get stuck on error */ |
rate += (wavefile_data[ix+4] << (ix*8)); | rate += (wavefile_data[ix+4] << (ix*8)); | ||||
} | } | ||||
if (version != version_phdata) { | |||||
if (version != version_phdata) | |||||
result = version; | result = version; | ||||
} | |||||
// set up phoneme tables | // set up phoneme tables | ||||
p = phoneme_tab_data; | p = phoneme_tab_data; | ||||
frames = &frames_buf[0]; | frames = &frames_buf[0]; | ||||
if (seq_break > 0) { | if (seq_break > 0) { | ||||
if (which == 1) { | |||||
if (which == 1) | |||||
nf = seq_break + 1; | nf = seq_break + 1; | ||||
} else { | |||||
else { | |||||
frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames | frames = &frames_buf[seq_break]; // body of vowel, skip past initial frames | ||||
nf -= seq_break; | nf -= seq_break; | ||||
} | } | ||||
} | } | ||||
// do we need to modify a frame for blending with a consonant? | // do we need to modify a frame for blending with a consonant? | ||||
if ((this_ph->type == phVOWEL) && (fmt_params->fmt2_addr == 0) && (fmt_params->use_vowelin)) { | |||||
if ((this_ph->type == phVOWEL) && (fmt_params->fmt2_addr == 0) && (fmt_params->use_vowelin)) | |||||
seq_len_adjust += FormantTransition2(frames, &nf, fmt_params->transition0, fmt_params->transition1, NULL, which); | seq_len_adjust += FormantTransition2(frames, &nf, fmt_params->transition0, fmt_params->transition1, NULL, which); | ||||
} | |||||
length1 = 0; | length1 = 0; | ||||
nf1 = nf - 1; | nf1 = nf - 1; | ||||
length_factor = (length_std * 256)/ length1; | length_factor = (length_std * 256)/ length1; | ||||
for (ix = 0; ix < nf1; ix++) { | |||||
for (ix = 0; ix < nf1; ix++) | |||||
frames[ix].length = (frames[ix].length * length_factor)/256; | frames[ix].length = (frames[ix].length * length_factor)/256; | ||||
} | |||||
} else { | } else { | ||||
if (which == 1) { | if (which == 1) { | ||||
// front of a vowel | // front of a vowel | ||||
} | } | ||||
} else { | } else { | ||||
// not a vowel | // not a vowel | ||||
if (fmt_params->std_length > 0) { | |||||
if (fmt_params->std_length > 0) | |||||
seq_len_adjust += (fmt_params->std_length - length1); | seq_len_adjust += (fmt_params->std_length - length1); | ||||
} | |||||
} | } | ||||
if (seq_len_adjust != 0) { | if (seq_len_adjust != 0) { | ||||
length_factor = ((length1 + seq_len_adjust) * 256)/length1; | length_factor = ((length1 + seq_len_adjust) * 256)/length1; | ||||
for (ix = 0; ix < nf1; ix++) { | |||||
for (ix = 0; ix < nf1; ix++) | |||||
frames[ix].length = (frames[ix].length * length_factor)/256; | frames[ix].length = (frames[ix].length * length_factor)/256; | ||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
int ph_code; | int ph_code; | ||||
PHONEME_TAB *phtab; | PHONEME_TAB *phtab; | ||||
if (recursing == 0) { | |||||
if (recursing == 0) | |||||
memset(phoneme_tab_flags, 0, sizeof(phoneme_tab_flags)); | memset(phoneme_tab_flags, 0, sizeof(phoneme_tab_flags)); | ||||
} | |||||
if ((includes = phoneme_tab_list[number].includes) > 0) { | if ((includes = phoneme_tab_list[number].includes) > 0) { | ||||
// recursively include base phoneme tables | // recursively include base phoneme tables | ||||
} | } | ||||
sprintf(buf, "%s%c%s", path_home, PATHSEP, "config"); | sprintf(buf, "%s%c%s", path_home, PATHSEP, "config"); | ||||
if ((f = fopen(buf, "r")) == NULL) { | |||||
if ((f = fopen(buf, "r")) == NULL) | |||||
return; | return; | ||||
} | |||||
while (fgets(buf, sizeof(buf), f) != NULL) { | while (fgets(buf, sizeof(buf), f) != NULL) { | ||||
if (buf[0] == '/') continue; | if (buf[0] == '/') continue; | ||||
fclose(f_logespeak); | fclose(f_logespeak); | ||||
f_logespeak = fopen(string, "w"); | f_logespeak = fopen(string, "w"); | ||||
} | } | ||||
} else if (memcmp(buf, "tone", 4) == 0) { | |||||
} else if (memcmp(buf, "tone", 4) == 0) | |||||
ReadTonePoints(&buf[5], tone_points); | ReadTonePoints(&buf[5], tone_points); | ||||
} else if (memcmp(buf, "pa_device", 9) == 0) { | |||||
else if (memcmp(buf, "pa_device", 9) == 0) | |||||
sscanf(&buf[10], "%d", &option_device_number); | sscanf(&buf[10], "%d", &option_device_number); | ||||
} else if (memcmp(buf, "soundicon", 9) == 0) { | |||||
else if (memcmp(buf, "soundicon", 9) == 0) { | |||||
ix = sscanf(&buf[10], "_%c %s", &c1, string); | ix = sscanf(&buf[10], "_%c %s", &c1, string); | ||||
if (ix == 2) { | if (ix == 2) { | ||||
soundicon_tab[n_soundicon_tab].name = c1; | soundicon_tab[n_soundicon_tab].name = c1; | ||||
PHONEME_LIST *pl; | PHONEME_LIST *pl; | ||||
static int condition_level[4] = { 1, 2, 4, 15 }; | static int condition_level[4] = { 1, 2, 4, 15 }; | ||||
if (phoneme_tab[plist[0].phcode]->type == phVOWEL) { | |||||
if (phoneme_tab[plist[0].phcode]->type == phVOWEL) | |||||
pl = plist; | pl = plist; | ||||
} else { | |||||
else { | |||||
// consonant, get stress from the following vowel | // consonant, get stress from the following vowel | ||||
if (phoneme_tab[plist[1].phcode]->type == phVOWEL) { | |||||
if (phoneme_tab[plist[1].phcode]->type == phVOWEL) | |||||
pl = &plist[1]; | pl = &plist[1]; | ||||
} else | |||||
else | |||||
return false; // no stress elevel for this consonant | return false; // no stress elevel for this consonant | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (condition == 4) { | |||||
if (condition == 4) | |||||
return stress_level >= pl->wordstress; | return stress_level >= pl->wordstress; | ||||
} | |||||
if (condition == 3) { | if (condition == 3) { | ||||
// if stressed | // if stressed | ||||
if (instn_type == 2) { | if (instn_type == 2) { | ||||
phdata->pd_control |= pd_FORNEXTPH; | phdata->pd_control |= pd_FORNEXTPH; | ||||
voweltype = plist[1].ph->start_type; // SwitchNextVowelType | voweltype = plist[1].ph->start_type; // SwitchNextVowelType | ||||
} else { | |||||
} else | |||||
voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType | voweltype = plist[-1].ph->end_type; // SwitchPrevVowelType | ||||
} | |||||
voweltype -= phonVOWELTYPES; | voweltype -= phonVOWELTYPES; | ||||
if ((voweltype >= 0) && (voweltype < 6)) { | if ((voweltype >= 0) && (voweltype < 6)) { | ||||
// This instruction is followed by addWav(), 2 more words | // This instruction is followed by addWav(), 2 more words | ||||
return 4; | return 4; | ||||
} | } | ||||
if (instn2 == i_CONTINUE) { | |||||
if (instn2 == i_CONTINUE) | |||||
return 3; | return 3; | ||||
} | |||||
return 2; | return 2; | ||||
} | } | ||||
} | } | ||||
phdata->pd_param[i_SET_LENGTH] = ph->std_length; | phdata->pd_param[i_SET_LENGTH] = ph->std_length; | ||||
phdata->pd_param[i_LENGTH_MOD] = ph->length_mod; | phdata->pd_param[i_LENGTH_MOD] = ph->length_mod; | ||||
if (ph->program == 0) { | |||||
if (ph->program == 0) | |||||
return; | return; | ||||
} | |||||
end_flag = 0; | end_flag = 0; | ||||
} | } | ||||
phdata->ipa_string[ix] = 0; | phdata->ipa_string[ix] = 0; | ||||
} else if (instn2 < N_PHONEME_DATA_PARAM) { | } else if (instn2 < N_PHONEME_DATA_PARAM) { | ||||
if (instn2 == i_CHANGE_PHONEME2) { | |||||
if (instn2 == i_CHANGE_PHONEME2) | |||||
phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme | phdata->pd_param[i_CHANGE_PHONEME] = data; // also set ChangePhoneme | ||||
} | |||||
phdata->pd_param[instn2] = data; | phdata->pd_param[instn2] = data; | ||||
if ((instn2 == i_CHANGE_PHONEME) && (control & 0x100)) { | if ((instn2 == i_CHANGE_PHONEME) && (control & 0x100)) { | ||||
// found ChangePhoneme() in PhonemeList mode, exit | // found ChangePhoneme() in PhonemeList mode, exit | ||||
end_flag = 1; | end_flag = 1; | ||||
} | } | ||||
} else { | |||||
} else | |||||
InvalidInstn(ph, instn); | InvalidInstn(ph, instn); | ||||
} | |||||
break; | break; | ||||
case 1: | case 1: | ||||
} | } | ||||
if (truth == false) { | if (truth == false) { | ||||
if ((instn & 0xf800) == i_JUMP_FALSE) { | |||||
if ((instn & 0xf800) == i_JUMP_FALSE) | |||||
prog += instn & 0xff; | prog += instn & 0xff; | ||||
} else { | |||||
else { | |||||
// instruction after a condition is not JUMP_FALSE, so skip the instruction. | // instruction after a condition is not JUMP_FALSE, so skip the instruction. | ||||
prog += NumInstnWords(prog); | prog += NumInstnWords(prog); | ||||
if ((prog[0] & 0xfe00) == 0x6000) | if ((prog[0] & 0xfe00) == 0x6000) | ||||
break; | break; | ||||
} | } | ||||
if (ph->phflags & phSINGLE_INSTN) { | |||||
if (ph->phflags & phSINGLE_INSTN) | |||||
end_flag = 1; // this phoneme has a one-instruction program, with an implicit Return | end_flag = 1; // this phoneme has a one-instruction program, with an implicit Return | ||||
} | |||||
if ((end_flag == 1) && (n_return > 0)) { | if ((end_flag == 1) && (n_return > 0)) { | ||||
// return from called procedure or phoneme | // return from called procedure or phoneme | ||||
} | } | ||||
} | } | ||||
if ((worddata != NULL) && (plist->type == phVOWEL)) { | |||||
if ((worddata != NULL) && (plist->type == phVOWEL)) | |||||
memcpy(&worddata->prev_vowel, &plist[0], sizeof(PHONEME_LIST)); | memcpy(&worddata->prev_vowel, &plist[0], sizeof(PHONEME_LIST)); | ||||
} | |||||
plist->std_length = phdata->pd_param[i_SET_LENGTH]; | plist->std_length = phdata->pd_param[i_SET_LENGTH]; | ||||
if (phdata->sound_addr[0] != 0) { | if (phdata->sound_addr[0] != 0) { |
else { | else { | ||||
len = PauseLength(length, control); | len = PauseLength(length, control); | ||||
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 { | |||||
srate2 = samplerate / 25; // avoid overflow | srate2 = samplerate / 25; // avoid overflow | ||||
len = (len * srate2) / 40; | len = (len * srate2) / 40; | ||||
} | } | ||||
std_length = wav_length; | std_length = wav_length; | ||||
} | } | ||||
if (length_mod > 0) { | |||||
if (length_mod > 0) | |||||
std_length = (std_length * length_mod)/256; | std_length = (std_length * length_mod)/256; | ||||
} | |||||
length = (std_length * speed.wav_factor)/256; | length = (std_length * speed.wav_factor)/256; | ||||
seq_len_adjust = 0; | seq_len_adjust = 0; | ||||
if (phdata->sound_addr[pd_WAV] == 0) { | |||||
if (phdata->sound_addr[pd_WAV] == 0) | |||||
len = 0; | len = 0; | ||||
} else { | |||||
else | |||||
len = DoSample2(phdata->sound_addr[pd_WAV], 2, phdata->pd_param[pd_LENGTHMOD]*2, phdata->pd_control, length_mod, amp2); | len = DoSample2(phdata->sound_addr[pd_WAV], 2, phdata->pd_param[pd_LENGTHMOD]*2, phdata->pd_control, length_mod, amp2); | ||||
} | |||||
last_frame = NULL; | last_frame = NULL; | ||||
return len; | return len; | ||||
} | } | ||||
}; | }; | ||||
if (voice->klattv[0]) { | if (voice->klattv[0]) { | ||||
if (new_rms == -1) { | |||||
if (new_rms == -1) | |||||
fr->klattp[KLATT_AV] = 50; | fr->klattp[KLATT_AV] = 50; | ||||
} | |||||
return; | return; | ||||
} | } | ||||
fr->ffreq[2] += x; | fr->ffreq[2] += x; | ||||
fr->ffreq[3] += f3_adj; | fr->ffreq[3] += f3_adj; | ||||
if (flags & 0x20) { | |||||
if (flags & 0x20) | |||||
f3_adj = -f3_adj; // . reverse direction for f4,f5 change | f3_adj = -f3_adj; // . reverse direction for f4,f5 change | ||||
} | |||||
fr->ffreq[4] += f3_adj; | fr->ffreq[4] += f3_adj; | ||||
fr->ffreq[5] += f3_adj; | fr->ffreq[5] += f3_adj; | ||||
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; | ||||
} | |||||
if (f2 != 0) { | if (f2 != 0) { | ||||
if (rms & 0x20) { | |||||
if (rms & 0x20) | |||||
set_frame_rms(fr, (next_rms * (rms & 0x1f))/30); | set_frame_rms(fr, (next_rms * (rms & 0x1f))/30); | ||||
} | |||||
AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags); | AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags); | ||||
if ((rms & 0x20) == 0) { | |||||
if ((rms & 0x20) == 0) | |||||
set_frame_rms(fr, rms*2); | set_frame_rms(fr, rms*2); | ||||
} | |||||
} else { | } else { | ||||
if (flags & 8) | if (flags & 8) | ||||
set_frame_rms(fr, (next_rms*24)/32); | set_frame_rms(fr, (next_rms*24)/32); | ||||
set_frame_rms(fr, RMS_START); | set_frame_rms(fr, RMS_START); | ||||
} | } | ||||
if (flags & 8) { | |||||
if (flags & 8) | |||||
modn_flags = 0x800 + (VowelCloseness(fr) << 8); | modn_flags = 0x800 + (VowelCloseness(fr) << 8); | ||||
} | |||||
} else { | } else { | ||||
// exit from vowel | // exit from vowel | ||||
rms = rms*2; | rms = rms*2; | ||||
if (len > 36) | if (len > 36) | ||||
seq_len_adjust += (len - 36); | seq_len_adjust += (len - 36); | ||||
if (f2 != 0) { | |||||
if (f2 != 0) | |||||
AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags); | AdjustFormants(fr, f2, f2_min, f2_max, f1, f3_adj, f3_amp, flags); | ||||
} | |||||
} | } | ||||
set_frame_rms(fr, rms); | set_frame_rms(fr, rms); | ||||
f2 = frame->ffreq[pk]; | f2 = frame->ffreq[pk]; | ||||
// backwards | // backwards | ||||
if ((diff = f2 - f1) > 0) { | |||||
if ((diff = f2 - f1) > 0) | |||||
allowed = f1*2 + f2; | allowed = f1*2 + f2; | ||||
} else { | |||||
else | |||||
allowed = f1 + f2*2; | allowed = f1 + f2*2; | ||||
} | |||||
// the allowed change is specified as percentage (%*10) of the frequency | // the allowed change is specified as percentage (%*10) of the frequency | ||||
// take "frequency" as 1/3 from the lower freq | // take "frequency" as 1/3 from the lower freq | ||||
f2 = frame->ffreq[pk]; | f2 = frame->ffreq[pk]; | ||||
// forwards | // forwards | ||||
if ((diff = f2 - f1) > 0) { | |||||
if ((diff = f2 - f1) > 0) | |||||
allowed = f1*2 + f2; | allowed = f1*2 + f2; | ||||
} else { | |||||
else | |||||
allowed = f1 + f2*2; | allowed = f1 + f2*2; | ||||
} | |||||
allowed = (allowed * formant_rate[pk])/3000; | allowed = (allowed * formant_rate[pk])/3000; | ||||
allowed = (allowed * len)/256; | allowed = (allowed * len)/256; | ||||
if (which == 1) { | if (which == 1) { | ||||
// limit the shortening of sonorants before shortened (eg. unstressed vowels) | // limit the shortening of sonorants before shortened (eg. unstressed vowels) | ||||
if ((this_ph->type == phLIQUID) || (plist[-1].type == phLIQUID) || (plist[-1].type == phNASAL)) { | if ((this_ph->type == phLIQUID) || (plist[-1].type == phLIQUID) || (plist[-1].type == phNASAL)) { | ||||
if (length_mod < (len = translator->langopts.param[LOPT_SONORANT_MIN])) { | |||||
if (length_mod < (len = translator->langopts.param[LOPT_SONORANT_MIN])) | |||||
length_mod = len; | length_mod = len; | ||||
} | |||||
} | } | ||||
} | } | ||||
if ((length_sum > 0) && (length_sum < length_min)) { | if ((length_sum > 0) && (length_sum < length_min)) { | ||||
// lengthen, so that the sequence is greater than one cycle at low pitch | // lengthen, so that the sequence is greater than one cycle at low pitch | ||||
for (frameix = 1; frameix < n_frames; frameix++) { | |||||
for (frameix = 1; frameix < n_frames; frameix++) | |||||
frame_lengths[frameix] = (frame_lengths[frameix] * length_min) / length_sum; | frame_lengths[frameix] = (frame_lengths[frameix] * length_min) / length_sum; | ||||
} | |||||
} | } | ||||
for (frameix = 1; frameix < n_frames; frameix++) { | for (frameix = 1; frameix < n_frames; frameix++) { | ||||
} | } | ||||
if (modulation >= 0) { | if (modulation >= 0) { | ||||
if (frame1->frflags & FRFLAG_MODULATE) { | |||||
if (frame1->frflags & FRFLAG_MODULATE) | |||||
modulation = 6; | modulation = 6; | ||||
} | |||||
if ((frameix == n_frames-1) && (modn_flags & 0xf00)) | if ((frameix == n_frames-1) && (modn_flags & 0xf00)) | ||||
modulation |= modn_flags; // before or after a glottal stop | modulation |= modn_flags; // before or after a glottal stop | ||||
} | } | ||||
next = &phoneme_list[ix+1]; | next = &phoneme_list[ix+1]; | ||||
next2 = &phoneme_list[ix+2]; | next2 = &phoneme_list[ix+2]; | ||||
if (p->synthflags & SFLAG_EMBEDDED) { | |||||
if (p->synthflags & SFLAG_EMBEDDED) | |||||
DoEmbedded(&embedded_ix, p->sourceix); | DoEmbedded(&embedded_ix, p->sourceix); | ||||
} | |||||
if (p->newword) { | if (p->newword) { | ||||
if (((p->type == phVOWEL) && (translator->langopts.param[LOPT_WORD_MERGE] & 1)) || | if (((p->type == phVOWEL) && (translator->langopts.param[LOPT_WORD_MERGE] & 1)) || | ||||
(p->ph->phflags & phNOPAUSE)) { | (p->ph->phflags & phNOPAUSE)) { | ||||
} else { | |||||
} else | |||||
last_frame = NULL; | last_frame = NULL; | ||||
} | |||||
sourceix = (p->sourceix & 0x7ff) + clause_start_char; | sourceix = (p->sourceix & 0x7ff) + clause_start_char; | ||||
if (p->newword & 4) | if (p->newword & 4) | ||||
DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences); // start of sentence | DoMarker(espeakEVENT_SENTENCE, sourceix, 0, count_sentences); // start of sentence | ||||
// if(p->newword & 2) | |||||
// DoMarker(espeakEVENT_END, count_characters, 0, count_sentences); // end of clause | |||||
if (p->newword & 1) | if (p->newword & 1) | ||||
DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++); // NOTE, this count doesn't include multiple-word pronunciations in *_list. eg (of a) | DoMarker(espeakEVENT_WORD, sourceix, p->sourceix >> 11, clause_start_word + word_count++); // NOTE, this count doesn't include multiple-word pronunciations in *_list. eg (of a) | ||||
} | } | ||||
case phSTOP: | case phSTOP: | ||||
released = 0; | released = 0; | ||||
ph = p->ph; | ph = p->ph; | ||||
if (next->type == phVOWEL) { | |||||
if (next->type == phVOWEL) | |||||
released = 1; | released = 1; | ||||
} else if (!next->newword) { | |||||
else if (!next->newword) { | |||||
if (next->type == phLIQUID) released = 1; | if (next->type == phLIQUID) released = 1; | ||||
} | } | ||||
if (released == 0) | if (released == 0) | ||||
case phFRICATIVE: | case phFRICATIVE: | ||||
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
if (p->synthflags & SFLAG_LENGTHEN) { | |||||
if (p->synthflags & SFLAG_LENGTHEN) | |||||
DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc. | DoSample3(&phdata, p->length, 0); // play it twice for [s:] etc. | ||||
} | |||||
DoSample3(&phdata, p->length, 0); | DoSample3(&phdata, p->length, 0); | ||||
break; | break; | ||||
DoSpect2(ph, 0, &fmtp, p, 0); | DoSpect2(ph, 0, &fmtp, p, 0); | ||||
} | } | ||||
} else { | } else { | ||||
if (p->synthflags & SFLAG_LENGTHEN) { | |||||
if (p->synthflags & SFLAG_LENGTHEN) | |||||
DoPause(50, 0); | DoPause(50, 0); | ||||
} | |||||
} | } | ||||
if (pre_voiced) { | if (pre_voiced) { | ||||
// followed by a vowel, or liquid + vowel | // followed by a vowel, or liquid + vowel | ||||
StartSyllable(); | StartSyllable(); | ||||
} else { | |||||
} else | |||||
p->synthflags |= SFLAG_NEXT_PAUSE; | p->synthflags |= SFLAG_NEXT_PAUSE; | ||||
} | |||||
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | ||||
fmtp.fmt_amp = phdata.sound_param[pd_FMT]; | fmtp.fmt_amp = phdata.sound_param[pd_FMT]; | ||||
} | } | ||||
} | } | ||||
if ((next->type == phVOWEL) || ((next->type == phLIQUID) && (next->newword == 0))) { // ?? test 14.Aug.2007 | |||||
if ((next->type == phVOWEL) || ((next->type == phLIQUID) && (next->newword == 0))) // ?? test 14.Aug.2007 | |||||
StartSyllable(); | StartSyllable(); | ||||
} else { | |||||
else | |||||
p->synthflags |= SFLAG_NEXT_PAUSE; | p->synthflags |= SFLAG_NEXT_PAUSE; | ||||
} | |||||
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
memset(&fmtp, 0, sizeof(fmtp)); | memset(&fmtp, 0, sizeof(fmtp)); | ||||
fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | ||||
DoPitch(envelope_data[p->env], p->pitch1, p->pitch2); | DoPitch(envelope_data[p->env], p->pitch1, p->pitch2); | ||||
} | } | ||||
if (prev->type == phNASAL) { | |||||
if (prev->type == phNASAL) | |||||
last_frame = NULL; | last_frame = NULL; | ||||
} | |||||
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | ||||
if (next->type == phVOWEL) { | if (next->type == phVOWEL) { | ||||
StartSyllable(); | StartSyllable(); | ||||
DoSpect2(p->ph, 0, &fmtp, p, 0); | DoSpect2(p->ph, 0, &fmtp, p, 0); | ||||
} else if (prev->type == phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE)) { | |||||
} else if (prev->type == phVOWEL && (p->synthflags & SFLAG_SEQCONTINUE)) | |||||
DoSpect2(p->ph, 0, &fmtp, p, 0); | DoSpect2(p->ph, 0, &fmtp, p, 0); | ||||
} else { | |||||
else { | |||||
last_frame = NULL; // only for nasal ? | last_frame = NULL; // only for nasal ? | ||||
DoSpect2(p->ph, 0, &fmtp, p, 0); | DoSpect2(p->ph, 0, &fmtp, p, 0); | ||||
last_frame = NULL; | last_frame = NULL; | ||||
DoPitch(envelope_data[p->env], p->pitch1, p->pitch2); | DoPitch(envelope_data[p->env], p->pitch1, p->pitch2); | ||||
} | } | ||||
if (prev->type == phNASAL) { | |||||
if (prev->type == phNASAL) | |||||
last_frame = NULL; | last_frame = NULL; | ||||
} | |||||
if (next->type == phVOWEL) { | |||||
if (next->type == phVOWEL) | |||||
StartSyllable(); | StartSyllable(); | ||||
} | |||||
InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | InterpretPhoneme(NULL, 0, p, &phdata, &worddata); | ||||
if ((value = (phdata.pd_param[i_PAUSE_BEFORE] - p->prepause)) > 0) { | |||||
if ((value = (phdata.pd_param[i_PAUSE_BEFORE] - p->prepause)) > 0) | |||||
DoPause(value, 1); | DoPause(value, 1); | ||||
} | |||||
fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | fmtp.std_length = phdata.pd_param[i_SET_LENGTH]*2; | ||||
fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | fmtp.fmt_addr = phdata.sound_addr[pd_FMT]; | ||||
fmtp.fmt_amp = phdata.sound_param[pd_FMT]; | fmtp.fmt_amp = phdata.sound_param[pd_FMT]; | ||||
fmtp.transition0 = 0; | fmtp.transition0 = 0; | ||||
fmtp.transition1 = 0; | fmtp.transition1 = 0; | ||||
if ((fmtp.fmt2_addr = phdata.sound_addr[pd_VWLEND]) != 0) { | |||||
if ((fmtp.fmt2_addr = phdata.sound_addr[pd_VWLEND]) != 0) | |||||
fmtp.fmt2_lenadj = phdata.sound_param[pd_VWLEND]; | fmtp.fmt2_lenadj = phdata.sound_param[pd_VWLEND]; | ||||
} else if (next->type != phPAUSE) { | |||||
else if (next->type != phPAUSE) { | |||||
fmtp.fmt2_lenadj = 0; | fmtp.fmt2_lenadj = 0; | ||||
InterpretPhoneme(NULL, 0, next, &phdata_next, NULL); | InterpretPhoneme(NULL, 0, next, &phdata_next, NULL); | ||||
int SynthOnTimer() | int SynthOnTimer() | ||||
{ | { | ||||
if (!timer_on) { | |||||
if (!timer_on) | |||||
return WavegenCloseSound(); | return WavegenCloseSound(); | ||||
} | |||||
do { | do { | ||||
if (WcmdqUsed() > 0) | if (WcmdqUsed() > 0) | ||||
WavegenOpenSound(); | WavegenOpenSound(); | ||||
if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) { | |||||
if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) | |||||
SpeakNextClause(NULL, NULL, 1); | SpeakNextClause(NULL, NULL, 1); | ||||
} | |||||
} while (skipping_text); | } while (skipping_text); | ||||
return 0; | return 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
if (current_phoneme_table != voice->phoneme_tab_ix) { | |||||
if (current_phoneme_table != voice->phoneme_tab_ix) | |||||
SelectPhonemeTable(voice->phoneme_tab_ix); | SelectPhonemeTable(voice->phoneme_tab_ix); | ||||
} | |||||
// read the next clause from the input text file, translate it, and generate | // read the next clause from the input text file, translate it, and generate | ||||
// entries in the wavegen command queue | // entries in the wavegen command queue | ||||
if ((option_phonemes & 0xf) || (phoneme_callback != NULL)) { | if ((option_phonemes & 0xf) || (phoneme_callback != NULL)) { | ||||
phon_out = GetTranslatedPhonemeString(option_phonemes); | phon_out = GetTranslatedPhonemeString(option_phonemes); | ||||
if (option_phonemes & 0xf) { | |||||
if (option_phonemes & 0xf) | |||||
fprintf(f_trans, "%s\n", phon_out); | fprintf(f_trans, "%s\n", phon_out); | ||||
} | |||||
if (phoneme_callback != NULL) { | |||||
if (phoneme_callback != NULL) | |||||
phoneme_callback(phon_out); | phoneme_callback(phon_out); | ||||
} | |||||
} | } | ||||
mask = ~groups; | mask = ~groups; | ||||
for (ix = 0; ix < sizeof(tr->letter_bits); ix++) { | |||||
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 ix; | int ix; | ||||
bits = (1L << group); | bits = (1L << group); | ||||
for (ix = first; ix <= last; ix++) { | |||||
for (ix = first; ix <= last; ix++) | |||||
tr->letter_bits[ix] |= bits; | tr->letter_bits[ix] |= bits; | ||||
} | |||||
} | } | ||||
// ignore these characters | // ignore these characters | ||||
// stress last syllable unless word ends with a vowel | // stress last syllable unless word ends with a vowel | ||||
tr->langopts.stress_rule = STRESSPOSN_1R; | tr->langopts.stress_rule = STRESSPOSN_1R; | ||||
tr->langopts.stress_flags = S_FINAL_VOWEL_UNSTRESSED | S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_NO_AUTO_2; | tr->langopts.stress_flags = S_FINAL_VOWEL_UNSTRESSED | S_FINAL_DIM_ONLY | S_FINAL_NO_2 | S_NO_AUTO_2; | ||||
} else { | |||||
} else | |||||
tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use es_rules for unpronouncable rules | tr->langopts.param[LOPT_UNPRONOUNCABLE] = 2; // use es_rules for unpronouncable rules | ||||
} | |||||
} | } | ||||
break; | break; | ||||
tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | tr->langopts.break_numbers = 0x14aa8; // for languages which have numbers for 100,000 and 100,00,000, eg Hindi | ||||
tr->letter_bits_offset = OFFSET_DEVANAGARI; | tr->letter_bits_offset = OFFSET_DEVANAGARI; | ||||
if (name2 == L('p', 'a')) { | |||||
if (name2 == L('p', 'a')) | |||||
tr->letter_bits_offset = OFFSET_GURMUKHI; | tr->letter_bits_offset = OFFSET_GURMUKHI; | ||||
} else if (name2 == L('g', 'u')) { | |||||
else if (name2 == L('g', 'u')) { | |||||
SetupTranslator(tr, stress_lengths_equal, stress_amps_equal); | SetupTranslator(tr, stress_lengths_equal, stress_amps_equal); | ||||
tr->letter_bits_offset = OFFSET_GUJARATI; | tr->letter_bits_offset = OFFSET_GUJARATI; | ||||
tr->langopts.stress_rule = STRESSPOSN_2R; | tr->langopts.stress_rule = STRESSPOSN_2R; | ||||
tr->langopts.break_numbers = 0x2aaaa8; | tr->langopts.break_numbers = 0x2aaaa8; | ||||
tr->langopts.max_digits = 22; | tr->langopts.max_digits = 22; | ||||
tr->langopts.numbers2 |= NUM2_ENGLISH_NUMERALS; | tr->langopts.numbers2 |= NUM2_ENGLISH_NUMERALS; | ||||
} else if (name2 == L('o', 'r')) { | |||||
} else if (name2 == L('o', 'r')) | |||||
tr->letter_bits_offset = OFFSET_ORIYA; | tr->letter_bits_offset = OFFSET_ORIYA; | ||||
} | |||||
SetIndicLetters(tr); | SetIndicLetters(tr); | ||||
} | } | ||||
break; | break; | ||||
tr->langopts.numbers = NUM_OMIT_1_THOUSAND; | tr->langopts.numbers = NUM_OMIT_1_THOUSAND; | ||||
tr->langopts.numbers2 = NUM2_ORDINAL_AND_THOUSANDS; | tr->langopts.numbers2 = NUM2_ORDINAL_AND_THOUSANDS; | ||||
tr->langopts.param[LOPT_WORD_MERGE] = 1; // don't break vowels betwen words | tr->langopts.param[LOPT_WORD_MERGE] = 1; // don't break vowels betwen words | ||||
} else if (name2 == L('m', 'r')) { | |||||
} else if (name2 == L('m', 'r')) | |||||
tr->letter_bits_offset = OFFSET_DEVANAGARI; | tr->letter_bits_offset = OFFSET_DEVANAGARI; | ||||
} else if (name2 == L('m', 'l')) { | |||||
else if (name2 == L('m', 'l')) { | |||||
static const short stress_lengths_ml[8] = { 180, 160, 240, 240, 0, 0, 260, 260 }; | static const short stress_lengths_ml[8] = { 180, 160, 240, 240, 0, 0, 260, 260 }; | ||||
SetupTranslator(tr, stress_lengths_ml, stress_amps_equal); | SetupTranslator(tr, stress_lengths_ml, stress_amps_equal); | ||||
tr->letter_bits_offset = OFFSET_MALAYALAM; | tr->letter_bits_offset = OFFSET_MALAYALAM; | ||||
tr->langopts.dotless_i = 1; | tr->langopts.dotless_i = 1; | ||||
tr->langopts.param[LOPT_SUFFIX] = 1; | tr->langopts.param[LOPT_SUFFIX] = 1; | ||||
if (name2 == L('a', 'z')) { | |||||
if (name2 == L('a', 'z')) | |||||
tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_ALLOW_SPACE | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | ||||
} else { | |||||
else | |||||
tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | tr->langopts.numbers = NUM_SINGLE_STRESS | NUM_DECIMAL_COMMA | NUM_OMIT_1_HUNDRED | NUM_OMIT_1_THOUSAND | NUM_DFRACTION_2; | ||||
} | |||||
tr->langopts.max_initial_consonants = 2; | tr->langopts.max_initial_consonants = 2; | ||||
} | } | ||||
break; | break; | ||||
langopts->thousands_sep = '.'; | langopts->thousands_sep = '.'; | ||||
langopts->decimal_sep = ','; | langopts->decimal_sep = ','; | ||||
} | } | ||||
if (langopts->numbers & NUM_THOUS_SPACE) { | |||||
if (langopts->numbers & NUM_THOUS_SPACE) | |||||
langopts->thousands_sep = 0; // don't allow thousands separator, except space | langopts->thousands_sep = 0; // don't allow thousands separator, except space | ||||
} | |||||
} | } | ||||
int value2; | int value2; | ||||
tr->langopts.length_mods0 = tr->langopts.length_mods = length_mod_tabs[value % 100]; | tr->langopts.length_mods0 = tr->langopts.length_mods = length_mod_tabs[value % 100]; | ||||
if ((value2 = value / 100) != 0) { | |||||
if ((value2 = value / 100) != 0) | |||||
tr->langopts.length_mods0 = length_mod_tabs[value2]; | tr->langopts.length_mods0 = length_mod_tabs[value2]; | ||||
} | |||||
} | } | ||||
n_bytes = 3; | n_bytes = 3; | ||||
c1 &= mask[n_bytes]; | c1 &= mask[n_bytes]; | ||||
for (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; | ||||
return n_bytes+1; | return n_bytes+1; | ||||
} | } | ||||
*p_out = 0; | *p_out = 0; | ||||
if (remove_stress) { | |||||
if (remove_stress) | |||||
SetWordStress(tr, phonbuf, NULL, -1, 0); | SetWordStress(tr, phonbuf, NULL, -1, 0); | ||||
} | |||||
strcpy(phonemes, phonbuf); | strcpy(phonemes, phonbuf); | ||||
found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word | found = LookupDictList(tr, &word1, phonemes, dictionary_flags, FLAG_ALLOW_TEXTMODE, wtab); // the original word | ||||
if ((dictionary_flags[0] & (FLAG_ALLOW_DOT | FLAG_NEEDS_DOT)) && (wordx[1] == '.')) { | |||||
if ((dictionary_flags[0] & (FLAG_ALLOW_DOT | FLAG_NEEDS_DOT)) && (wordx[1] == '.')) | |||||
wordx[1] = ' '; // remove a Dot after this word | wordx[1] = ' '; // remove a Dot after this word | ||||
} | |||||
if (dictionary_flags[0] & FLAG_TEXTMODE) { | if (dictionary_flags[0] & FLAG_TEXTMODE) { | ||||
if (word_out != NULL) | if (word_out != NULL) | ||||
// ?? should we say super/sub-script numbers and letters here? | // ?? should we say super/sub-script numbers and letters here? | ||||
utf8_in(&wc, wordx); | utf8_in(&wc, wordx); | ||||
if ((word_length == 1) && (IsAlpha(wc) || IsSuperscript(wc))) { | if ((word_length == 1) && (IsAlpha(wc) || IsSuperscript(wc))) { | ||||
if ((wordx = SpeakIndividualLetters(tr, wordx, phonemes, spell_word)) == NULL) { | |||||
if ((wordx = SpeakIndividualLetters(tr, wordx, phonemes, spell_word)) == NULL) | |||||
return 0; | return 0; | ||||
} | |||||
strcpy(word_phonemes, phonemes); | strcpy(word_phonemes, phonemes); | ||||
return 0; | return 0; | ||||
} | } | ||||
for (ix = 0; ix < n_chars; ix++) { // num. of bytes to remove | for (ix = 0; ix < n_chars; ix++) { // num. of bytes to remove | ||||
prefix_chars[pfix++] = *wordx++; | prefix_chars[pfix++] = *wordx++; | ||||
if ((prefix_type & SUFX_B) && (ix == (n_chars-1))) { | |||||
if ((prefix_type & SUFX_B) && (ix == (n_chars-1))) | |||||
prefix_chars[pfix-1] = 0; // discard the last character of the prefix, this is the separator character | prefix_chars[pfix-1] = 0; // discard the last character of the prefix, this is the separator character | ||||
} | |||||
} | } | ||||
prefix_chars[pfix] = 0; | prefix_chars[pfix] = 0; | ||||
} | } | ||||
// look for stress marker or $abbrev | // look for stress marker or $abbrev | ||||
found = LookupDictList(tr, &wordpf, phonemes, dictionary_flags, 0, wtab); | found = LookupDictList(tr, &wordpf, phonemes, dictionary_flags, 0, wtab); | ||||
if (found) { | |||||
if (found) | |||||
strcpy(prefix_phonemes, phonemes); | strcpy(prefix_phonemes, phonemes); | ||||
} | |||||
if (dictionary_flags[0] & FLAG_ABBREV) { | if (dictionary_flags[0] & FLAG_ABBREV) { | ||||
prefix_phonemes[0] = 0; | prefix_phonemes[0] = 0; | ||||
SpeakIndividualLetters(tr, wordpf, prefix_phonemes, 1); | SpeakIndividualLetters(tr, wordpf, prefix_phonemes, 1); | ||||
} | } | ||||
} else { | |||||
} else | |||||
strcat(prefix_phonemes, end_phonemes); | strcat(prefix_phonemes, end_phonemes); | ||||
} | |||||
end_phonemes[0] = 0; | end_phonemes[0] = 0; | ||||
end_type = 0; | end_type = 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
if (dictionary_flags2[0] & FLAG_ABBREV) { | |||||
// Removing the suffix leaves a word which should be spoken as individual letters | |||||
// Not yet implemented | |||||
} | |||||
if (dictionary_flags[0] == 0) { | if (dictionary_flags[0] == 0) { | ||||
dictionary_flags[0] = dictionary_flags2[0]; | dictionary_flags[0] = dictionary_flags2[0]; | ||||
dictionary_flags[1] = dictionary_flags2[1]; | dictionary_flags[1] = dictionary_flags2[1]; | ||||
/* determine stress pattern for this word */ | /* determine stress pattern for this word */ | ||||
/******************************************/ | /******************************************/ | ||||
add_suffix_phonemes = 0; | add_suffix_phonemes = 0; | ||||
if (end_phonemes[0] != 0) { | |||||
if (end_phonemes[0] != 0) | |||||
add_suffix_phonemes = 2; | add_suffix_phonemes = 2; | ||||
} | |||||
prefix_stress = 0; | prefix_stress = 0; | ||||
for (p = prefix_phonemes; *p != 0; p++) { | for (p = prefix_phonemes; *p != 0; p++) { | ||||
if ((*p == phonSTRESS_P) || (*p == phonSTRESS_P2)) { | |||||
if ((*p == phonSTRESS_P) || (*p == phonSTRESS_P2)) | |||||
prefix_stress = *p; | prefix_stress = *p; | ||||
} | |||||
} | } | ||||
if (prefix_flags || (prefix_stress != 0)) { | if (prefix_flags || (prefix_stress != 0)) { | ||||
if ((tr->langopts.param[LOPT_PREFIXES]) || (prefix_type & SUFX_T)) { | if ((tr->langopts.param[LOPT_PREFIXES]) || (prefix_type & SUFX_T)) { | ||||
dictionary_flags[0] &= ~FLAG_PAUSE1; | dictionary_flags[0] &= ~FLAG_PAUSE1; | ||||
} | } | ||||
if ((wflags & FLAG_HYPHEN) && (tr->langopts.stress_flags & S_HYPEN_UNSTRESS)) { | |||||
if ((wflags & FLAG_HYPHEN) && (tr->langopts.stress_flags & S_HYPEN_UNSTRESS)) | |||||
ChangeWordStress(tr, word_phonemes, 3); | ChangeWordStress(tr, word_phonemes, 3); | ||||
} else if (wflags & FLAG_EMPHASIZED2) { | |||||
else if (wflags & FLAG_EMPHASIZED2) { | |||||
// A word is indicated in the source text as stressed | // A word is indicated in the source text as stressed | ||||
// Give it stress level 6 (for the intonation module) | // Give it stress level 6 (for the intonation module) | ||||
ChangeWordStress(tr, word_phonemes, 6); | ChangeWordStress(tr, word_phonemes, 6); | ||||
dictionary_flags[0] |= FLAG_ALLOW_DOT; | dictionary_flags[0] |= FLAG_ALLOW_DOT; | ||||
} | } | ||||
if ((tr->langopts.param[LOPT_ALT] & 2) && ((dictionary_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT2_TRANS)) != 0)) { | |||||
if ((tr->langopts.param[LOPT_ALT] & 2) && ((dictionary_flags[0] & (FLAG_ALT_TRANS | FLAG_ALT2_TRANS)) != 0)) | |||||
ApplySpecialAttribute2(tr, word_phonemes, dictionary_flags[0]); | ApplySpecialAttribute2(tr, word_phonemes, dictionary_flags[0]); | ||||
} | |||||
dictionary_flags[0] |= was_unpronouncable; | dictionary_flags[0] |= was_unpronouncable; | ||||
memcpy(word_start, word_copy2, word_copy_length); | memcpy(word_start, word_copy2, word_copy_length); | ||||
translator2->dict_condition = 0x48; // bits 3, 6 | translator2->dict_condition = 0x48; // bits 3, 6 | ||||
translator2->langopts.param[LOPT_REDUCE_T] = 1; | translator2->langopts.param[LOPT_REDUCE_T] = 1; | ||||
} | } | ||||
if (dialect == DICTDIALECT_ES_LA) { | |||||
if (dialect == DICTDIALECT_ES_LA) | |||||
translator2->dict_condition = 0x04; // bit 2 | translator2->dict_condition = 0x04; // bit 2 | ||||
} | |||||
} | } | ||||
translator2->phoneme_tab_ix = new_phoneme_tab; | translator2->phoneme_tab_ix = new_phoneme_tab; | ||||
} | } | ||||
word_phonemes[1] = ix; | word_phonemes[1] = ix; | ||||
word_phonemes[2] = 0; | word_phonemes[2] = 0; | ||||
} | } | ||||
} else { | |||||
} else | |||||
EncodePhonemes(word, word_phonemes, &bad_phoneme); | EncodePhonemes(word, word_phonemes, &bad_phoneme); | ||||
} | |||||
flags = FLAG_FOUND; | flags = FLAG_FOUND; | ||||
} else { | } else { | ||||
int c2; | int c2; | ||||
while (*p2 != ' ') p2++; | while (*p2 != ' ') p2++; | ||||
utf8_in(&c_word2, p2+1); // first character of the next word; | utf8_in(&c_word2, p2+1); // first character of the next word; | ||||
if (!iswalpha2(c_word2)) { | |||||
if (!iswalpha2(c_word2)) | |||||
ok = 0; | ok = 0; | ||||
} | |||||
if (ok != 0) { | if (ok != 0) { | ||||
strcpy(ph_buf, word_phonemes); | strcpy(ph_buf, word_phonemes); | ||||
if (sylimit & 0x100) { | if (sylimit & 0x100) { | ||||
// only if the second word has $alt attribute | // only if the second word has $alt attribute | ||||
if ((flags2[0] & FLAG_ALT_TRANS) == 0) { | |||||
if ((flags2[0] & FLAG_ALT_TRANS) == 0) | |||||
ok = 0; | ok = 0; | ||||
} | |||||
} | } | ||||
if ((sylimit & 0x200) && ((wtab+1)->flags & FLAG_LAST_WORD)) { | if ((sylimit & 0x200) && ((wtab+1)->flags & FLAG_LAST_WORD)) { | ||||
ok = 0; | ok = 0; | ||||
} | } | ||||
if (ok == 0) { | |||||
if (ok == 0) | |||||
strcpy(word_phonemes, ph_buf); | strcpy(word_phonemes, ph_buf); | ||||
} | |||||
} | } | ||||
if (ok) { | if (ok) { | ||||
if (ph_list2[n_ph_list2-1].phcode == phonSWITCH) { | if (ph_list2[n_ph_list2-1].phcode == phonSWITCH) { | ||||
// previous phoneme is also a phonSWITCH, just change its phoneme table number | // previous phoneme is also a phonSWITCH, just change its phoneme table number | ||||
n_ph_list2--; | n_ph_list2--; | ||||
} else { | |||||
} else | |||||
SetPlist2(&ph_list2[n_ph_list2], phonSWITCH); | SetPlist2(&ph_list2[n_ph_list2], phonSWITCH); | ||||
} | |||||
ph_list2[n_ph_list2++].tone_ph = switch_phonemes; // temporary phoneme table number | ph_list2[n_ph_list2++].tone_ph = switch_phonemes; // temporary phoneme table number | ||||
} | } | ||||
} | } | ||||
next_stress = ph->std_length; | next_stress = ph->std_length; | ||||
else { | else { | ||||
// for tone languages, the tone number for a syllable follows the vowel | // for tone languages, the tone number for a syllable follows the vowel | ||||
if (prev_vowel >= 0) { | |||||
if (prev_vowel >= 0) | |||||
ph_list2[prev_vowel].tone_ph = ph_code; | ph_list2[prev_vowel].tone_ph = ph_code; | ||||
} else { | |||||
else | |||||
next_tone = ph_code; // no previous vowel, apply to the next vowel | next_tone = ph_code; // no previous vowel, apply to the next vowel | ||||
} | |||||
} | } | ||||
} else if (ph_code == phonSYLLABIC) { | } else if (ph_code == phonSYLLABIC) { | ||||
// mark the previous phoneme as a syllabic consonant | // mark the previous phoneme as a syllabic consonant | ||||
prev_vowel = n_ph_list2-1; | prev_vowel = n_ph_list2-1; | ||||
ph_list2[prev_vowel].synthflags |= SFLAG_SYLLABLE; | ph_list2[prev_vowel].synthflags |= SFLAG_SYLLABLE; | ||||
ph_list2[prev_vowel].stresslevel = next_stress; | ph_list2[prev_vowel].stresslevel = next_stress; | ||||
} else if (ph_code == phonLENGTHEN) { | |||||
} else if (ph_code == phonLENGTHEN) | |||||
ph_list2[n_ph_list2-1].synthflags |= SFLAG_LENGTHEN; | ph_list2[n_ph_list2-1].synthflags |= SFLAG_LENGTHEN; | ||||
} else if (ph_code == phonEND_WORD) { | |||||
else if (ph_code == phonEND_WORD) { | |||||
// a || symbol in a phoneme string was used to indicate a word boundary | // a || symbol in a phoneme string was used to indicate a word boundary | ||||
// Don't add this phoneme to the list, but make sure the next phoneme has | // Don't add this phoneme to the list, but make sure the next phoneme has | ||||
// a newword indication | // a newword indication | ||||
srcix = source_ix+1; | srcix = source_ix+1; | ||||
} else if (ph_code == phonX1) { | } else if (ph_code == phonX1) { | ||||
// a language specific action | // a language specific action | ||||
if (tr->langopts.param[LOPT_IT_DOUBLING]) { | |||||
if (tr->langopts.param[LOPT_IT_DOUBLING]) | |||||
flags |= FLAG_DOUBLING; | flags |= FLAG_DOUBLING; | ||||
} | |||||
} else { | } else { | ||||
ph_list2[n_ph_list2].phcode = ph_code; | ph_list2[n_ph_list2].phcode = ph_code; | ||||
ph_list2[n_ph_list2].tone_ph = 0; | ph_list2[n_ph_list2].tone_ph = 0; | ||||
stress = next_stress; | stress = next_stress; | ||||
next_stress = 1; // default is 'unstressed' | next_stress = 1; // default is 'unstressed' | ||||
if (stress >= 4) { | |||||
if (stress >= 4) | |||||
any_stressed_words = 1; | any_stressed_words = 1; | ||||
} | |||||
if ((prev_vowel >= 0) && (n_ph_list2-1) != prev_vowel) | if ((prev_vowel >= 0) && (n_ph_list2-1) != prev_vowel) | ||||
ph_list2[n_ph_list2-1].stresslevel = stress; // set stress for previous consonant | ph_list2[n_ph_list2-1].stresslevel = stress; // set stress for previous consonant | ||||
} | } | ||||
} | } | ||||
if (word_flags & FLAG_COMMA_AFTER) { | |||||
if (word_flags & FLAG_COMMA_AFTER) | |||||
SetPlist2(&ph_list2[n_ph_list2++], phonPAUSE_CLAUSE); | SetPlist2(&ph_list2[n_ph_list2++], phonPAUSE_CLAUSE); | ||||
} | |||||
// don't set new-word if there is a hyphen before it | // don't set new-word if there is a hyphen before it | ||||
if ((word_flags & FLAG_HYPHEN) == 0) { | |||||
if ((word_flags & FLAG_HYPHEN) == 0) | |||||
plist2->sourceix = source_ix; | plist2->sourceix = source_ix; | ||||
} | |||||
tr->end_stressed_vowel = 0; | tr->end_stressed_vowel = 0; | ||||
if ((stress >= 4) && (phoneme_tab[ph_list2[n_ph_list2-1].phcode]->type == phVOWEL)) { | |||||
if ((stress >= 4) && (phoneme_tab[ph_list2[n_ph_list2-1].phcode]->type == phVOWEL)) | |||||
tr->end_stressed_vowel = 1; // word ends with a stressed vowel | tr->end_stressed_vowel = 1; // word ends with a stressed vowel | ||||
} | |||||
if (switch_phonemes >= 0) { | if (switch_phonemes >= 0) { | ||||
// this word uses a different phoneme table, now switch back | // this word uses a different phoneme table, now switch back | ||||
int tone; | int tone; | ||||
int tone2; | int tone2; | ||||
if (tr == NULL) { | |||||
if (tr == NULL) | |||||
return NULL; | return NULL; | ||||
} | |||||
p_textinput = (unsigned char *)vp_input; | p_textinput = (unsigned char *)vp_input; | ||||
p_wchar_input = (wchar_t *)vp_input; | p_wchar_input = (wchar_t *)vp_input; | ||||
} | } | ||||
for (p = source; *p != 0; p++) { | for (p = source; *p != 0; p++) { | ||||
if (!isspace2(*p)) { | |||||
if (!isspace2(*p)) | |||||
break; | break; | ||||
} | |||||
} | } | ||||
if (*p == 0) { | if (*p == 0) { | ||||
// No characters except spaces. This is not a sentence. | // No characters except spaces. This is not a sentence. | ||||
if (prev_in_save != 0) { | if (prev_in_save != 0) { | ||||
prev_in = prev_in_save; | prev_in = prev_in_save; | ||||
prev_in_save = 0; | prev_in_save = 0; | ||||
} else if (source_index > 0) { | |||||
} else if (source_index > 0) | |||||
utf8_in2(&prev_in, &source[source_index-1], 1); // prev_in = source[source_index-1]; | utf8_in2(&prev_in, &source[source_index-1], 1); // prev_in = source[source_index-1]; | ||||
} | |||||
prev_source_index = source_index; | prev_source_index = source_index; | ||||
c = ' '; | c = ' '; | ||||
space_inserted = 1; | space_inserted = 1; | ||||
if (!IsBracket(prev_out)) { // ?? perhaps only set FLAG_NOSPACE for . - / (hyphenated words, URLs, etc) | |||||
if (!IsBracket(prev_out)) // ?? perhaps only set FLAG_NOSPACE for . - / (hyphenated words, URLs, etc) | |||||
next_word_flags |= FLAG_NOSPACE; | next_word_flags |= FLAG_NOSPACE; | ||||
} | |||||
} else { | } else { | ||||
if (iswupper2(c)) | if (iswupper2(c)) | ||||
word_flags |= FLAG_FIRST_UPPER; | word_flags |= FLAG_FIRST_UPPER; | ||||
if (((prev_in == '.') || iswalnum(prev_in)) && IsAlpha(next_in)) { | if (((prev_in == '.') || iswalnum(prev_in)) && IsAlpha(next_in)) { | ||||
// between two letters, or in an abbreviation (eg. u.s.a.'s). Consider the apostrophe as part of the word | // between two letters, or in an abbreviation (eg. u.s.a.'s). Consider the apostrophe as part of the word | ||||
single_quoted = 0; | single_quoted = 0; | ||||
} else if ((tr->langopts.param[LOPT_APOSTROPHE] & 1) && IsAlpha(next_in)) { | |||||
} else if ((tr->langopts.param[LOPT_APOSTROPHE] & 1) && IsAlpha(next_in)) | |||||
single_quoted = 0; // apostrophe at start of word is part of the word | single_quoted = 0; // apostrophe at start of word is part of the word | ||||
} else if ((tr->langopts.param[LOPT_APOSTROPHE] & 2) && IsAlpha(prev_in)) { | |||||
else if ((tr->langopts.param[LOPT_APOSTROPHE] & 2) && IsAlpha(prev_in)) | |||||
single_quoted = 0; // apostrophe at end of word is part of the word | single_quoted = 0; // apostrophe at end of word is part of the word | ||||
} else if ((wcschr(tr->char_plus_apostrophe, prev_in) != 0) && (prev_out2 == ' ')) { | |||||
else if ((wcschr(tr->char_plus_apostrophe, prev_in) != 0) && (prev_out2 == ' ')) { | |||||
// consider single character plus apostrophe as a word | // consider single character plus apostrophe as a word | ||||
single_quoted = 0; | single_quoted = 0; | ||||
if (next_in == ' ') { | |||||
if (next_in == ' ') | |||||
source_index++; // skip following space | source_index++; // skip following space | ||||
} | |||||
} else { | } else { | ||||
if ((prev_out == 's') && (single_quoted == 0)) { | if ((prev_out == 's') && (single_quoted == 0)) { | ||||
// looks like apostrophe after an 's' | // looks like apostrophe after an 's' | ||||
c = ' '; | c = ' '; | ||||
} | } | ||||
} | } | ||||
} else if (lookupwchar(breaks, c) != 0) { | |||||
} else if (lookupwchar(breaks, c) != 0) | |||||
c = ' '; // various characters to treat as space | c = ' '; // various characters to treat as space | ||||
} else if (iswdigit(c)) { | |||||
else if (iswdigit(c)) { | |||||
if (tr->langopts.tone_numbers && IsAlpha(prev_out) && !IsDigit(next_in)) { | if (tr->langopts.tone_numbers && IsAlpha(prev_out) && !IsDigit(next_in)) { | ||||
} else if ((prev_out != ' ') && !iswdigit(prev_out)) { | } else if ((prev_out != ' ') && !iswdigit(prev_out)) { | ||||
if ((prev_out != tr->langopts.decimal_sep) || ((decimal_sep_count > 0) && (tr->langopts.decimal_sep == ','))) { | if ((prev_out != tr->langopts.decimal_sep) || ((decimal_sep_count > 0) && (tr->langopts.decimal_sep == ','))) { | ||||
c = ' '; | c = ' '; | ||||
space_inserted = 1; | space_inserted = 1; | ||||
} else { | |||||
} else | |||||
decimal_sep_count = 1; | decimal_sep_count = 1; | ||||
} | |||||
} else if ((prev_out == ' ') && IsAlpha(prev_out2) && !IsAlpha(prev_in)) { | } else if ((prev_out == ' ') && IsAlpha(prev_out2) && !IsAlpha(prev_in)) { | ||||
// insert extra space between a word and a number, to distinguish 'a 2' from 'a2' | // insert extra space between a word and a number, to distinguish 'a 2' from 'a2' | ||||
sbuf[ix++] = ' '; | sbuf[ix++] = ' '; | ||||
continue; // multiple spaces | continue; // multiple spaces | ||||
} | } | ||||
if ((cc == 0x09) || (cc == 0x0a)) { | |||||
if ((cc == 0x09) || (cc == 0x0a)) | |||||
next_word_flags |= FLAG_MULTIPLE_SPACES; // tab or newline, not a simple space | next_word_flags |= FLAG_MULTIPLE_SPACES; // tab or newline, not a simple space | ||||
} | |||||
if (space_inserted) { | if (space_inserted) { | ||||
// count the number of characters since the start of the word | // count the number of characters since the start of the word | ||||
if (pre_pause > 0) { | if (pre_pause > 0) { | ||||
// insert an extra space before the word, to prevent influence from previous word across the pause | // insert an extra space before the word, to prevent influence from previous word across the pause | ||||
for (j = ix; j > words[word_count].start; j--) { | |||||
for (j = ix; j > words[word_count].start; j--) | |||||
sbuf[j] = sbuf[j-1]; | sbuf[j] = sbuf[j-1]; | ||||
} | |||||
sbuf[j] = ' '; | sbuf[j] = ' '; | ||||
words[word_count].start++; | words[word_count].start++; | ||||
ix++; | ix++; | ||||
// Languages with 100000 numbers. Remove thousands separators so that we can insert them again later | // Languages with 100000 numbers. Remove thousands separators so that we can insert them again later | ||||
pn = number_buf; | pn = number_buf; | ||||
while (pn < &number_buf[sizeof(number_buf)-20]) { | while (pn < &number_buf[sizeof(number_buf)-20]) { | ||||
if (iswdigit(*pw)) { | |||||
if (iswdigit(*pw)) | |||||
*pn++ = *pw++; | *pn++ = *pw++; | ||||
} else if ((*pw == tr->langopts.thousands_sep) && (pw[1] == ' ') | |||||
else if ((*pw == tr->langopts.thousands_sep) && (pw[1] == ' ') | |||||
&& iswdigit(pw[2]) && (pw[3] != ' ') && (pw[4] != ' ')) { // don't allow only 1 or 2 digits in the final part | && iswdigit(pw[2]) && (pw[3] != ' ') && (pw[4] != ' ')) { // don't allow only 1 or 2 digits in the final part | ||||
pw += 2; | pw += 2; | ||||
ix++; // skip "word" | ix++; // skip "word" | ||||
if ((nx > 0) && (tr->langopts.break_numbers & (1 << nx))) { | if ((nx > 0) && (tr->langopts.break_numbers & (1 << nx))) { | ||||
memcpy(&num_wtab[nw++], &words[ix], sizeof(WORD_TAB)); // copy the 'words' entry for each word of numbers | memcpy(&num_wtab[nw++], &words[ix], sizeof(WORD_TAB)); // copy the 'words' entry for each word of numbers | ||||
if (tr->langopts.thousands_sep != ' ') { | |||||
if (tr->langopts.thousands_sep != ' ') | |||||
*pn++ = tr->langopts.thousands_sep; | *pn++ = tr->langopts.thousands_sep; | ||||
} | |||||
*pn++ = ' '; | *pn++ = ' '; | ||||
if ((words[ix].flags & FLAG_INDIVIDUAL_DIGITS) == 0) { | if ((words[ix].flags & FLAG_INDIVIDUAL_DIGITS) == 0) { | ||||
pw--; | pw--; | ||||
memcpy(&num_wtab[nw], &words[ix], sizeof(WORD_TAB)*2); // the original number word, and the word after it | memcpy(&num_wtab[nw], &words[ix], sizeof(WORD_TAB)*2); // the original number word, and the word after it | ||||
for (j = 1; j <= nw; j++) { | |||||
for (j = 1; j <= nw; j++) | |||||
num_wtab[j].flags &= ~(FLAG_MULTIPLE_SPACES | FLAG_EMBEDDED); // don't use these flags for subsequent parts when splitting a number | num_wtab[j].flags &= ~(FLAG_MULTIPLE_SPACES | FLAG_EMBEDDED); // don't use these flags for subsequent parts when splitting a number | ||||
} | |||||
// include the next few characters, in case there are an ordinal indicator or other suffix | // include the next few characters, in case there are an ordinal indicator or other suffix | ||||
memcpy(pn, pw, 16); | memcpy(pn, pw, 16); | ||||
} | } | ||||
n_ph_list2 += 2; | n_ph_list2 += 2; | ||||
if (count_words == 0) { | |||||
if (count_words == 0) | |||||
clause_pause = 0; | clause_pause = 0; | ||||
} | |||||
if (Eof() && ((word_count == 0) || (option_endpause == 0))) { | |||||
if (Eof() && ((word_count == 0) || (option_endpause == 0))) | |||||
clause_pause = 10; | clause_pause = 10; | ||||
} | |||||
MakePhonemeList(tr, clause_pause, new_sentence2); | MakePhonemeList(tr, clause_pause, new_sentence2); | ||||
phoneme_list[N_PHONEME_LIST].ph = NULL; // recognize end of phoneme_list array, in Generate() | phoneme_list[N_PHONEME_LIST].ph = NULL; // recognize end of phoneme_list array, in Generate() | ||||
*tone_out = tone; | *tone_out = tone; | ||||
new_sentence = 0; | new_sentence = 0; | ||||
if (terminator & CLAUSE_BIT_SENTENCE) { | |||||
if (terminator & CLAUSE_BIT_SENTENCE) | |||||
new_sentence = 1; // next clause is a new sentence | new_sentence = 1; // next clause is a new sentence | ||||
} | |||||
if (voice_change != NULL) { | if (voice_change != NULL) { | ||||
InitText2(); | InitText2(); | ||||
if ((control & espeakKEEP_NAMEDATA) == 0) { | |||||
if ((control & espeakKEEP_NAMEDATA) == 0) | |||||
InitNamedata(); | InitNamedata(); | ||||
} | |||||
} | } |
langix += len; | langix += len; | ||||
n_languages++; | n_languages++; | ||||
} | } | ||||
} else if (memcmp(linebuf, "gender", 6) == 0) { | |||||
} else if (memcmp(linebuf, "gender", 6) == 0) | |||||
sscanf(&linebuf[6], "%s %d", vgender, &age); | sscanf(&linebuf[6], "%s %d", vgender, &age); | ||||
} else if (memcmp(linebuf, "variants", 8) == 0) { | |||||
else if (memcmp(linebuf, "variants", 8) == 0) | |||||
sscanf(&linebuf[8], "%d", &n_variants); | sscanf(&linebuf[8], "%d", &n_variants); | ||||
} | |||||
} | } | ||||
languages[langix++] = 0; | languages[langix++] = 0; | ||||
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 | ||||
} | |||||
p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1); | p = (char *)calloc(sizeof(espeak_VOICE) + langix + strlen(fname) + strlen(vname) + 3, 1); | ||||
voice_data = (espeak_VOICE *)p; | voice_data = (espeak_VOICE *)p; | ||||
int c; | int c; | ||||
unsigned int value = 0; | unsigned int value = 0; | ||||
for (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; | ||||
} | } | ||||
if (GetFileLength(buf) <= 0) { | if (GetFileLength(buf) <= 0) { | ||||
// then look in the appropriate subdirectory | // then look in the appropriate subdirectory | ||||
if ((voicename[0] == 'm') && (voicename[1] == 'b')) { | |||||
if ((voicename[0] == 'm') && (voicename[1] == 'b')) | |||||
voice_dir = "mb"; // mbrola voices | voice_dir = "mb"; // mbrola voices | ||||
} else { | |||||
else { | |||||
sprintf(name2, "%s ", voicename); | sprintf(name2, "%s ", voicename); | ||||
if (strstr(voices_europe, voicename) != NULL) | if (strstr(voices_europe, voicename) != NULL) | ||||
voice_dir = "europe"; | voice_dir = "europe"; | ||||
((sscanf(p, "%d %d %d", &ix, &value, &value2) >= 2) && (ix < N_LOPTS))) { | ((sscanf(p, "%d %d %d", &ix, &value, &value2) >= 2) && (ix < N_LOPTS))) { | ||||
langopts->param[ix] = value; | langopts->param[ix] = value; | ||||
langopts->param2[ix] = value2; | langopts->param2[ix] = value2; | ||||
} else { | |||||
} else | |||||
fprintf(stderr, "Bad voice option: %s %s\n", buf, p); | fprintf(stderr, "Bad voice option: %s %s\n", buf, p); | ||||
} | |||||
break; | break; | ||||
case V_ECHO: | case V_ECHO: | ||||
name1[0] = name2[0] = 0; | name1[0] = name2[0] = 0; | ||||
sscanf(p, "%s %s", name1, name2); | sscanf(p, "%s %s", name1, name2); | ||||
if (strcmp(name1, "latin") == 0) { | |||||
if (strcmp(name1, "latin") == 0) | |||||
strncpy0(langopts->ascii_language, name2, sizeof(langopts->ascii_language)); | strncpy0(langopts->ascii_language, name2, sizeof(langopts->ascii_language)); | ||||
} else if ((alphabet = AlphabetFromName(name1)) != 0) { | |||||
else if ((alphabet = AlphabetFromName(name1)) != 0) { | |||||
langopts->alt_alphabet = alphabet->offset; | langopts->alt_alphabet = alphabet->offset; | ||||
langopts->alt_alphabet_lang = StringToWord2(name2); | langopts->alt_alphabet_lang = StringToWord2(name2); | ||||
} else { | |||||
} else | |||||
fprintf(stderr, "alphabet name '%s' not found\n", name1); | fprintf(stderr, "alphabet name '%s' not found\n", name1); | ||||
} | |||||
} | } | ||||
break; | break; | ||||
case V_DICTDIALECT: | case V_DICTDIALECT: | ||||
// specify a dialect to use for foreign words, eg, en-us for _^_EN | // specify a dialect to use for foreign words, eg, en-us for _^_EN | ||||
if (sscanf(p, "%s", name1) == 1) { | if (sscanf(p, "%s", name1) == 1) { | ||||
if ((ix = LookupMnem(dict_dialects, name1)) > 0) { | |||||
if ((ix = LookupMnem(dict_dialects, name1)) > 0) | |||||
langopts->dict_dialect |= (1 << ix); | langopts->dict_dialect |= (1 << ix); | ||||
} else { | |||||
else | |||||
fprintf(stderr, "dictdialect name '%s' not recognized\n", name1); | fprintf(stderr, "dictdialect name '%s' not recognized\n", name1); | ||||
} | |||||
} | } | ||||
break; | break; | ||||
default: | default: | ||||
if ((key & 0xff00) == 0x100) { | |||||
if ((key & 0xff00) == 0x100) | |||||
sscanf(p, "%d", &langopts->param[key &0xff]); | sscanf(p, "%d", &langopts->param[key &0xff]); | ||||
} else { | |||||
else | |||||
fprintf(stderr, "Bad voice attribute: %s\n", buf); | fprintf(stderr, "Bad voice attribute: %s\n", buf); | ||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
voice->width2[ix] = voice->width[ix]; | voice->width2[ix] = voice->width[ix]; | ||||
} | } | ||||
if (tone_only) { | |||||
if (tone_only) | |||||
new_translator = translator; | new_translator = translator; | ||||
} else { | |||||
else { | |||||
if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) { | if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) { | ||||
fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name); | fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name); | ||||
ix = 0; | ix = 0; | ||||
langopts = &new_translator->langopts; | langopts = &new_translator->langopts; | ||||
if ((value = langopts->param[LOPT_LENGTH_MODS]) != 0) { | |||||
if ((value = langopts->param[LOPT_LENGTH_MODS]) != 0) | |||||
SetLengthMods(new_translator, value); | SetLengthMods(new_translator, value); | ||||
} | |||||
voice->width[0] = (voice->width[0] * 105)/100; | voice->width[0] = (voice->width[0] * 105)/100; | ||||
if (!tone_only) { | |||||
if (!tone_only) | |||||
translator = new_translator; | translator = new_translator; | ||||
} | |||||
// relative lengths of different stress syllables | // relative lengths of different stress syllables | ||||
for (ix = 0; ix < stress_lengths_set; ix++) { | |||||
for (ix = 0; ix < stress_lengths_set; ix++) | |||||
translator->stress_lengths[ix] = stress_lengths[ix]; | translator->stress_lengths[ix] = stress_lengths[ix]; | ||||
} | |||||
for (ix = 0; ix < stress_add_set; ix++) { | |||||
for (ix = 0; ix < stress_add_set; ix++) | |||||
translator->stress_lengths[ix] += stress_add[ix]; | translator->stress_lengths[ix] += stress_add[ix]; | ||||
} | |||||
for (ix = 0; ix < stress_amps_set; ix++) { | for (ix = 0; ix < stress_amps_set; ix++) { | ||||
translator->stress_amps[ix] = stress_amps[ix]; | translator->stress_amps[ix] = stress_amps[ix]; | ||||
translator->stress_amps_r[ix] = stress_amps[ix] -1; | translator->stress_amps_r[ix] = stress_amps[ix] -1; | ||||
// The voice name has a +variant suffix | // The voice name has a +variant suffix | ||||
variant_num = 0; | variant_num = 0; | ||||
*p++ = 0; // delete the suffix from the voice name | *p++ = 0; // delete the suffix from the voice name | ||||
if (IsDigit09(*p)) { | |||||
if (IsDigit09(*p)) | |||||
variant_num = atoi(p); // variant number | variant_num = atoi(p); // variant number | ||||
} else { | |||||
else { | |||||
// voice variant name, not number | // voice variant name, not number | ||||
sprintf(variant_name, "%s%s", variant_prefix, p); | sprintf(variant_name, "%s%s", variant_prefix, p); | ||||
} | } | ||||
if ((v = LoadVoice(buf, 0)) == NULL) | if ((v = LoadVoice(buf, 0)) == NULL) | ||||
return NULL; | return NULL; | ||||
if (variant_name[0] != 0) { | |||||
if (variant_name[0] != 0) | |||||
v = LoadVoice(variant_name, 2); | v = LoadVoice(variant_name, 2); | ||||
} | |||||
return v; | return v; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
if (spec_n_parts == 0) { | |||||
if (spec_n_parts == 0) | |||||
score = 100; | score = 100; | ||||
} else { | |||||
else { | |||||
if ((*p == 0) && (strcmp(spec_language, "variants") == 0)) { | if ((*p == 0) && (strcmp(spec_language, "variants") == 0)) { | ||||
// match on a voice with no languages if the required language is "variants" | // match on a voice with no languages if the required language is "variants" | ||||
score = 100; | score = 100; | ||||
if ((c2 = p[ix]) == '-') | if ((c2 = p[ix]) == '-') | ||||
c2 = 0; | c2 = 0; | ||||
if (c1 != c2) { | |||||
if (c1 != c2) | |||||
matching = 0; | matching = 0; | ||||
} | |||||
if (p[ix] == '-') { | if (p[ix] == '-') { | ||||
n_parts++; | n_parts++; | ||||
if (strcmp(voice_spec->name, voice->name) == 0) { | if (strcmp(voice_spec->name, voice->name) == 0) { | ||||
// match on voice name | // match on voice name | ||||
score += 500; | score += 500; | ||||
} else if (strcmp(voice_spec->name, voice->identifier) == 0) { | |||||
} else if (strcmp(voice_spec->name, voice->identifier) == 0) | |||||
score += 400; | score += 400; | ||||
} | |||||
} | } | ||||
if (((voice_spec->gender == 1) || (voice_spec->gender == 2)) && | if (((voice_spec->gender == 1) || (voice_spec->gender == 2)) && | ||||
score -= 50; | score -= 50; | ||||
} | } | ||||
if ((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12)) { | |||||
if ((voice_spec->age <= 12) && (voice->gender == 2) && (voice->age > 12)) | |||||
score += 5; // give some preference for non-child female voice if a child is requested | score += 5; // give some preference for non-child female voice if a child is requested | ||||
} | |||||
if (voice->age != 0) { | if (voice->age != 0) { | ||||
if (voice_spec->age == 0) | if (voice_spec->age == 0) | ||||
break; | break; | ||||
} else { | } else { | ||||
id = voices[ix]->identifier; | id = voices[ix]->identifier; | ||||
if (strcmp(name, id) == 0) { | |||||
if (strcmp(name, id) == 0) | |||||
match_fname = ix; // matching identifier, use this if no matching name | match_fname = ix; // matching identifier, use this if no matching name | ||||
} else if (strcmp(last_part, &id[strlen(id)-last_part_len]) == 0) { | |||||
else if (strcmp(last_part, &id[strlen(id)-last_part_len]) == 0) | |||||
match_fname2 = ix; | match_fname2 = ix; | ||||
} | |||||
} | } | ||||
} | } | ||||
// is the main voice the required gender? | // is the main voice the required gender? | ||||
skip = 0; | skip = 0; | ||||
if ((gender != 0) && (vp->gender != gender)) { | |||||
if ((gender != 0) && (vp->gender != gender)) | |||||
skip = 1; | skip = 1; | ||||
} | |||||
if ((ix2 == 0) && aged && (vp->age < AGE_OLD)) { | |||||
if ((ix2 == 0) && aged && (vp->age < AGE_OLD)) | |||||
skip = 1; | skip = 1; | ||||
} | |||||
if (skip == 0) { | |||||
if (skip == 0) | |||||
voices2[ix2++] = vp; | voices2[ix2++] = vp; | ||||
} | |||||
for (j = 0; (j < vp->xx1) && (n_variants < N_VOICE_VARIANTS);) { | for (j = 0; (j < vp->xx1) && (n_variants < N_VOICE_VARIANTS);) { | ||||
if ((variant_number = *p) == 0) { | if ((variant_number = *p) == 0) { | ||||
while (regs.r[3] > 0) { | while (regs.r[3] > 0) { | ||||
error = _kernel_swi(0x0c+0x20000, ®s, ®s); /* OS_GBPB 10, read directory entries */ | error = _kernel_swi(0x0c+0x20000, ®s, ®s); /* OS_GBPB 10, read directory entries */ | ||||
if ((error != NULL) || (regs.r[3] == 0)) { | |||||
if ((error != NULL) || (regs.r[3] == 0)) | |||||
break; | break; | ||||
} | |||||
type = (int *)(&buf[16]); | type = (int *)(&buf[16]); | ||||
len = strlen(&buf[20]); | len = strlen(&buf[20]); | ||||
sprintf(fname, "%s.%s", path, &buf[20]); | sprintf(fname, "%s.%s", path, &buf[20]); | ||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, &buf[20]); | voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, &buf[20]); | ||||
fclose(f_voice); | fclose(f_voice); | ||||
if (voice_data != NULL) { | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | voices_list[n_voices_list++] = voice_data; | ||||
} | |||||
} | } | ||||
} | } | ||||
#else | #else | ||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName); | voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, FindFileData.cFileName); | ||||
fclose(f_voice); | fclose(f_voice); | ||||
if (voice_data != NULL) { | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | voices_list[n_voices_list++] = voice_data; | ||||
} | |||||
} | } | ||||
} | } | ||||
} while (FindNextFileA(hFind, &FindFileData) != 0); | } while (FindNextFileA(hFind, &FindFileData) != 0); | ||||
voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, ent->d_name); | voice_data = ReadVoiceFile(f_voice, fname+len_path_voices, ent->d_name); | ||||
fclose(f_voice); | fclose(f_voice); | ||||
if (voice_data != NULL) { | |||||
if (voice_data != NULL) | |||||
voices_list[n_voices_list++] = voice_data; | voices_list[n_voices_list++] = voice_data; | ||||
} | |||||
} | } | ||||
} | } | ||||
closedir(dir); | closedir(dir); | ||||
// This may avoid the need to call espeak_ListVoices(). | // This may avoid the need to call espeak_ListVoices(). | ||||
if (LoadVoice(buf, 1) != NULL) { | if (LoadVoice(buf, 1) != NULL) { | ||||
if (variant_name[0] != 0) { | |||||
if (variant_name[0] != 0) | |||||
LoadVoice(variant_name, 2); | LoadVoice(variant_name, 2); | ||||
} | |||||
DoVoiceChange(voice); | DoVoiceChange(voice); | ||||
voice_selector.languages = voice->language_name; | voice_selector.languages = voice->language_name; | ||||
if ((v = SelectVoiceByName(voices_list, buf)) != NULL) { | if ((v = SelectVoiceByName(voices_list, buf)) != NULL) { | ||||
if (LoadVoice(v->identifier, 0) != NULL) { | if (LoadVoice(v->identifier, 0) != NULL) { | ||||
if (variant_name[0] != 0) { | |||||
if (variant_name[0] != 0) | |||||
LoadVoice(variant_name, 2); | LoadVoice(variant_name, 2); | ||||
} | |||||
DoVoiceChange(voice); | DoVoiceChange(voice); | ||||
voice_selector.languages = voice->language_name; | voice_selector.languages = voice->language_name; | ||||
SetVoiceStack(&voice_selector, variant_name); | SetVoiceStack(&voice_selector, variant_name); | ||||
j = 0; | j = 0; | ||||
for (ix = 0; (v = voices_list[ix]) != NULL; ix++) { | 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; | ||||
} | |||||
} | } | ||||
voices[j] = NULL; | voices[j] = NULL; | ||||
} | } |
&& (aWrite >= myBuffer) | && (aWrite >= myBuffer) | ||||
&& (aWrite <= myBuffer + BUFFER_LENGTH)); | && (aWrite <= myBuffer + BUFFER_LENGTH)); | ||||
if (aRead < aWrite) { | |||||
if (aRead < aWrite) | |||||
used = aWrite - aRead; | used = aWrite - aRead; | ||||
} else { | |||||
else | |||||
used = aWrite + BUFFER_LENGTH - aRead; | used = aWrite + BUFFER_LENGTH - aRead; | ||||
} | |||||
SHOW("get_used_mem > %d\n", used); | SHOW("get_used_mem > %d\n", used); | ||||
return used; | return used; | ||||
memset(p, 0, n - aUsedMem); | memset(p, 0, n - aUsedMem); | ||||
myRead = aWrite; | myRead = aWrite; | ||||
} | } | ||||
} else { // myRead > aWrite | |||||
} else { | |||||
if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n) { | if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n) { | ||||
memcpy(outputBuffer, myRead, n); | memcpy(outputBuffer, myRead, n); | ||||
myRead += n; | myRead += n; | ||||
SHOW("pa_callback > myRead=%x\n", (int)myRead); | SHOW("pa_callback > myRead=%x\n", (int)myRead); | ||||
#ifdef ARCH_BIG | #ifdef ARCH_BIG | ||||
{ | |||||
// BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer | |||||
int c; | |||||
unsigned char *out_ptr; | |||||
unsigned char *out_end; | |||||
out_ptr = (unsigned char *)outputBuffer; | |||||
out_end = out_ptr + framesPerBuffer*2 * out_channels; | |||||
while (out_ptr < out_end) { | |||||
c = out_ptr[0]; | |||||
out_ptr[0] = out_ptr[1]; | |||||
out_ptr[1] = c; | |||||
out_ptr += 2; | |||||
} | |||||
// BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer | |||||
int c; | |||||
unsigned char *out_ptr; | |||||
unsigned char *out_end; | |||||
out_ptr = (unsigned char *)outputBuffer; | |||||
out_end = out_ptr + framesPerBuffer*2 * out_channels; | |||||
while (out_ptr < out_end) { | |||||
c = out_ptr[0]; | |||||
out_ptr[0] = out_ptr[1]; | |||||
out_ptr[1] = c; | |||||
out_ptr += 2; | |||||
} | } | ||||
#endif | #endif | ||||
{ | { | ||||
ENTER("wave_flush"); | ENTER("wave_flush"); | ||||
if (my_stream_could_start) { | |||||
if (my_stream_could_start) | |||||
start_stream(); | start_stream(); | ||||
} | |||||
} | } | ||||
static int wave_open_sound() | static int wave_open_sound() | ||||
active = Pa_IsStreamActive(pa_stream); | active = Pa_IsStreamActive(pa_stream); | ||||
#endif | #endif | ||||
if (active == 1) { | |||||
if (active == 1) | |||||
SHOW_TIME("wave_open_sound > already active"); | SHOW_TIME("wave_open_sound > already active"); | ||||
return 0; | return 0; | ||||
} | |||||
if (active < 0) { | if (active < 0) { | ||||
out_channels = 1; | out_channels = 1; | ||||
for (i = 0; i < numDevices; i++) { | for (i = 0; i < numDevices; i++) { | ||||
deviceInfo = Pa_GetDeviceInfo(i); | deviceInfo = Pa_GetDeviceInfo(i); | ||||
if (deviceInfo == NULL) { | |||||
if (deviceInfo == NULL) | |||||
break; | break; | ||||
} | |||||
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); | const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); | ||||
if (hostInfo && hostInfo->type == paALSA) { | if (hostInfo && hostInfo->type == paALSA) { | ||||
} | } | ||||
} | } | ||||
if (selectedDeviceInfo) { | |||||
if (selectedDeviceInfo) | |||||
update_output_parameters(selectedIndex, selectedDeviceInfo); | update_output_parameters(selectedIndex, selectedDeviceInfo); | ||||
} else { | |||||
else { | |||||
i = Pa_GetDefaultOutputDevice(); | i = Pa_GetDefaultOutputDevice(); | ||||
deviceInfo = Pa_GetDeviceInfo(i); | deviceInfo = Pa_GetDeviceInfo(i); | ||||
update_output_parameters(i, deviceInfo); | update_output_parameters(i, deviceInfo); | ||||
// PortAudio sound output library | // PortAudio sound output library | ||||
err = Pa_Initialize(); | err = Pa_Initialize(); | ||||
pa_init_err = err; | pa_init_err = err; | ||||
if (err != paNoError) { | |||||
if (err != paNoError) | |||||
SHOW_TIME("wave_init > Failed to initialise the PortAudio sound"); | SHOW_TIME("wave_init > Failed to initialise the PortAudio sound"); | ||||
} | |||||
return err == paNoError; | return err == paNoError; | ||||
} | } | ||||
a_dest[2*i+1] = a_src[i]; | a_dest[2*i+1] = a_src[i]; | ||||
} | } | ||||
bytes_written = 2*theSizeInBytes; | bytes_written = 2*theSizeInBytes; | ||||
} // end if(out_channels==1) | |||||
} // end if ((src != NULL) && dest != NULL) | |||||
} | |||||
} | |||||
return bytes_written; | return bytes_written; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
my_stream_could_start = 1; | my_stream_could_start = 1; | ||||
} else if (!wave_is_busy(NULL)) { | |||||
} else if (!wave_is_busy(NULL)) | |||||
my_stream_could_start = 1; | my_stream_could_start = 1; | ||||
} | |||||
assert(BUFFER_LENGTH >= bytes_to_write); | assert(BUFFER_LENGTH >= bytes_to_write); | ||||
if (myWrite >= myBuffer + BUFFER_LENGTH) { | |||||
if (myWrite >= myBuffer + BUFFER_LENGTH) | |||||
myWrite = myBuffer; | myWrite = myBuffer; | ||||
} // end if (myWrite >= myBuffer + BUFFER_LENGTH) | |||||
size_t aTotalFreeMem = 0; | size_t aTotalFreeMem = 0; | ||||
char *aRead = myRead; | char *aRead = myRead; | ||||
aRead = myRead; | aRead = myRead; | ||||
// write pointer is before read pointer? | // write pointer is before read pointer? | ||||
if (myWrite >= aRead) { | |||||
if (myWrite >= aRead) | |||||
aTotalFreeMem = aRead + BUFFER_LENGTH - myWrite; | aTotalFreeMem = aRead + BUFFER_LENGTH - myWrite; | ||||
} else { // read pointer is before write pointer! | |||||
else // read pointer is before write pointer! | |||||
aTotalFreeMem = aRead - myWrite; | aTotalFreeMem = aRead - myWrite; | ||||
} // end if (myWrite >= aRead) | |||||
if (aTotalFreeMem > 1) { | if (aTotalFreeMem > 1) { | ||||
// -1 because myWrite must be different of aRead | // -1 because myWrite must be different of aRead | ||||
// otherwise buffer would be considered as empty | // otherwise buffer would be considered as empty | ||||
aTotalFreeMem -= 1; | aTotalFreeMem -= 1; | ||||
} // end if (aTotalFreeMem>1) | |||||
} | |||||
if (aTotalFreeMem >= bytes_to_write) { | |||||
if (aTotalFreeMem >= bytes_to_write) | |||||
break; | break; | ||||
} // end if (aTotalFreeMem >= bytes_to_write) | |||||
SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem); | SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem); | ||||
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite); | SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite); | ||||
usleep(10000); | usleep(10000); | ||||
} // end while (1) | |||||
} | |||||
aRead = myRead; | aRead = myRead; | ||||
copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem); | copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem); | ||||
myWrite = myBuffer; | myWrite = myBuffer; | ||||
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem); | myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem); | ||||
} // end if (out_channels == 2) | |||||
} // end if (aFreeMem >= bytes_to_write) | |||||
} // if (myWrite >= aRead) | |||||
else { // read pointer is ahead the write pointer | |||||
} | |||||
} | |||||
} else { // read pointer is ahead the write pointer | |||||
SHOW_TIME("wave_write > myWrite <= aRead"); | SHOW_TIME("wave_write > myWrite <= aRead"); | ||||
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize); | myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize); | ||||
} // end if (myWrite >= aRead) | |||||
} | |||||
bytes_written = bytes_to_write; | bytes_written = bytes_to_write; | ||||
myWritePosition += theSize/sizeof(uint16_t); // add number of samples | myWritePosition += theSize/sizeof(uint16_t); // add number of samples | ||||
if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) { | |||||
if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) | |||||
start_stream(); | start_stream(); | ||||
} // end if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) | |||||
SHOW_TIME("wave_write > LEAVE"); | SHOW_TIME("wave_write > LEAVE"); | ||||
// TBD: take in account time suplied by portaudio V18 API | // TBD: take in account time suplied by portaudio V18 API | ||||
a_time = sample - myReadPosition; | a_time = sample - myReadPosition; | ||||
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; | a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; | ||||
} else { | |||||
} else | |||||
a_time = 0; | a_time = 0; | ||||
} | |||||
SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time); | SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time); | ||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
assert(gettimeofday(&tv, NULL) != -1); | assert(gettimeofday(&tv, NULL) != -1); | ||||
ts->tv_sec = tv.tv_sec; | ts->tv_sec = tv.tv_sec; | ||||
void add_time_in_ms(struct timespec *ts, int time_in_ms) | void add_time_in_ms(struct timespec *ts, int time_in_ms) | ||||
{ | { | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | ||||
while (t_ns >= ONE_BILLION) { | while (t_ns >= ONE_BILLION) { |
for (;;) { | for (;;) { | ||||
CHECK_DEAD_GOTO(fail, 1); | CHECK_DEAD_GOTO(fail, 1); | ||||
if ((i = pa_stream_get_timing_info(stream))) { | |||||
if ((i = pa_stream_get_timing_info(stream))) | |||||
break; | break; | ||||
} | |||||
if (pa_context_errno(context) != PA_ERR_NODATA) { | if (pa_context_errno(context) != PA_ERR_NODATA) { | ||||
SHOW("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context))); | SHOW("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context))); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
SHOW_TIME("pa_threaded_mainloop_wait (ret)"); | SHOW_TIME("pa_threaded_mainloop_wait (ret)"); | ||||
if (!success) { | |||||
if (!success) | |||||
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context))); | ||||
} else { | |||||
else | |||||
ret = PULSE_OK; | ret = PULSE_OK; | ||||
} | |||||
fail: | fail: | ||||
SHOW_TIME("pa_operation_unref (call)"); | SHOW_TIME("pa_operation_unref (call)"); | ||||
pa_threaded_mainloop_free(mainloop); | pa_threaded_mainloop_free(mainloop); | ||||
mainloop = NULL; | mainloop = NULL; | ||||
} | } | ||||
} else { | |||||
} else | |||||
pulse_close(); | pulse_close(); | ||||
} | |||||
SHOW_TIME("pulse_open (ret false)"); | SHOW_TIME("pulse_open (ret false)"); | ||||
} | } | ||||
// TBD: check if really helpful | // TBD: check if really helpful | ||||
if (aTotalFreeMem >= MAXLENGTH*2) { | |||||
if (aTotalFreeMem >= MAXLENGTH*2) | |||||
aTotalFreeMem = MAXLENGTH*2; | aTotalFreeMem = MAXLENGTH*2; | ||||
} | |||||
SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write); | ||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
assert(gettimeofday(&tv, NULL) != -1); | assert(gettimeofday(&tv, NULL) != -1); | ||||
ts->tv_sec = tv.tv_sec; | ts->tv_sec = tv.tv_sec; | ||||
void add_time_in_ms(struct timespec *ts, int time_in_ms) | void add_time_in_ms(struct timespec *ts, int time_in_ms) | ||||
{ | { | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | ||||
while (t_ns >= ONE_BILLION) { | while (t_ns >= ONE_BILLION) { |
/* | /* | ||||
* Copyright (C) 2008, Sun Microsystems, Inc. | * Copyright (C) 2008, Sun Microsystems, Inc. | ||||
* Copyright (C) 2015 Reece H. Dunn | |||||
* eSpeak driver for Solaris Audio Device Architecture (SADA) | * eSpeak driver for Solaris Audio Device Architecture (SADA) | ||||
* Written by Willie Walker, based on the eSpeak PulseAudio driver | * Written by Willie Walker, based on the eSpeak PulseAudio driver | ||||
* from Gilles Casse | * from Gilles Casse | ||||
SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd); | SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd); | ||||
if (sun_audio_fd < 0) { | |||||
if (sun_audio_fd < 0) | |||||
return 0; | return 0; | ||||
} | |||||
ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); | ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); | ||||
SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size); | SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size); | ||||
} | } | ||||
#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN | #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN | ||||
{ | |||||
// BIG-ENDIAN, swap the order of bytes in each sound sample | |||||
int c; | |||||
char *out_ptr; | |||||
char *out_end; | |||||
out_ptr = (char *)theMono16BitsWaveBuffer; | |||||
out_end = out_ptr + theSize; | |||||
while (out_ptr < out_end) { | |||||
c = out_ptr[0]; | |||||
out_ptr[0] = out_ptr[1]; | |||||
out_ptr[1] = c; | |||||
out_ptr += 2; | |||||
} | |||||
// BIG-ENDIAN, swap the order of bytes in each sound sample | |||||
int c; | |||||
char *out_ptr; | |||||
char *out_end; | |||||
out_ptr = (char *)theMono16BitsWaveBuffer; | |||||
out_end = out_ptr + theSize; | |||||
while (out_ptr < out_end) { | |||||
c = out_ptr[0]; | |||||
out_ptr[0] = out_ptr[1]; | |||||
out_ptr[1] = c; | |||||
out_ptr += 2; | |||||
} | } | ||||
#endif | #endif | ||||
// | // | ||||
total_samples_sent += num / 2; | total_samples_sent += num / 2; | ||||
if (num < theSize) { | |||||
if (num < theSize) | |||||
SHOW("ERROR: wave_write only wrote %d of %d bytes\n", num, theSize); | SHOW("ERROR: wave_write only wrote %d of %d bytes\n", num, theSize); | ||||
} else { | |||||
else | |||||
SHOW("wave_write wrote %d bytes\n", theSize); | SHOW("wave_write wrote %d bytes\n", theSize); | ||||
} | |||||
SHOW_TIME("wave_write > LEAVE"); | SHOW_TIME("wave_write > LEAVE"); | ||||
return num; | return num; | ||||
int wave_is_busy(void *theHandler) | int wave_is_busy(void *theHandler) | ||||
{ | { | ||||
uint32_t time; | uint32_t time; | ||||
if (total_samples_sent >= 1) { | |||||
if (total_samples_sent >= 1) | |||||
wave_get_remaining_time(total_samples_sent - 1, &time); | wave_get_remaining_time(total_samples_sent - 1, &time); | ||||
} else { | |||||
else | |||||
time = 0; | time = 0; | ||||
} | |||||
return time != 0; | return time != 0; | ||||
} | } | ||||
{ | { | ||||
struct timeval tv; | struct timeval tv; | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
assert(gettimeofday(&tv, NULL) != -1); | assert(gettimeofday(&tv, NULL) != -1); | ||||
ts->tv_sec = tv.tv_sec; | ts->tv_sec = tv.tv_sec; | ||||
void add_time_in_ms(struct timespec *ts, int time_in_ms) | void add_time_in_ms(struct timespec *ts, int time_in_ms) | ||||
{ | { | ||||
if (!ts) { | |||||
if (!ts) | |||||
return; | return; | ||||
} | |||||
uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; | ||||
while (t_ns >= ONE_BILLION) { | while (t_ns >= ONE_BILLION) { |
pa_stream = NULL; | pa_stream = NULL; | ||||
return 1; | return 1; | ||||
} | } | ||||
} else { | |||||
} else | |||||
WavegenOpenSound(); // still items in the queue, shouldn't be closed | WavegenOpenSound(); // still items in the queue, shouldn't be closed | ||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
h = ((p->freq - p->left) / pitch) + 1; | h = ((p->freq - p->left) / pitch) + 1; | ||||
if (h <= 0) h = 1; | if (h <= 0) h = 1; | ||||
for (f = pitch*h; f < fp; f += pitch) { | |||||
for (f = pitch*h; f < fp; f += pitch) | |||||
htab[h++] += pk_shape[(fp-f)/(p->left>>8)] * p->height; | htab[h++] += pk_shape[(fp-f)/(p->left>>8)] * p->height; | ||||
} | |||||
for (; f < fhi; f += pitch) { | |||||
for (; f < fhi; f += pitch) | |||||
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 | |||||
y = peaks[1].height * 10; // addition as a multiple of 1/256s | |||||
h2 = (1000<<16)/pitch; // decrease until 1000Hz | |||||
if (h2 > 0) { | |||||
x = y/h2; | |||||
h = 1; | |||||
while (y > 0) { | |||||
htab[h++] += y; | |||||
y -= x; | |||||
} | |||||
int y; | |||||
int h2; | |||||
// increase bass | |||||
y = peaks[1].height * 10; // addition as a multiple of 1/256s | |||||
h2 = (1000<<16)/pitch; // decrease until 1000Hz | |||||
if (h2 > 0) { | |||||
x = y/h2; | |||||
h = 1; | |||||
while (y > 0) { | |||||
htab[h++] += y; | |||||
y -= x; | |||||
} | } | ||||
} | } | ||||
x = htab[h] >> 15; | x = htab[h] >> 15; | ||||
htab[h] = (x * x) >> 8; | htab[h] = (x * x) >> 8; | ||||
if ((ix = (f >> 19)) < N_TONE_ADJUST) { | |||||
if ((ix = (f >> 19)) < N_TONE_ADJUST) | |||||
htab[h] = (htab[h] * wvoice->tone_adjust[ix]) >> 13; // index tone_adjust with Hz/8 | htab[h] = (htab[h] * wvoice->tone_adjust[ix]) >> 13; // index tone_adjust with Hz/8 | ||||
} | |||||
} | } | ||||
// adjust the amplitude of the first harmonic, affects tonal quality | // adjust the amplitude of the first harmonic, affects tonal quality | ||||
// calc intermediate increments of LF harmonics | // calc intermediate increments of LF harmonics | ||||
if (control & 1) { | if (control & 1) { | ||||
for (h = 1; h < N_LOWHARM; h++) { | |||||
for (h = 1; h < N_LOWHARM; h++) | |||||
harm_inc[h] = (htab[h] - harmspect[h]) >> 3; | harm_inc[h] = (htab[h] - harmspect[h]) >> 3; | ||||
} | |||||
} | } | ||||
return hmax; // highest harmonic number | return hmax; // highest harmonic number | ||||
if (ix < 3) { | if (ix < 3) { | ||||
peaks[ix].right1 += peaks[ix].right_inc; | peaks[ix].right1 += peaks[ix].right_inc; | ||||
peaks[ix].right = (int)peaks[ix].right1; | peaks[ix].right = (int)peaks[ix].right1; | ||||
} else { | |||||
} else | |||||
peaks[ix].right = peaks[ix].left; | peaks[ix].right = peaks[ix].left; | ||||
} | |||||
} | } | ||||
for (; ix < 8; ix++) { | for (; ix < 8; ix++) { | ||||
// formants 6,7,8 don't have a width parameter | // formants 6,7,8 don't have a width parameter | ||||
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 (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); | ||||
} | |||||
#endif | #endif | ||||
} | } | ||||
SetBreath(); | SetBreath(); | ||||
} else if ((samplecount & 0x07) == 0) { | } else if ((samplecount & 0x07) == 0) { | ||||
for (h = 1; h < N_LOWHARM && h <= maxh2 && h <= maxh; h++) { | |||||
for (h = 1; h < N_LOWHARM && h <= maxh2 && h <= maxh; h++) | |||||
harmspect[h] += harm_inc[h]; | harmspect[h] += harm_inc[h]; | ||||
} | |||||
// bring automctic gain control back towards unity | // bring automctic gain control back towards unity | ||||
if (agc < 256) agc++; | if (agc < 256) agc++; | ||||
// This is the start of the second cycle, reduce its amplitude | // This is the start of the second cycle, reduce its amplitude | ||||
glottal_flag = 2; | glottal_flag = 2; | ||||
amplitude2 = (amplitude2 * glottal_reduce)/256; | amplitude2 = (amplitude2 * glottal_reduce)/256; | ||||
} else { | |||||
} else | |||||
glottal_flag--; | glottal_flag--; | ||||
} | |||||
} | } | ||||
if (amplitude_env != NULL) { | if (amplitude_env != NULL) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} else { | |||||
} else | |||||
wavephase += phaseinc; | wavephase += phaseinc; | ||||
} | |||||
waveph = (unsigned short)(wavephase >> 16); | waveph = (unsigned short)(wavephase >> 16); | ||||
total = 0; | total = 0; | ||||
} | } | ||||
#endif | #endif | ||||
if (voicing != 64) { | |||||
if (voicing != 64) | |||||
total = (total >> 6) * voicing; | total = (total >> 6) * voicing; | ||||
} | |||||
#ifndef PLATFORM_RISCOS | #ifndef PLATFORM_RISCOS | ||||
if (wvoice->breath[0]) { | |||||
if (wvoice->breath[0]) | |||||
total += ApplyBreath(); | total += ApplyBreath(); | ||||
} | |||||
#endif | #endif | ||||
// mix with sampled wave if required | // mix with sampled wave if required | ||||
factor = 256 + (25 * (pitch_value - 50))/50; | factor = 256 + (25 * (pitch_value - 50))/50; | ||||
} | } | ||||
for (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; | ||||
wvoice->height[0] = (wvoice->height2[0] * (256 - factor*2))/256; | wvoice->height[0] = (wvoice->height2[0] * (256 - factor*2))/256; | ||||
break; | break; | ||||
case WCMD_PAUSE: | case WCMD_PAUSE: | ||||
if (resume == 0) { | |||||
if (resume == 0) | |||||
echo_complete -= length; | echo_complete -= length; | ||||
} | |||||
wdata.n_mix_wavefile = 0; | wdata.n_mix_wavefile = 0; | ||||
wdata.amplitude_fmt = 100; | wdata.amplitude_fmt = 100; | ||||
#ifdef INCLUDE_KLATT | #ifdef INCLUDE_KLATT | ||||
case WCMD_MARKER: | case WCMD_MARKER: | ||||
marker_type = q[0] >> 8; | marker_type = q[0] >> 8; | ||||
MarkerEvent(marker_type, q[1], q[2], q[3], out_ptr); | MarkerEvent(marker_type, q[1], q[2], q[3], out_ptr); | ||||
if (marker_type == 1) { // word marker | |||||
if (marker_type == 1) // word marker | |||||
current_source_index = q[1] & 0xffffff; | current_source_index = q[1] & 0xffffff; | ||||
} | |||||
break; | break; | ||||
case WCMD_AMPLITUDE: | case WCMD_AMPLITUDE: | ||||
if (result == 0) { | if (result == 0) { | ||||
WcmdqIncHead(); | WcmdqIncHead(); | ||||
resume = 0; | resume = 0; | ||||
} else { | |||||
} else | |||||
resume = 1; | resume = 1; | ||||
} | |||||
} | } | ||||
return 0; | return 0; | ||||
static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text) | static int SpeedUp(short *outbuf, int length_in, int length_out, int end_of_text) | ||||
{ | { | ||||
if (length_in > 0) { | if (length_in > 0) { | ||||
if (sonicSpeedupStream == NULL) { | |||||
if (sonicSpeedupStream == NULL) | |||||
sonicSpeedupStream = sonicCreateStream(22050, 1); | sonicSpeedupStream = sonicCreateStream(22050, 1); | ||||
} | |||||
if (sonicGetSpeed(sonicSpeedupStream) != sonicSpeed) { | |||||
if (sonicGetSpeed(sonicSpeedupStream) != sonicSpeed) | |||||
sonicSetSpeed(sonicSpeedupStream, sonicSpeed); | sonicSetSpeed(sonicSpeedupStream, sonicSpeed); | ||||
} | |||||
sonicWriteShortToStream(sonicSpeedupStream, outbuf, length_in); | sonicWriteShortToStream(sonicSpeedupStream, outbuf, length_in); | ||||
} | } | ||||
if (sonicSpeedupStream == NULL) | if (sonicSpeedupStream == NULL) | ||||
return 0; | return 0; | ||||
if (end_of_text) { | |||||
if (end_of_text) | |||||
sonicFlushStream(sonicSpeedupStream); | sonicFlushStream(sonicSpeedupStream); | ||||
} | |||||
return sonicReadShortFromStream(sonicSpeedupStream, outbuf, length_out); | return sonicReadShortFromStream(sonicSpeedupStream, outbuf, length_out); | ||||
} | } | ||||
#endif | #endif |
voice_select.gender = 0; | voice_select.gender = 0; | ||||
voice_select.name = NULL; | voice_select.name = NULL; | ||||
voices = espeak_ListVoices(&voice_select); | voices = espeak_ListVoices(&voice_select); | ||||
} else { | |||||
} else | |||||
voices = espeak_ListVoices(NULL); | voices = espeak_ListVoices(NULL); | ||||
} | |||||
fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | fprintf(f_out, "Pty Language Age/Gender VoiceName File Other Languages\n"); | ||||
} | } | ||||
fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | fprintf(f_out, "%2d %-12s%s%c %-20s %-13s ", | ||||
p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | p[0], lang_name, age_buf, genders[v->gender], buf, v->identifier); | ||||
} else { | |||||
} else | |||||
fprintf(f_out, "(%s %d)", lang_name, p[0]); | fprintf(f_out, "(%s %d)", lang_name, p[0]); | ||||
} | |||||
count++; | count++; | ||||
p += len+2; | p += len+2; | ||||
} | } | ||||
} | } | ||||
snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); | snprintf(path_home, sizeof(path_home), "%s/espeak-data", getenv("HOME")); | ||||
if (access(path_home, R_OK) != 0) { | |||||
if (access(path_home, R_OK) != 0) | |||||
strcpy(path_home, PATH_ESPEAK_DATA); | strcpy(path_home, PATH_ESPEAK_DATA); | ||||
} | |||||
#endif | #endif | ||||
#endif | #endif | ||||
} | } | ||||
c = long_options[ix].val; | c = long_options[ix].val; | ||||
optarg2 = NULL; | optarg2 = NULL; | ||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) { | |||||
if ((long_options[ix].has_arg != 0) && (p[len] == '=')) | |||||
optarg2 = &p[len+1]; | optarg2 = &p[len+1]; | ||||
} | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (flag_stdin == 0) | if (flag_stdin == 0) | ||||
option_linelength = -1; // single input lines on stdin | option_linelength = -1; // single input lines on stdin | ||||
} | } | ||||
} else { | |||||
} else | |||||
f_text = fopen(filename, "r"); | f_text = fopen(filename, "r"); | ||||
} | |||||
if ((f_text == NULL) && (p_text == NULL)) { | if ((f_text == NULL) && (p_text == NULL)) { | ||||
fprintf(stderr, "%sfile '%s'\n", err_load, filename); | fprintf(stderr, "%sfile '%s'\n", err_load, filename); | ||||
break; // finished, wavegen command queue is empty | break; // finished, wavegen command queue is empty | ||||
} | } | ||||
if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) { | |||||
if (Generate(phoneme_list, &n_phoneme_list, 1) == 0) | |||||
ix = SpeakNextClause(NULL, NULL, 1); | ix = SpeakNextClause(NULL, NULL, 1); | ||||
} | |||||
} | } | ||||
CloseWaveFile(); | CloseWaveFile(); |