From: Bohdan Tkachenko <bohdan@tkachenko.dev> Previously both methods returned E_NOTIMPL. Store the priority on the speech_voice struct and validate the value against SPVPRI_NORMAL, SPVPRI_ALERT and SPVPRI_OVER. This unblocks Overwatch 2's Audio Captions accessibility TTS, which calls SetPriority during voice setup and aborts on any non-S_OK result. Add tests covering priority round-trips between SPVPRI_NORMAL, SPVPRI_ALERT and SPVPRI_OVER, and verify the default is SPVPRI_NORMAL. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59749 --- dlls/sapi/tests/tts.c | 25 +++++++++++++++++++++++++ dlls/sapi/tts.c | 28 ++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index c89bffae5a0..57fda412d79 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -494,6 +494,7 @@ static void test_spvoice(void) ISpDataKey *attrs_key; LONG rate; USHORT volume; + SPVPRIORITY priority; ULONG stream_num; DWORD regid; WAVEFORMATEX wfx; @@ -651,6 +652,30 @@ static void test_spvoice(void) hr = ISpVoice_SetVolume(voice, 101); ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + priority = 0xdead; + hr = ISpVoice_GetPriority(voice, &priority); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(priority == SPVPRI_NORMAL, "got %d.\n", priority); + + hr = ISpVoice_SetPriority(voice, SPVPRI_ALERT); + ok(hr == S_OK, "got %#lx.\n", hr); + + priority = 0xdead; + hr = ISpVoice_GetPriority(voice, &priority); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(priority == SPVPRI_ALERT, "got %d.\n", priority); + + hr = ISpVoice_SetPriority(voice, SPVPRI_OVER); + ok(hr == S_OK, "got %#lx.\n", hr); + + priority = 0xdead; + hr = ISpVoice_GetPriority(voice, &priority); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(priority == SPVPRI_OVER, "got %d.\n", priority); + + hr = ISpVoice_SetPriority(voice, SPVPRI_NORMAL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = CoRegisterClassObject(&CLSID_TestEngine, (IUnknown *)&test_engine_cf, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, ®id); ok(hr == S_OK, "got %#lx.\n", hr); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 159635a4559..3e11524cb59 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -58,6 +58,7 @@ struct speech_voice LONG rate; SPVSTATE state; struct async_queue queue; + SPVPRIORITY priority; CRITICAL_SECTION cs; }; @@ -997,6 +998,7 @@ done: ISpAudio_Release(audio); } CoTaskMemFree(wfx); + ISpTTSEngine_Release(speak_task->engine); free_frag_list(speak_task->frag_list); ISpTTSEngineSite_Release(speak_task->site); @@ -1230,16 +1232,33 @@ static HRESULT WINAPI spvoice_Skip(ISpVoice *iface, const WCHAR *type, LONG item static HRESULT WINAPI spvoice_SetPriority(ISpVoice *iface, SPVPRIORITY priority) { - FIXME("(%p, %d): stub.\n", iface, priority); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %d).\n", iface, priority); + + if (priority != SPVPRI_NORMAL && priority != SPVPRI_ALERT && priority != SPVPRI_OVER) + return E_INVALIDARG; + + EnterCriticalSection(&This->cs); + This->priority = priority; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetPriority(ISpVoice *iface, SPVPRIORITY *priority) { - FIXME("(%p, %p): stub.\n", iface, priority); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, priority); + + if (!priority) return E_POINTER; + + EnterCriticalSection(&This->cs); + *priority = This->priority; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_SetAlertBoundary(ISpVoice *iface, SPEVENTENUM boundary) @@ -1714,6 +1733,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->actions = SPVES_CONTINUE; This->volume = 100; This->rate = 0; + This->priority = SPVPRI_NORMAL; memset(&This->state, 0, sizeof(This->state)); This->state.Volume = 100; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10906