Use QString's arg method to format the float value
[openal-soft.git] / Alc / backends / mmdevapi.c
blob6ff9d931ec17944aec894a09695ecf5adba00cff
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 <wtypes.h>
29 #include <mmdeviceapi.h>
30 #include <audioclient.h>
31 #include <cguid.h>
32 #include <devpropdef.h>
33 #include <mmreg.h>
34 #include <propsys.h>
35 #include <propkey.h>
36 #include <devpkey.h>
37 #ifndef _WAVEFORMATEXTENSIBLE_
38 #include <ks.h>
39 #include <ksmedia.h>
40 #endif
42 #include "alMain.h"
43 #include "alu.h"
44 #include "threads.h"
45 #include "compat.h"
46 #include "alstring.h"
48 #include "backends/base.h"
51 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
52 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
54 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
55 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
56 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
58 #define MONO SPEAKER_FRONT_CENTER
59 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
60 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
61 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
62 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
63 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
64 #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)
65 #define X7DOT1_WIDE (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)
67 #define DEVNAME_HEAD "OpenAL Soft on "
70 typedef struct {
71 al_string name;
72 al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
73 WCHAR *devid;
74 } DevMap;
75 TYPEDEF_VECTOR(DevMap, vector_DevMap)
77 static void clear_devlist(vector_DevMap *list)
79 #define CLEAR_DEVMAP(i) do { \
80 AL_STRING_DEINIT((i)->name); \
81 AL_STRING_DEINIT((i)->endpoint_guid); \
82 free((i)->devid); \
83 (i)->devid = NULL; \
84 } while(0)
85 VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
86 VECTOR_RESIZE(*list, 0, 0);
87 #undef CLEAR_DEVMAP
90 static vector_DevMap PlaybackDevices;
91 static vector_DevMap CaptureDevices;
94 static HANDLE ThreadHdl;
95 static DWORD ThreadID;
97 typedef struct {
98 HANDLE FinishedEvt;
99 HRESULT result;
100 } ThreadRequest;
102 #define WM_USER_First (WM_USER+0)
103 #define WM_USER_OpenDevice (WM_USER+0)
104 #define WM_USER_ResetDevice (WM_USER+1)
105 #define WM_USER_StartDevice (WM_USER+2)
106 #define WM_USER_StopDevice (WM_USER+3)
107 #define WM_USER_CloseDevice (WM_USER+4)
108 #define WM_USER_Enumerate (WM_USER+5)
109 #define WM_USER_Last (WM_USER+5)
111 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
113 req->result = res;
114 SetEvent(req->FinishedEvt);
117 static HRESULT WaitForResponse(ThreadRequest *req)
119 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
120 return req->result;
121 ERR("Message response error: %lu\n", GetLastError());
122 return E_FAIL;
126 static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
128 IPropertyStore *ps;
129 PROPVARIANT pvname;
130 PROPVARIANT pvguid;
131 HRESULT hr;
133 al_string_copy_cstr(name, DEVNAME_HEAD);
135 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
136 if(FAILED(hr))
138 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
139 al_string_append_cstr(name, "Unknown Device Name");
140 if(guid!=NULL)al_string_copy_cstr(guid, "Unknown Device GUID");
141 return;
144 PropVariantInit(&pvname);
146 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
147 if(FAILED(hr))
149 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
150 al_string_append_cstr(name, "Unknown Device Name");
152 else if(pvname.vt == VT_LPWSTR)
153 al_string_append_wcstr(name, pvname.pwszVal);
154 else
156 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
157 al_string_append_cstr(name, "Unknown Device Name");
159 PropVariantClear(&pvname);
161 if(guid!=NULL){
162 PropVariantInit(&pvguid);
164 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
165 if(FAILED(hr))
167 WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
168 al_string_copy_cstr(guid, "Unknown Device GUID");
170 else if(pvguid.vt == VT_LPWSTR)
171 al_string_copy_wcstr(guid, pvguid.pwszVal);
172 else
174 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
175 al_string_copy_cstr(guid, "Unknown Device GUID");
178 PropVariantClear(&pvguid);
181 IPropertyStore_Release(ps);
184 static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
186 IPropertyStore *ps;
187 PROPVARIANT pvform;
188 HRESULT hr;
190 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
191 if(FAILED(hr))
193 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
194 return;
197 PropVariantInit(&pvform);
199 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
200 if(FAILED(hr))
201 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
202 else if(pvform.vt == VT_UI4)
203 *formfactor = pvform.ulVal;
204 else if(pvform.vt == VT_EMPTY)
205 *formfactor = UnknownFormFactor;
206 else
207 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
209 PropVariantClear(&pvform);
210 IPropertyStore_Release(ps);
214 static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
216 int count = 0;
217 al_string tmpname;
218 DevMap entry;
220 AL_STRING_INIT(tmpname);
221 AL_STRING_INIT(entry.name);
222 AL_STRING_INIT(entry.endpoint_guid);
224 entry.devid = strdupW(devid);
225 get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
227 while(1)
229 const DevMap *iter;
231 al_string_copy(&entry.name, tmpname);
232 if(count != 0)
234 char str[64];
235 snprintf(str, sizeof(str), " #%d", count+1);
236 al_string_append_cstr(&entry.name, str);
239 #define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
240 VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
241 if(iter == VECTOR_END(*list)) break;
242 #undef MATCH_ENTRY
243 count++;
246 TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), al_string_get_cstr(entry.endpoint_guid), entry.devid);
247 VECTOR_PUSH_BACK(*list, entry);
249 AL_STRING_DEINIT(tmpname);
252 static WCHAR *get_device_id(IMMDevice *device)
254 WCHAR *devid;
255 HRESULT hr;
257 hr = IMMDevice_GetId(device, &devid);
258 if(FAILED(hr))
260 ERR("Failed to get device id: %lx\n", hr);
261 return NULL;
264 return devid;
267 static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
269 IMMDeviceCollection *coll;
270 IMMDevice *defdev = NULL;
271 WCHAR *defdevid = NULL;
272 HRESULT hr;
273 UINT count;
274 UINT i;
276 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
277 if(FAILED(hr))
279 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
280 return hr;
283 count = 0;
284 hr = IMMDeviceCollection_GetCount(coll, &count);
285 if(SUCCEEDED(hr) && count > 0)
287 clear_devlist(list);
288 VECTOR_RESIZE(*list, 0, count);
290 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
291 eMultimedia, &defdev);
293 if(SUCCEEDED(hr) && defdev != NULL)
295 defdevid = get_device_id(defdev);
296 if(defdevid)
297 add_device(defdev, defdevid, list);
300 for(i = 0;i < count;++i)
302 IMMDevice *device;
303 WCHAR *devid;
305 hr = IMMDeviceCollection_Item(coll, i, &device);
306 if(FAILED(hr)) continue;
308 devid = get_device_id(device);
309 if(devid)
311 if(wcscmp(devid, defdevid) != 0)
312 add_device(device, devid, list);
313 CoTaskMemFree(devid);
315 IMMDevice_Release(device);
318 if(defdev) IMMDevice_Release(defdev);
319 if(defdevid) CoTaskMemFree(defdevid);
320 IMMDeviceCollection_Release(coll);
322 return S_OK;
326 /* Proxy interface used by the message handler. */
327 struct ALCmmdevProxyVtable;
329 typedef struct ALCmmdevProxy {
330 const struct ALCmmdevProxyVtable *vtbl;
331 } ALCmmdevProxy;
333 struct ALCmmdevProxyVtable {
334 HRESULT (*const openProxy)(ALCmmdevProxy*);
335 void (*const closeProxy)(ALCmmdevProxy*);
337 HRESULT (*const resetProxy)(ALCmmdevProxy*);
338 HRESULT (*const startProxy)(ALCmmdevProxy*);
339 void (*const stopProxy)(ALCmmdevProxy*);
342 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
343 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
344 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
345 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
346 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
347 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
349 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
350 T##_ALCmmdevProxy_openProxy, \
351 T##_ALCmmdevProxy_closeProxy, \
352 T##_ALCmmdevProxy_resetProxy, \
353 T##_ALCmmdevProxy_startProxy, \
354 T##_ALCmmdevProxy_stopProxy, \
357 static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
358 static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
360 static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
362 ThreadRequest *req = ptr;
363 IMMDeviceEnumerator *Enumerator;
364 ALuint deviceCount = 0;
365 ALCmmdevProxy *proxy;
366 HRESULT hr, cohr;
367 MSG msg;
369 TRACE("Starting message thread\n");
371 cohr = CoInitialize(NULL);
372 if(FAILED(cohr))
374 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
375 ReturnMsgResponse(req, cohr);
376 return 0;
379 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
380 if(FAILED(hr))
382 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
383 CoUninitialize();
384 ReturnMsgResponse(req, hr);
385 return 0;
387 Enumerator = ptr;
388 IMMDeviceEnumerator_Release(Enumerator);
389 Enumerator = NULL;
391 CoUninitialize();
393 /* HACK: Force Windows to create a message queue for this thread before
394 * returning success, otherwise PostThreadMessage may fail if it gets
395 * called before GetMessage.
397 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
399 TRACE("Message thread initialization complete\n");
400 ReturnMsgResponse(req, S_OK);
402 TRACE("Starting message loop\n");
403 while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
405 TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
406 switch(msg.message)
408 case WM_USER_OpenDevice:
409 req = (ThreadRequest*)msg.wParam;
410 proxy = (ALCmmdevProxy*)msg.lParam;
412 hr = cohr = S_OK;
413 if(++deviceCount == 1)
414 hr = cohr = CoInitialize(NULL);
415 if(SUCCEEDED(hr))
416 hr = V0(proxy,openProxy)();
417 if(FAILED(hr))
419 if(--deviceCount == 0 && SUCCEEDED(cohr))
420 CoUninitialize();
423 ReturnMsgResponse(req, hr);
424 continue;
426 case WM_USER_ResetDevice:
427 req = (ThreadRequest*)msg.wParam;
428 proxy = (ALCmmdevProxy*)msg.lParam;
430 hr = V0(proxy,resetProxy)();
431 ReturnMsgResponse(req, hr);
432 continue;
434 case WM_USER_StartDevice:
435 req = (ThreadRequest*)msg.wParam;
436 proxy = (ALCmmdevProxy*)msg.lParam;
438 hr = V0(proxy,startProxy)();
439 ReturnMsgResponse(req, hr);
440 continue;
442 case WM_USER_StopDevice:
443 req = (ThreadRequest*)msg.wParam;
444 proxy = (ALCmmdevProxy*)msg.lParam;
446 V0(proxy,stopProxy)();
447 ReturnMsgResponse(req, S_OK);
448 continue;
450 case WM_USER_CloseDevice:
451 req = (ThreadRequest*)msg.wParam;
452 proxy = (ALCmmdevProxy*)msg.lParam;
454 V0(proxy,closeProxy)();
455 if(--deviceCount == 0)
456 CoUninitialize();
458 ReturnMsgResponse(req, S_OK);
459 continue;
461 case WM_USER_Enumerate:
462 req = (ThreadRequest*)msg.wParam;
464 hr = cohr = S_OK;
465 if(++deviceCount == 1)
466 hr = cohr = CoInitialize(NULL);
467 if(SUCCEEDED(hr))
468 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
469 if(SUCCEEDED(hr))
471 Enumerator = ptr;
473 if(msg.lParam == ALL_DEVICE_PROBE)
474 hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
475 else if(msg.lParam == CAPTURE_DEVICE_PROBE)
476 hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
478 IMMDeviceEnumerator_Release(Enumerator);
479 Enumerator = NULL;
482 if(--deviceCount == 0 && SUCCEEDED(cohr))
483 CoUninitialize();
485 ReturnMsgResponse(req, hr);
486 continue;
488 default:
489 ERR("Unexpected message: %u\n", msg.message);
490 continue;
493 TRACE("Message loop finished\n");
495 return 0;
499 typedef struct ALCmmdevPlayback {
500 DERIVE_FROM_TYPE(ALCbackend);
501 DERIVE_FROM_TYPE(ALCmmdevProxy);
503 WCHAR *devid;
505 IMMDevice *mmdev;
506 IAudioClient *client;
507 IAudioRenderClient *render;
508 HANDLE NotifyEvent;
510 HANDLE MsgEvent;
512 volatile UINT32 Padding;
514 volatile int killNow;
515 althrd_t thread;
516 } ALCmmdevPlayback;
518 static int ALCmmdevPlayback_mixerProc(void *arg);
520 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
521 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
522 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
523 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
524 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
525 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
526 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
527 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
528 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
529 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
530 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
531 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
532 static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
533 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
534 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
535 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
536 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
537 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
539 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
540 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
543 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
545 SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
546 SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
547 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
548 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
550 self->devid = NULL;
552 self->mmdev = NULL;
553 self->client = NULL;
554 self->render = NULL;
555 self->NotifyEvent = NULL;
557 self->MsgEvent = NULL;
559 self->Padding = 0;
561 self->killNow = 0;
564 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
566 if(self->NotifyEvent != NULL)
567 CloseHandle(self->NotifyEvent);
568 self->NotifyEvent = NULL;
569 if(self->MsgEvent != NULL)
570 CloseHandle(self->MsgEvent);
571 self->MsgEvent = NULL;
573 free(self->devid);
574 self->devid = NULL;
576 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
577 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
581 FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
583 ALCmmdevPlayback *self = arg;
584 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
585 UINT32 buffer_len, written;
586 ALuint update_size, len;
587 BYTE *buffer;
588 HRESULT hr;
590 hr = CoInitialize(NULL);
591 if(FAILED(hr))
593 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
594 V0(device->Backend,lock)();
595 aluHandleDisconnect(device);
596 V0(device->Backend,unlock)();
597 return 1;
600 SetRTPriority();
601 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
603 update_size = device->UpdateSize;
604 buffer_len = update_size * device->NumUpdates;
605 while(!self->killNow)
607 hr = IAudioClient_GetCurrentPadding(self->client, &written);
608 if(FAILED(hr))
610 ERR("Failed to get padding: 0x%08lx\n", hr);
611 V0(device->Backend,lock)();
612 aluHandleDisconnect(device);
613 V0(device->Backend,unlock)();
614 break;
616 self->Padding = written;
618 len = buffer_len - written;
619 if(len < update_size)
621 DWORD res;
622 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
623 if(res != WAIT_OBJECT_0)
624 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
625 continue;
627 len -= len%update_size;
629 hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
630 if(SUCCEEDED(hr))
632 ALCmmdevPlayback_lock(self);
633 aluMixData(device, buffer, len);
634 self->Padding = written + len;
635 ALCmmdevPlayback_unlock(self);
636 hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
638 if(FAILED(hr))
640 ERR("Failed to buffer data: 0x%08lx\n", hr);
641 V0(device->Backend,lock)();
642 aluHandleDisconnect(device);
643 V0(device->Backend,unlock)();
644 break;
647 self->Padding = 0;
649 CoUninitialize();
650 return 0;
654 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
656 memset(out, 0, sizeof(*out));
657 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
658 *out = *(const WAVEFORMATEXTENSIBLE*)in;
659 else if(in->wFormatTag == WAVE_FORMAT_PCM)
661 out->Format = *in;
662 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
663 out->Format.cbSize = sizeof(*out) - sizeof(*in);
664 if(out->Format.nChannels == 1)
665 out->dwChannelMask = MONO;
666 else if(out->Format.nChannels == 2)
667 out->dwChannelMask = STEREO;
668 else
669 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
670 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
672 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
674 out->Format = *in;
675 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
676 out->Format.cbSize = sizeof(*out) - sizeof(*in);
677 if(out->Format.nChannels == 1)
678 out->dwChannelMask = MONO;
679 else if(out->Format.nChannels == 2)
680 out->dwChannelMask = STEREO;
681 else
682 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
683 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
685 else
687 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
688 return ALC_FALSE;
690 return ALC_TRUE;
693 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
695 HRESULT hr = S_OK;
697 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
698 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
699 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
701 ERR("Failed to create message events: %lu\n", GetLastError());
702 hr = E_FAIL;
705 if(SUCCEEDED(hr))
707 if(deviceName)
709 const DevMap *iter;
711 if(VECTOR_SIZE(PlaybackDevices) == 0)
713 ThreadRequest req = { self->MsgEvent, 0 };
714 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
715 (void)WaitForResponse(&req);
718 hr = E_FAIL;
719 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \
720 al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
721 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
722 #undef MATCH_NAME
723 if(iter == VECTOR_END(PlaybackDevices))
725 int len;
726 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
728 WCHAR *wname = calloc(sizeof(WCHAR), len);
729 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
730 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
731 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
732 #undef MATCH_NAME
733 free(wname);
736 if(iter == VECTOR_END(PlaybackDevices))
737 WARN("Failed to find device name matching \"%s\"\n", deviceName);
738 else
740 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
741 self->devid = strdupW(iter->devid);
742 al_string_copy(&device->DeviceName, iter->name);
743 hr = S_OK;
748 if(SUCCEEDED(hr))
750 ThreadRequest req = { self->MsgEvent, 0 };
752 hr = E_FAIL;
753 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
754 hr = WaitForResponse(&req);
755 else
756 ERR("Failed to post thread message: %lu\n", GetLastError());
759 if(FAILED(hr))
761 if(self->NotifyEvent != NULL)
762 CloseHandle(self->NotifyEvent);
763 self->NotifyEvent = NULL;
764 if(self->MsgEvent != NULL)
765 CloseHandle(self->MsgEvent);
766 self->MsgEvent = NULL;
768 free(self->devid);
769 self->devid = NULL;
771 ERR("Device init failed: 0x%08lx\n", hr);
772 return ALC_INVALID_VALUE;
775 return ALC_NO_ERROR;
778 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
780 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
781 void *ptr;
782 HRESULT hr;
784 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
785 if(SUCCEEDED(hr))
787 IMMDeviceEnumerator *Enumerator = ptr;
788 if(!self->devid)
789 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
790 else
791 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
792 IMMDeviceEnumerator_Release(Enumerator);
793 Enumerator = NULL;
795 if(SUCCEEDED(hr))
796 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
797 if(SUCCEEDED(hr))
799 self->client = ptr;
800 if(al_string_empty(device->DeviceName))
801 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
804 if(FAILED(hr))
806 if(self->mmdev)
807 IMMDevice_Release(self->mmdev);
808 self->mmdev = NULL;
811 return hr;
815 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
817 ThreadRequest req = { self->MsgEvent, 0 };
819 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
820 (void)WaitForResponse(&req);
822 CloseHandle(self->MsgEvent);
823 self->MsgEvent = NULL;
825 CloseHandle(self->NotifyEvent);
826 self->NotifyEvent = NULL;
828 free(self->devid);
829 self->devid = NULL;
832 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
834 if(self->client)
835 IAudioClient_Release(self->client);
836 self->client = NULL;
838 if(self->mmdev)
839 IMMDevice_Release(self->mmdev);
840 self->mmdev = NULL;
844 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
846 ThreadRequest req = { self->MsgEvent, 0 };
847 HRESULT hr = E_FAIL;
849 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
850 hr = WaitForResponse(&req);
852 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
855 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
857 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
858 EndpointFormFactor formfactor = UnknownFormFactor;
859 WAVEFORMATEXTENSIBLE OutputType;
860 WAVEFORMATEX *wfx = NULL;
861 REFERENCE_TIME min_per, buf_time;
862 UINT32 buffer_len, min_len;
863 void *ptr = NULL;
864 HRESULT hr;
866 if(self->client)
867 IAudioClient_Release(self->client);
868 self->client = NULL;
870 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
871 if(FAILED(hr))
873 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
874 return hr;
876 self->client = ptr;
878 hr = IAudioClient_GetMixFormat(self->client, &wfx);
879 if(FAILED(hr))
881 ERR("Failed to get mix format: 0x%08lx\n", hr);
882 return hr;
885 if(!MakeExtensible(&OutputType, wfx))
887 CoTaskMemFree(wfx);
888 return E_FAIL;
890 CoTaskMemFree(wfx);
891 wfx = NULL;
893 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
894 device->Frequency-1) / device->Frequency;
896 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
897 device->Frequency = OutputType.Format.nSamplesPerSec;
898 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
900 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
901 device->FmtChans = DevFmtMono;
902 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
903 device->FmtChans = DevFmtStereo;
904 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
905 device->FmtChans = DevFmtQuad;
906 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
907 device->FmtChans = DevFmtX51;
908 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
909 device->FmtChans = DevFmtX51Rear;
910 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
911 device->FmtChans = DevFmtX61;
912 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
913 device->FmtChans = DevFmtX71;
914 else
915 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
918 switch(device->FmtChans)
920 case DevFmtMono:
921 OutputType.Format.nChannels = 1;
922 OutputType.dwChannelMask = MONO;
923 break;
924 case DevFmtAmbi1:
925 case DevFmtAmbi2:
926 case DevFmtAmbi3:
927 device->FmtChans = DevFmtStereo;
928 /*fall-through*/
929 case DevFmtStereo:
930 OutputType.Format.nChannels = 2;
931 OutputType.dwChannelMask = STEREO;
932 break;
933 case DevFmtQuad:
934 OutputType.Format.nChannels = 4;
935 OutputType.dwChannelMask = QUAD;
936 break;
937 case DevFmtX51:
938 OutputType.Format.nChannels = 6;
939 OutputType.dwChannelMask = X5DOT1;
940 break;
941 case DevFmtX51Rear:
942 OutputType.Format.nChannels = 6;
943 OutputType.dwChannelMask = X5DOT1REAR;
944 break;
945 case DevFmtX61:
946 OutputType.Format.nChannels = 7;
947 OutputType.dwChannelMask = X6DOT1;
948 break;
949 case DevFmtX71:
950 OutputType.Format.nChannels = 8;
951 OutputType.dwChannelMask = X7DOT1;
952 break;
954 switch(device->FmtType)
956 case DevFmtByte:
957 device->FmtType = DevFmtUByte;
958 /* fall-through */
959 case DevFmtUByte:
960 OutputType.Format.wBitsPerSample = 8;
961 OutputType.Samples.wValidBitsPerSample = 8;
962 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
963 break;
964 case DevFmtUShort:
965 device->FmtType = DevFmtShort;
966 /* fall-through */
967 case DevFmtShort:
968 OutputType.Format.wBitsPerSample = 16;
969 OutputType.Samples.wValidBitsPerSample = 16;
970 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
971 break;
972 case DevFmtUInt:
973 device->FmtType = DevFmtInt;
974 /* fall-through */
975 case DevFmtInt:
976 OutputType.Format.wBitsPerSample = 32;
977 OutputType.Samples.wValidBitsPerSample = 32;
978 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
979 break;
980 case DevFmtFloat:
981 OutputType.Format.wBitsPerSample = 32;
982 OutputType.Samples.wValidBitsPerSample = 32;
983 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
984 break;
986 OutputType.Format.nSamplesPerSec = device->Frequency;
988 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
989 OutputType.Format.wBitsPerSample / 8;
990 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
991 OutputType.Format.nBlockAlign;
993 hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
994 if(FAILED(hr))
996 ERR("Failed to check format support: 0x%08lx\n", hr);
997 hr = IAudioClient_GetMixFormat(self->client, &wfx);
999 if(FAILED(hr))
1001 ERR("Failed to find a supported format: 0x%08lx\n", hr);
1002 return hr;
1005 if(wfx != NULL)
1007 if(!MakeExtensible(&OutputType, wfx))
1009 CoTaskMemFree(wfx);
1010 return E_FAIL;
1012 CoTaskMemFree(wfx);
1013 wfx = NULL;
1015 device->Frequency = OutputType.Format.nSamplesPerSec;
1016 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
1017 device->FmtChans = DevFmtMono;
1018 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
1019 device->FmtChans = DevFmtStereo;
1020 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
1021 device->FmtChans = DevFmtQuad;
1022 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
1023 device->FmtChans = DevFmtX51;
1024 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
1025 device->FmtChans = DevFmtX51Rear;
1026 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
1027 device->FmtChans = DevFmtX61;
1028 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
1029 device->FmtChans = DevFmtX71;
1030 else
1032 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
1033 device->FmtChans = DevFmtStereo;
1034 OutputType.Format.nChannels = 2;
1035 OutputType.dwChannelMask = STEREO;
1038 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
1040 if(OutputType.Format.wBitsPerSample == 8)
1041 device->FmtType = DevFmtUByte;
1042 else if(OutputType.Format.wBitsPerSample == 16)
1043 device->FmtType = DevFmtShort;
1044 else if(OutputType.Format.wBitsPerSample == 32)
1045 device->FmtType = DevFmtInt;
1046 else
1048 device->FmtType = DevFmtShort;
1049 OutputType.Format.wBitsPerSample = 16;
1052 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1054 device->FmtType = DevFmtFloat;
1055 OutputType.Format.wBitsPerSample = 32;
1057 else
1059 ERR("Unhandled format sub-type\n");
1060 device->FmtType = DevFmtShort;
1061 OutputType.Format.wBitsPerSample = 16;
1062 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1064 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1066 get_device_formfactor(self->mmdev, &formfactor);
1067 device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
1068 (formfactor == Headphones || formfactor == Headset)
1071 SetDefaultWFXChannelOrder(device);
1073 hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
1074 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1075 buf_time, 0, &OutputType.Format, NULL);
1076 if(FAILED(hr))
1078 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1079 return hr;
1082 hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
1083 if(SUCCEEDED(hr))
1085 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
1086 /* Find the nearest multiple of the period size to the update size */
1087 if(min_len < device->UpdateSize)
1088 min_len *= (device->UpdateSize + min_len/2)/min_len;
1089 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1091 if(FAILED(hr))
1093 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
1094 return hr;
1097 device->UpdateSize = min_len;
1098 device->NumUpdates = buffer_len / device->UpdateSize;
1099 if(device->NumUpdates <= 1)
1101 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1102 device->NumUpdates = 2;
1103 device->UpdateSize = buffer_len / device->NumUpdates;
1106 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1107 if(FAILED(hr))
1109 ERR("Failed to set event handle: 0x%08lx\n", hr);
1110 return hr;
1113 return hr;
1117 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
1119 ThreadRequest req = { self->MsgEvent, 0 };
1120 HRESULT hr = E_FAIL;
1122 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1123 hr = WaitForResponse(&req);
1125 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1128 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
1130 HRESULT hr;
1131 void *ptr;
1133 ResetEvent(self->NotifyEvent);
1134 hr = IAudioClient_Start(self->client);
1135 if(FAILED(hr))
1136 ERR("Failed to start audio client: 0x%08lx\n", hr);
1138 if(SUCCEEDED(hr))
1139 hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
1140 if(SUCCEEDED(hr))
1142 self->render = ptr;
1143 self->killNow = 0;
1144 if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
1146 if(self->render)
1147 IAudioRenderClient_Release(self->render);
1148 self->render = NULL;
1149 IAudioClient_Stop(self->client);
1150 ERR("Failed to start thread\n");
1151 hr = E_FAIL;
1155 return hr;
1159 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
1161 ThreadRequest req = { self->MsgEvent, 0 };
1162 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1163 (void)WaitForResponse(&req);
1166 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
1168 int res;
1170 if(!self->render)
1171 return;
1173 self->killNow = 1;
1174 althrd_join(self->thread, &res);
1176 IAudioRenderClient_Release(self->render);
1177 self->render = NULL;
1178 IAudioClient_Stop(self->client);
1182 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
1184 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1185 ClockLatency ret;
1187 ALCmmdevPlayback_lock(self);
1188 ret.ClockTime = GetDeviceClockTime(device);
1189 ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
1190 ALCmmdevPlayback_unlock(self);
1192 return ret;
1196 typedef struct ALCmmdevCapture {
1197 DERIVE_FROM_TYPE(ALCbackend);
1198 DERIVE_FROM_TYPE(ALCmmdevProxy);
1200 WCHAR *devid;
1202 IMMDevice *mmdev;
1203 IAudioClient *client;
1204 IAudioCaptureClient *capture;
1205 HANDLE NotifyEvent;
1207 HANDLE MsgEvent;
1209 ll_ringbuffer_t *Ring;
1211 volatile int killNow;
1212 althrd_t thread;
1213 } ALCmmdevCapture;
1215 static int ALCmmdevCapture_recordProc(void *arg);
1217 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
1218 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
1219 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
1220 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
1221 static void ALCmmdevCapture_close(ALCmmdevCapture *self);
1222 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
1223 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
1224 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
1225 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
1226 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
1227 static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
1228 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
1229 static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
1230 static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
1231 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
1232 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
1233 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
1234 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
1236 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
1237 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
1240 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
1242 SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
1243 SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
1244 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
1245 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
1247 self->devid = NULL;
1249 self->mmdev = NULL;
1250 self->client = NULL;
1251 self->capture = NULL;
1252 self->NotifyEvent = NULL;
1254 self->MsgEvent = NULL;
1256 self->Ring = NULL;
1258 self->killNow = 0;
1261 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
1263 ll_ringbuffer_free(self->Ring);
1264 self->Ring = NULL;
1266 if(self->NotifyEvent != NULL)
1267 CloseHandle(self->NotifyEvent);
1268 self->NotifyEvent = NULL;
1269 if(self->MsgEvent != NULL)
1270 CloseHandle(self->MsgEvent);
1271 self->MsgEvent = NULL;
1273 free(self->devid);
1274 self->devid = NULL;
1276 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
1277 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
1281 FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
1283 ALCmmdevCapture *self = arg;
1284 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1285 HRESULT hr;
1287 hr = CoInitialize(NULL);
1288 if(FAILED(hr))
1290 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
1291 V0(device->Backend,lock)();
1292 aluHandleDisconnect(device);
1293 V0(device->Backend,unlock)();
1294 return 1;
1297 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
1299 while(!self->killNow)
1301 UINT32 avail;
1302 DWORD res;
1304 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1305 if(FAILED(hr))
1306 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1307 else while(avail > 0 && SUCCEEDED(hr))
1309 UINT32 numsamples;
1310 DWORD flags;
1311 BYTE *data;
1313 hr = IAudioCaptureClient_GetBuffer(self->capture,
1314 &data, &numsamples, &flags, NULL, NULL
1316 if(FAILED(hr))
1318 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
1319 break;
1322 ll_ringbuffer_write(self->Ring, (char*)data, numsamples);
1324 hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
1325 if(FAILED(hr))
1327 ERR("Failed to release capture buffer: 0x%08lx\n", hr);
1328 break;
1331 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1332 if(FAILED(hr))
1333 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1336 if(FAILED(hr))
1338 V0(device->Backend,lock)();
1339 aluHandleDisconnect(device);
1340 V0(device->Backend,unlock)();
1341 break;
1344 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
1345 if(res != WAIT_OBJECT_0)
1346 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
1349 CoUninitialize();
1350 return 0;
1354 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
1356 HRESULT hr = S_OK;
1358 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1359 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1360 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
1362 ERR("Failed to create message events: %lu\n", GetLastError());
1363 hr = E_FAIL;
1366 if(SUCCEEDED(hr))
1368 if(deviceName)
1370 const DevMap *iter;
1372 if(VECTOR_SIZE(CaptureDevices) == 0)
1374 ThreadRequest req = { self->MsgEvent, 0 };
1375 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
1376 (void)WaitForResponse(&req);
1379 hr = E_FAIL;
1380 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \
1381 al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1382 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1383 #undef MATCH_NAME
1384 if(iter == VECTOR_END(CaptureDevices))
1386 int len;
1387 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
1389 WCHAR *wname = calloc(sizeof(WCHAR), len);
1390 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
1391 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1392 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1393 #undef MATCH_NAME
1394 free(wname);
1397 if(iter == VECTOR_END(CaptureDevices))
1398 WARN("Failed to find device name matching \"%s\"\n", deviceName);
1399 else
1401 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
1402 self->devid = strdupW(iter->devid);
1403 al_string_copy(&device->DeviceName, iter->name);
1404 hr = S_OK;
1409 if(SUCCEEDED(hr))
1411 ThreadRequest req = { self->MsgEvent, 0 };
1413 hr = E_FAIL;
1414 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1415 hr = WaitForResponse(&req);
1416 else
1417 ERR("Failed to post thread message: %lu\n", GetLastError());
1420 if(FAILED(hr))
1422 if(self->NotifyEvent != NULL)
1423 CloseHandle(self->NotifyEvent);
1424 self->NotifyEvent = NULL;
1425 if(self->MsgEvent != NULL)
1426 CloseHandle(self->MsgEvent);
1427 self->MsgEvent = NULL;
1429 free(self->devid);
1430 self->devid = NULL;
1432 ERR("Device init failed: 0x%08lx\n", hr);
1433 return ALC_INVALID_VALUE;
1435 else
1437 ThreadRequest req = { self->MsgEvent, 0 };
1439 hr = E_FAIL;
1440 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1441 hr = WaitForResponse(&req);
1442 else
1443 ERR("Failed to post thread message: %lu\n", GetLastError());
1445 if(FAILED(hr))
1447 ALCmmdevCapture_close(self);
1448 if(hr == E_OUTOFMEMORY)
1449 return ALC_OUT_OF_MEMORY;
1450 return ALC_INVALID_VALUE;
1454 return ALC_NO_ERROR;
1457 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
1459 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1460 void *ptr;
1461 HRESULT hr;
1463 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
1464 if(SUCCEEDED(hr))
1466 IMMDeviceEnumerator *Enumerator = ptr;
1467 if(!self->devid)
1468 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
1469 else
1470 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
1471 IMMDeviceEnumerator_Release(Enumerator);
1472 Enumerator = NULL;
1474 if(SUCCEEDED(hr))
1475 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1476 if(SUCCEEDED(hr))
1478 self->client = ptr;
1479 if(al_string_empty(device->DeviceName))
1480 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
1483 if(FAILED(hr))
1485 if(self->mmdev)
1486 IMMDevice_Release(self->mmdev);
1487 self->mmdev = NULL;
1490 return hr;
1494 static void ALCmmdevCapture_close(ALCmmdevCapture *self)
1496 ThreadRequest req = { self->MsgEvent, 0 };
1498 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1499 (void)WaitForResponse(&req);
1501 ll_ringbuffer_free(self->Ring);
1502 self->Ring = NULL;
1504 CloseHandle(self->MsgEvent);
1505 self->MsgEvent = NULL;
1507 CloseHandle(self->NotifyEvent);
1508 self->NotifyEvent = NULL;
1510 free(self->devid);
1511 self->devid = NULL;
1514 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
1516 if(self->client)
1517 IAudioClient_Release(self->client);
1518 self->client = NULL;
1520 if(self->mmdev)
1521 IMMDevice_Release(self->mmdev);
1522 self->mmdev = NULL;
1526 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
1528 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1529 WAVEFORMATEXTENSIBLE OutputType;
1530 WAVEFORMATEX *wfx = NULL;
1531 REFERENCE_TIME buf_time;
1532 UINT32 buffer_len;
1533 void *ptr = NULL;
1534 HRESULT hr;
1536 if(self->client)
1537 IAudioClient_Release(self->client);
1538 self->client = NULL;
1540 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1541 if(FAILED(hr))
1543 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
1544 return hr;
1546 self->client = ptr;
1548 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
1549 device->Frequency-1) / device->Frequency;
1551 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1552 switch(device->FmtChans)
1554 case DevFmtMono:
1555 OutputType.Format.nChannels = 1;
1556 OutputType.dwChannelMask = MONO;
1557 break;
1558 case DevFmtStereo:
1559 OutputType.Format.nChannels = 2;
1560 OutputType.dwChannelMask = STEREO;
1561 break;
1562 case DevFmtQuad:
1563 OutputType.Format.nChannels = 4;
1564 OutputType.dwChannelMask = QUAD;
1565 break;
1566 case DevFmtX51:
1567 OutputType.Format.nChannels = 6;
1568 OutputType.dwChannelMask = X5DOT1;
1569 break;
1570 case DevFmtX51Rear:
1571 OutputType.Format.nChannels = 6;
1572 OutputType.dwChannelMask = X5DOT1REAR;
1573 break;
1574 case DevFmtX61:
1575 OutputType.Format.nChannels = 7;
1576 OutputType.dwChannelMask = X6DOT1;
1577 break;
1578 case DevFmtX71:
1579 OutputType.Format.nChannels = 8;
1580 OutputType.dwChannelMask = X7DOT1;
1581 break;
1583 case DevFmtAmbi1:
1584 case DevFmtAmbi2:
1585 case DevFmtAmbi3:
1586 return E_FAIL;
1588 switch(device->FmtType)
1590 case DevFmtUByte:
1591 OutputType.Format.wBitsPerSample = 8;
1592 OutputType.Samples.wValidBitsPerSample = 8;
1593 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1594 break;
1595 case DevFmtShort:
1596 OutputType.Format.wBitsPerSample = 16;
1597 OutputType.Samples.wValidBitsPerSample = 16;
1598 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1599 break;
1600 case DevFmtInt:
1601 OutputType.Format.wBitsPerSample = 32;
1602 OutputType.Samples.wValidBitsPerSample = 32;
1603 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1604 break;
1605 case DevFmtFloat:
1606 OutputType.Format.wBitsPerSample = 32;
1607 OutputType.Samples.wValidBitsPerSample = 32;
1608 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1609 break;
1611 case DevFmtByte:
1612 case DevFmtUShort:
1613 case DevFmtUInt:
1614 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
1615 return E_FAIL;
1617 OutputType.Format.nSamplesPerSec = device->Frequency;
1619 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1620 OutputType.Format.wBitsPerSample / 8;
1621 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1622 OutputType.Format.nBlockAlign;
1623 OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
1625 hr = IAudioClient_IsFormatSupported(self->client,
1626 AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx
1628 if(FAILED(hr))
1630 ERR("Failed to check format support: 0x%08lx\n", hr);
1631 return hr;
1634 /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
1635 if(wfx->nSamplesPerSec != OutputType.Format.nSamplesPerSec ||
1636 wfx->wBitsPerSample != OutputType.Format.wBitsPerSample ||
1637 wfx->nChannels != OutputType.Format.nChannels ||
1638 wfx->nBlockAlign != OutputType.Format.nBlockAlign)
1640 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1641 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1642 device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
1643 wfx->nSamplesPerSec);
1644 CoTaskMemFree(wfx);
1645 return E_FAIL;
1648 if(!MakeExtensible(&OutputType, wfx))
1650 CoTaskMemFree(wfx);
1651 return E_FAIL;
1653 CoTaskMemFree(wfx);
1654 wfx = NULL;
1656 hr = IAudioClient_Initialize(self->client,
1657 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1658 buf_time, 0, &OutputType.Format, NULL
1660 if(FAILED(hr))
1662 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1663 return hr;
1666 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1667 if(FAILED(hr))
1669 ERR("Failed to get buffer size: 0x%08lx\n", hr);
1670 return hr;
1673 buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
1674 ll_ringbuffer_free(self->Ring);
1675 self->Ring = ll_ringbuffer_create(buffer_len, OutputType.Format.nBlockAlign);
1676 if(!self->Ring)
1678 ERR("Failed to allocate capture ring buffer\n");
1679 return E_OUTOFMEMORY;
1682 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1683 if(FAILED(hr))
1685 ERR("Failed to set event handle: 0x%08lx\n", hr);
1686 return hr;
1689 return hr;
1693 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
1695 ThreadRequest req = { self->MsgEvent, 0 };
1696 HRESULT hr = E_FAIL;
1698 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1699 hr = WaitForResponse(&req);
1701 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1704 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
1706 HRESULT hr;
1707 void *ptr;
1709 ResetEvent(self->NotifyEvent);
1710 hr = IAudioClient_Start(self->client);
1711 if(FAILED(hr))
1713 ERR("Failed to start audio client: 0x%08lx\n", hr);
1714 return hr;
1717 hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
1718 if(SUCCEEDED(hr))
1720 self->capture = ptr;
1721 self->killNow = 0;
1722 if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
1724 ERR("Failed to start thread\n");
1725 IAudioCaptureClient_Release(self->capture);
1726 self->capture = NULL;
1727 hr = E_FAIL;
1731 if(FAILED(hr))
1733 IAudioClient_Stop(self->client);
1734 IAudioClient_Reset(self->client);
1737 return hr;
1741 static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
1743 ThreadRequest req = { self->MsgEvent, 0 };
1744 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1745 (void)WaitForResponse(&req);
1748 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
1750 int res;
1752 if(!self->capture)
1753 return;
1755 self->killNow = 1;
1756 althrd_join(self->thread, &res);
1758 IAudioCaptureClient_Release(self->capture);
1759 self->capture = NULL;
1760 IAudioClient_Stop(self->client);
1761 IAudioClient_Reset(self->client);
1765 ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
1767 return (ALuint)ll_ringbuffer_read_space(self->Ring);
1770 ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
1772 if(ALCmmdevCapture_availableSamples(self) < samples)
1773 return ALC_INVALID_VALUE;
1774 ll_ringbuffer_read(self->Ring, buffer, samples);
1775 return ALC_NO_ERROR;
1779 static inline void AppendAllDevicesList2(const DevMap *entry)
1780 { AppendAllDevicesList(al_string_get_cstr(entry->name)); }
1781 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1782 { AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
1784 typedef struct ALCmmdevBackendFactory {
1785 DERIVE_FROM_TYPE(ALCbackendFactory);
1786 } ALCmmdevBackendFactory;
1787 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1789 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
1790 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
1791 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
1792 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
1793 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
1795 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
1798 static BOOL MMDevApiLoad(void)
1800 static HRESULT InitResult;
1801 if(!ThreadHdl)
1803 ThreadRequest req;
1804 InitResult = E_FAIL;
1806 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
1807 if(req.FinishedEvt == NULL)
1808 ERR("Failed to create event: %lu\n", GetLastError());
1809 else
1811 ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
1812 if(ThreadHdl != NULL)
1813 InitResult = WaitForResponse(&req);
1814 CloseHandle(req.FinishedEvt);
1817 return SUCCEEDED(InitResult);
1820 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
1822 VECTOR_INIT(PlaybackDevices);
1823 VECTOR_INIT(CaptureDevices);
1825 if(!MMDevApiLoad())
1826 return ALC_FALSE;
1827 return ALC_TRUE;
1830 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
1832 clear_devlist(&PlaybackDevices);
1833 VECTOR_DEINIT(PlaybackDevices);
1835 clear_devlist(&CaptureDevices);
1836 VECTOR_DEINIT(CaptureDevices);
1838 if(ThreadHdl)
1840 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
1841 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
1842 CloseHandle(ThreadHdl);
1843 ThreadHdl = NULL;
1847 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
1849 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
1850 * rechanneling or resampling; if the device is configured for 48000hz
1851 * stereo input, for example, and the app asks for 22050hz mono,
1852 * initialization will fail.
1854 if(type == ALCbackend_Playback /*|| type == ALCbackend_Capture*/)
1855 return ALC_TRUE;
1856 return ALC_FALSE;
1859 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
1861 ThreadRequest req = { NULL, 0 };
1863 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
1864 if(req.FinishedEvt == NULL)
1865 ERR("Failed to create event: %lu\n", GetLastError());
1866 else
1868 HRESULT hr = E_FAIL;
1869 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1870 hr = WaitForResponse(&req);
1871 if(SUCCEEDED(hr)) switch(type)
1873 case ALL_DEVICE_PROBE:
1874 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
1875 break;
1877 case CAPTURE_DEVICE_PROBE:
1878 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
1879 break;
1881 CloseHandle(req.FinishedEvt);
1882 req.FinishedEvt = NULL;
1886 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1888 if(type == ALCbackend_Playback)
1890 ALCmmdevPlayback *backend;
1891 NEW_OBJ(backend, ALCmmdevPlayback)(device);
1892 if(!backend) return NULL;
1893 return STATIC_CAST(ALCbackend, backend);
1895 if(type == ALCbackend_Capture)
1897 ALCmmdevCapture *backend;
1898 NEW_OBJ(backend, ALCmmdevCapture)(device);
1899 if(!backend) return NULL;
1900 return STATIC_CAST(ALCbackend, backend);
1903 return NULL;
1907 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
1909 static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
1910 return STATIC_CAST(ALCbackendFactory, &factory);