From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/tests/tts.c | 52 ++++++++++++++++++++++++++++++++++++++++++- dlls/sapi/tts.c | 49 +++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 47703847279..a6aa26d37b8 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -112,6 +112,9 @@ struct test_engine DWORD flags; GUID fmtid; SPVTEXTFRAG *frag_list; + LONG rate; + USHORT volume; + BOOL aborted; };
static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frag_list) @@ -154,6 +157,9 @@ static void reset_engine_params(struct test_engine *engine) engine->speak_called = FALSE; engine->flags = 0xdeadbeef; memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); + engine->rate = 0xdeadbeef; + engine->volume = 0xbeef; + engine->aborted = FALSE;
for (frag = engine->frag_list; frag; frag = next) { @@ -207,6 +213,7 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI ISpTTSEngineSite *site) { struct test_engine *engine = impl_from_ISpTTSEngine(iface); + DWORD actions; char *buf; int i; HRESULT hr; @@ -216,9 +223,28 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI copy_frag_list(frag_list, &engine->frag_list); engine->speak_called = TRUE;
+ actions = ISpTTSEngineSite_GetActions(site); + ok(actions == (SPVES_CONTINUE | SPVES_RATE | SPVES_VOLUME), "got %#lx.\n", actions); + + hr = ISpTTSEngineSite_GetRate(site, &engine->rate); + ok(hr == S_OK, "got %#lx.\n", hr); + actions = ISpTTSEngineSite_GetActions(site); + ok(actions == (SPVES_CONTINUE | SPVES_VOLUME), "got %#lx.\n", actions); + + hr = ISpTTSEngineSite_GetVolume(site, &engine->volume); + ok(hr == S_OK, "got %#lx.\n", hr); + actions = ISpTTSEngineSite_GetActions(site); + ok(actions == SPVES_CONTINUE, "got %#lx.\n", actions); + buf = calloc(1, 22050 * 2 / 5); for (i = 0; i < 5; i++) { + actions = ISpTTSEngineSite_GetActions(site); + if (actions & SPVES_ABORT) + { + engine->aborted = TRUE; + break; + } hr = ISpTTSEngineSite_Write(site, buf, 22050 * 2 / 5, NULL); ok(hr == S_OK, "got %#lx.\n", hr); Sleep(100); @@ -508,7 +534,10 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(stream_num == 0xdeadbeef, "got %lu.\n", stream_num);
- test_engine.speak_called = FALSE; + ISpVoice_SetRate(voice, 0); + ISpVoice_SetVolume(voice, 100); + + reset_engine_params(&test_engine); stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num); @@ -521,9 +550,14 @@ static void test_spvoice(void) ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); + ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration >= 1000 && duration < 3000, "took %lu ms.\n", duration);
+ ISpVoice_SetRate(voice, 0); + ISpVoice_SetVolume(voice, 90); + reset_engine_params(&test_engine); stream_num = 0xdeadbeef; start = GetTickCount(); @@ -541,6 +575,22 @@ static void test_spvoice(void) ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); + ok(test_engine.volume == 90, "got %d.\n", test_engine.volume); + + Sleep(2000); + + reset_engine_params(&test_engine); + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + Sleep(200); + start = GetTickCount(); + hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); + duration = GetTickCount() - start; + ok(hr == S_OK, "got %#lx.\n", hr); + ok(duration < 300, "took %lu ms.\n", duration); + ok(test_engine.aborted, "not aborted.\n");
done: reset_engine_params(&test_engine); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 84f39c3039c..816b2be2539 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -45,6 +45,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; ULONG cur_stream_num; + DWORD actions; USHORT volume; LONG rate; struct async_queue queue; @@ -777,6 +778,13 @@ static void speak_proc(struct async_task *task)
EnterCriticalSection(&This->cs);
+ if (This->actions & SPVES_ABORT) + { + LeaveCriticalSection(&This->cs); + hr = S_OK; + goto done; + } + if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx))) { LeaveCriticalSection(&This->cs); @@ -847,6 +855,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR
EnterCriticalSection(&This->cs);
+ This->actions = SPVES_ABORT; if (This->output && SUCCEEDED(ISpStreamFormat_QueryInterface(This->output, &IID_ISpAudio, (void **)&audio))) { ISpAudio_SetState(audio, SPAS_CLOSED, 0); @@ -857,6 +866,10 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR
async_empty_queue(&This->queue);
+ EnterCriticalSection(&This->cs); + This->actions = SPVES_CONTINUE; + LeaveCriticalSection(&This->cs); + if (!contents || !*contents) return S_OK; } @@ -1011,6 +1024,7 @@ static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG rate)
EnterCriticalSection(&This->cs); This->rate = rate; + This->actions |= SPVES_RATE; LeaveCriticalSection(&This->cs);
return S_OK; @@ -1040,6 +1054,7 @@ static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume)
EnterCriticalSection(&This->cs); This->volume = volume; + This->actions |= SPVES_VOLUME; LeaveCriticalSection(&This->cs);
return S_OK; @@ -1209,9 +1224,16 @@ static HRESULT WINAPI ttsenginesite_GetEventInterest(ISpTTSEngineSite *iface, UL
static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface) { - FIXME("(%p): stub.\n", iface); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); + DWORD actions;
- return SPVES_CONTINUE; + TRACE("(%p).\n", iface); + + EnterCriticalSection(&This->voice->cs); + actions = This->voice->actions; + LeaveCriticalSection(&This->voice->cs); + + return actions; }
static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written) @@ -1228,16 +1250,30 @@ static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *b
static HRESULT WINAPI ttsenginesite_GetRate(ISpTTSEngineSite *iface, long *rate) { - FIXME("(%p, %p): stub.\n", iface, rate); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
- return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, rate); + + EnterCriticalSection(&This->voice->cs); + *rate = This->voice->rate; + This->voice->actions &= ~SPVES_RATE; + LeaveCriticalSection(&This->voice->cs); + + return S_OK; }
static HRESULT WINAPI ttsenginesite_GetVolume(ISpTTSEngineSite *iface, USHORT *volume) { - FIXME("(%p, %p): stub.\n", iface, volume); + struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface);
- return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, volume); + + EnterCriticalSection(&This->voice->cs); + *volume = This->voice->volume; + This->voice->actions &= ~SPVES_VOLUME; + LeaveCriticalSection(&This->voice->cs); + + return S_OK; }
static HRESULT WINAPI ttsenginesite_GetSkipInfo(ISpTTSEngineSite *iface, SPVSKIPTYPE *type, long *skip_count) @@ -1355,6 +1391,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; This->cur_stream_num = 0; + This->actions = SPVES_CONTINUE; This->volume = 100; This->rate = 0; memset(&This->queue, 0, sizeof(This->queue));