From: Bernhard Kölbl besentv@gmail.com
Signed-off-by: Bernhard Kölbl besentv@gmail.com --- dlls/windows.media.speech/private.h | 1 + dlls/windows.media.speech/recognizer.c | 118 +++++++++++++++++++++-- dlls/windows.media.speech/tests/speech.c | 8 +- 3 files changed, 117 insertions(+), 10 deletions(-)
diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 41f7b02e3de..e80d73ec1fb 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -23,6 +23,7 @@ #include <stdarg.h>
#define COBJMACROS +#include "corerror.h" #include "windef.h" #include "winbase.h" #include "winstring.h" diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 54a0e165f5f..c6721f4092a 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -160,6 +160,11 @@ struct session
struct list completed_handlers; struct list result_handlers; + + HANDLE worker_thread; + BOOLEAN worker_running; + CONDITION_VARIABLE cv; + CRITICAL_SECTION cs; };
/* @@ -173,6 +178,23 @@ static inline struct session *impl_from_ISpeechContinuousRecognitionSession( ISp return CONTAINING_RECORD(iface, struct session, ISpeechContinuousRecognitionSession_iface); }
+static DWORD CALLBACK session_worker_thread_cb( void *args ) +{ + ISpeechContinuousRecognitionSession *iface = args; + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + + EnterCriticalSection(&impl->cs); + while (impl->worker_running) + { + /* TODO: Send mic data to recognizer and handle results. */ + + SleepConditionVariableCS(&impl->cv, &impl->cs, 500); /* Wait 500ms to slow down spinning for now. */ + } + LeaveCriticalSection(&impl->cs); + + return 0; +} + static HRESULT WINAPI session_QueryInterface( ISpeechContinuousRecognitionSession *iface, REFIID iid, void **out ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); @@ -200,6 +222,26 @@ static ULONG WINAPI session_AddRef( ISpeechContinuousRecognitionSession *iface ) return ref; }
+static HRESULT session_join_worker_thread( ISpeechContinuousRecognitionSession *iface ) +{ + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + + WakeConditionVariable(&impl->cv); + + EnterCriticalSection(&impl->cs); + if (impl->worker_thread) + { + if (WaitForSingleObject(impl->worker_thread, 1000)) + ERR("Wait for shut down recognition session timed out!\n"); + + CloseHandle(impl->worker_thread); + impl->worker_thread = NULL; + } + LeaveCriticalSection(&impl->cs); + + return S_OK; +} + static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); @@ -208,8 +250,18 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface
if (!ref) { + EnterCriticalSection(&impl->cs); + impl->worker_running = FALSE; + LeaveCriticalSection(&impl->cs); + + session_join_worker_thread(iface); + typed_event_handlers_clear(&impl->completed_handlers); typed_event_handlers_clear(&impl->result_handlers); + + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&impl->cs); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -249,13 +301,43 @@ static HRESULT WINAPI session_set_AutoStopSilenceTimeout( ISpeechContinuousRecog
static HRESULT session_start_async( IInspectable *invoker ) { - return S_OK; + struct session *impl = impl_from_ISpeechContinuousRecognitionSession((ISpeechContinuousRecognitionSession *)invoker); + HRESULT hr = S_OK; + + EnterCriticalSection(&impl->cs); + if (!(impl->worker_thread = CreateThread(NULL, 0, session_worker_thread_cb, impl, 0, NULL))) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + impl->worker_running = FALSE; + } + LeaveCriticalSection(&impl->cs); + + return hr; }
static HRESULT WINAPI session_StartAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { - FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, session_start_async, action); + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + BOOLEAN running; + HRESULT hr; + + TRACE("iface %p, action %p stub!\n", iface, action); + + *action = NULL; + + EnterCriticalSection(&impl->cs); + if (!(running = impl->worker_running)) + { + impl->worker_running = TRUE; + } + LeaveCriticalSection(&impl->cs); + + if (running) + hr = COR_E_INVALIDOPERATION; + else + hr = async_action_create((IInspectable *)iface, session_start_async, action); + + return hr; }
static HRESULT WINAPI session_StartWithModeAsync( ISpeechContinuousRecognitionSession *iface, @@ -268,13 +350,32 @@ static HRESULT WINAPI session_StartWithModeAsync( ISpeechContinuousRecognitionSe
static HRESULT session_stop_async( IInspectable *invoker ) { - return S_OK; + return session_join_worker_thread((ISpeechContinuousRecognitionSession *)invoker); }
static HRESULT WINAPI session_StopAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) { - FIXME("iface %p, action %p stub!\n", iface, action); - return async_action_create(NULL, session_stop_async, action); + struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + BOOLEAN running; + HRESULT hr; + + TRACE("iface %p, action %p stub!\n", iface, action); + + *action = NULL; + + EnterCriticalSection(&impl->cs); + if ((running = impl->worker_running)) + { + impl->worker_running = FALSE; + } + LeaveCriticalSection(&impl->cs); + + if (!running) + hr = COR_E_INVALIDOPERATION; + else + hr = async_action_create((IInspectable *)iface, session_stop_async, action); + + return hr; }
static HRESULT WINAPI session_CancelAsync( ISpeechContinuousRecognitionSession *iface, IAsyncAction **action ) @@ -818,6 +919,10 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface list_init(&session->completed_handlers); list_init(&session->result_handlers);
+ InitializeConditionVariable(&session->cv); + InitializeCriticalSection(&session->cs); + session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); + if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints))) goto error;
@@ -835,6 +940,7 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface
error: if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints); + DeleteCriticalSection(&session->cs); free(session); free(impl);
diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index a8ed8cff1e7..8b31031d3c5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1744,8 +1744,8 @@ static void test_Recognition(void)
action2 = (void *)0xdeadbeef; hr = ISpeechContinuousRecognitionSession_StartAsync(session, &action2); - todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); - todo_wine ok(action2 == NULL, "action2 was %p.\n", action2); + ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StartAsync failed, hr %#lx.\n", hr); + ok(action2 == NULL, "action2 was %p.\n", action2);
hr = IAsyncAction_QueryInterface(action, &IID_IAsyncInfo, (void **)&info); ok(hr == S_OK, "IAsyncAction_QueryInterface failed, hr %#lx.\n", hr); @@ -1863,8 +1863,8 @@ static void test_Recognition(void)
/* Try stopping, when already stopped. */ hr = ISpeechContinuousRecognitionSession_StopAsync(session, &action); - todo_wine ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); - todo_wine ok(action == NULL, "action was %p.\n", action); + ok(hr == COR_E_INVALIDOPERATION, "ISpeechContinuousRecognitionSession_StopAsync failed, hr %#lx.\n", hr); + ok(action == NULL, "action was %p.\n", action);
hr = ISpeechContinuousRecognitionSession_remove_ResultGenerated(session, token); ok(hr == S_OK, "ISpeechContinuousRecognitionSession_remove_ResultGenerated failed, hr %#lx.\n", hr);