aboutsummaryrefslogtreecommitdiffstats
path: root/Alc/backends/mmdevapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'Alc/backends/mmdevapi.c')
-rw-r--r--Alc/backends/mmdevapi.c902
1 files changed, 509 insertions, 393 deletions
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
index 18feab02..968b73ff 100644
--- a/Alc/backends/mmdevapi.c
+++ b/Alc/backends/mmdevapi.c
@@ -44,6 +44,8 @@
#include "compat.h"
#include "alstring.h"
+#include "backends/base.h"
+
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
@@ -60,23 +62,6 @@ DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,
typedef struct {
- WCHAR *devid;
-
- IMMDevice *mmdev;
- IAudioClient *client;
- IAudioRenderClient *render;
- HANDLE NotifyEvent;
-
- HANDLE MsgEvent;
-
- volatile UINT32 Padding;
-
- volatile int killNow;
- althrd_t thread;
-} MMDevApiData;
-
-
-typedef struct {
al_string name;
WCHAR *devid;
} DevMap;
@@ -178,7 +163,7 @@ static void add_device(IMMDevice *device, vector_DevMap *list)
}
}
-static HRESULT ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
+static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
{
IMMDeviceCollection *coll;
IMMDevice *defdev = NULL;
@@ -230,10 +215,242 @@ static HRESULT ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vec
}
-FORCE_ALIGN static int MMDevApiProc(void *ptr)
+/* Proxy interface used by the message handler. */
+struct ALCmmdevProxyVtable;
+
+typedef struct ALCmmdevProxy {
+ const struct ALCmmdevProxyVtable *vtbl;
+} ALCmmdevProxy;
+
+struct ALCmmdevProxyVtable {
+ HRESULT (*const openProxy)(ALCmmdevProxy*);
+ void (*const closeProxy)(ALCmmdevProxy*);
+
+ HRESULT (*const resetProxy)(ALCmmdevProxy*);
+ HRESULT (*const startProxy)(ALCmmdevProxy*);
+ void (*const stopProxy)(ALCmmdevProxy*);
+};
+
+#define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
+DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
+DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
+ \
+static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
+ T##_ALCmmdevProxy_openProxy, \
+ T##_ALCmmdevProxy_closeProxy, \
+ T##_ALCmmdevProxy_resetProxy, \
+ T##_ALCmmdevProxy_startProxy, \
+ T##_ALCmmdevProxy_stopProxy, \
+}
+
+static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
+static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
+
+static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
+{
+ ThreadRequest *req = ptr;
+ IMMDeviceEnumerator *Enumerator;
+ ALuint deviceCount = 0;
+ ALCmmdevProxy *proxy;
+ HRESULT hr, cohr;
+ MSG msg;
+
+ TRACE("Starting message thread\n");
+
+ cohr = CoInitialize(NULL);
+ if(FAILED(cohr))
+ {
+ WARN("Failed to initialize COM: 0x%08lx\n", cohr);
+ ReturnMsgResponse(req, cohr);
+ return 0;
+ }
+
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+ if(FAILED(hr))
+ {
+ WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
+ CoUninitialize();
+ ReturnMsgResponse(req, hr);
+ return 0;
+ }
+ Enumerator = ptr;
+ IMMDeviceEnumerator_Release(Enumerator);
+ Enumerator = NULL;
+
+ CoUninitialize();
+
+ /* HACK: Force Windows to create a message queue for this thread before
+ * returning success, otherwise PostThreadMessage may fail if it gets
+ * called before GetMessage.
+ */
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ TRACE("Message thread initialization complete\n");
+ ReturnMsgResponse(req, S_OK);
+
+ TRACE("Starting message loop\n");
+ while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
+ {
+ TRACE("Got message %u\n", msg.message);
+ switch(msg.message)
+ {
+ case WM_USER_OpenDevice:
+ req = (ThreadRequest*)msg.wParam;
+ proxy = (ALCmmdevProxy*)msg.lParam;
+
+ hr = cohr = S_OK;
+ if(++deviceCount == 1)
+ hr = cohr = CoInitialize(NULL);
+ if(SUCCEEDED(hr))
+ hr = V0(proxy,openProxy)();
+ if(FAILED(hr))
+ {
+ if(--deviceCount == 0 && SUCCEEDED(cohr))
+ CoUninitialize();
+ }
+
+ ReturnMsgResponse(req, hr);
+ continue;
+
+ case WM_USER_ResetDevice:
+ req = (ThreadRequest*)msg.wParam;
+ proxy = (ALCmmdevProxy*)msg.lParam;
+
+ hr = V0(proxy,resetProxy)();
+ ReturnMsgResponse(req, hr);
+ continue;
+
+ case WM_USER_StartDevice:
+ req = (ThreadRequest*)msg.wParam;
+ proxy = (ALCmmdevProxy*)msg.lParam;
+
+ hr = V0(proxy,startProxy)();
+ ReturnMsgResponse(req, hr);
+ continue;
+
+ case WM_USER_StopDevice:
+ req = (ThreadRequest*)msg.wParam;
+ proxy = (ALCmmdevProxy*)msg.lParam;
+
+ V0(proxy,stopProxy)();
+ ReturnMsgResponse(req, S_OK);
+ continue;
+
+ case WM_USER_CloseDevice:
+ req = (ThreadRequest*)msg.wParam;
+ proxy = (ALCmmdevProxy*)msg.lParam;
+
+ V0(proxy,closeProxy)();
+ if(--deviceCount == 0)
+ CoUninitialize();
+
+ ReturnMsgResponse(req, S_OK);
+ continue;
+
+ case WM_USER_Enumerate:
+ req = (ThreadRequest*)msg.wParam;
+
+ hr = cohr = S_OK;
+ if(++deviceCount == 1)
+ hr = cohr = CoInitialize(NULL);
+ if(SUCCEEDED(hr))
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+ if(SUCCEEDED(hr))
+ {
+ Enumerator = ptr;
+
+ if(msg.lParam == ALL_DEVICE_PROBE)
+ hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
+ else if(msg.lParam == CAPTURE_DEVICE_PROBE)
+ hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
+
+ IMMDeviceEnumerator_Release(Enumerator);
+ Enumerator = NULL;
+ }
+
+ if(--deviceCount == 0 && SUCCEEDED(cohr))
+ CoUninitialize();
+
+ ReturnMsgResponse(req, hr);
+ continue;
+
+ default:
+ ERR("Unexpected message: %u\n", msg.message);
+ continue;
+ }
+ }
+ TRACE("Message loop finished\n");
+
+ return 0;
+}
+
+
+typedef struct ALCmmdevPlayback {
+ DERIVE_FROM_TYPE(ALCbackend);
+ DERIVE_FROM_TYPE(ALCmmdevProxy);
+
+ WCHAR *devid;
+
+ IMMDevice *mmdev;
+ IAudioClient *client;
+ IAudioRenderClient *render;
+ HANDLE NotifyEvent;
+
+ HANDLE MsgEvent;
+
+ volatile UINT32 Padding;
+
+ volatile int killNow;
+ althrd_t thread;
+} ALCmmdevPlayback;
+
+static int ALCmmdevPlayback_mixerProc(void *arg);
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
+static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
+static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
+
+DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
+DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
+
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
{
- ALCdevice *device = ptr;
- MMDevApiData *data = device->ExtraData;
+ SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
+ SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
+ ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+ ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+}
+
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
+{
+ ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+ ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
+{
+ ALCmmdevPlayback *self = arg;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
UINT32 buffer_len, written;
ALuint update_size, len;
BYTE *buffer;
@@ -246,7 +463,7 @@ FORCE_ALIGN static int MMDevApiProc(void *ptr)
ALCdevice_Lock(device);
aluHandleDisconnect(device);
ALCdevice_Unlock(device);
- return 0;
+ return 1;
}
SetRTPriority();
@@ -254,9 +471,9 @@ FORCE_ALIGN static int MMDevApiProc(void *ptr)
update_size = device->UpdateSize;
buffer_len = update_size * device->NumUpdates;
- while(!data->killNow)
+ while(!self->killNow)
{
- hr = IAudioClient_GetCurrentPadding(data->client, &written);
+ hr = IAudioClient_GetCurrentPadding(self->client, &written);
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
@@ -265,27 +482,27 @@ FORCE_ALIGN static int MMDevApiProc(void *ptr)
ALCdevice_Unlock(device);
break;
}
- data->Padding = written;
+ self->Padding = written;
len = buffer_len - written;
if(len < update_size)
{
DWORD res;
- res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
+ res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
if(res != WAIT_OBJECT_0)
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
continue;
}
len -= len%update_size;
- hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
+ hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
if(SUCCEEDED(hr))
{
ALCdevice_Lock(device);
aluMixData(device, buffer, len);
- data->Padding = written + len;
+ self->Padding = written + len;
ALCdevice_Unlock(device);
- hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
+ hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
}
if(FAILED(hr))
{
@@ -296,7 +513,7 @@ FORCE_ALIGN static int MMDevApiProc(void *ptr)
break;
}
}
- data->Padding = 0;
+ self->Padding = 0;
CoUninitialize();
return 0;
@@ -342,9 +559,158 @@ static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *
return ALC_TRUE;
}
-static HRESULT DoReset(ALCdevice *device)
+
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
+{
+ HRESULT hr = S_OK;
+
+ self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
+ {
+ ERR("Failed to create message events: %lu\n", GetLastError());
+ hr = E_FAIL;
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ if(deviceName)
+ {
+ const DevMap *iter, *end;
+
+ if(VECTOR_SIZE(PlaybackDevices) == 0)
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
+ (void)WaitForResponse(&req);
+ }
+
+ hr = E_FAIL;
+ iter = VECTOR_ITER_BEGIN(PlaybackDevices);
+ end = VECTOR_ITER_END(PlaybackDevices);
+ for(;iter != end;iter++)
+ {
+ if(al_string_cmp_cstr(iter->name, deviceName) == 0)
+ {
+ self->devid = strdupW(iter->devid);
+ hr = S_OK;
+ break;
+ }
+ }
+ if(FAILED(hr))
+ WARN("Failed to find device name matching \"%s\"\n", deviceName);
+ }
+ }
+
+ if(SUCCEEDED(hr))
+ {
+ ThreadRequest req = { self->MsgEvent, 0 };
+
+ hr = E_FAIL;
+ if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ hr = WaitForResponse(&req);
+ else
+ ERR("Failed to post thread message: %lu\n", GetLastError());
+ }
+
+ if(FAILED(hr))
+ {
+ if(self->NotifyEvent != NULL)
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+ if(self->MsgEvent != NULL)
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+
+ free(self->devid);
+ self->devid = NULL;
+
+ ERR("Device init failed: 0x%08lx\n", hr);
+ return ALC_INVALID_VALUE;
+ }
+
+ return ALC_NO_ERROR;
+}
+
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ void *ptr;
+ HRESULT hr;
+
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+ if(SUCCEEDED(hr))
+ {
+ IMMDeviceEnumerator *Enumerator = ptr;
+ if(!self->devid)
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
+ else
+ hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
+ IMMDeviceEnumerator_Release(Enumerator);
+ Enumerator = NULL;
+ }
+ if(SUCCEEDED(hr))
+ hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+ if(SUCCEEDED(hr))
+ {
+ self->client = ptr;
+ get_device_name(self->mmdev, &device->DeviceName);
+ }
+
+ if(FAILED(hr))
+ {
+ if(self->mmdev)
+ IMMDevice_Release(self->mmdev);
+ self->mmdev = NULL;
+ }
+
+ return hr;
+}
+
+
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
+{
+ ThreadRequest req = { self->MsgEvent, 0 };
+
+ if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ (void)WaitForResponse(&req);
+
+ CloseHandle(self->MsgEvent);
+ self->MsgEvent = NULL;
+
+ CloseHandle(self->NotifyEvent);
+ self->NotifyEvent = NULL;
+
+ free(self->devid);
+ self->devid = NULL;
+}
+
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
+{
+ if(self->client)
+ IAudioClient_Release(self->client);
+ self->client = NULL;
+
+ if(self->mmdev)
+ IMMDevice_Release(self->mmdev);
+ self->mmdev = NULL;
+}
+
+
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
+{
+ ThreadRequest req = { self->MsgEvent, 0 };
+ HRESULT hr = E_FAIL;
+
+ if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ hr = WaitForResponse(&req);
+
+ return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
{
- MMDevApiData *data = device->ExtraData;
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
WAVEFORMATEXTENSIBLE OutputType;
WAVEFORMATEX *wfx = NULL;
REFERENCE_TIME min_per, buf_time;
@@ -352,19 +718,19 @@ static HRESULT DoReset(ALCdevice *device)
void *ptr = NULL;
HRESULT hr;
- if(data->client)
- IAudioClient_Release(data->client);
- data->client = NULL;
+ if(self->client)
+ IAudioClient_Release(self->client);
+ self->client = NULL;
- hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+ hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
if(FAILED(hr))
{
ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
return hr;
}
- data->client = ptr;
+ self->client = ptr;
- hr = IAudioClient_GetMixFormat(data->client, &wfx);
+ hr = IAudioClient_GetMixFormat(self->client, &wfx);
if(FAILED(hr))
{
ERR("Failed to get mix format: 0x%08lx\n", hr);
@@ -474,11 +840,11 @@ static HRESULT DoReset(ALCdevice *device)
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
OutputType.Format.nBlockAlign;
- hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
+ hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
if(FAILED(hr))
{
ERR("Failed to check format support: 0x%08lx\n", hr);
- hr = IAudioClient_GetMixFormat(data->client, &wfx);
+ hr = IAudioClient_GetMixFormat(self->client, &wfx);
}
if(FAILED(hr))
{
@@ -550,7 +916,7 @@ static HRESULT DoReset(ALCdevice *device)
SetDefaultWFXChannelOrder(device);
- hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
+ hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
buf_time, 0, &OutputType.Format, NULL);
if(FAILED(hr))
@@ -559,14 +925,14 @@ static HRESULT DoReset(ALCdevice *device)
return hr;
}
- hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
+ hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
if(SUCCEEDED(hr))
{
min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
/* Find the nearest multiple of the period size to the update size */
if(min_len < device->UpdateSize)
min_len *= (device->UpdateSize + min_len/2)/min_len;
- hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
+ hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
}
if(FAILED(hr))
{
@@ -583,7 +949,7 @@ static HRESULT DoReset(ALCdevice *device)
device->UpdateSize = buffer_len / device->NumUpdates;
}
- hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
+ hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
if(FAILED(hr))
{
ERR("Failed to set event handle: 0x%08lx\n", hr);
@@ -594,208 +960,90 @@ static HRESULT DoReset(ALCdevice *device)
}
-static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
{
- ThreadRequest *req = ptr;
- IMMDeviceEnumerator *Enumerator;
- ALuint deviceCount = 0;
- MMDevApiData *data;
- ALCdevice *device;
- HRESULT hr, cohr;
- MSG msg;
-
- TRACE("Starting message thread\n");
-
- cohr = CoInitialize(NULL);
- if(FAILED(cohr))
- {
- WARN("Failed to initialize COM: 0x%08lx\n", cohr);
- ReturnMsgResponse(req, cohr);
- return 0;
- }
+ ThreadRequest req = { self->MsgEvent, 0 };
+ HRESULT hr = E_FAIL;
- hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
- if(FAILED(hr))
- {
- WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
- CoUninitialize();
- ReturnMsgResponse(req, hr);
- return 0;
- }
- Enumerator = ptr;
- IMMDeviceEnumerator_Release(Enumerator);
- Enumerator = NULL;
+ if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ hr = WaitForResponse(&req);
- CoUninitialize();
+ return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
- /* HACK: Force Windows to create a message queue for this thread before
- * returning success, otherwise PostThreadMessage may fail if it gets
- * called before GetMessage.
- */
- PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
+{
+ HRESULT hr;
+ void *ptr;
- TRACE("Message thread initialization complete\n");
- ReturnMsgResponse(req, S_OK);
+ ResetEvent(self->NotifyEvent);
+ hr = IAudioClient_Start(self->client);
+ if(FAILED(hr))
+ ERR("Failed to start audio client: 0x%08lx\n", hr);
- TRACE("Starting message loop\n");
- while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
+ if(SUCCEEDED(hr))
+ hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
+ if(SUCCEEDED(hr))
{
- TRACE("Got message %u\n", msg.message);
- switch(msg.message)
+ self->render = ptr;
+ self->killNow = 0;
+ if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
{
- case WM_USER_OpenDevice:
- req = (ThreadRequest*)msg.wParam;
- device = (ALCdevice*)msg.lParam;
- data = device->ExtraData;
-
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
- if(SUCCEEDED(hr))
- hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
- if(SUCCEEDED(hr))
- {
- Enumerator = ptr;
- if(!data->devid)
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
- else
- hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
- IMMDeviceEnumerator_Release(Enumerator);
- Enumerator = NULL;
- }
- if(SUCCEEDED(hr))
- hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
- if(SUCCEEDED(hr))
- {
- data->client = ptr;
- get_device_name(data->mmdev, &device->DeviceName);
- }
-
- if(FAILED(hr))
- {
- if(data->mmdev)
- IMMDevice_Release(data->mmdev);
- data->mmdev = NULL;
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- }
-
- ReturnMsgResponse(req, hr);
- continue;
-
- case WM_USER_ResetDevice:
- req = (ThreadRequest*)msg.wParam;
- device = (ALCdevice*)msg.lParam;
-
- hr = DoReset(device);
- ReturnMsgResponse(req, hr);
- continue;
-
- case WM_USER_StartDevice:
- req = (ThreadRequest*)msg.wParam;
- device = (ALCdevice*)msg.lParam;
- data = device->ExtraData;
-
- ResetEvent(data->NotifyEvent);
- hr = IAudioClient_Start(data->client);
- if(FAILED(hr))
- ERR("Failed to start audio client: 0x%08lx\n", hr);
-
- if(SUCCEEDED(hr))
- hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
- if(SUCCEEDED(hr))
- {
- data->render = ptr;
- data->killNow = 0;
- if(althrd_create(&data->thread, MMDevApiProc, device) != althrd_success)
- {
- if(data->render)
- IAudioRenderClient_Release(data->render);
- data->render = NULL;
- IAudioClient_Stop(data->client);
- ERR("Failed to start thread\n");
- hr = E_FAIL;
- }
- }
-
- ReturnMsgResponse(req, hr);
- continue;
-
- case WM_USER_StopDevice:
- req = (ThreadRequest*)msg.wParam;
- device = (ALCdevice*)msg.lParam;
- data = device->ExtraData;
-
- if(data->render)
- {
- int res;
-
- data->killNow = 1;
- althrd_join(data->thread, &res);
-
- IAudioRenderClient_Release(data->render);
- data->render = NULL;
- IAudioClient_Stop(data->client);
- }
-
- ReturnMsgResponse(req, S_OK);
- continue;
+ if(self->render)
+ IAudioRenderClient_Release(self->render);
+ self->render = NULL;
+ IAudioClient_Stop(self->client);
+ ERR("Failed to start thread\n");
+ hr = E_FAIL;
+ }
+ }
- case WM_USER_CloseDevice:
- req = (ThreadRequest*)msg.wParam;
- device = (ALCdevice*)msg.lParam;
- data = device->ExtraData;
+ return hr;
+}
- if(data->client)
- IAudioClient_Release(data->client);
- data->client = NULL;
- if(data->mmdev)
- IMMDevice_Release(data->mmdev);
- data->mmdev = NULL;
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
+{
+ ThreadRequest req = { self->MsgEvent, 0 };
+ if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+ (void)WaitForResponse(&req);
+}
- if(--deviceCount == 0)
- CoUninitialize();
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
+{
+ int res;
- ReturnMsgResponse(req, S_OK);
- continue;
+ if(!self->render)
+ return;
- case WM_USER_Enumerate:
- req = (ThreadRequest*)msg.wParam;
+ self->killNow = 1;
+ althrd_join(self->thread, &res);
- hr = cohr = S_OK;
- if(++deviceCount == 1)
- hr = cohr = CoInitialize(NULL);
- if(SUCCEEDED(hr))
- hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
- if(SUCCEEDED(hr))
- {
- Enumerator = ptr;
+ IAudioRenderClient_Release(self->render);
+ self->render = NULL;
+ IAudioClient_Stop(self->client);
+}
- if(msg.lParam == ALL_DEVICE_PROBE)
- hr = ProbeDevices(Enumerator, eRender, &PlaybackDevices);
- else if(msg.lParam == CAPTURE_DEVICE_PROBE)
- hr = ProbeDevices(Enumerator, eCapture, &CaptureDevices);
- IMMDeviceEnumerator_Release(Enumerator);
- Enumerator = NULL;
- }
+static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
+{
+ ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+ return (ALint64)self->Padding * 1000000000 / device->Frequency;
+}
- if(--deviceCount == 0 && SUCCEEDED(cohr))
- CoUninitialize();
- ReturnMsgResponse(req, hr);
- continue;
+typedef struct ALCmmdevBackendFactory {
+ DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCmmdevBackendFactory;
+#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
- default:
- ERR("Unexpected message: %u\n", msg.message);
- continue;
- }
- }
- TRACE("Message loop finished\n");
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
- return 0;
-}
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
static BOOL MMDevApiLoad(void)
@@ -811,7 +1059,7 @@ static BOOL MMDevApiLoad(void)
ERR("Failed to create event: %lu\n", GetLastError());
else
{
- ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
+ ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
if(ThreadHdl != NULL)
InitResult = WaitForResponse(&req);
CloseHandle(req.FinishedEvt);
@@ -820,181 +1068,17 @@ static BOOL MMDevApiLoad(void)
return SUCCEEDED(InitResult);
}
-
-static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
-{
- MMDevApiData *data = NULL;
- HRESULT hr;
-
- //Initialise requested device
- data = calloc(1, sizeof(MMDevApiData));
- if(!data)
- return ALC_OUT_OF_MEMORY;
- device->ExtraData = data;
-
- hr = S_OK;
- data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
- {
- ERR("Failed to create message events: %lu\n", GetLastError());
- hr = E_FAIL;
- }
-
- if(SUCCEEDED(hr))
- {
- if(deviceName)
- {
- const DevMap *iter, *end;
-
- if(VECTOR_SIZE(PlaybackDevices) == 0)
- {
- ThreadRequest req = { data->MsgEvent, 0 };
- if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
- (void)WaitForResponse(&req);
- }
-
- hr = E_FAIL;
- iter = VECTOR_ITER_BEGIN(PlaybackDevices);
- end = VECTOR_ITER_END(PlaybackDevices);
- for(;iter != end;iter++)
- {
- if(al_string_cmp_cstr(iter->name, deviceName) == 0)
- {
- data->devid = strdupW(iter->devid);
- hr = S_OK;
- break;
- }
- }
- if(FAILED(hr))
- WARN("Failed to find device name matching \"%s\"\n", deviceName);
- }
- }
-
- if(SUCCEEDED(hr))
- {
- ThreadRequest req = { data->MsgEvent, 0 };
-
- hr = E_FAIL;
- if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
- hr = WaitForResponse(&req);
- else
- ERR("Failed to post thread message: %lu\n", GetLastError());
- }
-
- if(FAILED(hr))
- {
- if(data->NotifyEvent != NULL)
- CloseHandle(data->NotifyEvent);
- data->NotifyEvent = NULL;
- if(data->MsgEvent != NULL)
- CloseHandle(data->MsgEvent);
- data->MsgEvent = NULL;
-
- free(data->devid);
- data->devid = NULL;
-
- free(data);
- device->ExtraData = NULL;
-
- ERR("Device init failed: 0x%08lx\n", hr);
- return ALC_INVALID_VALUE;
- }
-
- return ALC_NO_ERROR;
-}
-
-static void MMDevApiClosePlayback(ALCdevice *device)
-{
- MMDevApiData *data = device->ExtraData;
- ThreadRequest req = { data->MsgEvent, 0 };
-
- if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
- (void)WaitForResponse(&req);
-
- CloseHandle(data->MsgEvent);
- data->MsgEvent = NULL;
-
- CloseHandle(data->NotifyEvent);
- data->NotifyEvent = NULL;
-
- free(data->devid);
- data->devid = NULL;
-
- free(data);
- device->ExtraData = NULL;
-}
-
-static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
-{
- MMDevApiData *data = device->ExtraData;
- ThreadRequest req = { data->MsgEvent, 0 };
- HRESULT hr = E_FAIL;
-
- if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
- hr = WaitForResponse(&req);
-
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
-{
- MMDevApiData *data = device->ExtraData;
- ThreadRequest req = { data->MsgEvent, 0 };
- HRESULT hr = E_FAIL;
-
- if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
- hr = WaitForResponse(&req);
-
- return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
-}
-
-static void MMDevApiStopPlayback(ALCdevice *device)
-{
- MMDevApiData *data = device->ExtraData;
- ThreadRequest req = { data->MsgEvent, 0 };
-
- if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
- (void)WaitForResponse(&req);
-}
-
-
-static ALint64 MMDevApiGetLatency(ALCdevice *device)
-{
- MMDevApiData *data = device->ExtraData;
-
- return (ALint64)data->Padding * 1000000000 / device->Frequency;
-}
-
-
-static const BackendFuncs MMDevApiFuncs = {
- MMDevApiOpenPlayback,
- MMDevApiClosePlayback,
- MMDevApiResetPlayback,
- MMDevApiStartPlayback,
- MMDevApiStopPlayback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- MMDevApiGetLatency
-};
-
-
-ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
{
VECTOR_INIT(PlaybackDevices);
VECTOR_INIT(CaptureDevices);
if(!MMDevApiLoad())
return ALC_FALSE;
- *FuncList = MMDevApiFuncs;
return ALC_TRUE;
}
-void alcMMDevApiDeinit(void)
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
{
clear_devlist(&PlaybackDevices);
VECTOR_DEINIT(PlaybackDevices);
@@ -1011,7 +1095,14 @@ void alcMMDevApiDeinit(void)
}
}
-void alcMMDevApiProbe(enum DevProbe type)
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ return ALC_TRUE;
+ return ALC_FALSE;
+}
+
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
{
ThreadRequest req = { NULL, 0 };
const DevMap *iter, *end;
@@ -1044,3 +1135,28 @@ void alcMMDevApiProbe(enum DevProbe type)
req.FinishedEvt = NULL;
}
}
+
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+ if(type == ALCbackend_Playback)
+ {
+ ALCmmdevPlayback *backend;
+
+ backend = ALCmmdevPlayback_New(sizeof(*backend));
+ if(!backend) return NULL;
+ memset(backend, 0, sizeof(*backend));
+
+ ALCmmdevPlayback_Construct(backend, device);
+
+ return STATIC_CAST(ALCbackend, backend);
+ }
+
+ return NULL;
+}
+
+
+ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
+{
+ static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
+ return STATIC_CAST(ALCbackendFactory, &factory);
+}