Update ChangeLog
[openal-soft.git] / Alc / backends / mmdevapi.c
blob9afb62f09914aa20aac3c0c6e489de5a3c5db6a4
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"
47 #include "converter.h"
49 #include "backends/base.h"
52 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
53 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
55 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
56 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
57 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
59 #define MONO SPEAKER_FRONT_CENTER
60 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
61 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
62 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
63 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
64 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
65 #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)
66 #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)
68 #define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
70 #define DEVNAME_HEAD "OpenAL Soft on "
73 typedef struct {
74 al_string name;
75 al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
76 WCHAR *devid;
77 } DevMap;
78 TYPEDEF_VECTOR(DevMap, vector_DevMap)
80 static void clear_devlist(vector_DevMap *list)
82 #define CLEAR_DEVMAP(i) do { \
83 AL_STRING_DEINIT((i)->name); \
84 AL_STRING_DEINIT((i)->endpoint_guid); \
85 free((i)->devid); \
86 (i)->devid = NULL; \
87 } while(0)
88 VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
89 VECTOR_RESIZE(*list, 0, 0);
90 #undef CLEAR_DEVMAP
93 static vector_DevMap PlaybackDevices;
94 static vector_DevMap CaptureDevices;
97 static HANDLE ThreadHdl;
98 static DWORD ThreadID;
100 typedef struct {
101 HANDLE FinishedEvt;
102 HRESULT result;
103 } ThreadRequest;
105 #define WM_USER_First (WM_USER+0)
106 #define WM_USER_OpenDevice (WM_USER+0)
107 #define WM_USER_ResetDevice (WM_USER+1)
108 #define WM_USER_StartDevice (WM_USER+2)
109 #define WM_USER_StopDevice (WM_USER+3)
110 #define WM_USER_CloseDevice (WM_USER+4)
111 #define WM_USER_Enumerate (WM_USER+5)
112 #define WM_USER_Last (WM_USER+5)
114 static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
115 "Open Device",
116 "Reset Device",
117 "Start Device",
118 "Stop Device",
119 "Close Device",
120 "Enumerate Devices",
123 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
125 req->result = res;
126 SetEvent(req->FinishedEvt);
129 static HRESULT WaitForResponse(ThreadRequest *req)
131 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
132 return req->result;
133 ERR("Message response error: %lu\n", GetLastError());
134 return E_FAIL;
138 static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
140 IPropertyStore *ps;
141 PROPVARIANT pvname;
142 PROPVARIANT pvguid;
143 HRESULT hr;
145 alstr_copy_cstr(name, DEVNAME_HEAD);
147 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
148 if(FAILED(hr))
150 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
151 alstr_append_cstr(name, "Unknown Device Name");
152 if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
153 return;
156 PropVariantInit(&pvname);
158 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
159 if(FAILED(hr))
161 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
162 alstr_append_cstr(name, "Unknown Device Name");
164 else if(pvname.vt == VT_LPWSTR)
165 alstr_append_wcstr(name, pvname.pwszVal);
166 else
168 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
169 alstr_append_cstr(name, "Unknown Device Name");
171 PropVariantClear(&pvname);
173 if(guid!=NULL){
174 PropVariantInit(&pvguid);
176 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
177 if(FAILED(hr))
179 WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
180 alstr_copy_cstr(guid, "Unknown Device GUID");
182 else if(pvguid.vt == VT_LPWSTR)
183 alstr_copy_wcstr(guid, pvguid.pwszVal);
184 else
186 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
187 alstr_copy_cstr(guid, "Unknown Device GUID");
190 PropVariantClear(&pvguid);
193 IPropertyStore_Release(ps);
196 static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
198 IPropertyStore *ps;
199 PROPVARIANT pvform;
200 HRESULT hr;
202 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
203 if(FAILED(hr))
205 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
206 return;
209 PropVariantInit(&pvform);
211 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
212 if(FAILED(hr))
213 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
214 else if(pvform.vt == VT_UI4)
215 *formfactor = pvform.ulVal;
216 else if(pvform.vt == VT_EMPTY)
217 *formfactor = UnknownFormFactor;
218 else
219 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
221 PropVariantClear(&pvform);
222 IPropertyStore_Release(ps);
226 static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
228 int count = 0;
229 al_string tmpname;
230 DevMap entry;
232 AL_STRING_INIT(tmpname);
233 AL_STRING_INIT(entry.name);
234 AL_STRING_INIT(entry.endpoint_guid);
236 entry.devid = strdupW(devid);
237 get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
239 while(1)
241 const DevMap *iter;
243 alstr_copy(&entry.name, tmpname);
244 if(count != 0)
246 char str[64];
247 snprintf(str, sizeof(str), " #%d", count+1);
248 alstr_append_cstr(&entry.name, str);
251 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
252 VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
253 if(iter == VECTOR_END(*list)) break;
254 #undef MATCH_ENTRY
255 count++;
258 TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
259 VECTOR_PUSH_BACK(*list, entry);
261 AL_STRING_DEINIT(tmpname);
264 static WCHAR *get_device_id(IMMDevice *device)
266 WCHAR *devid;
267 HRESULT hr;
269 hr = IMMDevice_GetId(device, &devid);
270 if(FAILED(hr))
272 ERR("Failed to get device id: %lx\n", hr);
273 return NULL;
276 return devid;
279 static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
281 IMMDeviceCollection *coll;
282 IMMDevice *defdev = NULL;
283 WCHAR *defdevid = NULL;
284 HRESULT hr;
285 UINT count;
286 UINT i;
288 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
289 if(FAILED(hr))
291 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
292 return hr;
295 count = 0;
296 hr = IMMDeviceCollection_GetCount(coll, &count);
297 if(SUCCEEDED(hr) && count > 0)
299 clear_devlist(list);
300 VECTOR_RESIZE(*list, 0, count);
302 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
303 eMultimedia, &defdev);
305 if(SUCCEEDED(hr) && defdev != NULL)
307 defdevid = get_device_id(defdev);
308 if(defdevid)
309 add_device(defdev, defdevid, list);
312 for(i = 0;i < count;++i)
314 IMMDevice *device;
315 WCHAR *devid;
317 hr = IMMDeviceCollection_Item(coll, i, &device);
318 if(FAILED(hr)) continue;
320 devid = get_device_id(device);
321 if(devid)
323 if(wcscmp(devid, defdevid) != 0)
324 add_device(device, devid, list);
325 CoTaskMemFree(devid);
327 IMMDevice_Release(device);
330 if(defdev) IMMDevice_Release(defdev);
331 if(defdevid) CoTaskMemFree(defdevid);
332 IMMDeviceCollection_Release(coll);
334 return S_OK;
338 /* Proxy interface used by the message handler. */
339 struct ALCmmdevProxyVtable;
341 typedef struct ALCmmdevProxy {
342 const struct ALCmmdevProxyVtable *vtbl;
343 } ALCmmdevProxy;
345 struct ALCmmdevProxyVtable {
346 HRESULT (*const openProxy)(ALCmmdevProxy*);
347 void (*const closeProxy)(ALCmmdevProxy*);
349 HRESULT (*const resetProxy)(ALCmmdevProxy*);
350 HRESULT (*const startProxy)(ALCmmdevProxy*);
351 void (*const stopProxy)(ALCmmdevProxy*);
354 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
355 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
356 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
357 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
358 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
359 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
361 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
362 T##_ALCmmdevProxy_openProxy, \
363 T##_ALCmmdevProxy_closeProxy, \
364 T##_ALCmmdevProxy_resetProxy, \
365 T##_ALCmmdevProxy_startProxy, \
366 T##_ALCmmdevProxy_stopProxy, \
369 static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
370 static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
372 static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
374 ThreadRequest *req = ptr;
375 IMMDeviceEnumerator *Enumerator;
376 ALuint deviceCount = 0;
377 ALCmmdevProxy *proxy;
378 HRESULT hr, cohr;
379 MSG msg;
381 TRACE("Starting message thread\n");
383 cohr = CoInitialize(NULL);
384 if(FAILED(cohr))
386 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
387 ReturnMsgResponse(req, cohr);
388 return 0;
391 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
392 if(FAILED(hr))
394 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
395 CoUninitialize();
396 ReturnMsgResponse(req, hr);
397 return 0;
399 Enumerator = ptr;
400 IMMDeviceEnumerator_Release(Enumerator);
401 Enumerator = NULL;
403 CoUninitialize();
405 /* HACK: Force Windows to create a message queue for this thread before
406 * returning success, otherwise PostThreadMessage may fail if it gets
407 * called before GetMessage.
409 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
411 TRACE("Message thread initialization complete\n");
412 ReturnMsgResponse(req, S_OK);
414 TRACE("Starting message loop\n");
415 while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
417 TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
418 (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
419 MessageStr[msg.message-WM_USER] : "Unknown",
420 msg.message, (void*)msg.lParam, (void*)msg.wParam
422 switch(msg.message)
424 case WM_USER_OpenDevice:
425 req = (ThreadRequest*)msg.wParam;
426 proxy = (ALCmmdevProxy*)msg.lParam;
428 hr = cohr = S_OK;
429 if(++deviceCount == 1)
430 hr = cohr = CoInitialize(NULL);
431 if(SUCCEEDED(hr))
432 hr = V0(proxy,openProxy)();
433 if(FAILED(hr))
435 if(--deviceCount == 0 && SUCCEEDED(cohr))
436 CoUninitialize();
439 ReturnMsgResponse(req, hr);
440 continue;
442 case WM_USER_ResetDevice:
443 req = (ThreadRequest*)msg.wParam;
444 proxy = (ALCmmdevProxy*)msg.lParam;
446 hr = V0(proxy,resetProxy)();
447 ReturnMsgResponse(req, hr);
448 continue;
450 case WM_USER_StartDevice:
451 req = (ThreadRequest*)msg.wParam;
452 proxy = (ALCmmdevProxy*)msg.lParam;
454 hr = V0(proxy,startProxy)();
455 ReturnMsgResponse(req, hr);
456 continue;
458 case WM_USER_StopDevice:
459 req = (ThreadRequest*)msg.wParam;
460 proxy = (ALCmmdevProxy*)msg.lParam;
462 V0(proxy,stopProxy)();
463 ReturnMsgResponse(req, S_OK);
464 continue;
466 case WM_USER_CloseDevice:
467 req = (ThreadRequest*)msg.wParam;
468 proxy = (ALCmmdevProxy*)msg.lParam;
470 V0(proxy,closeProxy)();
471 if(--deviceCount == 0)
472 CoUninitialize();
474 ReturnMsgResponse(req, S_OK);
475 continue;
477 case WM_USER_Enumerate:
478 req = (ThreadRequest*)msg.wParam;
480 hr = cohr = S_OK;
481 if(++deviceCount == 1)
482 hr = cohr = CoInitialize(NULL);
483 if(SUCCEEDED(hr))
484 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
485 if(SUCCEEDED(hr))
487 Enumerator = ptr;
489 if(msg.lParam == ALL_DEVICE_PROBE)
490 hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
491 else if(msg.lParam == CAPTURE_DEVICE_PROBE)
492 hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
494 IMMDeviceEnumerator_Release(Enumerator);
495 Enumerator = NULL;
498 if(--deviceCount == 0 && SUCCEEDED(cohr))
499 CoUninitialize();
501 ReturnMsgResponse(req, hr);
502 continue;
504 default:
505 ERR("Unexpected message: %u\n", msg.message);
506 continue;
509 TRACE("Message loop finished\n");
511 return 0;
515 typedef struct ALCmmdevPlayback {
516 DERIVE_FROM_TYPE(ALCbackend);
517 DERIVE_FROM_TYPE(ALCmmdevProxy);
519 WCHAR *devid;
521 IMMDevice *mmdev;
522 IAudioClient *client;
523 IAudioRenderClient *render;
524 HANDLE NotifyEvent;
526 HANDLE MsgEvent;
528 volatile UINT32 Padding;
530 volatile int killNow;
531 althrd_t thread;
532 } ALCmmdevPlayback;
534 static int ALCmmdevPlayback_mixerProc(void *arg);
536 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
537 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
538 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
539 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
540 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
541 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
542 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
543 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
544 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
545 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
546 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
547 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
548 static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
549 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
550 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
551 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
552 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
553 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
555 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
556 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
559 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
561 SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
562 SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
563 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
564 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
566 self->devid = NULL;
568 self->mmdev = NULL;
569 self->client = NULL;
570 self->render = NULL;
571 self->NotifyEvent = NULL;
573 self->MsgEvent = NULL;
575 self->Padding = 0;
577 self->killNow = 0;
580 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
582 if(self->NotifyEvent != NULL)
583 CloseHandle(self->NotifyEvent);
584 self->NotifyEvent = NULL;
585 if(self->MsgEvent != NULL)
586 CloseHandle(self->MsgEvent);
587 self->MsgEvent = NULL;
589 free(self->devid);
590 self->devid = NULL;
592 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
593 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
597 FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
599 ALCmmdevPlayback *self = arg;
600 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
601 UINT32 buffer_len, written;
602 ALuint update_size, len;
603 BYTE *buffer;
604 HRESULT hr;
606 hr = CoInitialize(NULL);
607 if(FAILED(hr))
609 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
610 V0(device->Backend,lock)();
611 aluHandleDisconnect(device);
612 V0(device->Backend,unlock)();
613 return 1;
616 SetRTPriority();
617 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
619 update_size = device->UpdateSize;
620 buffer_len = update_size * device->NumUpdates;
621 while(!self->killNow)
623 hr = IAudioClient_GetCurrentPadding(self->client, &written);
624 if(FAILED(hr))
626 ERR("Failed to get padding: 0x%08lx\n", hr);
627 V0(device->Backend,lock)();
628 aluHandleDisconnect(device);
629 V0(device->Backend,unlock)();
630 break;
632 self->Padding = written;
634 len = buffer_len - written;
635 if(len < update_size)
637 DWORD res;
638 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
639 if(res != WAIT_OBJECT_0)
640 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
641 continue;
643 len -= len%update_size;
645 hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
646 if(SUCCEEDED(hr))
648 ALCmmdevPlayback_lock(self);
649 aluMixData(device, buffer, len);
650 self->Padding = written + len;
651 ALCmmdevPlayback_unlock(self);
652 hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
654 if(FAILED(hr))
656 ERR("Failed to buffer data: 0x%08lx\n", hr);
657 V0(device->Backend,lock)();
658 aluHandleDisconnect(device);
659 V0(device->Backend,unlock)();
660 break;
663 self->Padding = 0;
665 CoUninitialize();
666 return 0;
670 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
672 memset(out, 0, sizeof(*out));
673 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
674 *out = *(const WAVEFORMATEXTENSIBLE*)in;
675 else if(in->wFormatTag == WAVE_FORMAT_PCM)
677 out->Format = *in;
678 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
679 out->Format.cbSize = sizeof(*out) - sizeof(*in);
680 if(out->Format.nChannels == 1)
681 out->dwChannelMask = MONO;
682 else if(out->Format.nChannels == 2)
683 out->dwChannelMask = STEREO;
684 else
685 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
686 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
688 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
690 out->Format = *in;
691 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
692 out->Format.cbSize = sizeof(*out) - sizeof(*in);
693 if(out->Format.nChannels == 1)
694 out->dwChannelMask = MONO;
695 else if(out->Format.nChannels == 2)
696 out->dwChannelMask = STEREO;
697 else
698 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
699 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
701 else
703 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
704 return ALC_FALSE;
706 return ALC_TRUE;
709 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
711 HRESULT hr = S_OK;
713 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
714 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
715 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
717 ERR("Failed to create message events: %lu\n", GetLastError());
718 hr = E_FAIL;
721 if(SUCCEEDED(hr))
723 if(deviceName)
725 const DevMap *iter;
727 if(VECTOR_SIZE(PlaybackDevices) == 0)
729 ThreadRequest req = { self->MsgEvent, 0 };
730 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
731 (void)WaitForResponse(&req);
734 hr = E_FAIL;
735 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
736 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
737 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
738 #undef MATCH_NAME
739 if(iter == VECTOR_END(PlaybackDevices))
741 int len;
742 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
744 WCHAR *wname = calloc(sizeof(WCHAR), len);
745 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
746 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
747 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
748 #undef MATCH_NAME
749 free(wname);
752 if(iter == VECTOR_END(PlaybackDevices))
753 WARN("Failed to find device name matching \"%s\"\n", deviceName);
754 else
756 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
757 self->devid = strdupW(iter->devid);
758 alstr_copy(&device->DeviceName, iter->name);
759 hr = S_OK;
764 if(SUCCEEDED(hr))
766 ThreadRequest req = { self->MsgEvent, 0 };
768 hr = E_FAIL;
769 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
770 hr = WaitForResponse(&req);
771 else
772 ERR("Failed to post thread message: %lu\n", GetLastError());
775 if(FAILED(hr))
777 if(self->NotifyEvent != NULL)
778 CloseHandle(self->NotifyEvent);
779 self->NotifyEvent = NULL;
780 if(self->MsgEvent != NULL)
781 CloseHandle(self->MsgEvent);
782 self->MsgEvent = NULL;
784 free(self->devid);
785 self->devid = NULL;
787 ERR("Device init failed: 0x%08lx\n", hr);
788 return ALC_INVALID_VALUE;
791 return ALC_NO_ERROR;
794 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
796 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
797 void *ptr;
798 HRESULT hr;
800 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
801 if(SUCCEEDED(hr))
803 IMMDeviceEnumerator *Enumerator = ptr;
804 if(!self->devid)
805 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
806 else
807 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
808 IMMDeviceEnumerator_Release(Enumerator);
809 Enumerator = NULL;
811 if(SUCCEEDED(hr))
812 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
813 if(SUCCEEDED(hr))
815 self->client = ptr;
816 if(alstr_empty(device->DeviceName))
817 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
820 if(FAILED(hr))
822 if(self->mmdev)
823 IMMDevice_Release(self->mmdev);
824 self->mmdev = NULL;
827 return hr;
831 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
833 ThreadRequest req = { self->MsgEvent, 0 };
835 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
836 (void)WaitForResponse(&req);
838 CloseHandle(self->MsgEvent);
839 self->MsgEvent = NULL;
841 CloseHandle(self->NotifyEvent);
842 self->NotifyEvent = NULL;
844 free(self->devid);
845 self->devid = NULL;
848 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
850 if(self->client)
851 IAudioClient_Release(self->client);
852 self->client = NULL;
854 if(self->mmdev)
855 IMMDevice_Release(self->mmdev);
856 self->mmdev = NULL;
860 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
862 ThreadRequest req = { self->MsgEvent, 0 };
863 HRESULT hr = E_FAIL;
865 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
866 hr = WaitForResponse(&req);
868 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
871 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
873 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
874 EndpointFormFactor formfactor = UnknownFormFactor;
875 WAVEFORMATEXTENSIBLE OutputType;
876 WAVEFORMATEX *wfx = NULL;
877 REFERENCE_TIME min_per, buf_time;
878 UINT32 buffer_len, min_len;
879 void *ptr = NULL;
880 HRESULT hr;
882 if(self->client)
883 IAudioClient_Release(self->client);
884 self->client = NULL;
886 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
887 if(FAILED(hr))
889 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
890 return hr;
892 self->client = ptr;
894 hr = IAudioClient_GetMixFormat(self->client, &wfx);
895 if(FAILED(hr))
897 ERR("Failed to get mix format: 0x%08lx\n", hr);
898 return hr;
901 if(!MakeExtensible(&OutputType, wfx))
903 CoTaskMemFree(wfx);
904 return E_FAIL;
906 CoTaskMemFree(wfx);
907 wfx = NULL;
909 buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
910 device->Frequency);
912 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
913 device->Frequency = OutputType.Format.nSamplesPerSec;
914 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
916 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
917 device->FmtChans = DevFmtMono;
918 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
919 device->FmtChans = DevFmtStereo;
920 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
921 device->FmtChans = DevFmtQuad;
922 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
923 device->FmtChans = DevFmtX51;
924 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
925 device->FmtChans = DevFmtX51Rear;
926 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
927 device->FmtChans = DevFmtX61;
928 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
929 device->FmtChans = DevFmtX71;
930 else
931 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
934 switch(device->FmtChans)
936 case DevFmtMono:
937 OutputType.Format.nChannels = 1;
938 OutputType.dwChannelMask = MONO;
939 break;
940 case DevFmtAmbi3D:
941 device->FmtChans = DevFmtStereo;
942 /*fall-through*/
943 case DevFmtStereo:
944 OutputType.Format.nChannels = 2;
945 OutputType.dwChannelMask = STEREO;
946 break;
947 case DevFmtQuad:
948 OutputType.Format.nChannels = 4;
949 OutputType.dwChannelMask = QUAD;
950 break;
951 case DevFmtX51:
952 OutputType.Format.nChannels = 6;
953 OutputType.dwChannelMask = X5DOT1;
954 break;
955 case DevFmtX51Rear:
956 OutputType.Format.nChannels = 6;
957 OutputType.dwChannelMask = X5DOT1REAR;
958 break;
959 case DevFmtX61:
960 OutputType.Format.nChannels = 7;
961 OutputType.dwChannelMask = X6DOT1;
962 break;
963 case DevFmtX71:
964 OutputType.Format.nChannels = 8;
965 OutputType.dwChannelMask = X7DOT1;
966 break;
968 switch(device->FmtType)
970 case DevFmtByte:
971 device->FmtType = DevFmtUByte;
972 /* fall-through */
973 case DevFmtUByte:
974 OutputType.Format.wBitsPerSample = 8;
975 OutputType.Samples.wValidBitsPerSample = 8;
976 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
977 break;
978 case DevFmtUShort:
979 device->FmtType = DevFmtShort;
980 /* fall-through */
981 case DevFmtShort:
982 OutputType.Format.wBitsPerSample = 16;
983 OutputType.Samples.wValidBitsPerSample = 16;
984 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
985 break;
986 case DevFmtUInt:
987 device->FmtType = DevFmtInt;
988 /* fall-through */
989 case DevFmtInt:
990 OutputType.Format.wBitsPerSample = 32;
991 OutputType.Samples.wValidBitsPerSample = 32;
992 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
993 break;
994 case DevFmtFloat:
995 OutputType.Format.wBitsPerSample = 32;
996 OutputType.Samples.wValidBitsPerSample = 32;
997 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
998 break;
1000 OutputType.Format.nSamplesPerSec = device->Frequency;
1002 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1003 OutputType.Format.wBitsPerSample / 8;
1004 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1005 OutputType.Format.nBlockAlign;
1007 hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
1008 if(FAILED(hr))
1010 ERR("Failed to check format support: 0x%08lx\n", hr);
1011 hr = IAudioClient_GetMixFormat(self->client, &wfx);
1013 if(FAILED(hr))
1015 ERR("Failed to find a supported format: 0x%08lx\n", hr);
1016 return hr;
1019 if(wfx != NULL)
1021 if(!MakeExtensible(&OutputType, wfx))
1023 CoTaskMemFree(wfx);
1024 return E_FAIL;
1026 CoTaskMemFree(wfx);
1027 wfx = NULL;
1029 device->Frequency = OutputType.Format.nSamplesPerSec;
1030 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
1031 device->FmtChans = DevFmtMono;
1032 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
1033 device->FmtChans = DevFmtStereo;
1034 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
1035 device->FmtChans = DevFmtQuad;
1036 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
1037 device->FmtChans = DevFmtX51;
1038 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
1039 device->FmtChans = DevFmtX51Rear;
1040 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
1041 device->FmtChans = DevFmtX61;
1042 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
1043 device->FmtChans = DevFmtX71;
1044 else
1046 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
1047 device->FmtChans = DevFmtStereo;
1048 OutputType.Format.nChannels = 2;
1049 OutputType.dwChannelMask = STEREO;
1052 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
1054 if(OutputType.Format.wBitsPerSample == 8)
1055 device->FmtType = DevFmtUByte;
1056 else if(OutputType.Format.wBitsPerSample == 16)
1057 device->FmtType = DevFmtShort;
1058 else if(OutputType.Format.wBitsPerSample == 32)
1059 device->FmtType = DevFmtInt;
1060 else
1062 device->FmtType = DevFmtShort;
1063 OutputType.Format.wBitsPerSample = 16;
1066 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1068 device->FmtType = DevFmtFloat;
1069 OutputType.Format.wBitsPerSample = 32;
1071 else
1073 ERR("Unhandled format sub-type\n");
1074 device->FmtType = DevFmtShort;
1075 OutputType.Format.wBitsPerSample = 16;
1076 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1078 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1080 get_device_formfactor(self->mmdev, &formfactor);
1081 device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
1082 (formfactor == Headphones || formfactor == Headset)
1085 SetDefaultWFXChannelOrder(device);
1087 hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
1088 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1089 buf_time, 0, &OutputType.Format, NULL);
1090 if(FAILED(hr))
1092 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1093 return hr;
1096 hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
1097 if(SUCCEEDED(hr))
1099 min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
1100 /* Find the nearest multiple of the period size to the update size */
1101 if(min_len < device->UpdateSize)
1102 min_len *= (device->UpdateSize + min_len/2)/min_len;
1103 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1105 if(FAILED(hr))
1107 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
1108 return hr;
1111 device->UpdateSize = min_len;
1112 device->NumUpdates = buffer_len / device->UpdateSize;
1113 if(device->NumUpdates <= 1)
1115 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1116 device->NumUpdates = 2;
1117 device->UpdateSize = buffer_len / device->NumUpdates;
1120 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1121 if(FAILED(hr))
1123 ERR("Failed to set event handle: 0x%08lx\n", hr);
1124 return hr;
1127 return hr;
1131 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
1133 ThreadRequest req = { self->MsgEvent, 0 };
1134 HRESULT hr = E_FAIL;
1136 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1137 hr = WaitForResponse(&req);
1139 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1142 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
1144 HRESULT hr;
1145 void *ptr;
1147 ResetEvent(self->NotifyEvent);
1148 hr = IAudioClient_Start(self->client);
1149 if(FAILED(hr))
1150 ERR("Failed to start audio client: 0x%08lx\n", hr);
1152 if(SUCCEEDED(hr))
1153 hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
1154 if(SUCCEEDED(hr))
1156 self->render = ptr;
1157 self->killNow = 0;
1158 if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
1160 if(self->render)
1161 IAudioRenderClient_Release(self->render);
1162 self->render = NULL;
1163 IAudioClient_Stop(self->client);
1164 ERR("Failed to start thread\n");
1165 hr = E_FAIL;
1169 return hr;
1173 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
1175 ThreadRequest req = { self->MsgEvent, 0 };
1176 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1177 (void)WaitForResponse(&req);
1180 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
1182 int res;
1184 if(!self->render)
1185 return;
1187 self->killNow = 1;
1188 althrd_join(self->thread, &res);
1190 IAudioRenderClient_Release(self->render);
1191 self->render = NULL;
1192 IAudioClient_Stop(self->client);
1196 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
1198 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1199 ClockLatency ret;
1201 ALCmmdevPlayback_lock(self);
1202 ret.ClockTime = GetDeviceClockTime(device);
1203 ret.Latency = self->Padding * DEVICE_CLOCK_RES / device->Frequency;
1204 ALCmmdevPlayback_unlock(self);
1206 return ret;
1210 typedef struct ALCmmdevCapture {
1211 DERIVE_FROM_TYPE(ALCbackend);
1212 DERIVE_FROM_TYPE(ALCmmdevProxy);
1214 WCHAR *devid;
1216 IMMDevice *mmdev;
1217 IAudioClient *client;
1218 IAudioCaptureClient *capture;
1219 HANDLE NotifyEvent;
1221 HANDLE MsgEvent;
1223 ChannelConverter *ChannelConv;
1224 SampleConverter *SampleConv;
1225 ll_ringbuffer_t *Ring;
1227 volatile int killNow;
1228 althrd_t thread;
1229 } ALCmmdevCapture;
1231 static int ALCmmdevCapture_recordProc(void *arg);
1233 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
1234 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
1235 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
1236 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
1237 static void ALCmmdevCapture_close(ALCmmdevCapture *self);
1238 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
1239 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
1240 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
1241 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
1242 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
1243 static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
1244 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
1245 static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
1246 static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
1247 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
1248 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
1249 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
1250 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
1252 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
1253 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
1256 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
1258 SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
1259 SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
1260 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
1261 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
1263 self->devid = NULL;
1265 self->mmdev = NULL;
1266 self->client = NULL;
1267 self->capture = NULL;
1268 self->NotifyEvent = NULL;
1270 self->MsgEvent = NULL;
1272 self->ChannelConv = NULL;
1273 self->SampleConv = NULL;
1274 self->Ring = NULL;
1276 self->killNow = 0;
1279 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
1281 ll_ringbuffer_free(self->Ring);
1282 self->Ring = NULL;
1284 DestroySampleConverter(&self->SampleConv);
1285 DestroyChannelConverter(&self->ChannelConv);
1287 if(self->NotifyEvent != NULL)
1288 CloseHandle(self->NotifyEvent);
1289 self->NotifyEvent = NULL;
1290 if(self->MsgEvent != NULL)
1291 CloseHandle(self->MsgEvent);
1292 self->MsgEvent = NULL;
1294 free(self->devid);
1295 self->devid = NULL;
1297 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
1298 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
1302 FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
1304 ALCmmdevCapture *self = arg;
1305 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1306 ALfloat *samples = NULL;
1307 size_t samplesmax = 0;
1308 HRESULT hr;
1310 hr = CoInitialize(NULL);
1311 if(FAILED(hr))
1313 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
1314 V0(device->Backend,lock)();
1315 aluHandleDisconnect(device);
1316 V0(device->Backend,unlock)();
1317 return 1;
1320 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
1322 while(!self->killNow)
1324 UINT32 avail;
1325 DWORD res;
1327 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1328 if(FAILED(hr))
1329 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1330 else if(avail > 0)
1332 UINT32 numsamples;
1333 DWORD flags;
1334 BYTE *rdata;
1336 hr = IAudioCaptureClient_GetBuffer(self->capture,
1337 &rdata, &numsamples, &flags, NULL, NULL
1339 if(FAILED(hr))
1340 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
1341 else
1343 ll_ringbuffer_data_t data[2];
1344 size_t dstframes = 0;
1346 if(self->ChannelConv)
1348 if(samplesmax < numsamples)
1350 size_t newmax = RoundUp(numsamples, 4096);
1351 ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
1352 al_free(samples);
1353 samples = tmp;
1354 samplesmax = newmax;
1356 ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
1357 rdata = (BYTE*)samples;
1360 ll_ringbuffer_get_write_vector(self->Ring, data);
1362 if(self->SampleConv)
1364 const ALvoid *srcdata = rdata;
1365 ALsizei srcframes = numsamples;
1367 dstframes = SampleConverterInput(self->SampleConv,
1368 &srcdata, &srcframes, data[0].buf, data[0].len
1370 if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
1372 /* If some source samples remain, all of the first dest
1373 * block was filled, and there's space in the second
1374 * dest block, do another run for the second block.
1376 dstframes += SampleConverterInput(self->SampleConv,
1377 &srcdata, &srcframes, data[1].buf, data[1].len
1381 else
1383 size_t framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
1384 device->AmbiOrder);
1385 ALuint len1 = minu(data[0].len, numsamples);
1386 ALuint len2 = minu(data[1].len, numsamples-len1);
1388 memcpy(data[0].buf, rdata, len1*framesize);
1389 if(len2 > 0)
1390 memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
1391 dstframes = len1 + len2;
1394 ll_ringbuffer_write_advance(self->Ring, dstframes);
1396 hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
1397 if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
1401 if(FAILED(hr))
1403 V0(device->Backend,lock)();
1404 aluHandleDisconnect(device);
1405 V0(device->Backend,unlock)();
1406 break;
1409 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
1410 if(res != WAIT_OBJECT_0)
1411 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
1414 al_free(samples);
1415 samples = NULL;
1416 samplesmax = 0;
1418 CoUninitialize();
1419 return 0;
1423 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
1425 HRESULT hr = S_OK;
1427 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1428 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1429 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
1431 ERR("Failed to create message events: %lu\n", GetLastError());
1432 hr = E_FAIL;
1435 if(SUCCEEDED(hr))
1437 if(deviceName)
1439 const DevMap *iter;
1441 if(VECTOR_SIZE(CaptureDevices) == 0)
1443 ThreadRequest req = { self->MsgEvent, 0 };
1444 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
1445 (void)WaitForResponse(&req);
1448 hr = E_FAIL;
1449 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
1450 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1451 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1452 #undef MATCH_NAME
1453 if(iter == VECTOR_END(CaptureDevices))
1455 int len;
1456 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
1458 WCHAR *wname = calloc(sizeof(WCHAR), len);
1459 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
1460 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1461 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1462 #undef MATCH_NAME
1463 free(wname);
1466 if(iter == VECTOR_END(CaptureDevices))
1467 WARN("Failed to find device name matching \"%s\"\n", deviceName);
1468 else
1470 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
1471 self->devid = strdupW(iter->devid);
1472 alstr_copy(&device->DeviceName, iter->name);
1473 hr = S_OK;
1478 if(SUCCEEDED(hr))
1480 ThreadRequest req = { self->MsgEvent, 0 };
1482 hr = E_FAIL;
1483 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1484 hr = WaitForResponse(&req);
1485 else
1486 ERR("Failed to post thread message: %lu\n", GetLastError());
1489 if(FAILED(hr))
1491 if(self->NotifyEvent != NULL)
1492 CloseHandle(self->NotifyEvent);
1493 self->NotifyEvent = NULL;
1494 if(self->MsgEvent != NULL)
1495 CloseHandle(self->MsgEvent);
1496 self->MsgEvent = NULL;
1498 free(self->devid);
1499 self->devid = NULL;
1501 ERR("Device init failed: 0x%08lx\n", hr);
1502 return ALC_INVALID_VALUE;
1504 else
1506 ThreadRequest req = { self->MsgEvent, 0 };
1508 hr = E_FAIL;
1509 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1510 hr = WaitForResponse(&req);
1511 else
1512 ERR("Failed to post thread message: %lu\n", GetLastError());
1514 if(FAILED(hr))
1516 ALCmmdevCapture_close(self);
1517 if(hr == E_OUTOFMEMORY)
1518 return ALC_OUT_OF_MEMORY;
1519 return ALC_INVALID_VALUE;
1523 return ALC_NO_ERROR;
1526 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
1528 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1529 void *ptr;
1530 HRESULT hr;
1532 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
1533 if(SUCCEEDED(hr))
1535 IMMDeviceEnumerator *Enumerator = ptr;
1536 if(!self->devid)
1537 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
1538 else
1539 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
1540 IMMDeviceEnumerator_Release(Enumerator);
1541 Enumerator = NULL;
1543 if(SUCCEEDED(hr))
1544 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1545 if(SUCCEEDED(hr))
1547 self->client = ptr;
1548 if(alstr_empty(device->DeviceName))
1549 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
1552 if(FAILED(hr))
1554 if(self->mmdev)
1555 IMMDevice_Release(self->mmdev);
1556 self->mmdev = NULL;
1559 return hr;
1563 static void ALCmmdevCapture_close(ALCmmdevCapture *self)
1565 ThreadRequest req = { self->MsgEvent, 0 };
1567 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1568 (void)WaitForResponse(&req);
1570 ll_ringbuffer_free(self->Ring);
1571 self->Ring = NULL;
1573 CloseHandle(self->MsgEvent);
1574 self->MsgEvent = NULL;
1576 CloseHandle(self->NotifyEvent);
1577 self->NotifyEvent = NULL;
1579 free(self->devid);
1580 self->devid = NULL;
1583 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
1585 if(self->client)
1586 IAudioClient_Release(self->client);
1587 self->client = NULL;
1589 if(self->mmdev)
1590 IMMDevice_Release(self->mmdev);
1591 self->mmdev = NULL;
1595 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
1597 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1598 WAVEFORMATEXTENSIBLE OutputType;
1599 WAVEFORMATEX *wfx = NULL;
1600 enum DevFmtType srcType;
1601 REFERENCE_TIME buf_time;
1602 UINT32 buffer_len;
1603 void *ptr = NULL;
1604 HRESULT hr;
1606 if(self->client)
1607 IAudioClient_Release(self->client);
1608 self->client = NULL;
1610 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1611 if(FAILED(hr))
1613 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
1614 return hr;
1616 self->client = ptr;
1618 buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
1619 device->Frequency);
1620 // Make sure buffer is at least 100ms in size
1621 buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
1622 device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
1623 device->NumUpdates;
1625 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1626 switch(device->FmtChans)
1628 case DevFmtMono:
1629 OutputType.Format.nChannels = 1;
1630 OutputType.dwChannelMask = MONO;
1631 break;
1632 case DevFmtStereo:
1633 OutputType.Format.nChannels = 2;
1634 OutputType.dwChannelMask = STEREO;
1635 break;
1636 case DevFmtQuad:
1637 OutputType.Format.nChannels = 4;
1638 OutputType.dwChannelMask = QUAD;
1639 break;
1640 case DevFmtX51:
1641 OutputType.Format.nChannels = 6;
1642 OutputType.dwChannelMask = X5DOT1;
1643 break;
1644 case DevFmtX51Rear:
1645 OutputType.Format.nChannels = 6;
1646 OutputType.dwChannelMask = X5DOT1REAR;
1647 break;
1648 case DevFmtX61:
1649 OutputType.Format.nChannels = 7;
1650 OutputType.dwChannelMask = X6DOT1;
1651 break;
1652 case DevFmtX71:
1653 OutputType.Format.nChannels = 8;
1654 OutputType.dwChannelMask = X7DOT1;
1655 break;
1657 case DevFmtAmbi3D:
1658 return E_FAIL;
1660 switch(device->FmtType)
1662 /* NOTE: Signedness doesn't matter, the converter will handle it. */
1663 case DevFmtByte:
1664 case DevFmtUByte:
1665 OutputType.Format.wBitsPerSample = 8;
1666 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1667 break;
1668 case DevFmtShort:
1669 case DevFmtUShort:
1670 OutputType.Format.wBitsPerSample = 16;
1671 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1672 break;
1673 case DevFmtInt:
1674 case DevFmtUInt:
1675 OutputType.Format.wBitsPerSample = 32;
1676 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1677 break;
1678 case DevFmtFloat:
1679 OutputType.Format.wBitsPerSample = 32;
1680 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1681 break;
1683 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1684 OutputType.Format.nSamplesPerSec = device->Frequency;
1686 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1687 OutputType.Format.wBitsPerSample / 8;
1688 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1689 OutputType.Format.nBlockAlign;
1690 OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
1692 hr = IAudioClient_IsFormatSupported(self->client,
1693 AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx
1695 if(FAILED(hr))
1697 ERR("Failed to check format support: 0x%08lx\n", hr);
1698 return hr;
1701 DestroySampleConverter(&self->SampleConv);
1702 DestroyChannelConverter(&self->ChannelConv);
1704 if(wfx != NULL)
1706 if(!(wfx->nChannels == OutputType.Format.nChannels ||
1707 (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
1708 (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
1710 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1711 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1712 device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
1713 wfx->nSamplesPerSec);
1714 CoTaskMemFree(wfx);
1715 return E_FAIL;
1718 if(!MakeExtensible(&OutputType, wfx))
1720 CoTaskMemFree(wfx);
1721 return E_FAIL;
1723 CoTaskMemFree(wfx);
1724 wfx = NULL;
1727 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
1729 if(OutputType.Format.wBitsPerSample == 8)
1730 srcType = DevFmtUByte;
1731 else if(OutputType.Format.wBitsPerSample == 16)
1732 srcType = DevFmtShort;
1733 else if(OutputType.Format.wBitsPerSample == 32)
1734 srcType = DevFmtInt;
1735 else
1737 ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
1738 return E_FAIL;
1741 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1743 if(OutputType.Format.wBitsPerSample == 32)
1744 srcType = DevFmtFloat;
1745 else
1747 ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
1748 return E_FAIL;
1751 else
1753 ERR("Unhandled format sub-type\n");
1754 return E_FAIL;
1757 if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
1759 self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
1760 device->FmtChans);
1761 if(!self->ChannelConv)
1763 ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
1764 return E_FAIL;
1766 TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
1767 /* The channel converter always outputs float, so change the input type
1768 * for the resampler/type-converter.
1770 srcType = DevFmtFloat;
1772 else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
1774 self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
1775 device->FmtChans);
1776 if(!self->ChannelConv)
1778 ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
1779 return E_FAIL;
1781 TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
1782 srcType = DevFmtFloat;
1785 if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
1787 self->SampleConv = CreateSampleConverter(
1788 srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
1789 OutputType.Format.nSamplesPerSec, device->Frequency
1791 if(!self->SampleConv)
1793 ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1794 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1795 device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
1796 return E_FAIL;
1798 TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1799 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1800 device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
1803 hr = IAudioClient_Initialize(self->client,
1804 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1805 buf_time, 0, &OutputType.Format, NULL
1807 if(FAILED(hr))
1809 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1810 return hr;
1813 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1814 if(FAILED(hr))
1816 ERR("Failed to get buffer size: 0x%08lx\n", hr);
1817 return hr;
1820 buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
1821 ll_ringbuffer_free(self->Ring);
1822 self->Ring = ll_ringbuffer_create(buffer_len,
1823 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
1825 if(!self->Ring)
1827 ERR("Failed to allocate capture ring buffer\n");
1828 return E_OUTOFMEMORY;
1831 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1832 if(FAILED(hr))
1834 ERR("Failed to set event handle: 0x%08lx\n", hr);
1835 return hr;
1838 return hr;
1842 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
1844 ThreadRequest req = { self->MsgEvent, 0 };
1845 HRESULT hr = E_FAIL;
1847 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1848 hr = WaitForResponse(&req);
1850 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1853 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
1855 HRESULT hr;
1856 void *ptr;
1858 ResetEvent(self->NotifyEvent);
1859 hr = IAudioClient_Start(self->client);
1860 if(FAILED(hr))
1862 ERR("Failed to start audio client: 0x%08lx\n", hr);
1863 return hr;
1866 hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
1867 if(SUCCEEDED(hr))
1869 self->capture = ptr;
1870 self->killNow = 0;
1871 if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
1873 ERR("Failed to start thread\n");
1874 IAudioCaptureClient_Release(self->capture);
1875 self->capture = NULL;
1876 hr = E_FAIL;
1880 if(FAILED(hr))
1882 IAudioClient_Stop(self->client);
1883 IAudioClient_Reset(self->client);
1886 return hr;
1890 static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
1892 ThreadRequest req = { self->MsgEvent, 0 };
1893 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1894 (void)WaitForResponse(&req);
1897 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
1899 int res;
1901 if(!self->capture)
1902 return;
1904 self->killNow = 1;
1905 althrd_join(self->thread, &res);
1907 IAudioCaptureClient_Release(self->capture);
1908 self->capture = NULL;
1909 IAudioClient_Stop(self->client);
1910 IAudioClient_Reset(self->client);
1914 ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
1916 return (ALuint)ll_ringbuffer_read_space(self->Ring);
1919 ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
1921 if(ALCmmdevCapture_availableSamples(self) < samples)
1922 return ALC_INVALID_VALUE;
1923 ll_ringbuffer_read(self->Ring, buffer, samples);
1924 return ALC_NO_ERROR;
1928 static inline void AppendAllDevicesList2(const DevMap *entry)
1929 { AppendAllDevicesList(alstr_get_cstr(entry->name)); }
1930 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1931 { AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
1933 typedef struct ALCmmdevBackendFactory {
1934 DERIVE_FROM_TYPE(ALCbackendFactory);
1935 } ALCmmdevBackendFactory;
1936 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1938 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
1939 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
1940 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
1941 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
1942 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
1944 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
1947 static BOOL MMDevApiLoad(void)
1949 static HRESULT InitResult;
1950 if(!ThreadHdl)
1952 ThreadRequest req;
1953 InitResult = E_FAIL;
1955 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
1956 if(req.FinishedEvt == NULL)
1957 ERR("Failed to create event: %lu\n", GetLastError());
1958 else
1960 ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
1961 if(ThreadHdl != NULL)
1962 InitResult = WaitForResponse(&req);
1963 CloseHandle(req.FinishedEvt);
1966 return SUCCEEDED(InitResult);
1969 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
1971 VECTOR_INIT(PlaybackDevices);
1972 VECTOR_INIT(CaptureDevices);
1974 if(!MMDevApiLoad())
1975 return ALC_FALSE;
1976 return ALC_TRUE;
1979 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
1981 clear_devlist(&PlaybackDevices);
1982 VECTOR_DEINIT(PlaybackDevices);
1984 clear_devlist(&CaptureDevices);
1985 VECTOR_DEINIT(CaptureDevices);
1987 if(ThreadHdl)
1989 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
1990 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
1991 CloseHandle(ThreadHdl);
1992 ThreadHdl = NULL;
1996 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
1998 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
1999 * rechanneling or resampling; if the device is configured for 48000hz
2000 * stereo input, for example, and the app asks for 22050hz mono,
2001 * initialization will fail.
2003 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
2004 return ALC_TRUE;
2005 return ALC_FALSE;
2008 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
2010 ThreadRequest req = { NULL, 0 };
2012 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
2013 if(req.FinishedEvt == NULL)
2014 ERR("Failed to create event: %lu\n", GetLastError());
2015 else
2017 HRESULT hr = E_FAIL;
2018 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
2019 hr = WaitForResponse(&req);
2020 if(SUCCEEDED(hr)) switch(type)
2022 case ALL_DEVICE_PROBE:
2023 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
2024 break;
2026 case CAPTURE_DEVICE_PROBE:
2027 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
2028 break;
2030 CloseHandle(req.FinishedEvt);
2031 req.FinishedEvt = NULL;
2035 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
2037 if(type == ALCbackend_Playback)
2039 ALCmmdevPlayback *backend;
2040 NEW_OBJ(backend, ALCmmdevPlayback)(device);
2041 if(!backend) return NULL;
2042 return STATIC_CAST(ALCbackend, backend);
2044 if(type == ALCbackend_Capture)
2046 ALCmmdevCapture *backend;
2047 NEW_OBJ(backend, ALCmmdevCapture)(device);
2048 if(!backend) return NULL;
2049 return STATIC_CAST(ALCbackend, backend);
2052 return NULL;
2056 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
2058 static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
2059 return STATIC_CAST(ALCbackendFactory, &factory);