diff --git a/src/libespeak-ng/readclause.c b/src/libespeak-ng/readclause.c index de5c183e..b45f4ff2 100644 --- a/src/libespeak-ng/readclause.c +++ b/src/libespeak-ng/readclause.c @@ -603,16 +603,6 @@ static void RemoveChar(char *p) memset(p, ' ', utf8_in(&c, p)); } -static MNEM_TAB xml_char_mnemonics[] = { - { "gt", '>' }, - { "lt", 0xe000 + '<' }, // private usage area, to avoid confusion with XML tag - { "amp", '&' }, - { "quot", '"' }, - { "nbsp", ' ' }, - { "apos", '\'' }, - { NULL, -1 } -}; - int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_buf, int *tone_type, char *voice_change) { /* Find the end of the current clause. @@ -641,9 +631,7 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ int phoneme_mode = 0; int n_xml_buf; int terminator; - int found; bool any_alnum = false; - bool self_closing; int punct_data = 0; bool is_end_clause; int announced_punctuation = 0; @@ -728,22 +716,10 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ c2 = GetC(); sprintf(ungot_string, "%s%c%c", &xml_buf2[0], c1, c2); + int found = -1; if (c1 == ';') { - if (xml_buf2[0] == '#') { - // character code number - if (xml_buf2[1] == 'x') - found = sscanf(&xml_buf2[2], "%x", (unsigned int *)(&c1)); - else - found = sscanf(&xml_buf2[1], "%d", &c1); - } else { - if ((found = LookupMnem(xml_char_mnemonics, xml_buf2)) != -1) { - c1 = found; - if (c2 == 0) - c2 = ' '; - } - } - } else - found = -1; + found = ParseSsmlReference(xml_buf2, &c1, &c2); + } if (found <= 0) { ungot_string_ix = 0; @@ -754,12 +730,7 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ if ((c1 <= 0x20) && ((sayas_mode == SAYAS_SINGLE_CHARS) || (sayas_mode == SAYAS_KEY))) c1 += 0xe000; // move into unicode private usage area } else if ((c1 == '<') && (ssml_ignore_l_angle != '<')) { - if ((c2 == '!') || (c2 == '?')) { - // a comment, ignore until closing '<' (or ')) - c1 = GetC(); - c2 = ' '; - } else if ((c2 == '/') || iswalpha(c2)) { + if ((c2 == '/') || iswalpha(c2) || c2 == '!' || c2 == '?') { // check for space in the output buffer for embedded commands produced by the SSML tag if (ix > (n_buf - 20)) { // Perhaps not enough room, end the clause before the SSML tag @@ -780,14 +751,7 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ xml_buf[n_xml_buf] = 0; c2 = ' '; - self_closing = false; - if (xml_buf[n_xml_buf-1] == '/') { - // a self-closing tag - xml_buf[n_xml_buf-1] = ' '; - self_closing = true; - } - - terminator = ProcessSsmlTag(xml_buf, buf, &ix, n_buf, self_closing, 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) { buf[ix] = ' '; diff --git a/src/libespeak-ng/ssml.c b/src/libespeak-ng/ssml.c index 9a37aa26..d4535f46 100644 --- a/src/libespeak-ng/ssml.c +++ b/src/libespeak-ng/ssml.c @@ -549,7 +549,7 @@ static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp, } } -int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, bool self_closing, const char *xmlbase, bool *audio_text, char *current_voice_id, espeak_VOICE *base_voice, char *base_voice_variant_name, bool *ignore_text, bool *clear_skipping_text, int *sayas_mode, int *sayas_start, SSML_STACK *ssml_stack, int *n_ssml_stack, int *n_param_stack, int *speech_parameters) +int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, const char *xmlbase, bool *audio_text, char *current_voice_id, espeak_VOICE *base_voice, char *base_voice_variant_name, bool *ignore_text, bool *clear_skipping_text, int *sayas_mode, int *sayas_start, SSML_STACK *ssml_stack, int *n_ssml_stack, int *n_param_stack, int *speech_parameters) { // xml_buf is the tag and attributes with a zero terminator in place of the original '>' // returns a clause terminator value. @@ -574,9 +574,23 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, boo PARAM_STACK *sp; SSML_STACK *ssml_sp; + // don't process comments and xml declarations + if (wcsncmp(xml_buf, (wchar_t *) "!--", 3) == 0 || wcsncmp(xml_buf, (wchar_t *) "?xml", 4) == 0) { + return 0; + } + // these tags have no effect if they are self-closing, eg. static char ignore_if_self_closing[] = { 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0 }; + bool self_closing = false; + int len; + len = wcslen(xml_buf); + if (xml_buf[len - 1] == '/') { + // a self-closing tag + xml_buf[len - 1] = ' '; + self_closing = true; + } + static const MNEM_TAB mnem_phoneme_alphabet[] = { { "espeak", 1 }, { NULL, -1 } @@ -935,3 +949,38 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, boo } return 0; } + +static MNEM_TAB xml_entity_mnemonics[] = { + { "gt", '>' }, + { "lt", 0xe000 + '<' }, // private usage area, to avoid confusion with XML tag + { "amp", '&' }, + { "quot", '"' }, + { "nbsp", ' ' }, + { "apos", '\'' }, + { NULL, -1 } +}; + +int ParseSsmlReference(char *ref, int *c1, int *c2) { + // Check if buffer *ref contains an XML character or entity reference + // if found, set *c1 to the replacement char + // change *c2 for entity references + // returns >= 0 on success + + if (ref[0] == '#') { + // character reference + if (ref[1] == 'x') + return sscanf(&ref[2], "%x", c1); + else + return sscanf(&ref[1], "%d", c1); + } else { + // entity reference + int found; + if ((found = LookupMnem(xml_entity_mnemonics, ref)) != -1) { + *c1 = found; + if (*c2 == 0) + *c2 = ' '; + return found; + } + } + return -1; +} diff --git a/src/libespeak-ng/ssml.h b/src/libespeak-ng/ssml.h index 29cd1530..bcc8a8aa 100644 --- a/src/libespeak-ng/ssml.h +++ b/src/libespeak-ng/ssml.h @@ -66,7 +66,6 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, - bool self_closing, const char *xmlbase, bool *audio_text, char *current_voice_id, @@ -81,6 +80,10 @@ int ProcessSsmlTag(wchar_t *xml_buf, int *n_param_stack, int *speech_parameters); +int ParseSsmlReference(char *ref, + int *c1, + int *c2); + #ifdef __cplusplus } #endif diff --git a/src/libespeak-ng/synthesize.h b/src/libespeak-ng/synthesize.h index ddadc1ea..91c42498 100644 --- a/src/libespeak-ng/synthesize.h +++ b/src/libespeak-ng/synthesize.h @@ -458,7 +458,6 @@ extern short echo_buf[N_ECHO_BUF]; void SynthesizeInit(void); int Generate(PHONEME_LIST *phoneme_list, int *n_ph, bool resume); -void MakeWave2(PHONEME_LIST *p, int n_ph); int SpeakNextClause(int control); void SetSpeed(int control); void SetEmbedded(int control, int value); diff --git a/src/libespeak-ng/translate.h b/src/libespeak-ng/translate.h index c1666ee3..92579196 100644 --- a/src/libespeak-ng/translate.h +++ b/src/libespeak-ng/translate.h @@ -261,8 +261,6 @@ extern "C" // match 1 pre 2 post 0 - use common phoneme string // match 1 pre 2 post 3 0 - empty phoneme string -typedef const char *constcharptr; - // used to mark words with the source[] buffer typedef struct { unsigned int flags; diff --git a/tests/ssml.test b/tests/ssml.test index c337f1e9..69a6a462 100755 --- a/tests/ssml.test +++ b/tests/ssml.test @@ -2,12 +2,21 @@ test_ssml() { INPUT=$1 + + if [ "$2" = "punct" ] + then + PARAMETERS="--punct -x" + else + PARAMETERS="-v en-US --ipa=2" + fi + echo "testing ${INPUT}" cp $(dirname $INPUT)/$(basename ${INPUT%.*}).expected expected.txt ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} \ - src/espeak-ng -m -q -v en-US --ipa=2 -f ${INPUT} > actual.txt + src/espeak-ng -m -q $PARAMETERS -f ${INPUT} > actual.txt diff expected.txt actual.txt || exit 1 } -for i in `ls tests/ssml/*.ssml` ; do test_ssml $i ; done +for i in `ls tests/ssml/*.ssml` ; do test_ssml $i; done +for i in `ls tests/ssml/*.ssml2` ; do test_ssml $i punct; done diff --git a/tests/ssml/references.expected b/tests/ssml/references.expected new file mode 100644 index 00000000..989aabe0 --- /dev/null +++ b/tests/ssml/references.expected @@ -0,0 +1,4 @@ +l'EsDan_: gr'eIt@D,an_: 'amp@s,and t'Ik_: kw'oUts +b'i: b'i: +z'Ed z'Ed + diff --git a/tests/ssml/references.ssml2 b/tests/ssml/references.ssml2 new file mode 100644 index 00000000..f00521a8 --- /dev/null +++ b/tests/ssml/references.ssml2 @@ -0,0 +1,9 @@ + +< > & ' " +B B +z z