diff --git a/src/libespeak-ng/readclause.c b/src/libespeak-ng/readclause.c
index 7dd2873f..671d8edf 100644
--- a/src/libespeak-ng/readclause.c
+++ b/src/libespeak-ng/readclause.c
@@ -707,7 +707,7 @@ static void PopParamStack(int tag_type, char *outbuf, int *outix)
ProcessParamStack(outbuf, outix);
}
-static wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name)
+wchar_t *GetSsmlAttribute(wchar_t *pw, const char *name)
{
// Gets the value string for an attribute.
// Returns NULL if the attribute is not present
@@ -839,76 +839,6 @@ void SetVoiceStack(espeak_VOICE *v, const char *variant_name)
memcpy(&base_voice, ¤t_voice_selected, sizeof(base_voice));
}
-static int GetVoiceAttributes(wchar_t *pw, int tag_type)
-{
- // Determines whether voice attribute are specified in this tag, and if so, whether this means
- // a voice change.
- // If it's a closing tag, delete the top frame of the stack and determine whether this implies
- // a voice change.
- // Returns CLAUSE_TYPE_VOICE_CHANGE if there is a voice change
-
- wchar_t *lang;
- wchar_t *gender;
- wchar_t *name;
- wchar_t *age;
- wchar_t *variant;
- int value;
- const char *new_voice_id;
- SSML_STACK *ssml_sp;
-
- static const MNEM_TAB mnem_gender[] = {
- { "male", ENGENDER_MALE },
- { "female", ENGENDER_FEMALE },
- { "neutral", ENGENDER_NEUTRAL },
- { NULL, ENGENDER_UNKNOWN }
- };
-
- if (tag_type & SSML_CLOSE) {
- // delete a stack frame
- if (n_ssml_stack > 1)
- n_ssml_stack--;
- } else {
- // add a stack frame if any voice details are specified
- lang = GetSsmlAttribute(pw, "xml:lang");
-
- if (tag_type != SSML_VOICE) {
- // only expect an xml:lang attribute
- name = NULL;
- variant = NULL;
- age = NULL;
- gender = NULL;
- } else {
- name = GetSsmlAttribute(pw, "name");
- variant = GetSsmlAttribute(pw, "variant");
- age = GetSsmlAttribute(pw, "age");
- gender = GetSsmlAttribute(pw, "gender");
- }
-
- if ((tag_type != SSML_VOICE) && (lang == NULL))
- return 0; // or
without language spec, nothing to do
-
- ssml_sp = &ssml_stack[n_ssml_stack++];
-
- attrcopy_utf8(ssml_sp->language, lang, sizeof(ssml_sp->language));
- attrcopy_utf8(ssml_sp->voice_name, name, sizeof(ssml_sp->voice_name));
- if ((value = attrnumber(variant, 1, 0)) > 0)
- value--; // variant='0' and variant='1' the same
- ssml_sp->voice_variant_number = value;
- ssml_sp->voice_age = attrnumber(age, 0, 0);
- ssml_sp->voice_gender = attrlookup(gender, mnem_gender);
- ssml_sp->tag_type = tag_type;
- }
-
- new_voice_id = VoiceFromStack();
- if (strcmp(new_voice_id, current_voice_id) != 0) {
- // add an embedded command to change the voice
- strcpy(current_voice_id, new_voice_id);
- return CLAUSE_TYPE_VOICE_CHANGE;
- }
-
- return 0;
-}
-
static void SetProsodyParameter(int param_type, wchar_t *attr1, PARAM_STACK *sp)
{
int value;
@@ -1329,57 +1259,57 @@ static int ProcessSsmlTag(wchar_t *xml_buf, char *outbuf, int *outix, int n_outb
if ((index = AddNameData(buf, 0)) >= 0)
xmlbase = &namedata[index];
}
- if (GetVoiceAttributes(px, tag_type) == 0)
+ if (GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id) == 0)
return 0; // no voice change
return CLAUSE_VOICE;
case SSML_VOICE:
- if (GetVoiceAttributes(px, tag_type) == 0)
+ if (GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id) == 0)
return 0; // no voice change
return CLAUSE_VOICE;
case SSML_SPEAK + SSML_CLOSE:
// unwind stack until the previous or tag
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_SPEAK))
n_ssml_stack--;
- return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type);
+ return CLAUSE_PERIOD + GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
case SSML_VOICE + SSML_CLOSE:
// unwind stack until the previous or tag
while ((n_ssml_stack > 1) && (ssml_stack[n_ssml_stack-1].tag_type != SSML_VOICE))
n_ssml_stack--;
terminator = 0; // ?? Sentence intonation, but no pause ??
- return terminator + GetVoiceAttributes(px, tag_type);
+ return terminator + GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
case HTML_BREAK:
case HTML_BREAK + SSML_CLOSE:
return CLAUSE_COLON;
case SSML_SENTENCE:
if (ssml_sp->tag_type == SSML_SENTENCE) {
// new sentence implies end-of-sentence
- voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE);
+ voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
}
- voice_change_flag |= GetVoiceAttributes(px, tag_type);
+ voice_change_flag |= GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
return CLAUSE_PARAGRAPH + voice_change_flag;
case SSML_PARAGRAPH:
if (ssml_sp->tag_type == SSML_SENTENCE) {
// new paragraph implies end-of-sentence or end-of-paragraph
- voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE);
+ voice_change_flag = GetVoiceAttributes(px, SSML_SENTENCE+SSML_CLOSE, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
}
if (ssml_sp->tag_type == SSML_PARAGRAPH) {
// new paragraph implies end-of-sentence or end-of-paragraph
- voice_change_flag |= GetVoiceAttributes(px, SSML_PARAGRAPH+SSML_CLOSE);
+ voice_change_flag |= GetVoiceAttributes(px, SSML_PARAGRAPH+SSML_CLOSE, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
}
- voice_change_flag |= GetVoiceAttributes(px, tag_type);
+ voice_change_flag |= GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
return CLAUSE_PARAGRAPH + voice_change_flag;
case SSML_SENTENCE + SSML_CLOSE:
if (ssml_sp->tag_type == SSML_SENTENCE) {
// end of a sentence which specified a language
- voice_change_flag = GetVoiceAttributes(px, tag_type);
+ voice_change_flag = GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id);
}
return CLAUSE_PERIOD + voice_change_flag;
case SSML_PARAGRAPH + SSML_CLOSE:
if ((ssml_sp->tag_type == SSML_SENTENCE) || (ssml_sp->tag_type == SSML_PARAGRAPH)) {
// End of a paragraph which specified a language.
// (End-of-paragraph also implies end-of-sentence)
- return GetVoiceAttributes(px, tag_type) + CLAUSE_PARAGRAPH;
+ return GetVoiceAttributes(px, tag_type, ssml_sp, ssml_stack, n_ssml_stack, ¤t_voice_id) + CLAUSE_PARAGRAPH;
}
return CLAUSE_PARAGRAPH;
}
diff --git a/src/libespeak-ng/ssml.c b/src/libespeak-ng/ssml.c
index 1411e5c4..8b05a3ac 100644
--- a/src/libespeak-ng/ssml.c
+++ b/src/libespeak-ng/ssml.c
@@ -44,11 +44,7 @@
#include "voice.h"
#include "synthesize.h"
#include "translate.h"
-
-int attrcmp(const wchar_t *string1, const char *string2);
-int attrlookup(const wchar_t *string1, const MNEM_TAB *mtab);
-int attrnumber(const wchar_t *pw, int default_value, int type);
-int attrcopy_utf8(char *buf, const wchar_t *pw, int len);
+#include "ssml.h"
int attrcmp(const wchar_t *string1, const char *string2)
{
@@ -113,3 +109,71 @@ int attrcopy_utf8(char *buf, const wchar_t *pw, int len)
return ix;
}
+int GetVoiceAttributes(wchar_t *pw, int tag_type, SSML_STACK *ssml_sp, SSML_STACK *ssml_stack, int n_ssml_stack, char current_voice_id[])
+{
+ // Determines whether voice attribute are specified in this tag, and if so, whether this means
+ // a voice change.
+ // If it's a closing tag, delete the top frame of the stack and determine whether this implies
+ // a voice change.
+ // Returns CLAUSE_TYPE_VOICE_CHANGE if there is a voice change
+
+ wchar_t *lang;
+ wchar_t *gender;
+ wchar_t *name;
+ wchar_t *age;
+ wchar_t *variant;
+ int value;
+ const char *new_voice_id;
+
+ static const MNEM_TAB mnem_gender[] = {
+ { "male", ENGENDER_MALE },
+ { "female", ENGENDER_FEMALE },
+ { "neutral", ENGENDER_NEUTRAL },
+ { NULL, ENGENDER_UNKNOWN }
+ };
+
+ if (tag_type & SSML_CLOSE) {
+ // delete a stack frame
+ if (n_ssml_stack > 1)
+ n_ssml_stack--;
+ } else {
+ // add a stack frame if any voice details are specified
+ lang = GetSsmlAttribute(pw, "xml:lang");
+
+ if (tag_type != SSML_VOICE) {
+ // only expect an xml:lang attribute
+ name = NULL;
+ variant = NULL;
+ age = NULL;
+ gender = NULL;
+ } else {
+ name = GetSsmlAttribute(pw, "name");
+ variant = GetSsmlAttribute(pw, "variant");
+ age = GetSsmlAttribute(pw, "age");
+ gender = GetSsmlAttribute(pw, "gender");
+ }
+
+ if ((tag_type != SSML_VOICE) && (lang == NULL))
+ return 0; // or without language spec, nothing to do
+
+ ssml_sp = &ssml_stack[n_ssml_stack++];
+
+ attrcopy_utf8(ssml_sp->language, lang, sizeof(ssml_sp->language));
+ attrcopy_utf8(ssml_sp->voice_name, name, sizeof(ssml_sp->voice_name));
+ if ((value = attrnumber(variant, 1, 0)) > 0)
+ value--; // variant='0' and variant='1' the same
+ ssml_sp->voice_variant_number = value;
+ ssml_sp->voice_age = attrnumber(age, 0, 0);
+ ssml_sp->voice_gender = attrlookup(gender, mnem_gender);
+ ssml_sp->tag_type = tag_type;
+ }
+
+ new_voice_id = VoiceFromStack();
+ if (strcmp(new_voice_id, current_voice_id) != 0) {
+ // add an embedded command to change the voice
+ strcpy(current_voice_id, new_voice_id);
+ return CLAUSE_TYPE_VOICE_CHANGE;
+ }
+
+ return 0;
+}