|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define N_XML_BUF 500 |
|
|
#define N_XML_BUF 500 |
|
|
|
|
|
|
|
|
|
|
|
static void DecodeWithPhonemeMode(char *buf, char *phonemes, Translator *tr, Translator *tr2, unsigned int flags[]); |
|
|
|
|
|
static void TerminateBufWithSpaceAndZero(char *buf, int index, int *ungetc); |
|
|
|
|
|
|
|
|
static const char *xmlbase = ""; // base URL from <speak> |
|
|
static const char *xmlbase = ""; // base URL from <speak> |
|
|
|
|
|
|
|
|
static int namedata_ix = 0; |
|
|
static int namedata_ix = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flags[0] = flags[1] = 0; |
|
|
flags[0] = flags[1] = 0; |
|
|
if (LookupDictList(tr, &string1, phonemes, flags, 0, NULL)) { |
|
|
if (LookupDictList(tr, &string1, phonemes, flags, 0, NULL)) { |
|
|
char phonemes2[55]; |
|
|
|
|
|
SetWordStress(tr, phonemes, flags, -1, 0); |
|
|
|
|
|
DecodePhonemes(phonemes, phonemes2); |
|
|
|
|
|
sprintf(text_out, "[\002%s]]", phonemes2); |
|
|
|
|
|
|
|
|
DecodeWithPhonemeMode(text_out, phonemes, tr, NULL, flags); |
|
|
return text_out; |
|
|
return text_out; |
|
|
} |
|
|
} |
|
|
return NULL; |
|
|
return NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static const char *LookupCharName(Translator *tr, int c, int only) |
|
|
|
|
|
|
|
|
static const char *LookupCharName(Translator *tr, int c, bool only) |
|
|
{ |
|
|
{ |
|
|
// Find the phoneme string (in ascii) to speak the name of character c |
|
|
// Find the phoneme string (in ascii) to speak the name of character c |
|
|
// Used for punctuation characters and symbols |
|
|
// Used for punctuation characters and symbols |
|
|
|
|
|
|
|
|
ix = utf8_out(c, &single_letter[2]); |
|
|
ix = utf8_out(c, &single_letter[2]); |
|
|
single_letter[2+ix] = 0; |
|
|
single_letter[2+ix] = 0; |
|
|
|
|
|
|
|
|
if (only) { |
|
|
|
|
|
|
|
|
if (only == true) { |
|
|
string = &single_letter[2]; |
|
|
string = &single_letter[2]; |
|
|
LookupDictList(tr, &string, phonemes, flags, 0, NULL); |
|
|
LookupDictList(tr, &string, phonemes, flags, 0, NULL); |
|
|
} else { |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (only == false) { |
|
|
string = &single_letter[1]; |
|
|
string = &single_letter[1]; |
|
|
if (LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0) { |
|
|
if (LookupDictList(tr, &string, phonemes, flags, 0, NULL) == 0) { |
|
|
// try _* then * |
|
|
// try _* then * |
|
|
|
|
|
|
|
|
TranslateRules(tr, &single_letter[2], phonemes, sizeof(phonemes), NULL, 0, NULL); |
|
|
TranslateRules(tr, &single_letter[2], phonemes, sizeof(phonemes), NULL, 0, NULL); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ((only == 0) && ((phonemes[0] == 0) || (phonemes[0] == phonSWITCH)) && (tr->translator_name != L('e', 'n'))) { |
|
|
|
|
|
// not found, try English |
|
|
|
|
|
SetTranslator2(ESPEAKNG_DEFAULT_VOICE); |
|
|
|
|
|
string = &single_letter[1]; |
|
|
|
|
|
single_letter[1] = '_'; |
|
|
|
|
|
if (LookupDictList(translator2, &string, phonemes, flags, 0, NULL) == 0) { |
|
|
|
|
|
string = &single_letter[2]; |
|
|
|
|
|
LookupDictList(translator2, &string, phonemes, flags, 0, NULL); |
|
|
|
|
|
} |
|
|
|
|
|
if (phonemes[0]) |
|
|
|
|
|
lang_name = ESPEAKNG_DEFAULT_VOICE; |
|
|
|
|
|
else |
|
|
|
|
|
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (((phonemes[0] == 0) || (phonemes[0] == phonSWITCH)) && (tr->translator_name != L('e', 'n'))) { |
|
|
|
|
|
// not found, try English |
|
|
|
|
|
SetTranslator2(ESPEAKNG_DEFAULT_VOICE); |
|
|
|
|
|
string = &single_letter[1]; |
|
|
|
|
|
single_letter[1] = '_'; |
|
|
|
|
|
if (LookupDictList(translator2, &string, phonemes, flags, 0, NULL) == 0) { |
|
|
|
|
|
string = &single_letter[2]; |
|
|
|
|
|
LookupDictList(translator2, &string, phonemes, flags, 0, NULL); |
|
|
|
|
|
} |
|
|
|
|
|
if (phonemes[0]) |
|
|
|
|
|
lang_name = ESPEAKNG_DEFAULT_VOICE; |
|
|
|
|
|
else |
|
|
|
|
|
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (phonemes[0]) { |
|
|
if (phonemes[0]) { |
|
|
char phonemes2[60]; |
|
|
|
|
|
if (lang_name) { |
|
|
if (lang_name) { |
|
|
SetWordStress(translator2, phonemes, flags, -1, 0); |
|
|
|
|
|
DecodePhonemes(phonemes, phonemes2); |
|
|
|
|
|
sprintf(buf, "[\002_^_%s %s _^_%s]]", ESPEAKNG_DEFAULT_VOICE, phonemes2, WordToString2(tr->translator_name)); |
|
|
|
|
|
|
|
|
DecodeWithPhonemeMode(buf, phonemes, tr, translator2, flags); |
|
|
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table |
|
|
SelectPhonemeTable(voice->phoneme_tab_ix); // revert to original phoneme table |
|
|
} else { |
|
|
} else { |
|
|
SetWordStress(tr, phonemes, flags, -1, 0); |
|
|
|
|
|
DecodePhonemes(phonemes, phonemes2); |
|
|
|
|
|
sprintf(buf, "[\002%s]] ", phonemes2); |
|
|
|
|
|
|
|
|
DecodeWithPhonemeMode(buf, phonemes, tr, NULL, flags); |
|
|
} |
|
|
} |
|
|
} else if (only == 0) |
|
|
|
|
|
|
|
|
} else if (only == false) |
|
|
strcpy(buf, "[\002(X1)(X1)(X1)]]"); |
|
|
strcpy(buf, "[\002(X1)(X1)(X1)]]"); |
|
|
|
|
|
|
|
|
return buf; |
|
|
return 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, false); |
|
|
|
|
|
|
|
|
if (punctname == NULL) |
|
|
if (punctname == NULL) |
|
|
return -1; |
|
|
return -1; |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
ungot_char2 = c1; |
|
|
ungot_char2 = c1; |
|
|
buf[0] = ' '; |
|
|
|
|
|
buf[1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, 0, &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 |
|
|
if (ix > (n_buf - 20)) { |
|
|
if (ix > (n_buf - 20)) { |
|
|
// Perhaps not enough room, end the clause before the SSML tag |
|
|
// Perhaps not enough room, end the clause before the SSML tag |
|
|
UngetC(c2); |
|
|
|
|
|
ungot_char2 = c1; |
|
|
ungot_char2 = c1; |
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, &c2); |
|
|
return CLAUSE_NONE; |
|
|
return CLAUSE_NONE; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
terminator = ProcessSsmlTag(xml_buf, buf, &ix, n_buf, xmlbase, &audio_text, current_voice_id, &base_voice, base_voice_variant_name, &ignore_text, &clear_skipping_text, &sayas_mode, &sayas_start, ssml_stack, &n_ssml_stack, &n_param_stack, (int *)speech_parameters); |
|
|
terminator = ProcessSsmlTag(xml_buf, buf, &ix, n_buf, xmlbase, &audio_text, current_voice_id, &base_voice, base_voice_variant_name, &ignore_text, &clear_skipping_text, &sayas_mode, &sayas_start, ssml_stack, &n_ssml_stack, &n_param_stack, (int *)speech_parameters); |
|
|
|
|
|
|
|
|
if (terminator != 0) { |
|
|
if (terminator != 0) { |
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix++] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, NULL); |
|
|
|
|
|
|
|
|
if (terminator & CLAUSE_TYPE_VOICE_CHANGE) |
|
|
if (terminator & CLAUSE_TYPE_VOICE_CHANGE) |
|
|
strcpy(voice_change, current_voice_id); |
|
|
strcpy(voice_change, current_voice_id); |
|
|
|
|
|
|
|
|
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 |
|
|
} |
|
|
} |
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, NULL); |
|
|
return terminator; |
|
|
return terminator; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
if (parag > 0) { |
|
|
if (parag > 0) { |
|
|
// 2nd newline, assume paragraph |
|
|
// 2nd newline, assume paragraph |
|
|
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+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, &c2); |
|
|
if (parag > 3) |
|
|
if (parag > 3) |
|
|
parag = 3; |
|
|
parag = 3; |
|
|
if (option_ssml) parag = 1; |
|
|
if (option_ssml) parag = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (linelength <= option_linelength) { |
|
|
if (linelength <= option_linelength) { |
|
|
// treat lines shorter than a specified length as end-of-clause |
|
|
// treat lines shorter than a specified length as end-of-clause |
|
|
UngetC(c2); |
|
|
|
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, &c2); |
|
|
return CLAUSE_COLON; |
|
|
return CLAUSE_COLON; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!iswspace(c1)) { |
|
|
if (!iswspace(c1)) { |
|
|
if (!IsAlpha(c1) || !iswlower(c1)) { |
|
|
if (!IsAlpha(c1) || !iswlower(c1)) { |
|
|
UngetC(c2); |
|
|
|
|
|
ungot_char2 = c1; |
|
|
ungot_char2 = c1; |
|
|
buf[end_clause_index] = ' '; // delete the end-clause punctuation |
|
|
|
|
|
buf[end_clause_index+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, end_clause_index, &c2); |
|
|
return end_clause_after_tag; |
|
|
return end_clause_after_tag; |
|
|
} |
|
|
} |
|
|
end_clause_after_tag = 0; |
|
|
end_clause_after_tag = 0; |
|
|
|
|
|
|
|
|
char *p2; |
|
|
char *p2; |
|
|
|
|
|
|
|
|
p2 = &buf[ix]; |
|
|
p2 = &buf[ix]; |
|
|
sprintf(p2, "%s", LookupCharName(tr, c1, 1)); |
|
|
|
|
|
|
|
|
sprintf(p2, "%s", LookupCharName(tr, c1, true)); |
|
|
if (p2[0] != 0) { |
|
|
if (p2[0] != 0) { |
|
|
ix += strlen(p2); |
|
|
ix += strlen(p2); |
|
|
announced_punctuation = c1; |
|
|
announced_punctuation = c1; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (is_end_clause) { |
|
|
if (is_end_clause) { |
|
|
UngetC(c_next); |
|
|
|
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, &c_next); |
|
|
|
|
|
|
|
|
if (iswdigit(cprev) && !IsAlpha(c_next)) // ???? |
|
|
if (iswdigit(cprev) && !IsAlpha(c_next)) // ???? |
|
|
punct_data &= ~CLAUSE_DOT_AFTER_LAST_WORD; |
|
|
punct_data &= ~CLAUSE_DOT_AFTER_LAST_WORD; |
|
|
|
|
|
|
|
|
// clause too long, getting near end of buffer, so break here |
|
|
// clause too long, getting near end of buffer, so break here |
|
|
// try to break at a word boundary (unless we actually reach the end of buffer). |
|
|
// try to break at a word boundary (unless we actually reach the end of buffer). |
|
|
// (n_buf-4) is to allow for 3 bytes of multibyte character plus terminator. |
|
|
// (n_buf-4) is to allow for 3 bytes of multibyte character plus terminator. |
|
|
buf[ix] = ' '; |
|
|
|
|
|
buf[ix+1] = 0; |
|
|
|
|
|
UngetC(c2); |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, &c2); |
|
|
return CLAUSE_NONE; |
|
|
return CLAUSE_NONE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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+1] = 0; |
|
|
|
|
|
|
|
|
TerminateBufWithSpaceAndZero(buf, ix, NULL); |
|
|
return CLAUSE_EOF; // end of file |
|
|
return CLAUSE_EOF; // end of file |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
xmlbase = NULL; |
|
|
xmlbase = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void TerminateBufWithSpaceAndZero(char *buf, int index, int *ungetc) { |
|
|
|
|
|
buf[index] = ' '; |
|
|
|
|
|
buf[index+1] = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (ungetc != NULL) { |
|
|
|
|
|
UngetC(*ungetc); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void DecodeWithPhonemeMode(char *buf, char *phonemes, Translator *tr, Translator *tr2, unsigned int flags[]) { |
|
|
|
|
|
char phonemes2[55]; |
|
|
|
|
|
if (tr2 == NULL) { |
|
|
|
|
|
SetWordStress(tr, phonemes, flags, -1, 0); |
|
|
|
|
|
DecodePhonemes(phonemes, phonemes2); |
|
|
|
|
|
sprintf(buf, "[\002%s]]", phonemes2); |
|
|
|
|
|
} else { |
|
|
|
|
|
SetWordStress(tr2, phonemes, flags, -1, 0); |
|
|
|
|
|
DecodePhonemes(phonemes, phonemes2); |
|
|
|
|
|
sprintf(buf, "[\002_^_%s %s _^_%s]]", ESPEAKNG_DEFAULT_VOICE, phonemes2, WordToString2(tr->translator_name)); |
|
|
|
|
|
} |
|
|
|
|
|
} |