Instead of creating the TTS voice engine directly in SetVoice, we save the specified voice token, and only create them in speak_proc when necessary.
-- v2: sapi: Create a new engine only when needed in ISpVoice.
From: Shaun Ren sren@codeweavers.com
Instead of creating the TTS voice engine directly in SetVoice, we save the specified voice token, and only create them in speak_proc when necessary. --- dlls/sapi/tts.c | 56 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 19 deletions(-)
diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 410a57b4bfc..52af0a12a05 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -43,6 +43,7 @@ struct speech_voice LONG ref;
ISpStreamFormat *output; + ISpObjectToken *engine_token; ISpTTSEngine *engine; LONG cur_stream_num; DWORD actions; @@ -163,6 +164,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) { async_cancel_queue(&This->queue); if (This->output) ISpStreamFormat_Release(This->output); + if (This->engine_token) ISpObjectToken_Release(This->engine_token); if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs);
@@ -660,7 +662,7 @@ static HRESULT WINAPI spvoice_Resume(ISpVoice *iface) static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) { struct speech_voice *This = impl_from_ISpVoice(iface); - ISpTTSEngine *engine; + WCHAR *id = NULL, *old_id = NULL; HRESULT hr;
TRACE("(%p, %p).\n", iface, token); @@ -673,27 +675,37 @@ static HRESULT WINAPI spvoice_SetVoice(ISpVoice *iface, ISpObjectToken *token) else ISpObjectToken_AddRef(token);
- 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_token && + SUCCEEDED(ISpObjectToken_GetId(token, &id)) && + SUCCEEDED(ISpObjectToken_GetId(This->engine_token, &old_id)) && + !wcscmp(id, old_id)) + { + ISpObjectToken_Release(token); + goto done; + } + + if (This->engine_token) + ISpObjectToken_Release(This->engine_token); + This->engine_token = token; + if (This->engine) + { ISpTTSEngine_Release(This->engine); - This->engine = engine; + This->engine = NULL; + }
+done: LeaveCriticalSection(&This->cs); - + CoTaskMemFree(id); + CoTaskMemFree(old_id); return S_OK; }
static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token) { struct speech_voice *This = impl_from_ISpVoice(iface); - ISpObjectWithToken *engine_token_iface; - HRESULT hr;
TRACE("(%p, %p).\n", iface, token);
@@ -702,21 +714,18 @@ static HRESULT WINAPI spvoice_GetVoice(ISpVoice *iface, ISpObjectToken **token)
EnterCriticalSection(&This->cs);
- if (!This->engine) + if (!This->engine_token) { 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); - } + ISpObjectToken_AddRef(This->engine_token); + *token = This->engine_token;
LeaveCriticalSection(&This->cs);
- return hr; + return S_OK; }
struct async_result @@ -788,6 +797,14 @@ static void speak_proc(struct async_task *task) goto done; }
+ if (!This->engine && + FAILED(hr = ISpObjectToken_CreateInstance(This->engine_token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&This->engine))) + { + LeaveCriticalSection(&This->cs); + ERR("failed creating engine: %#lx.\n", hr); + goto done; + } + if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx))) { LeaveCriticalSection(&This->cs); @@ -891,9 +908,9 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR return hr; }
- if (!This->engine) + if (!This->engine_token) { - /* Create a new engine with the default voice. */ + /* Set the engine token to default. */ if (FAILED(hr = ISpVoice_SetVoice(iface, NULL))) return hr; } @@ -1397,6 +1414,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1;
This->output = NULL; + This->engine_token = NULL; This->engine = NULL; This->cur_stream_num = 0; This->actions = SPVES_CONTINUE;
This is causing test failures - see the pipeline.
On Tue Jan 30 17:31:17 2024 +0000, Huw Davies wrote:
This is causing test failures - see the pipeline.
The failure is unrelated: ``` xcopy.exe:xcopy:0158 done (0) in 1s 122B xinput1_3:xinput start dlls/xinput1_3/tests/xinput.c xinput1_3:xinput:0818 done (0) in 0s 5796B xmllite:reader start dlls/xmllite/tests/reader.c xmllite:reader:0310 done (0) in 0s 1028B xmllite:writer start dlls/xmllite/tests/writer.c xmllite:writer:0240 done (0) in 0s 4253B Cleaning up project directory and file based variables ERROR: Job failed: exit code 1 ```
On Tue Jan 30 17:31:17 2024 +0000, Shaun Ren wrote:
The failure is unrelated:
xcopy.exe:xcopy:0158 done (0) in 1s 122B xinput1_3:xinput start dlls/xinput1_3/tests/xinput.c xinput1_3:xinput:0818 done (0) in 0s 5796B xmllite:reader start dlls/xmllite/tests/reader.c xmllite:reader:0310 done (0) in 0s 1028B xmllite:writer start dlls/xmllite/tests/writer.c xmllite:writer:0240 done (0) in 0s 4253B Cleaning up project directory and file based variables ERROR: Job failed: exit code 1
``` sapi:tts start dlls/sapi/tests/tts.c wine: Unhandled page fault on read access to 00000000 at address 0040BAAF (thread 0818), starting debugger... tts.c:478: Test marked todo: got 0. tts.c:481: Test marked todo: got 0x8004503a. tts.c:493: Test marked todo: got 0x8004503a. tts.c:622: Test failed: got 0x80040154. tts.c:623: Test failed: ISpTTSEngine::Speak was not called. tts.c:624: Test failed: got 0xdeadbeef. tts.c:625: Test failed: frag_list is NULL. Unhandled exception: page fault on read access to 0x00000000 in wow64 32-bit code (0x0040baaf). ```