[PATCH 0/4] MR3128: sapi: Implement some basic SpVoice functions.
From: Shaun Ren <sren(a)codeweavers.com> --- dlls/sapi/tests/tts.c | 25 +++++++++++++++++++ dlls/sapi/tts.c | 56 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 8303dfc6ebc..7e13abf7a0d 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -93,9 +93,34 @@ static void test_interfaces(void) ISpeechVoice_Release(speech_voice); } +static void test_spvoice(void) +{ + ISpVoice *voice; + ISpMMSysAudio *audio_out; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpVoice, (void **)&voice); + ok(hr == S_OK, "Failed to create SpVoice: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&audio_out); + ok(hr == S_OK, "Failed to create SpMMAudioOut: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); + todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr); + + ISpVoice_Release(voice); + ISpMMSysAudio_Release(audio_out); +} + START_TEST(tts) { CoInitialize(NULL); test_interfaces(); + test_spvoice(); CoUninitialize(); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 9f60c70e6c4..bebda86f09d 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -40,6 +40,9 @@ struct speech_voice ISpVoice ISpVoice_iface; IConnectionPointContainer IConnectionPointContainer_iface; LONG ref; + + ISpStreamFormat *output; + CRITICAL_SECTION cs; }; static inline struct speech_voice *impl_from_ISpeechVoice(ISpeechVoice *iface) @@ -102,6 +105,9 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) if (!ref) { + if (This->output) ISpStreamFormat_Release(This->output); + DeleteCriticalSection(&This->cs); + heap_free(This); } @@ -515,11 +521,51 @@ static HRESULT WINAPI spvoice_GetInfo(ISpVoice *iface, SPEVENTSOURCEINFO *info) return E_NOTIMPL; } -static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL changes) +static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL allow_format_changes) { - FIXME("(%p, %p, %d): stub.\n", iface, unk, changes); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpStreamFormat *stream = NULL; + ISpObjectToken *token = NULL; + HRESULT hr; - return E_NOTIMPL; + TRACE("(%p, %p, %d).\n", iface, unk, allow_format_changes); + + if (!allow_format_changes) + FIXME("ignoring allow_format_changes = FALSE.\n"); + + if (!unk) + { + /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */ + if (FAILED(hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStreamFormat, (void **)&stream))) + return hr; + } + else + { + if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpStreamFormat, (void **)&stream))) + { + if (FAILED(IUnknown_QueryInterface(unk, &IID_ISpObjectToken, (void **)&token))) + return E_INVALIDARG; + } + } + + if (!stream) + { + hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpStreamFormat, (void **)&stream); + ISpObjectToken_Release(token); + if (FAILED(hr)) + return hr; + } + + EnterCriticalSection(&This->cs); + + if (This->output) + ISpStreamFormat_Release(This->output); + This->output = stream; + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetOutputObjectToken(ISpVoice *iface, ISpObjectToken **token) @@ -798,6 +844,10 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->IConnectionPointContainer_iface.lpVtbl = &container_vtbl; This->ref = 1; + This->output = NULL; + + InitializeCriticalSection(&This->cs); + hr = ISpeechVoice_QueryInterface(&This->ISpeechVoice_iface, iid, obj); ISpeechVoice_Release(&This->ISpeechVoice_iface); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3128
From: Shaun Ren <sren(a)codeweavers.com> --- dlls/sapi/tests/tts.c | 28 +++++++++++++++++++++ dlls/sapi/tts.c | 58 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 7e13abf7a0d..225b1d8d172 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -97,6 +97,9 @@ static void test_spvoice(void) { ISpVoice *voice; ISpMMSysAudio *audio_out; + ISpObjectTokenCategory *token_cat; + ISpObjectToken *token; + WCHAR *token_id = NULL, *default_token_id = NULL; HRESULT hr; hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, @@ -113,6 +116,31 @@ static void test_spvoice(void) hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr); + hr = ISpVoice_SetVoice(voice, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpVoice_GetVoice(voice, &token); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpObjectToken_GetId(token, &token_id); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpObjectTokenCategory, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectTokenCategory, (void **)&token_cat); + ok(hr == S_OK, "Failed to create SpObjectTokenCategory: %#lx.\n", hr); + + hr = ISpObjectTokenCategory_SetId(token_cat, SPCAT_VOICES, FALSE); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpObjectTokenCategory_GetDefaultTokenId(token_cat, &default_token_id); + ok(hr == S_OK, "got %#lx.\n", hr); + + ok(!wcscmp(token_id, default_token_id), "token_id != default_token_id\n"); + + CoTaskMemFree(token_id); + CoTaskMemFree(default_token_id); + ISpObjectToken_Release(token); + ISpObjectTokenCategory_Release(token_cat); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index bebda86f09d..95bbb755668 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -42,6 +42,7 @@ struct speech_voice LONG ref; ISpStreamFormat *output; + ISpTTSEngine *engine; CRITICAL_SECTION cs; }; @@ -106,6 +107,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) if (!ref) { if (This->output) ISpStreamFormat_Release(This->output); + if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs); heap_free(This); @@ -598,16 +600,63 @@ static HRESULT WINAPI spvoice_Resume(ISpVoice *iface) static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) { - FIXME("(%p, %p): stub.\n", iface, token); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpTTSEngine *engine; + HRESULT hr; - return E_NOTIMPL; + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + { + if (FAILED(hr = create_default_token(SPCAT_VOICES, &token))) + return hr; + } + + hr = ISpObjectToken_CreateInstance(token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&engine); + ISpObjectToken_Release(token); + if (FAILED(hr)) + return hr; + + EnterCriticalSection(&This->cs); + + if (This->engine) + ISpTTSEngine_Release(This->engine); + This->engine = engine; + + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) { - FIXME("(%p, %p): stub.\n", iface, token); + struct speech_voice *This = impl_from_ISpVoice(iface); + ISpObjectWithToken *engine_token_iface; + HRESULT hr; + + TRACE("(%p, %p).\n", iface, token); + + if (!token) + return E_POINTER; + + EnterCriticalSection(&This->cs); + + if (!This->engine) + { + LeaveCriticalSection(&This->cs); + return create_default_token(SPCAT_VOICES, token); + } + + if (SUCCEEDED(hr = ISpTTSEngine_QueryInterface(This->engine, &IID_ISpObjectWithToken, (void **)&engine_token_iface))) + { + hr = ISpObjectWithToken_GetObjectToken(engine_token_iface, token); + ISpObjectWithToken_Release(engine_token_iface); + } + + LeaveCriticalSection(&This->cs); - return token_create(NULL, &IID_ISpObjectToken, (void **)token); + return hr; } static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWORD flags, ULONG *number) @@ -845,6 +894,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1; This->output = NULL; + This->engine = NULL; InitializeCriticalSection(&This->cs); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3128
From: Shaun Ren <sren(a)codeweavers.com> --- dlls/sapi/tests/tts.c | 30 ++++++++++++++++++++++++++++++ dlls/sapi/tts.c | 26 ++++++++++++++++++++------ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 225b1d8d172..4b58fbe7480 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -100,6 +100,7 @@ static void test_spvoice(void) ISpObjectTokenCategory *token_cat; ISpObjectToken *token; WCHAR *token_id = NULL, *default_token_id = NULL; + LONG rate; HRESULT hr; hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, @@ -141,6 +142,35 @@ static void test_spvoice(void) ISpObjectToken_Release(token); ISpObjectTokenCategory_Release(token_cat); + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 0, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, 1); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 1, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, -1000); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == -1000, "rate = %ld\n", rate); + + hr = ISpVoice_SetRate(voice, 1000); + ok(hr == S_OK, "got %#lx.\n", hr); + + rate = 0xdeadbeef; + hr = ISpVoice_GetRate(voice, &rate); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(rate == 1000, "rate = %ld\n", rate); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 95bbb755668..a6adaabbb3c 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; + LONG rate; CRITICAL_SECTION cs; }; @@ -715,18 +716,30 @@ static HRESULT WINAPI spvoice_GetAlertBoundary(ISpVoice *iface, SPEVENTENUM *bou return E_NOTIMPL; } -static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG adjust) +static HRESULT WINAPI spvoice_SetRate(ISpVoice *iface, LONG rate) { - FIXME("(%p, %ld): stub.\n", iface, adjust); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %ld).\n", iface, rate); + + EnterCriticalSection(&This->cs); + This->rate = rate; + LeaveCriticalSection(&This->cs); + + return S_OK; } -static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *adjust) +static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *rate) { - FIXME("(%p, %p): stub.\n", iface, adjust); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, rate); + + EnterCriticalSection(&This->cs); + *rate = This->rate; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume) @@ -895,6 +908,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; + This->rate = 0; InitializeCriticalSection(&This->cs); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3128
From: Shaun Ren <sren(a)codeweavers.com> --- dlls/sapi/tests/tts.c | 25 +++++++++++++++++++++++++ dlls/sapi/tts.c | 25 +++++++++++++++++++++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 4b58fbe7480..23523474a3b 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -101,6 +101,7 @@ static void test_spvoice(void) ISpObjectToken *token; WCHAR *token_id = NULL, *default_token_id = NULL; LONG rate; + USHORT volume; HRESULT hr; hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, @@ -171,6 +172,30 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(rate == 1000, "rate = %ld\n", rate); + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 100, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 0); + ok(hr == S_OK, "got %#lx.\n", hr); + + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 0, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 100); + ok(hr == S_OK, "got %#lx.\n", hr); + + volume = 0xbeef; + hr = ISpVoice_GetVolume(voice, &volume); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(volume == 100, "volume = %d\n", volume); + + hr = ISpVoice_SetVolume(voice, 101); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + ISpVoice_Release(voice); ISpMMSysAudio_Release(audio_out); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index a6adaabbb3c..9cbdc02436f 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice ISpStreamFormat *output; ISpTTSEngine *engine; + USHORT volume; LONG rate; CRITICAL_SECTION cs; }; @@ -744,16 +745,31 @@ static HRESULT WINAPI spvoice_GetRate(ISpVoice *iface, LONG *rate) static HRESULT WINAPI spvoice_SetVolume(ISpVoice *iface, USHORT volume) { - FIXME("(%p, %d): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %d).\n", iface, volume); + + if (volume > 100) + return E_INVALIDARG; + + EnterCriticalSection(&This->cs); + This->volume = volume; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_GetVolume(ISpVoice *iface, USHORT *volume) { - FIXME("(%p, %p): stub.\n", iface, volume); + struct speech_voice *This = impl_from_ISpVoice(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, volume); + + EnterCriticalSection(&This->cs); + *volume = This->volume; + LeaveCriticalSection(&This->cs); + + return S_OK; } static HRESULT WINAPI spvoice_WaitUntilDone(ISpVoice *iface, ULONG timeout) @@ -908,6 +924,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->engine = NULL; + This->volume = 100; This->rate = 0; InitializeCriticalSection(&This->cs); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/3128
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=134012 Your paranoid android. === w11pro64_amd (64 bit report) === sapi: tts.c:112: Test failed: got 0x8004503a. tts.c:119: Test failed: got 0x80045007. === debian11 (build log) === /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sapi/tts.c:614: undefined reference to `create_default_token' /home/winetest/tools/testbot/var/wine-win32/../wine/dlls/sapi/tts.c:650: undefined reference to `create_default_token' collect2: error: ld returned 1 exit status Task: The win32 Wine build failed === debian11b (build log) === /home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/sapi/tts.c:614: undefined reference to `create_default_token' /home/winetest/tools/testbot/var/wine-wow64/../wine/dlls/sapi/tts.c:650: undefined reference to `create_default_token' collect2: error: ld returned 1 exit status Task: The wow64 Wine build failed
participants (3)
-
Marvin -
Shaun Ren -
Shaun Ren (@shaunren)