mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
windows.media.speech: Add an audio capturing system.
Signed-off-by: Bernhard Kölbl <besentv@gmail.com>
This commit is contained in:
parent
c8a68f2a7a
commit
85ce23a9bf
1 changed files with 120 additions and 6 deletions
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
|
#include "initguid.h"
|
||||||
|
#include "audioclient.h"
|
||||||
|
#include "mmdeviceapi.h"
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(speech);
|
WINE_DEFAULT_DEBUG_CHANNEL(speech);
|
||||||
|
@ -161,7 +165,11 @@ struct session
|
||||||
struct list completed_handlers;
|
struct list completed_handlers;
|
||||||
struct list result_handlers;
|
struct list result_handlers;
|
||||||
|
|
||||||
HANDLE worker_thread, worker_control_event;
|
IAudioClient *audio_client;
|
||||||
|
IAudioCaptureClient *capture_client;
|
||||||
|
WAVEFORMATEX capture_wfx;
|
||||||
|
|
||||||
|
HANDLE worker_thread, worker_control_event, audio_buf_event;
|
||||||
BOOLEAN worker_running, worker_paused;
|
BOOLEAN worker_running, worker_paused;
|
||||||
CRITICAL_SECTION cs;
|
CRITICAL_SECTION cs;
|
||||||
};
|
};
|
||||||
|
@ -181,25 +189,73 @@ static DWORD CALLBACK session_worker_thread_cb( void *args )
|
||||||
{
|
{
|
||||||
ISpeechContinuousRecognitionSession *iface = args;
|
ISpeechContinuousRecognitionSession *iface = args;
|
||||||
struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface);
|
struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface);
|
||||||
BOOLEAN running = TRUE;
|
BOOLEAN running = TRUE, paused = FALSE;
|
||||||
DWORD status;
|
DWORD flags, status;
|
||||||
|
HANDLE events[2];
|
||||||
|
BYTE *audio_buf;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker");
|
SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker");
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_Start(impl->audio_client)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
status = WaitForMultipleObjects(1, &impl->worker_control_event, FALSE, INFINITE);
|
BOOLEAN old_paused = paused;
|
||||||
|
UINT32 count = 0;
|
||||||
|
|
||||||
|
events[count++] = impl->worker_control_event;
|
||||||
|
if (!paused) events[count++] = impl->audio_buf_event;
|
||||||
|
|
||||||
|
status = WaitForMultipleObjects(count, events, FALSE, INFINITE);
|
||||||
if (status == 0) /* worker_control_event signaled */
|
if (status == 0) /* worker_control_event signaled */
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&impl->cs);
|
EnterCriticalSection(&impl->cs);
|
||||||
|
paused = impl->worker_paused;
|
||||||
running = impl->worker_running;
|
running = impl->worker_running;
|
||||||
LeaveCriticalSection(&impl->cs);
|
LeaveCriticalSection(&impl->cs);
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Send mic data to recognizer and handle results. */
|
if (old_paused < paused)
|
||||||
|
{
|
||||||
|
if (FAILED(hr = IAudioClient_Stop(impl->audio_client))) goto error;
|
||||||
|
if (FAILED(hr = IAudioClient_Reset(impl->audio_client))) goto error;
|
||||||
|
TRACE("session worker paused.\n");
|
||||||
|
}
|
||||||
|
else if (old_paused > paused)
|
||||||
|
{
|
||||||
|
if (FAILED(hr = IAudioClient_Start(impl->audio_client))) goto error;
|
||||||
|
TRACE("session worker resumed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (status == 1) /* audio_buf_event signaled */
|
||||||
|
{
|
||||||
|
UINT32 frames_available = 0;
|
||||||
|
|
||||||
|
while (IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK)
|
||||||
|
{
|
||||||
|
/* TODO: Send mic data to recognizer and handle results. */
|
||||||
|
IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERR("Unexpected state entered. Aborting worker.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_Stop(impl->audio_client)))
|
||||||
|
ERR("IAudioClient_Stop failed with %#lx.\n", hr);
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_Reset(impl->audio_client)))
|
||||||
|
ERR("IAudioClient_Reset failed with %#lx.\n", hr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
ERR("The recognition session worker encountered a serious error and needs to stop. hr: %lx.\n", hr);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI session_QueryInterface( ISpeechContinuousRecognitionSession *iface, REFIID iid, void **out )
|
static HRESULT WINAPI session_QueryInterface( ISpeechContinuousRecognitionSession *iface, REFIID iid, void **out )
|
||||||
|
@ -252,6 +308,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface
|
||||||
typed_event_handlers_clear(&impl->completed_handlers);
|
typed_event_handlers_clear(&impl->completed_handlers);
|
||||||
typed_event_handlers_clear(&impl->result_handlers);
|
typed_event_handlers_clear(&impl->result_handlers);
|
||||||
|
|
||||||
|
IAudioCaptureClient_Release(impl->capture_client);
|
||||||
|
IAudioClient_Release(impl->audio_client);
|
||||||
|
|
||||||
impl->cs.DebugInfo->Spare[0] = 0;
|
impl->cs.DebugInfo->Spare[0] = 0;
|
||||||
DeleteCriticalSection(&impl->cs);
|
DeleteCriticalSection(&impl->cs);
|
||||||
|
|
||||||
|
@ -926,6 +985,55 @@ static const struct IActivationFactoryVtbl activation_factory_vtbl =
|
||||||
|
|
||||||
DEFINE_IINSPECTABLE(recognizer_factory, ISpeechRecognizerFactory, struct recognizer_statics, IActivationFactory_iface)
|
DEFINE_IINSPECTABLE(recognizer_factory, ISpeechRecognizerFactory, struct recognizer_statics, IActivationFactory_iface)
|
||||||
|
|
||||||
|
static HRESULT recognizer_factory_create_audio_capture(struct session *session)
|
||||||
|
{
|
||||||
|
const REFERENCE_TIME buffer_duration = 5000000; /* 0.5 second */
|
||||||
|
IMMDeviceEnumerator *mm_enum = NULL;
|
||||||
|
IMMDevice *mm_device = NULL;
|
||||||
|
WAVEFORMATEX wfx = { 0 };
|
||||||
|
WCHAR *str = NULL;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
if (!(session->audio_buf_event = CreateEventW(NULL, FALSE, FALSE, NULL)))
|
||||||
|
return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
|
||||||
|
if (FAILED(hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void **)&mm_enum)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(mm_enum, eCapture, eMultimedia, &mm_device)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMMDevice_Activate(mm_device, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&session->audio_client)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
hr = IMMDevice_GetId(mm_device, &str);
|
||||||
|
TRACE("selected capture device ID: %s, hr %#lx\n", debugstr_w(str), hr);
|
||||||
|
|
||||||
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
wfx.nSamplesPerSec = 16000;
|
||||||
|
wfx.nChannels = 1;
|
||||||
|
wfx.wBitsPerSample = 16;
|
||||||
|
wfx.nBlockAlign = (wfx.wBitsPerSample + 7) / 8 * wfx.nChannels;
|
||||||
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||||
|
TRACE("wfx tag %u, channels %u, samples %lu, bits %u, align %u.\n", wfx.wFormatTag, wfx.nChannels, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nBlockAlign);
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_Initialize(session->audio_client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffer_duration, 0, &wfx, NULL)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_SetEventHandle(session->audio_client, session->audio_buf_event)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
hr = IAudioClient_GetService(session->audio_client, &IID_IAudioCaptureClient, (void **)&session->capture_client);
|
||||||
|
|
||||||
|
session->capture_wfx = wfx;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (mm_device) IMMDevice_Release(mm_device);
|
||||||
|
if (mm_enum) IMMDeviceEnumerator_Release(mm_enum);
|
||||||
|
CoTaskMemFree(str);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer )
|
static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer )
|
||||||
{
|
{
|
||||||
struct recognizer *impl;
|
struct recognizer *impl;
|
||||||
|
@ -969,6 +1077,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface
|
||||||
if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints)))
|
if (FAILED(hr = vector_inspectable_create(&constraints_iids, (IVector_IInspectable**)&session->constraints)))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if (FAILED(hr = recognizer_factory_create_audio_capture(session)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
InitializeCriticalSection(&session->cs);
|
InitializeCriticalSection(&session->cs);
|
||||||
session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs");
|
session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs");
|
||||||
|
|
||||||
|
@ -984,6 +1095,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
if (session->capture_client) IAudioCaptureClient_Release(session->capture_client);
|
||||||
|
if (session->audio_client) IAudioClient_Release(session->audio_client);
|
||||||
|
if (session->audio_buf_event) CloseHandle(session->audio_buf_event);
|
||||||
if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints);
|
if (session->constraints) IVector_ISpeechRecognitionConstraint_Release(session->constraints);
|
||||||
if (session->worker_control_event) CloseHandle(session->worker_control_event);
|
if (session->worker_control_event) CloseHandle(session->worker_control_event);
|
||||||
free(session);
|
free(session);
|
||||||
|
|
Loading…
Reference in a new issue