From 50f58168e1003608bf729f7167cebc46d5c3d517 Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Thu, 6 Aug 2020 20:58:39 +0300 Subject: [PATCH 1/6] code cleanup: remove unused *constcharptr and MakeWawe2() --- src/libespeak-ng/synthesize.h | 1 - src/libespeak-ng/translate.h | 2 -- 2 files changed, 3 deletions(-) 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; From 75d66c89d03535ad441b5fd46943f7bb5f5d8e32 Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Sun, 16 Aug 2020 16:25:12 +0300 Subject: [PATCH 2/6] code cleanup: Move self_closing checks to ProcessSsmlTag() This is a bit slower since we don't pass n_xml_buf as an argument but rather get it with a call to wcslen. It is much cleaner though, since the name ProcessSsmlTag() implies that all processing should be done there. --- src/libespeak-ng/readclause.c | 10 +--------- src/libespeak-ng/ssml.c | 11 ++++++++++- src/libespeak-ng/ssml.h | 1 - 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libespeak-ng/readclause.c b/src/libespeak-ng/readclause.c index de5c183e..e0f9fe3f 100644 --- a/src/libespeak-ng/readclause.c +++ b/src/libespeak-ng/readclause.c @@ -643,7 +643,6 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ int terminator; int found; bool any_alnum = false; - bool self_closing; int punct_data = 0; bool is_end_clause; int announced_punctuation = 0; @@ -780,14 +779,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..1bc849a7 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. @@ -577,6 +577,15 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, boo // 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 } diff --git a/src/libespeak-ng/ssml.h b/src/libespeak-ng/ssml.h index 29cd1530..6c6235e2 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, From 34657e7ea4fb1a31db40fecd557bd7038ff20b10 Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Sat, 22 Aug 2020 21:01:58 +0300 Subject: [PATCH 3/6] code cleanup: move check for SSML comments and declarations to ProcessSsmlTag() Note the line in readclause.c: if ((c2 == '/') || iswalpha(c2) || c2 == '!' || c2 == '?') { It might be enough to pass everything to ProcessSsmlTag. What are the cases that are skipped because of this? --- src/libespeak-ng/readclause.c | 7 +------ src/libespeak-ng/ssml.c | 5 +++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libespeak-ng/readclause.c b/src/libespeak-ng/readclause.c index e0f9fe3f..9a7cb3a4 100644 --- a/src/libespeak-ng/readclause.c +++ b/src/libespeak-ng/readclause.c @@ -753,12 +753,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 diff --git a/src/libespeak-ng/ssml.c b/src/libespeak-ng/ssml.c index 1bc849a7..de811d84 100644 --- a/src/libespeak-ng/ssml.c +++ b/src/libespeak-ng/ssml.c @@ -574,6 +574,11 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, con 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 }; From 959bf26b6b1a9d77b09687dfd6d558ca9e4dcf3a Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Sun, 23 Aug 2020 08:48:39 +0300 Subject: [PATCH 4/6] Add a test for XML/SSML character and entity references --- tests/ssml.test | 13 +++++++++++-- tests/ssml/references.expected | 4 ++++ tests/ssml/references.ssml2 | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/ssml/references.expected create mode 100644 tests/ssml/references.ssml2 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..46bf248e --- /dev/null +++ b/tests/ssml/references.ssml2 @@ -0,0 +1,9 @@ + + < > & ' " + B B +z z From 54d93cf2b4bc7bcef460109aeb7be0c77d910d12 Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Sun, 23 Aug 2020 18:44:15 +0300 Subject: [PATCH 5/6] code cleanup: move ssml reference handling logic to a new function ParseSsmlReference() It's unclear why c2 needs to be set after an entity reference. --- src/libespeak-ng/readclause.c | 29 +++-------------------------- src/libespeak-ng/ssml.c | 35 +++++++++++++++++++++++++++++++++++ src/libespeak-ng/ssml.h | 4 ++++ 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/libespeak-ng/readclause.c b/src/libespeak-ng/readclause.c index 9a7cb3a4..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,7 +631,6 @@ 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; int punct_data = 0; bool is_end_clause; @@ -727,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; diff --git a/src/libespeak-ng/ssml.c b/src/libespeak-ng/ssml.c index de811d84..d4535f46 100644 --- a/src/libespeak-ng/ssml.c +++ b/src/libespeak-ng/ssml.c @@ -949,3 +949,38 @@ int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outbuf, con } 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 6c6235e2..bcc8a8aa 100644 --- a/src/libespeak-ng/ssml.h +++ b/src/libespeak-ng/ssml.h @@ -80,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 From c9dee003bf62fe2a6acba452b10c164e3bfc345a Mon Sep 17 00:00:00 2001 From: Juho Hiltunen Date: Thu, 27 Aug 2020 06:12:25 +0300 Subject: [PATCH 6/6] better explanation and nicer formatting for the ssml reference test. --- tests/ssml/references.ssml2 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ssml/references.ssml2 b/tests/ssml/references.ssml2 index 46bf248e..f00521a8 100644 --- a/tests/ssml/references.ssml2 +++ b/tests/ssml/references.ssml2 @@ -1,9 +1,9 @@ - < > & ' " - B B +< > & ' " +B B z z