From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/tests/tts.c | 26 +++++++------- dlls/sapi/xml.c | 80 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 14 deletions(-)
diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 569ee918164..86b484b268e 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -1052,24 +1052,22 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine);
hr = ISpVoice_Speak(voice, text4, SPF_DEFAULT, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count);
- if (test_engine.frag_count == 2) { - check_frag_text(0, L"One, "); - check_frag_state_field(0, eAction, SPVA_Speak, "%d"); - check_frag_state_field(0, RateAdj, 0, "%ld"); + check_frag_text(0, L"One, "); + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + check_frag_state_field(0, RateAdj, 0, "%ld");
- check_frag_text(1, L"two."); - check_frag_state_field(1, eAction, SPVA_Speak, "%d"); - check_frag_state_field(1, RateAdj, -17, "%ld"); /* 3^(-17/10) ~= 0.15 */ - } + check_frag_text(1, L"two."); + check_frag_state_field(1, eAction, SPVA_Speak, "%d"); + check_frag_state_field(1, RateAdj, -17, "%ld"); /* 3^(-17/10) ~= 0.15 */
reset_engine_params(&test_engine);
hr = ISpVoice_Speak(voice, text5, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 8 || broken(test_engine.frag_count == 3) /* win7 */, + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 8 || broken(test_engine.frag_count == 3) /* win7 */, "got %Iu.\n", test_engine.frag_count);
if (test_engine.frag_count == 8) { @@ -1086,7 +1084,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine);
hr = ISpVoice_Speak(voice, text6, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr);
if (hr == S_OK) { ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); @@ -1101,7 +1099,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine);
hr = ISpVoice_Speak(voice, text7, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr);
if (hr == S_OK) { ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index aad32462272..97305df344c 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -25,6 +25,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <math.h> #include <assert.h>
#define COBJMACROS @@ -457,6 +458,81 @@ static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE return S_OK; }
+static HRESULT parse_double_value(const xmlstr_t *value, double *res, size_t *read_len) +{ + WCHAR *buf, *end; + + if (!value->len) + return SPERR_UNSUPPORTED_FORMAT; + + if (!(buf = xmlstrdupW(value))) + return E_OUTOFMEMORY; + + *res = wcstod(buf, &end); + *read_len = end - buf; + + free(buf); + return S_OK; +} + +static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent); + +static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state, const struct xml_elem *parent) +{ + struct xml_attr attr; + BOOL end = FALSE; + size_t read_len; + HRESULT hr; + + while (next_xml_attr(parser, &attr, &end)) + { + if (xml_attr_eq(&attr, L"rate")) + { + if (xmlstr_eq(&attr.value, L"x-slow")) + state.RateAdj = -9; + else if (xmlstr_eq(&attr.value, L"slow")) + state.RateAdj = -4; + else if (xmlstr_eq(&attr.value, L"medium")) + state.RateAdj = 0; + else if (xmlstr_eq(&attr.value, L"fast")) + state.RateAdj = 4; + else if (xmlstr_eq(&attr.value, L"x-fast")) + state.RateAdj = 9; + else + { + double rate; + + if (FAILED(hr = parse_double_value(&attr.value, &rate, &read_len))) + return hr; + if (read_len < attr.value.len - 1 || + (read_len == attr.value.len - 1 && attr.value.ptr[read_len] != '%')) + { + ERR("Invalid value %s for the rate attribute in <prosody>.\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (attr.value.ptr[attr.value.len - 1] == '%') + rate = 1 + rate / 100; + + if (rate < 0) + state.RateAdj = 0; + else if (rate <= 0.01) + state.RateAdj = -10; + else + state.RateAdj = lround(log(rate) * (10 / log(3))); + } + } + else + { + FIXME("Unknown <prosody> attribute %s.\n", debugstr_xmlstr(&attr.name)); + return E_NOTIMPL; + } + } + + if (end) return S_OK; + return parse_ssml_elems(parser, &state, parent); +} + static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) { struct xml_attr attr; @@ -476,6 +552,10 @@ static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state if (end) continue; hr = parse_ssml_elems(parser, state, &elem); } + else if (xml_elem_eq(&elem, ssml_ns, L"prosody")) + { + hr = parse_ssml_prosody_elem(parser, *state, &elem); + } else { FIXME("Unknown element %s.\n", debugstr_xmlstr(&elem.name));