Accept a "narrow" layout for 7.1 with mmdevapi
[openal-soft.git] / Alc / backends / mmdevapi.c
blob16d9a555cede12c983a8ca009d2c61a1a22e551d
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #define COBJMACROS
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
30 #include <cguid.h>
31 #include <devpropdef.h>
32 #include <mmreg.h>
33 #include <propsys.h>
34 #include <propkey.h>
35 #include <devpkey.h>
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #include <ks.h>
38 #include <ksmedia.h>
39 #endif
41 #include "alMain.h"
42 #include "alu.h"
43 #include "threads.h"
44 #include "compat.h"
45 #include "alstring.h"
47 #include "backends/base.h"
50 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
53 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
54 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
56 #define MONO SPEAKER_FRONT_CENTER
57 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
58 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
59 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
60 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
61 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
62 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
63 #define X7DOT1_NARROW (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
66 typedef struct {
67 al_string name;
68 WCHAR *devid;
69 } DevMap;
70 TYPEDEF_VECTOR(DevMap, vector_DevMap)
72 static void clear_devlist(vector_DevMap *list)
74 #define CLEAR_DEVMAP(i) do { \
75 AL_STRING_DEINIT((i)->name); \
76 free((i)->devid); \
77 (i)->devid = NULL; \
78 } while(0)
79 VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
80 VECTOR_RESIZE(*list, 0);
81 #undef CLEAR_DEVMAP
84 static vector_DevMap PlaybackDevices;
85 static vector_DevMap CaptureDevices;
88 static HANDLE ThreadHdl;
89 static DWORD ThreadID;
91 typedef struct {
92 HANDLE FinishedEvt;
93 HRESULT result;
94 } ThreadRequest;
96 #define WM_USER_First (WM_USER+0)
97 #define WM_USER_OpenDevice (WM_USER+0)
98 #define WM_USER_ResetDevice (WM_USER+1)
99 #define WM_USER_StartDevice (WM_USER+2)
100 #define WM_USER_StopDevice (WM_USER+3)
101 #define WM_USER_CloseDevice (WM_USER+4)
102 #define WM_USER_Enumerate (WM_USER+5)
103 #define WM_USER_Last (WM_USER+5)
105 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
107 req->result = res;
108 SetEvent(req->FinishedEvt);
111 static HRESULT WaitForResponse(ThreadRequest *req)
113 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
114 return req->result;
115 ERR("Message response error: %lu\n", GetLastError());
116 return E_FAIL;
120 static void get_device_name(IMMDevice *device, al_string *name)
122 IPropertyStore *ps;
123 PROPVARIANT pvname;
124 HRESULT hr;
126 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
127 if(FAILED(hr))
129 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
130 return;
133 PropVariantInit(&pvname);
135 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
136 if(FAILED(hr))
137 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
138 else if(pvname.vt == VT_LPWSTR)
139 al_string_copy_wcstr(name, pvname.pwszVal);
140 else
141 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
143 PropVariantClear(&pvname);
144 IPropertyStore_Release(ps);
147 static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
149 IPropertyStore *ps;
150 PROPVARIANT pvform;
151 HRESULT hr;
153 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
154 if(FAILED(hr))
156 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
157 return;
160 PropVariantInit(&pvform);
162 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
163 if(FAILED(hr))
164 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
165 else if(pvform.vt == VT_UI4)
166 *formfactor = pvform.ulVal;
167 else if(pvform.vt == VT_EMPTY)
168 *formfactor = UnknownFormFactor;
169 else
170 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
172 PropVariantClear(&pvform);
173 IPropertyStore_Release(ps);
177 static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
179 DevMap entry;
181 AL_STRING_INIT(entry.name);
182 entry.devid = strdupW(devid);
183 get_device_name(device, &entry.name);
185 TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
186 VECTOR_PUSH_BACK(*list, entry);
189 static LPWSTR get_device_id(IMMDevice *device)
191 LPWSTR devid;
192 HRESULT hr;
194 hr = IMMDevice_GetId(device, &devid);
195 if(FAILED(hr))
197 ERR("Failed to get device id: %lx\n", hr);
198 return NULL;
201 return devid;
204 static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
206 IMMDeviceCollection *coll;
207 IMMDevice *defdev = NULL;
208 LPWSTR defdevid = NULL;
209 HRESULT hr;
210 UINT count;
211 UINT i;
213 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
214 if(FAILED(hr))
216 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
217 return hr;
220 count = 0;
221 hr = IMMDeviceCollection_GetCount(coll, &count);
222 if(SUCCEEDED(hr) && count > 0)
224 clear_devlist(list);
225 if(!VECTOR_RESERVE(*list, count))
227 IMMDeviceCollection_Release(coll);
228 return E_OUTOFMEMORY;
231 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
232 eMultimedia, &defdev);
234 if(SUCCEEDED(hr) && defdev != NULL)
236 defdevid = get_device_id(defdev);
237 if(defdevid)
238 add_device(defdev, defdevid, list);
241 for(i = 0;i < count;++i)
243 IMMDevice *device;
244 LPWSTR devid;
246 hr = IMMDeviceCollection_Item(coll, i, &device);
247 if(FAILED(hr)) continue;
249 devid = get_device_id(device);
250 if(devid)
252 if(wcscmp(devid, defdevid) != 0)
253 add_device(device, devid, list);
254 CoTaskMemFree(devid);
256 IMMDevice_Release(device);
259 if(defdev) IMMDevice_Release(defdev);
260 if(defdevid) CoTaskMemFree(defdevid);
261 IMMDeviceCollection_Release(coll);
263 return S_OK;
267 /* Proxy interface used by the message handler. */
268 struct ALCmmdevProxyVtable;
270 typedef struct ALCmmdevProxy {
271 const struct ALCmmdevProxyVtable *vtbl;
272 } ALCmmdevProxy;
274 struct ALCmmdevProxyVtable {
275 HRESULT (*const openProxy)(ALCmmdevProxy*);
276 void (*const closeProxy)(ALCmmdevProxy*);
278 HRESULT (*const resetProxy)(ALCmmdevProxy*);
279 HRESULT (*const startProxy)(ALCmmdevProxy*);
280 void (*const stopProxy)(ALCmmdevProxy*);
283 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
284 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
285 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
286 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
287 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
288 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
290 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
291 T##_ALCmmdevProxy_openProxy, \
292 T##_ALCmmdevProxy_closeProxy, \
293 T##_ALCmmdevProxy_resetProxy, \
294 T##_ALCmmdevProxy_startProxy, \
295 T##_ALCmmdevProxy_stopProxy, \
298 static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
299 static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
301 static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
303 ThreadRequest *req = ptr;
304 IMMDeviceEnumerator *Enumerator;
305 ALuint deviceCount = 0;
306 ALCmmdevProxy *proxy;
307 HRESULT hr, cohr;
308 MSG msg;
310 TRACE("Starting message thread\n");
312 cohr = CoInitialize(NULL);
313 if(FAILED(cohr))
315 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
316 ReturnMsgResponse(req, cohr);
317 return 0;
320 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
321 if(FAILED(hr))
323 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
324 CoUninitialize();
325 ReturnMsgResponse(req, hr);
326 return 0;
328 Enumerator = ptr;
329 IMMDeviceEnumerator_Release(Enumerator);
330 Enumerator = NULL;
332 CoUninitialize();
334 /* HACK: Force Windows to create a message queue for this thread before
335 * returning success, otherwise PostThreadMessage may fail if it gets
336 * called before GetMessage.
338 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
340 TRACE("Message thread initialization complete\n");
341 ReturnMsgResponse(req, S_OK);
343 TRACE("Starting message loop\n");
344 while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
346 TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
347 switch(msg.message)
349 case WM_USER_OpenDevice:
350 req = (ThreadRequest*)msg.wParam;
351 proxy = (ALCmmdevProxy*)msg.lParam;
353 hr = cohr = S_OK;
354 if(++deviceCount == 1)
355 hr = cohr = CoInitialize(NULL);
356 if(SUCCEEDED(hr))
357 hr = V0(proxy,openProxy)();
358 if(FAILED(hr))
360 if(--deviceCount == 0 && SUCCEEDED(cohr))
361 CoUninitialize();
364 ReturnMsgResponse(req, hr);
365 continue;
367 case WM_USER_ResetDevice:
368 req = (ThreadRequest*)msg.wParam;
369 proxy = (ALCmmdevProxy*)msg.lParam;
371 hr = V0(proxy,resetProxy)();
372 ReturnMsgResponse(req, hr);
373 continue;
375 case WM_USER_StartDevice:
376 req = (ThreadRequest*)msg.wParam;
377 proxy = (ALCmmdevProxy*)msg.lParam;
379 hr = V0(proxy,startProxy)();
380 ReturnMsgResponse(req, hr);
381 continue;
383 case WM_USER_StopDevice:
384 req = (ThreadRequest*)msg.wParam;
385 proxy = (ALCmmdevProxy*)msg.lParam;
387 V0(proxy,stopProxy)();
388 ReturnMsgResponse(req, S_OK);
389 continue;
391 case WM_USER_CloseDevice:
392 req = (ThreadRequest*)msg.wParam;
393 proxy = (ALCmmdevProxy*)msg.lParam;
395 V0(proxy,closeProxy)();
396 if(--deviceCount == 0)
397 CoUninitialize();
399 ReturnMsgResponse(req, S_OK);
400 continue;
402 case WM_USER_Enumerate:
403 req = (ThreadRequest*)msg.wParam;
405 hr = cohr = S_OK;
406 if(++deviceCount == 1)
407 hr = cohr = CoInitialize(NULL);
408 if(SUCCEEDED(hr))
409 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
410 if(SUCCEEDED(hr))
412 Enumerator = ptr;
414 if(msg.lParam == ALL_DEVICE_PROBE)
415 hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
416 else if(msg.lParam == CAPTURE_DEVICE_PROBE)
417 hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
419 IMMDeviceEnumerator_Release(Enumerator);
420 Enumerator = NULL;
423 if(--deviceCount == 0 && SUCCEEDED(cohr))
424 CoUninitialize();
426 ReturnMsgResponse(req, hr);
427 continue;
429 default:
430 ERR("Unexpected message: %u\n", msg.message);
431 continue;
434 TRACE("Message loop finished\n");
436 return 0;
440 typedef struct ALCmmdevPlayback {
441 DERIVE_FROM_TYPE(ALCbackend);
442 DERIVE_FROM_TYPE(ALCmmdevProxy);
444 WCHAR *devid;
446 IMMDevice *mmdev;
447 IAudioClient *client;
448 IAudioRenderClient *render;
449 HANDLE NotifyEvent;
451 HANDLE MsgEvent;
453 volatile UINT32 Padding;
455 volatile int killNow;
456 althrd_t thread;
457 } ALCmmdevPlayback;
459 static int ALCmmdevPlayback_mixerProc(void *arg);
461 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
462 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
463 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
464 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
465 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
466 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
467 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
468 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
469 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
470 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
471 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
472 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
473 static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
474 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
475 static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
476 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
477 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
478 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
480 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
481 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
484 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
486 SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
487 SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
488 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
489 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
491 self->devid = NULL;
493 self->mmdev = NULL;
494 self->client = NULL;
495 self->render = NULL;
496 self->NotifyEvent = NULL;
498 self->MsgEvent = NULL;
500 self->Padding = 0;
502 self->killNow = 0;
505 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
507 if(self->NotifyEvent != NULL)
508 CloseHandle(self->NotifyEvent);
509 self->NotifyEvent = NULL;
510 if(self->MsgEvent != NULL)
511 CloseHandle(self->MsgEvent);
512 self->MsgEvent = NULL;
514 free(self->devid);
515 self->devid = NULL;
517 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
518 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
522 FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
524 ALCmmdevPlayback *self = arg;
525 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
526 UINT32 buffer_len, written;
527 ALuint update_size, len;
528 BYTE *buffer;
529 HRESULT hr;
531 hr = CoInitialize(NULL);
532 if(FAILED(hr))
534 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
535 V0(device->Backend,lock)();
536 aluHandleDisconnect(device);
537 V0(device->Backend,unlock)();
538 return 1;
541 SetRTPriority();
542 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
544 update_size = device->UpdateSize;
545 buffer_len = update_size * device->NumUpdates;
546 while(!self->killNow)
548 hr = IAudioClient_GetCurrentPadding(self->client, &written);
549 if(FAILED(hr))
551 ERR("Failed to get padding: 0x%08lx\n", hr);
552 V0(device->Backend,lock)();
553 aluHandleDisconnect(device);
554 V0(device->Backend,unlock)();
555 break;
557 self->Padding = written;
559 len = buffer_len - written;
560 if(len < update_size)
562 DWORD res;
563 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
564 if(res != WAIT_OBJECT_0)
565 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
566 continue;
568 len -= len%update_size;
570 hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
571 if(SUCCEEDED(hr))
573 V0(device->Backend,lock)();
574 aluMixData(device, buffer, len);
575 self->Padding = written + len;
576 V0(device->Backend,unlock)();
577 hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
579 if(FAILED(hr))
581 ERR("Failed to buffer data: 0x%08lx\n", hr);
582 V0(device->Backend,lock)();
583 aluHandleDisconnect(device);
584 V0(device->Backend,unlock)();
585 break;
588 self->Padding = 0;
590 CoUninitialize();
591 return 0;
595 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
597 memset(out, 0, sizeof(*out));
598 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
599 *out = *(const WAVEFORMATEXTENSIBLE*)in;
600 else if(in->wFormatTag == WAVE_FORMAT_PCM)
602 out->Format = *in;
603 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
604 out->Format.cbSize = sizeof(*out) - sizeof(*in);
605 if(out->Format.nChannels == 1)
606 out->dwChannelMask = MONO;
607 else if(out->Format.nChannels == 2)
608 out->dwChannelMask = STEREO;
609 else
610 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
611 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
613 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
615 out->Format = *in;
616 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
617 out->Format.cbSize = sizeof(*out) - sizeof(*in);
618 if(out->Format.nChannels == 1)
619 out->dwChannelMask = MONO;
620 else if(out->Format.nChannels == 2)
621 out->dwChannelMask = STEREO;
622 else
623 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
624 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
626 else
628 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
629 return ALC_FALSE;
631 return ALC_TRUE;
635 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
637 HRESULT hr = S_OK;
639 self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
640 self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
641 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
643 ERR("Failed to create message events: %lu\n", GetLastError());
644 hr = E_FAIL;
647 if(SUCCEEDED(hr))
649 if(deviceName)
651 const DevMap *iter, *end;
653 if(VECTOR_SIZE(PlaybackDevices) == 0)
655 ThreadRequest req = { self->MsgEvent, 0 };
656 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
657 (void)WaitForResponse(&req);
660 hr = E_FAIL;
661 iter = VECTOR_ITER_BEGIN(PlaybackDevices);
662 end = VECTOR_ITER_END(PlaybackDevices);
663 for(;iter != end;iter++)
665 if(al_string_cmp_cstr(iter->name, deviceName) == 0)
667 self->devid = strdupW(iter->devid);
668 hr = S_OK;
669 break;
672 if(FAILED(hr))
673 WARN("Failed to find device name matching \"%s\"\n", deviceName);
677 if(SUCCEEDED(hr))
679 ThreadRequest req = { self->MsgEvent, 0 };
681 hr = E_FAIL;
682 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
683 hr = WaitForResponse(&req);
684 else
685 ERR("Failed to post thread message: %lu\n", GetLastError());
688 if(FAILED(hr))
690 if(self->NotifyEvent != NULL)
691 CloseHandle(self->NotifyEvent);
692 self->NotifyEvent = NULL;
693 if(self->MsgEvent != NULL)
694 CloseHandle(self->MsgEvent);
695 self->MsgEvent = NULL;
697 free(self->devid);
698 self->devid = NULL;
700 ERR("Device init failed: 0x%08lx\n", hr);
701 return ALC_INVALID_VALUE;
704 return ALC_NO_ERROR;
707 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
709 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
710 void *ptr;
711 HRESULT hr;
713 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
714 if(SUCCEEDED(hr))
716 IMMDeviceEnumerator *Enumerator = ptr;
717 if(!self->devid)
718 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
719 else
720 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
721 IMMDeviceEnumerator_Release(Enumerator);
722 Enumerator = NULL;
724 if(SUCCEEDED(hr))
725 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
726 if(SUCCEEDED(hr))
728 self->client = ptr;
729 get_device_name(self->mmdev, &device->DeviceName);
732 if(FAILED(hr))
734 if(self->mmdev)
735 IMMDevice_Release(self->mmdev);
736 self->mmdev = NULL;
739 return hr;
743 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
745 ThreadRequest req = { self->MsgEvent, 0 };
747 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
748 (void)WaitForResponse(&req);
750 CloseHandle(self->MsgEvent);
751 self->MsgEvent = NULL;
753 CloseHandle(self->NotifyEvent);
754 self->NotifyEvent = NULL;
756 free(self->devid);
757 self->devid = NULL;
760 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
762 if(self->client)
763 IAudioClient_Release(self->client);
764 self->client = NULL;
766 if(self->mmdev)
767 IMMDevice_Release(self->mmdev);
768 self->mmdev = NULL;
772 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
774 ThreadRequest req = { self->MsgEvent, 0 };
775 HRESULT hr = E_FAIL;
777 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
778 hr = WaitForResponse(&req);
780 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
783 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
785 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
786 EndpointFormFactor formfactor = UnknownFormFactor;
787 WAVEFORMATEXTENSIBLE OutputType;
788 WAVEFORMATEX *wfx = NULL;
789 REFERENCE_TIME min_per, buf_time;
790 UINT32 buffer_len, min_len;
791 void *ptr = NULL;
792 HRESULT hr;
794 if(self->client)
795 IAudioClient_Release(self->client);
796 self->client = NULL;
798 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
799 if(FAILED(hr))
801 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
802 return hr;
804 self->client = ptr;
806 hr = IAudioClient_GetMixFormat(self->client, &wfx);
807 if(FAILED(hr))
809 ERR("Failed to get mix format: 0x%08lx\n", hr);
810 return hr;
813 if(!MakeExtensible(&OutputType, wfx))
815 CoTaskMemFree(wfx);
816 return E_FAIL;
818 CoTaskMemFree(wfx);
819 wfx = NULL;
821 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
822 device->Frequency-1) / device->Frequency;
824 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
825 device->Frequency = OutputType.Format.nSamplesPerSec;
826 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
828 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
829 device->FmtChans = DevFmtMono;
830 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
831 device->FmtChans = DevFmtStereo;
832 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
833 device->FmtChans = DevFmtQuad;
834 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
835 device->FmtChans = DevFmtX51;
836 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
837 device->FmtChans = DevFmtX51Rear;
838 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
839 device->FmtChans = DevFmtX61;
840 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_NARROW))
841 device->FmtChans = DevFmtX71;
842 else
843 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
846 switch(device->FmtChans)
848 case DevFmtMono:
849 OutputType.Format.nChannels = 1;
850 OutputType.dwChannelMask = MONO;
851 break;
852 case DevFmtBFormat3D:
853 device->FmtChans = DevFmtStereo;
854 /*fall-through*/
855 case DevFmtStereo:
856 OutputType.Format.nChannels = 2;
857 OutputType.dwChannelMask = STEREO;
858 break;
859 case DevFmtQuad:
860 OutputType.Format.nChannels = 4;
861 OutputType.dwChannelMask = QUAD;
862 break;
863 case DevFmtX51:
864 OutputType.Format.nChannels = 6;
865 OutputType.dwChannelMask = X5DOT1;
866 break;
867 case DevFmtX51Rear:
868 OutputType.Format.nChannels = 6;
869 OutputType.dwChannelMask = X5DOT1REAR;
870 break;
871 case DevFmtX61:
872 OutputType.Format.nChannels = 7;
873 OutputType.dwChannelMask = X6DOT1;
874 break;
875 case DevFmtX71:
876 OutputType.Format.nChannels = 8;
877 OutputType.dwChannelMask = X7DOT1;
878 break;
880 switch(device->FmtType)
882 case DevFmtByte:
883 device->FmtType = DevFmtUByte;
884 /* fall-through */
885 case DevFmtUByte:
886 OutputType.Format.wBitsPerSample = 8;
887 OutputType.Samples.wValidBitsPerSample = 8;
888 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
889 break;
890 case DevFmtUShort:
891 device->FmtType = DevFmtShort;
892 /* fall-through */
893 case DevFmtShort:
894 OutputType.Format.wBitsPerSample = 16;
895 OutputType.Samples.wValidBitsPerSample = 16;
896 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
897 break;
898 case DevFmtUInt:
899 device->FmtType = DevFmtInt;
900 /* fall-through */
901 case DevFmtInt:
902 OutputType.Format.wBitsPerSample = 32;
903 OutputType.Samples.wValidBitsPerSample = 32;
904 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
905 break;
906 case DevFmtFloat:
907 OutputType.Format.wBitsPerSample = 32;
908 OutputType.Samples.wValidBitsPerSample = 32;
909 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
910 break;
912 OutputType.Format.nSamplesPerSec = device->Frequency;
914 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
915 OutputType.Format.wBitsPerSample / 8;
916 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
917 OutputType.Format.nBlockAlign;
919 hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
920 if(FAILED(hr))
922 ERR("Failed to check format support: 0x%08lx\n", hr);
923 hr = IAudioClient_GetMixFormat(self->client, &wfx);
925 if(FAILED(hr))
927 ERR("Failed to find a supported format: 0x%08lx\n", hr);
928 return hr;
931 if(wfx != NULL)
933 if(!MakeExtensible(&OutputType, wfx))
935 CoTaskMemFree(wfx);
936 return E_FAIL;
938 CoTaskMemFree(wfx);
939 wfx = NULL;
941 device->Frequency = OutputType.Format.nSamplesPerSec;
942 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
943 device->FmtChans = DevFmtMono;
944 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
945 device->FmtChans = DevFmtStereo;
946 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
947 device->FmtChans = DevFmtQuad;
948 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
949 device->FmtChans = DevFmtX51;
950 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
951 device->FmtChans = DevFmtX51Rear;
952 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
953 device->FmtChans = DevFmtX61;
954 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_NARROW))
955 device->FmtChans = DevFmtX71;
956 else
958 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
959 device->FmtChans = DevFmtStereo;
960 OutputType.Format.nChannels = 2;
961 OutputType.dwChannelMask = STEREO;
964 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
966 if(OutputType.Format.wBitsPerSample == 8)
967 device->FmtType = DevFmtUByte;
968 else if(OutputType.Format.wBitsPerSample == 16)
969 device->FmtType = DevFmtShort;
970 else if(OutputType.Format.wBitsPerSample == 32)
971 device->FmtType = DevFmtInt;
972 else
974 device->FmtType = DevFmtShort;
975 OutputType.Format.wBitsPerSample = 16;
978 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
980 device->FmtType = DevFmtFloat;
981 OutputType.Format.wBitsPerSample = 32;
983 else
985 ERR("Unhandled format sub-type\n");
986 device->FmtType = DevFmtShort;
987 OutputType.Format.wBitsPerSample = 16;
988 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
990 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
992 get_device_formfactor(self->mmdev, &formfactor);
993 device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
995 SetDefaultWFXChannelOrder(device);
997 hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
998 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
999 buf_time, 0, &OutputType.Format, NULL);
1000 if(FAILED(hr))
1002 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1003 return hr;
1006 hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
1007 if(SUCCEEDED(hr))
1009 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
1010 /* Find the nearest multiple of the period size to the update size */
1011 if(min_len < device->UpdateSize)
1012 min_len *= (device->UpdateSize + min_len/2)/min_len;
1013 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1015 if(FAILED(hr))
1017 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
1018 return hr;
1021 device->UpdateSize = min_len;
1022 device->NumUpdates = buffer_len / device->UpdateSize;
1023 if(device->NumUpdates <= 1)
1025 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1026 device->NumUpdates = 2;
1027 device->UpdateSize = buffer_len / device->NumUpdates;
1030 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1031 if(FAILED(hr))
1033 ERR("Failed to set event handle: 0x%08lx\n", hr);
1034 return hr;
1037 return hr;
1041 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
1043 ThreadRequest req = { self->MsgEvent, 0 };
1044 HRESULT hr = E_FAIL;
1046 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1047 hr = WaitForResponse(&req);
1049 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1052 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
1054 HRESULT hr;
1055 void *ptr;
1057 ResetEvent(self->NotifyEvent);
1058 hr = IAudioClient_Start(self->client);
1059 if(FAILED(hr))
1060 ERR("Failed to start audio client: 0x%08lx\n", hr);
1062 if(SUCCEEDED(hr))
1063 hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
1064 if(SUCCEEDED(hr))
1066 self->render = ptr;
1067 self->killNow = 0;
1068 if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
1070 if(self->render)
1071 IAudioRenderClient_Release(self->render);
1072 self->render = NULL;
1073 IAudioClient_Stop(self->client);
1074 ERR("Failed to start thread\n");
1075 hr = E_FAIL;
1079 return hr;
1083 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
1085 ThreadRequest req = { self->MsgEvent, 0 };
1086 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1087 (void)WaitForResponse(&req);
1090 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
1092 int res;
1094 if(!self->render)
1095 return;
1097 self->killNow = 1;
1098 althrd_join(self->thread, &res);
1100 IAudioRenderClient_Release(self->render);
1101 self->render = NULL;
1102 IAudioClient_Stop(self->client);
1106 static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
1108 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1109 return (ALint64)self->Padding * 1000000000 / device->Frequency;
1113 typedef struct ALCmmdevCapture {
1114 DERIVE_FROM_TYPE(ALCbackend);
1115 DERIVE_FROM_TYPE(ALCmmdevProxy);
1117 WCHAR *devid;
1119 IMMDevice *mmdev;
1120 IAudioClient *client;
1121 IAudioCaptureClient *capture;
1122 HANDLE NotifyEvent;
1124 HANDLE MsgEvent;
1126 RingBuffer *Ring;
1128 volatile int killNow;
1129 althrd_t thread;
1130 } ALCmmdevCapture;
1132 static int ALCmmdevCapture_recordProc(void *arg);
1134 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
1135 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
1136 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
1137 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
1138 static void ALCmmdevCapture_close(ALCmmdevCapture *self);
1139 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
1140 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
1141 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
1142 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
1143 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
1144 static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
1145 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
1146 static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
1147 static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
1148 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
1149 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
1150 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
1151 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
1153 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
1154 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
1157 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
1159 SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
1160 SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
1161 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
1162 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
1164 self->devid = NULL;
1166 self->mmdev = NULL;
1167 self->client = NULL;
1168 self->capture = NULL;
1169 self->NotifyEvent = NULL;
1171 self->MsgEvent = NULL;
1173 self->Ring = NULL;
1175 self->killNow = 0;
1178 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
1180 DestroyRingBuffer(self->Ring);
1181 self->Ring = NULL;
1183 if(self->NotifyEvent != NULL)
1184 CloseHandle(self->NotifyEvent);
1185 self->NotifyEvent = NULL;
1186 if(self->MsgEvent != NULL)
1187 CloseHandle(self->MsgEvent);
1188 self->MsgEvent = NULL;
1190 free(self->devid);
1191 self->devid = NULL;
1193 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
1194 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
1198 FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
1200 ALCmmdevCapture *self = arg;
1201 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1202 HRESULT hr;
1204 hr = CoInitialize(NULL);
1205 if(FAILED(hr))
1207 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
1208 V0(device->Backend,lock)();
1209 aluHandleDisconnect(device);
1210 V0(device->Backend,unlock)();
1211 return 1;
1214 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
1216 while(!self->killNow)
1218 UINT32 avail;
1219 DWORD res;
1221 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1222 if(FAILED(hr))
1223 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1224 else while(avail > 0 && SUCCEEDED(hr))
1226 UINT32 numsamples;
1227 DWORD flags;
1228 BYTE *data;
1230 hr = IAudioCaptureClient_GetBuffer(self->capture,
1231 &data, &numsamples, &flags, NULL, NULL
1233 if(FAILED(hr))
1235 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
1236 break;
1239 WriteRingBuffer(self->Ring, data, numsamples);
1241 hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
1242 if(FAILED(hr))
1244 ERR("Failed to release capture buffer: 0x%08lx\n", hr);
1245 break;
1248 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1249 if(FAILED(hr))
1250 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1253 if(FAILED(hr))
1255 V0(device->Backend,lock)();
1256 aluHandleDisconnect(device);
1257 V0(device->Backend,unlock)();
1258 break;
1261 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
1262 if(res != WAIT_OBJECT_0)
1263 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
1266 CoUninitialize();
1267 return 0;
1271 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
1273 HRESULT hr = S_OK;
1275 self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1276 self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1277 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
1279 ERR("Failed to create message events: %lu\n", GetLastError());
1280 hr = E_FAIL;
1283 if(SUCCEEDED(hr))
1285 if(deviceName)
1287 const DevMap *iter;
1289 if(VECTOR_SIZE(CaptureDevices) == 0)
1291 ThreadRequest req = { self->MsgEvent, 0 };
1292 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
1293 (void)WaitForResponse(&req);
1296 hr = E_FAIL;
1297 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
1298 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1299 if(iter == VECTOR_ITER_END(CaptureDevices))
1300 WARN("Failed to find device name matching \"%s\"\n", deviceName);
1301 else
1303 self->devid = strdupW(iter->devid);
1304 hr = S_OK;
1306 #undef MATCH_NAME
1310 if(SUCCEEDED(hr))
1312 ThreadRequest req = { self->MsgEvent, 0 };
1314 hr = E_FAIL;
1315 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1316 hr = WaitForResponse(&req);
1317 else
1318 ERR("Failed to post thread message: %lu\n", GetLastError());
1321 if(FAILED(hr))
1323 if(self->NotifyEvent != NULL)
1324 CloseHandle(self->NotifyEvent);
1325 self->NotifyEvent = NULL;
1326 if(self->MsgEvent != NULL)
1327 CloseHandle(self->MsgEvent);
1328 self->MsgEvent = NULL;
1330 free(self->devid);
1331 self->devid = NULL;
1333 ERR("Device init failed: 0x%08lx\n", hr);
1334 return ALC_INVALID_VALUE;
1336 else
1338 ThreadRequest req = { self->MsgEvent, 0 };
1340 hr = E_FAIL;
1341 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1342 hr = WaitForResponse(&req);
1343 else
1344 ERR("Failed to post thread message: %lu\n", GetLastError());
1346 if(FAILED(hr))
1348 ALCmmdevCapture_close(self);
1349 if(hr == E_OUTOFMEMORY)
1350 return ALC_OUT_OF_MEMORY;
1351 return ALC_INVALID_VALUE;
1355 return ALC_NO_ERROR;
1358 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
1360 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1361 void *ptr;
1362 HRESULT hr;
1364 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
1365 if(SUCCEEDED(hr))
1367 IMMDeviceEnumerator *Enumerator = ptr;
1368 if(!self->devid)
1369 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
1370 else
1371 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
1372 IMMDeviceEnumerator_Release(Enumerator);
1373 Enumerator = NULL;
1375 if(SUCCEEDED(hr))
1376 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1377 if(SUCCEEDED(hr))
1379 self->client = ptr;
1380 get_device_name(self->mmdev, &device->DeviceName);
1383 if(FAILED(hr))
1385 if(self->mmdev)
1386 IMMDevice_Release(self->mmdev);
1387 self->mmdev = NULL;
1390 return hr;
1394 static void ALCmmdevCapture_close(ALCmmdevCapture *self)
1396 ThreadRequest req = { self->MsgEvent, 0 };
1398 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1399 (void)WaitForResponse(&req);
1401 DestroyRingBuffer(self->Ring);
1402 self->Ring = NULL;
1404 CloseHandle(self->MsgEvent);
1405 self->MsgEvent = NULL;
1407 CloseHandle(self->NotifyEvent);
1408 self->NotifyEvent = NULL;
1410 free(self->devid);
1411 self->devid = NULL;
1414 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
1416 if(self->client)
1417 IAudioClient_Release(self->client);
1418 self->client = NULL;
1420 if(self->mmdev)
1421 IMMDevice_Release(self->mmdev);
1422 self->mmdev = NULL;
1426 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
1428 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1429 WAVEFORMATEXTENSIBLE OutputType;
1430 REFERENCE_TIME buf_time;
1431 UINT32 buffer_len;
1432 void *ptr = NULL;
1433 HRESULT hr;
1435 if(self->client)
1436 IAudioClient_Release(self->client);
1437 self->client = NULL;
1439 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1440 if(FAILED(hr))
1442 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
1443 return hr;
1445 self->client = ptr;
1447 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
1448 device->Frequency-1) / device->Frequency;
1450 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1451 switch(device->FmtChans)
1453 case DevFmtMono:
1454 OutputType.Format.nChannels = 1;
1455 OutputType.dwChannelMask = MONO;
1456 break;
1457 case DevFmtStereo:
1458 OutputType.Format.nChannels = 2;
1459 OutputType.dwChannelMask = STEREO;
1460 break;
1461 case DevFmtQuad:
1462 OutputType.Format.nChannels = 4;
1463 OutputType.dwChannelMask = QUAD;
1464 break;
1465 case DevFmtX51:
1466 OutputType.Format.nChannels = 6;
1467 OutputType.dwChannelMask = X5DOT1;
1468 break;
1469 case DevFmtX51Rear:
1470 OutputType.Format.nChannels = 6;
1471 OutputType.dwChannelMask = X5DOT1REAR;
1472 break;
1473 case DevFmtX61:
1474 OutputType.Format.nChannels = 7;
1475 OutputType.dwChannelMask = X6DOT1;
1476 break;
1477 case DevFmtX71:
1478 OutputType.Format.nChannels = 8;
1479 OutputType.dwChannelMask = X7DOT1;
1480 break;
1482 case DevFmtBFormat3D:
1483 return E_FAIL;
1485 switch(device->FmtType)
1487 case DevFmtUByte:
1488 OutputType.Format.wBitsPerSample = 8;
1489 OutputType.Samples.wValidBitsPerSample = 8;
1490 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1491 break;
1492 case DevFmtShort:
1493 OutputType.Format.wBitsPerSample = 16;
1494 OutputType.Samples.wValidBitsPerSample = 16;
1495 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1496 break;
1497 case DevFmtInt:
1498 OutputType.Format.wBitsPerSample = 32;
1499 OutputType.Samples.wValidBitsPerSample = 32;
1500 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1501 break;
1502 case DevFmtFloat:
1503 OutputType.Format.wBitsPerSample = 32;
1504 OutputType.Samples.wValidBitsPerSample = 32;
1505 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1506 break;
1508 case DevFmtByte:
1509 case DevFmtUShort:
1510 case DevFmtUInt:
1511 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
1512 return E_FAIL;
1514 OutputType.Format.nSamplesPerSec = device->Frequency;
1516 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1517 OutputType.Format.wBitsPerSample / 8;
1518 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1519 OutputType.Format.nBlockAlign;
1521 hr = IAudioClient_IsFormatSupported(self->client,
1522 AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, NULL
1524 if(FAILED(hr))
1526 ERR("Failed to check format support: 0x%08lx\n", hr);
1527 return hr;
1530 hr = IAudioClient_Initialize(self->client,
1531 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1532 buf_time, 0, &OutputType.Format, NULL
1534 if(FAILED(hr))
1536 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1537 return hr;
1540 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1541 if(FAILED(hr))
1543 ERR("Failed to get buffer size: 0x%08lx\n", hr);
1544 return hr;
1547 buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len);
1548 self->Ring = CreateRingBuffer(OutputType.Format.nBlockAlign, buffer_len);
1549 if(!self->Ring)
1551 ERR("Failed to allocate capture ring buffer\n");
1552 return E_OUTOFMEMORY;
1555 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1556 if(FAILED(hr))
1558 ERR("Failed to set event handle: 0x%08lx\n", hr);
1559 return hr;
1562 return hr;
1566 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
1568 ThreadRequest req = { self->MsgEvent, 0 };
1569 HRESULT hr = E_FAIL;
1571 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1572 hr = WaitForResponse(&req);
1574 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1577 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
1579 HRESULT hr;
1580 void *ptr;
1582 ResetEvent(self->NotifyEvent);
1583 hr = IAudioClient_Start(self->client);
1584 if(FAILED(hr))
1586 ERR("Failed to start audio client: 0x%08lx\n", hr);
1587 return hr;
1590 hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
1591 if(SUCCEEDED(hr))
1593 self->capture = ptr;
1594 self->killNow = 0;
1595 if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
1597 ERR("Failed to start thread\n");
1598 IAudioCaptureClient_Release(self->capture);
1599 self->capture = NULL;
1600 hr = E_FAIL;
1604 if(FAILED(hr))
1606 IAudioClient_Stop(self->client);
1607 IAudioClient_Reset(self->client);
1610 return hr;
1614 static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
1616 ThreadRequest req = { self->MsgEvent, 0 };
1617 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1618 (void)WaitForResponse(&req);
1621 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
1623 int res;
1625 if(!self->capture)
1626 return;
1628 self->killNow = 1;
1629 althrd_join(self->thread, &res);
1631 IAudioCaptureClient_Release(self->capture);
1632 self->capture = NULL;
1633 IAudioClient_Stop(self->client);
1634 IAudioClient_Reset(self->client);
1638 ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
1640 return RingBufferSize(self->Ring);
1643 ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
1645 if(ALCmmdevCapture_availableSamples(self) < samples)
1646 return ALC_INVALID_VALUE;
1647 ReadRingBuffer(self->Ring, buffer, samples);
1648 return ALC_NO_ERROR;
1652 static inline void AppendAllDevicesList2(const DevMap *entry)
1653 { AppendAllDevicesList(al_string_get_cstr(entry->name)); }
1654 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1655 { AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
1657 typedef struct ALCmmdevBackendFactory {
1658 DERIVE_FROM_TYPE(ALCbackendFactory);
1659 } ALCmmdevBackendFactory;
1660 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1662 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
1663 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
1664 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
1665 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
1666 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
1668 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
1671 static BOOL MMDevApiLoad(void)
1673 static HRESULT InitResult;
1674 if(!ThreadHdl)
1676 ThreadRequest req;
1677 InitResult = E_FAIL;
1679 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1680 if(req.FinishedEvt == NULL)
1681 ERR("Failed to create event: %lu\n", GetLastError());
1682 else
1684 ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
1685 if(ThreadHdl != NULL)
1686 InitResult = WaitForResponse(&req);
1687 CloseHandle(req.FinishedEvt);
1690 return SUCCEEDED(InitResult);
1693 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
1695 VECTOR_INIT(PlaybackDevices);
1696 VECTOR_INIT(CaptureDevices);
1698 if(!MMDevApiLoad())
1699 return ALC_FALSE;
1700 return ALC_TRUE;
1703 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
1705 clear_devlist(&PlaybackDevices);
1706 VECTOR_DEINIT(PlaybackDevices);
1708 clear_devlist(&CaptureDevices);
1709 VECTOR_DEINIT(CaptureDevices);
1711 if(ThreadHdl)
1713 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
1714 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
1715 CloseHandle(ThreadHdl);
1716 ThreadHdl = NULL;
1720 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
1722 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
1723 return ALC_TRUE;
1724 return ALC_FALSE;
1727 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
1729 ThreadRequest req = { NULL, 0 };
1731 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1732 if(req.FinishedEvt == NULL)
1733 ERR("Failed to create event: %lu\n", GetLastError());
1734 else
1736 HRESULT hr = E_FAIL;
1737 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1738 hr = WaitForResponse(&req);
1739 if(SUCCEEDED(hr)) switch(type)
1741 case ALL_DEVICE_PROBE:
1742 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
1743 break;
1745 case CAPTURE_DEVICE_PROBE:
1746 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
1747 break;
1749 CloseHandle(req.FinishedEvt);
1750 req.FinishedEvt = NULL;
1754 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1756 if(type == ALCbackend_Playback)
1758 ALCmmdevPlayback *backend;
1760 backend = ALCmmdevPlayback_New(sizeof(*backend));
1761 if(!backend) return NULL;
1762 memset(backend, 0, sizeof(*backend));
1764 ALCmmdevPlayback_Construct(backend, device);
1766 return STATIC_CAST(ALCbackend, backend);
1768 if(type == ALCbackend_Capture)
1770 ALCmmdevCapture *backend;
1772 backend = ALCmmdevCapture_New(sizeof(*backend));
1773 if(!backend) return NULL;
1774 memset(backend, 0, sizeof(*backend));
1776 ALCmmdevCapture_Construct(backend, device);
1778 return STATIC_CAST(ALCbackend, backend);
1781 return NULL;
1785 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
1787 static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
1788 return STATIC_CAST(ALCbackendFactory, &factory);