|
|
|
|
|
|
|
|
static int attrcopy_utf8(char *buf, const wchar_t *pw, int len) |
|
|
static int attrcopy_utf8(char *buf, const wchar_t *pw, int len) |
|
|
{ |
|
|
{ |
|
|
// Convert attribute string into utf8, write to buf, and return its utf8 length |
|
|
// Convert attribute string into utf8, write to buf, and return its utf8 length |
|
|
unsigned int c; |
|
|
|
|
|
int ix = 0; |
|
|
int ix = 0; |
|
|
int n; |
|
|
|
|
|
int prev_c = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (pw != NULL) { |
|
|
if (pw != NULL) { |
|
|
|
|
|
unsigned int c; |
|
|
|
|
|
int prev_c = 0; |
|
|
while ((ix < (len-4)) && ((c = *pw++) != 0)) { |
|
|
while ((ix < (len-4)) && ((c = *pw++) != 0)) { |
|
|
if ((c == '"') && (prev_c != '\\')) |
|
|
if ((c == '"') && (prev_c != '\\')) |
|
|
break; // " indicates end of attribute, unless preceded by backstroke |
|
|
break; // " indicates end of attribute, unless preceded by backstroke |
|
|
n = utf8_out(c, &buf[ix]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int n = utf8_out(c, &buf[ix]); |
|
|
ix += n; |
|
|
ix += n; |
|
|
prev_c = c; |
|
|
prev_c = c; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const char *p; |
|
|
const char *p; |
|
|
SSML_STACK *sp; |
|
|
SSML_STACK *sp; |
|
|
const char *v_id; |
|
|
const char *v_id; |
|
|
int voice_name_specified; |
|
|
|
|
|
int voice_found; |
|
|
int voice_found; |
|
|
espeak_VOICE voice_select; |
|
|
espeak_VOICE voice_select; |
|
|
static char voice_name[40]; |
|
|
static char voice_name[40]; |
|
|
char language[40]; |
|
|
char language[40]; |
|
|
char buf[80]; |
|
|
|
|
|
|
|
|
|
|
|
MAKE_MEM_UNDEFINED(&voice_name, sizeof(voice_name)); |
|
|
MAKE_MEM_UNDEFINED(&voice_name, sizeof(voice_name)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (ix = 0; ix < n_ssml_stack; ix++) { |
|
|
for (ix = 0; ix < n_ssml_stack; ix++) { |
|
|
sp = &ssml_stack[ix]; |
|
|
sp = &ssml_stack[ix]; |
|
|
voice_name_specified = 0; |
|
|
|
|
|
|
|
|
int voice_name_specified = 0; |
|
|
|
|
|
|
|
|
if ((sp->voice_name[0] != 0) && (SelectVoiceByName(NULL, sp->voice_name) != NULL)) { |
|
|
if ((sp->voice_name[0] != 0) && (SelectVoiceByName(NULL, sp->voice_name) != NULL)) { |
|
|
voice_name_specified = 1; |
|
|
voice_name_specified = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((strchr(v_id, '+') == NULL) && ((voice_select.gender == ENGENDER_UNKNOWN) || (voice_select.gender == base_voice->gender)) && (base_voice_variant_name[0] != 0)) { |
|
|
if ((strchr(v_id, '+') == NULL) && ((voice_select.gender == ENGENDER_UNKNOWN) || (voice_select.gender == base_voice->gender)) && (base_voice_variant_name[0] != 0)) { |
|
|
// a voice variant has not been selected, use the original voice variant |
|
|
// a voice variant has not been selected, use the original voice variant |
|
|
|
|
|
char buf[80]; |
|
|
sprintf(buf, "%s+%s", v_id, base_voice_variant_name); |
|
|
sprintf(buf, "%s+%s", v_id, base_voice_variant_name); |
|
|
strncpy0(voice_name, buf, sizeof(voice_name)); |
|
|
strncpy0(voice_name, buf, sizeof(voice_name)); |
|
|
return voice_name; |
|
|
return voice_name; |
|
|
|
|
|
|
|
|
// a voice change. |
|
|
// a voice change. |
|
|
// Returns CLAUSE_TYPE_VOICE_CHANGE if there is a voice change |
|
|
// Returns CLAUSE_TYPE_VOICE_CHANGE if there is a voice change |
|
|
|
|
|
|
|
|
const wchar_t *lang; |
|
|
|
|
|
const wchar_t *gender; |
|
|
|
|
|
const wchar_t *name; |
|
|
|
|
|
const wchar_t *age; |
|
|
|
|
|
const wchar_t *variant; |
|
|
|
|
|
int value; |
|
|
|
|
|
const char *new_voice_id; |
|
|
const char *new_voice_id; |
|
|
|
|
|
|
|
|
static const MNEM_TAB mnem_gender[] = { |
|
|
static const MNEM_TAB mnem_gender[] = { |
|
|
|
|
|
|
|
|
if (n_ssml_stack > 1) |
|
|
if (n_ssml_stack > 1) |
|
|
n_ssml_stack--; |
|
|
n_ssml_stack--; |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
const wchar_t *lang; |
|
|
|
|
|
const wchar_t *gender; |
|
|
|
|
|
const wchar_t *name; |
|
|
|
|
|
const wchar_t *age; |
|
|
|
|
|
const wchar_t *variant; |
|
|
|
|
|
|
|
|
// add a stack frame if any voice details are specified |
|
|
// add a stack frame if any voice details are specified |
|
|
lang = GetSsmlAttribute(pw, "xml:lang"); |
|
|
lang = GetSsmlAttribute(pw, "xml:lang"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ssml_sp = &ssml_stack[n_ssml_stack++]; |
|
|
ssml_sp = &ssml_stack[n_ssml_stack++]; |
|
|
|
|
|
|
|
|
|
|
|
int value; |
|
|
|
|
|
|
|
|
attrcopy_utf8(ssml_sp->language, lang, sizeof(ssml_sp->language)); |
|
|
attrcopy_utf8(ssml_sp->language, lang, sizeof(ssml_sp->language)); |
|
|
attrcopy_utf8(ssml_sp->voice_name, name, sizeof(ssml_sp->voice_name)); |
|
|
attrcopy_utf8(ssml_sp->voice_name, name, sizeof(ssml_sp->voice_name)); |
|
|
if ((value = attrnumber(variant, 1, 0)) > 0) |
|
|
if ((value = attrnumber(variant, 1, 0)) > 0) |
|
|
|
|
|
|
|
|
// Set the speech parameters from the parameter stack |
|
|
// Set the speech parameters from the parameter stack |
|
|
int param; |
|
|
int param; |
|
|
int ix; |
|
|
int ix; |
|
|
int value; |
|
|
|
|
|
char buf[20]; |
|
|
char buf[20]; |
|
|
int new_parameters[N_SPEECH_PARAM]; |
|
|
int new_parameters[N_SPEECH_PARAM]; |
|
|
static const char cmd_letter[N_SPEECH_PARAM] = { 0, 'S', 'A', 'P', 'R', 0, 'C', 0, 0, 0, 0, 0, 'F' }; // embedded command letters |
|
|
static const char cmd_letter[N_SPEECH_PARAM] = { 0, 'S', 'A', 'P', 'R', 0, 'C', 0, 0, 0, 0, 0, 'F' }; // embedded command letters |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (param = 0; param < N_SPEECH_PARAM; param++) { |
|
|
for (param = 0; param < N_SPEECH_PARAM; param++) { |
|
|
|
|
|
int value; |
|
|
if ((value = new_parameters[param]) != speech_parameters[param]) { |
|
|
if ((value = new_parameters[param]) != speech_parameters[param]) { |
|
|
buf[0] = 0; |
|
|
buf[0] = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ NULL, 0 } |
|
|
{ NULL, 0 } |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
int ix; |
|
|
|
|
|
int letter; |
|
|
int letter; |
|
|
char *p; |
|
|
char *p; |
|
|
|
|
|
|
|
|
p = &outbuf[index]; |
|
|
p = &outbuf[index]; |
|
|
|
|
|
|
|
|
if ((letter = LookupMnem(keynames, p)) != 0) { |
|
|
if ((letter = LookupMnem(keynames, p)) != 0) { |
|
|
ix = utf8_out(letter, p); |
|
|
|
|
|
|
|
|
int ix; |
|
|
|
|
|
ix = utf8_out(letter, p); |
|
|
*outix = index + ix; |
|
|
*outix = index + ix; |
|
|
return letter; |
|
|
return letter; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void SetProsodyParameter(int param_type, const wchar_t *attr1, PARAM_STACK *sp, PARAM_STACK *param_stack, int *speech_parameters) |
|
|
static void SetProsodyParameter(int param_type, const wchar_t *attr1, PARAM_STACK *sp, PARAM_STACK *param_stack, int *speech_parameters) |
|
|
{ |
|
|
{ |
|
|
int value; |
|
|
int value; |
|
|
int sign; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const MNEM_TAB mnem_volume[] = { |
|
|
static const MNEM_TAB mnem_volume[] = { |
|
|
{ "default", 100 }, |
|
|
{ "default", 100 }, |
|
|
|
|
|
|
|
|
// mnemonic specifies a value as a percentage of the base pitch/range/rate/volume |
|
|
// mnemonic specifies a value as a percentage of the base pitch/range/rate/volume |
|
|
sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; |
|
|
sp->parameter[param_type] = (param_stack[0].parameter[param_type] * value)/100; |
|
|
} else { |
|
|
} else { |
|
|
sign = attr_prosody_value(param_type, attr1, &value); |
|
|
|
|
|
|
|
|
int sign = attr_prosody_value(param_type, attr1, &value); |
|
|
|
|
|
|
|
|
if (sign == 0) |
|
|
if (sign == 0) |
|
|
sp->parameter[param_type] = value; // absolute value in Hz |
|
|
sp->parameter[param_type] = value; // absolute value in Hz |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int ix; |
|
|
unsigned int ix; |
|
|
int index; |
|
|
int index; |
|
|
int c; |
|
|
|
|
|
int tag_type; |
|
|
int tag_type; |
|
|
int value; |
|
|
int value; |
|
|
int value2; |
|
|
int value2; |
|
|
|
|
|
|
|
|
const wchar_t *attr2; |
|
|
const wchar_t *attr2; |
|
|
const wchar_t *attr3; |
|
|
const wchar_t *attr3; |
|
|
int terminator; |
|
|
int terminator; |
|
|
char *uri; |
|
|
|
|
|
int param_type; |
|
|
int param_type; |
|
|
char tag_name[40]; |
|
|
char tag_name[40]; |
|
|
char buf[160]; |
|
|
char buf[160]; |
|
|
|
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
for (ix = 0; ix < (sizeof(tag_name)-1); ix++) { |
|
|
for (ix = 0; ix < (sizeof(tag_name)-1); ix++) { |
|
|
|
|
|
int c; |
|
|
if (((c = xml_buf[ix]) == 0) || iswspace(c)) |
|
|
if (((c = xml_buf[ix]) == 0) || iswspace(c)) |
|
|
break; |
|
|
break; |
|
|
tag_name[ix] = tolower((char)c); |
|
|
tag_name[ix] = tolower((char)c); |
|
|
|
|
|
|
|
|
sp = PushParamStack(tag_type, n_param_stack, (PARAM_STACK *)param_stack); |
|
|
sp = PushParamStack(tag_type, n_param_stack, (PARAM_STACK *)param_stack); |
|
|
|
|
|
|
|
|
if ((attr1 = GetSsmlAttribute(px, "src")) != NULL) { |
|
|
if ((attr1 = GetSsmlAttribute(px, "src")) != NULL) { |
|
|
char fname[256]; |
|
|
|
|
|
attrcopy_utf8(buf, attr1, sizeof(buf)); |
|
|
attrcopy_utf8(buf, attr1, sizeof(buf)); |
|
|
|
|
|
|
|
|
if (uri_callback == NULL) { |
|
|
if (uri_callback == NULL) { |
|
|
if ((xmlbase != NULL) && (buf[0] != '/')) { |
|
|
if ((xmlbase != NULL) && (buf[0] != '/')) { |
|
|
|
|
|
char fname[256]; |
|
|
sprintf(fname, "%s/%s", xmlbase, buf); |
|
|
sprintf(fname, "%s/%s", xmlbase, buf); |
|
|
index = LoadSoundFile2(fname); |
|
|
index = LoadSoundFile2(fname); |
|
|
} else |
|
|
} else |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
if ((index = AddNameData(buf, 0)) >= 0) { |
|
|
if ((index = AddNameData(buf, 0)) >= 0) { |
|
|
|
|
|
char *uri; |
|
|
uri = &namedata[index]; |
|
|
uri = &namedata[index]; |
|
|
if (uri_callback(1, uri, xmlbase) == 0) { |
|
|
if (uri_callback(1, uri, xmlbase) == 0) { |
|
|
sprintf(buf, "%c%dU", CTRL_EMBEDDED, index); |
|
|
sprintf(buf, "%c%dU", CTRL_EMBEDDED, index); |