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