This MR fixes a bug in Far Cry 6 where the necessary objects cannot be initialized in its speech synthesis thread.
From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/async.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index b02778ae7ba..61b99019abe 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -71,6 +71,7 @@ static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) HANDLE handles[2] = { queue->cancel, queue->wait }; DWORD ret;
+ CoInitializeEx(NULL, COINIT_MULTITHREADED); SetEvent(queue->ready);
for (;;) @@ -99,6 +100,7 @@ static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx)
cancel: async_empty_queue(queue); + CoUninitialize(); TRACE("cancelled.\n"); SetEvent(queue->ready); }
From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/tts.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 4763ef9f324..b1c90627d5e 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -591,6 +591,9 @@ static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL all if (!allow_format_changes) FIXME("ignoring allow_format_changes = FALSE.\n");
+ if (FAILED(hr = async_start_queue(&This->queue))) + return hr; + if (!unk) { /* TODO: Create the default SpAudioOut token here once SpMMAudioEnum is implemented. */
From: Shaun Ren sren@codeweavers.com
Based on a patch by Connor McAdams. --- dlls/sapi/tests/tts.c | 96 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-)
diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 39f62ac551e..eb0258bbef3 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -34,6 +34,46 @@ static void _expect_ref(IUnknown *obj, ULONG ref, int line) ok_(__FILE__,line)(rc == ref, "Unexpected refcount %ld, expected %ld.\n", rc, ref); }
+#define APTTYPE_UNITIALIZED APTTYPE_CURRENT +static struct +{ + APTTYPE type; + APTTYPEQUALIFIER qualifier; +} test_apt_data; + +static DWORD WINAPI test_apt_thread(void *param) +{ + HRESULT hr; + + hr = CoGetApartmentType(&test_apt_data.type, &test_apt_data.qualifier); + if (hr == CO_E_NOTINITIALIZED) + { + test_apt_data.type = APTTYPE_UNITIALIZED; + test_apt_data.qualifier = 0; + } + + return 0; +} + +static void check_apttype(void) +{ + HANDLE thread; + MSG msg; + + memset(&test_apt_data, 0xde, sizeof(test_apt_data)); + + thread = CreateThread(NULL, 0, test_apt_thread, NULL, 0, NULL); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + CloseHandle(thread); +} + static void test_interfaces(void) { ISpeechVoice *speech_voice, *speech_voice2; @@ -380,6 +420,7 @@ static void test_spvoice(void) static const WCHAR test_text[] = L"Hello! This is a test sentence.";
ISpVoice *voice; + IUnknown *dummy; ISpMMSysAudio *audio_out; ISpObjectTokenCategory *token_cat; ISpObjectToken *token; @@ -397,23 +438,57 @@ static void test_spvoice(void) return; }
+ check_apttype(); + ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); + 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); + check_apttype(); + ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); + + /* SpVoice initializes a MTA in SetOutput even if an invalid output object is given. */ + hr = CoCreateInstance(&CLSID_SpDataKey, NULL, CLSCTX_INPROC_SERVER, + &IID_IUnknown, (void **)&dummy); + ok(hr == S_OK, "Failed to create dummy: %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, dummy, TRUE); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + + IUnknown_Release(dummy);
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, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = ISpVoice_SetOutput(voice, (IUnknown *)audio_out, TRUE); todo_wine ok(hr == S_FALSE, "got %#lx.\n", hr);
hr = ISpVoice_SetVoice(voice, NULL); todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
+ check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + hr = ISpVoice_GetVoice(voice, &token); todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
@@ -517,6 +592,15 @@ static void test_spvoice(void) hr = ISpVoice_SetVoice(voice, token); ok(hr == S_OK, "got %#lx.\n", hr);
+ check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA || broken(test_apt_data.type == APTTYPE_UNITIALIZED) /* w8, w10v1507 */, + "got apt type %d.\n", test_apt_data.type); + if (test_apt_data.type == APTTYPE_MTA) + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + else + win_skip("apt type is not MTA.\n"); + test_engine.speak_called = FALSE; hr = ISpVoice_Speak(voice, NULL, SPF_PURGEBEFORESPEAK, NULL); ok(hr == S_OK, "got %#lx.\n", hr); @@ -548,6 +632,11 @@ static void test_spvoice(void) ok(stream_num == 1, "got %lu.\n", stream_num); ok(duration > 800 && duration < 3000, "took %lu ms.\n", duration);
+ check_apttype(); + ok(test_apt_data.type == APTTYPE_MTA, "got apt type %d.\n", test_apt_data.type); + ok(test_apt_data.qualifier == APTTYPEQUALIFIER_IMPLICIT_MTA, + "got apt type qualifier %d.\n", test_apt_data.qualifier); + start = GetTickCount(); hr = ISpVoice_WaitUntilDone(voice, INFINITE); duration = GetTickCount() - start; @@ -602,7 +691,8 @@ done: START_TEST(tts) { CoInitialize(NULL); - test_interfaces(); + /* Run spvoice tests before interface tests so that a MTA won't be created before this test is run. */ test_spvoice(); + test_interfaces(); CoUninitialize(); }
From: Shaun Ren sren@codeweavers.com
--- dlls/sapi/token.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/sapi/token.c b/dlls/sapi/token.c index af98db02048..f599bdb6b14 100644 --- a/dlls/sapi/token.c +++ b/dlls/sapi/token.c @@ -1305,7 +1305,7 @@ static HRESULT WINAPI token_SetId( ISpObjectToken *iface, HKEY root, key; const WCHAR *subkey;
- FIXME( "(%p)->(%s %s %d): semi-stub\n", This, debugstr_w( category_id ), + TRACE( "(%p)->(%s %s %d)\n", This, debugstr_w( category_id ), debugstr_w(token_id), create );
if (This->data_key) return SPERR_ALREADY_INITIALIZED;
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 tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=135693
Your paranoid android.
=== w7pro64 (64 bit report) ===
sapi: tts.c:633: Test failed: took 3089 ms.