Call the backend close method in the destructor
[openal-soft.git] / Alc / backends / mmdevapi.c
blob899f73d4960afc2e902f8698ecb6854cd63b1a73
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 "ringbuffer.h"
45 #include "threads.h"
46 #include "compat.h"
47 #include "alstring.h"
48 #include "converter.h"
50 #include "backends/base.h"
53 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
54 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
56 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
57 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
58 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
60 #define MONO SPEAKER_FRONT_CENTER
61 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
62 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
63 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
64 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
65 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
66 #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)
67 #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)
69 #define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
71 #define DEVNAME_HEAD "OpenAL Soft on "
74 /* Scales the given value using 64-bit integer math, ceiling the result. */
75 static inline ALuint64 ScaleCeil(ALuint64 val, ALuint64 new_scale, ALuint64 old_scale)
77 return (val*new_scale + old_scale-1) / old_scale;
81 typedef struct {
82 al_string name;
83 al_string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
84 WCHAR *devid;
85 } DevMap;
86 TYPEDEF_VECTOR(DevMap, vector_DevMap)
88 static void clear_devlist(vector_DevMap *list)
90 #define CLEAR_DEVMAP(i) do { \
91 AL_STRING_DEINIT((i)->name); \
92 AL_STRING_DEINIT((i)->endpoint_guid); \
93 free((i)->devid); \
94 (i)->devid = NULL; \
95 } while(0)
96 VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
97 VECTOR_RESIZE(*list, 0, 0);
98 #undef CLEAR_DEVMAP
101 static vector_DevMap PlaybackDevices;
102 static vector_DevMap CaptureDevices;
105 static HANDLE ThreadHdl;
106 static DWORD ThreadID;
108 typedef struct {
109 HANDLE FinishedEvt;
110 HRESULT result;
111 } ThreadRequest;
113 #define WM_USER_First (WM_USER+0)
114 #define WM_USER_OpenDevice (WM_USER+0)
115 #define WM_USER_ResetDevice (WM_USER+1)
116 #define WM_USER_StartDevice (WM_USER+2)
117 #define WM_USER_StopDevice (WM_USER+3)
118 #define WM_USER_CloseDevice (WM_USER+4)
119 #define WM_USER_Enumerate (WM_USER+5)
120 #define WM_USER_Last (WM_USER+5)
122 static const char MessageStr[WM_USER_Last+1-WM_USER][20] = {
123 "Open Device",
124 "Reset Device",
125 "Start Device",
126 "Stop Device",
127 "Close Device",
128 "Enumerate Devices",
131 static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
133 req->result = res;
134 SetEvent(req->FinishedEvt);
137 static HRESULT WaitForResponse(ThreadRequest *req)
139 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
140 return req->result;
141 ERR("Message response error: %lu\n", GetLastError());
142 return E_FAIL;
146 static void get_device_name_and_guid(IMMDevice *device, al_string *name, al_string *guid)
148 IPropertyStore *ps;
149 PROPVARIANT pvname;
150 PROPVARIANT pvguid;
151 HRESULT hr;
153 alstr_copy_cstr(name, DEVNAME_HEAD);
155 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
156 if(FAILED(hr))
158 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
159 alstr_append_cstr(name, "Unknown Device Name");
160 if(guid!=NULL)alstr_copy_cstr(guid, "Unknown Device GUID");
161 return;
164 PropVariantInit(&pvname);
166 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
167 if(FAILED(hr))
169 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
170 alstr_append_cstr(name, "Unknown Device Name");
172 else if(pvname.vt == VT_LPWSTR)
173 alstr_append_wcstr(name, pvname.pwszVal);
174 else
176 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
177 alstr_append_cstr(name, "Unknown Device Name");
179 PropVariantClear(&pvname);
181 if(guid!=NULL){
182 PropVariantInit(&pvguid);
184 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&PKEY_AudioEndpoint_GUID, &pvguid);
185 if(FAILED(hr))
187 WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
188 alstr_copy_cstr(guid, "Unknown Device GUID");
190 else if(pvguid.vt == VT_LPWSTR)
191 alstr_copy_wcstr(guid, pvguid.pwszVal);
192 else
194 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid.vt);
195 alstr_copy_cstr(guid, "Unknown Device GUID");
198 PropVariantClear(&pvguid);
201 IPropertyStore_Release(ps);
204 static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
206 IPropertyStore *ps;
207 PROPVARIANT pvform;
208 HRESULT hr;
210 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
211 if(FAILED(hr))
213 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
214 return;
217 PropVariantInit(&pvform);
219 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
220 if(FAILED(hr))
221 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
222 else if(pvform.vt == VT_UI4)
223 *formfactor = pvform.ulVal;
224 else if(pvform.vt == VT_EMPTY)
225 *formfactor = UnknownFormFactor;
226 else
227 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
229 PropVariantClear(&pvform);
230 IPropertyStore_Release(ps);
234 static void add_device(IMMDevice *device, const WCHAR *devid, vector_DevMap *list)
236 int count = 0;
237 al_string tmpname;
238 DevMap entry;
240 AL_STRING_INIT(tmpname);
241 AL_STRING_INIT(entry.name);
242 AL_STRING_INIT(entry.endpoint_guid);
244 entry.devid = strdupW(devid);
245 get_device_name_and_guid(device, &tmpname, &entry.endpoint_guid);
247 while(1)
249 const DevMap *iter;
251 alstr_copy(&entry.name, tmpname);
252 if(count != 0)
254 char str[64];
255 snprintf(str, sizeof(str), " #%d", count+1);
256 alstr_append_cstr(&entry.name, str);
259 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
260 VECTOR_FIND_IF(iter, const DevMap, *list, MATCH_ENTRY);
261 if(iter == VECTOR_END(*list)) break;
262 #undef MATCH_ENTRY
263 count++;
266 TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry.name), alstr_get_cstr(entry.endpoint_guid), entry.devid);
267 VECTOR_PUSH_BACK(*list, entry);
269 AL_STRING_DEINIT(tmpname);
272 static WCHAR *get_device_id(IMMDevice *device)
274 WCHAR *devid;
275 HRESULT hr;
277 hr = IMMDevice_GetId(device, &devid);
278 if(FAILED(hr))
280 ERR("Failed to get device id: %lx\n", hr);
281 return NULL;
284 return devid;
287 static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
289 IMMDeviceCollection *coll;
290 IMMDevice *defdev = NULL;
291 WCHAR *defdevid = NULL;
292 HRESULT hr;
293 UINT count;
294 UINT i;
296 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
297 if(FAILED(hr))
299 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
300 return hr;
303 count = 0;
304 hr = IMMDeviceCollection_GetCount(coll, &count);
305 if(SUCCEEDED(hr) && count > 0)
307 clear_devlist(list);
308 VECTOR_RESIZE(*list, 0, count);
310 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
311 eMultimedia, &defdev);
313 if(SUCCEEDED(hr) && defdev != NULL)
315 defdevid = get_device_id(defdev);
316 if(defdevid)
317 add_device(defdev, defdevid, list);
320 for(i = 0;i < count;++i)
322 IMMDevice *device;
323 WCHAR *devid;
325 hr = IMMDeviceCollection_Item(coll, i, &device);
326 if(FAILED(hr)) continue;
328 devid = get_device_id(device);
329 if(devid)
331 if(wcscmp(devid, defdevid) != 0)
332 add_device(device, devid, list);
333 CoTaskMemFree(devid);
335 IMMDevice_Release(device);
338 if(defdev) IMMDevice_Release(defdev);
339 if(defdevid) CoTaskMemFree(defdevid);
340 IMMDeviceCollection_Release(coll);
342 return S_OK;
346 /* Proxy interface used by the message handler. */
347 struct ALCmmdevProxyVtable;
349 typedef struct ALCmmdevProxy {
350 const struct ALCmmdevProxyVtable *vtbl;
351 } ALCmmdevProxy;
353 struct ALCmmdevProxyVtable {
354 HRESULT (*const openProxy)(ALCmmdevProxy*);
355 void (*const closeProxy)(ALCmmdevProxy*);
357 HRESULT (*const resetProxy)(ALCmmdevProxy*);
358 HRESULT (*const startProxy)(ALCmmdevProxy*);
359 void (*const stopProxy)(ALCmmdevProxy*);
362 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
363 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
364 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
365 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
366 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
367 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
369 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
370 T##_ALCmmdevProxy_openProxy, \
371 T##_ALCmmdevProxy_closeProxy, \
372 T##_ALCmmdevProxy_resetProxy, \
373 T##_ALCmmdevProxy_startProxy, \
374 T##_ALCmmdevProxy_stopProxy, \
377 static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
378 static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
380 static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
382 ThreadRequest *req = ptr;
383 IMMDeviceEnumerator *Enumerator;
384 ALuint deviceCount = 0;
385 ALCmmdevProxy *proxy;
386 HRESULT hr, cohr;
387 MSG msg;
389 TRACE("Starting message thread\n");
391 cohr = CoInitialize(NULL);
392 if(FAILED(cohr))
394 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
395 ReturnMsgResponse(req, cohr);
396 return 0;
399 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
400 if(FAILED(hr))
402 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
403 CoUninitialize();
404 ReturnMsgResponse(req, hr);
405 return 0;
407 Enumerator = ptr;
408 IMMDeviceEnumerator_Release(Enumerator);
409 Enumerator = NULL;
411 CoUninitialize();
413 /* HACK: Force Windows to create a message queue for this thread before
414 * returning success, otherwise PostThreadMessage may fail if it gets
415 * called before GetMessage.
417 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
419 TRACE("Message thread initialization complete\n");
420 ReturnMsgResponse(req, S_OK);
422 TRACE("Starting message loop\n");
423 while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
425 TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
426 (msg.message >= WM_USER && msg.message <= WM_USER_Last) ?
427 MessageStr[msg.message-WM_USER] : "Unknown",
428 msg.message, (void*)msg.lParam, (void*)msg.wParam
430 switch(msg.message)
432 case WM_USER_OpenDevice:
433 req = (ThreadRequest*)msg.wParam;
434 proxy = (ALCmmdevProxy*)msg.lParam;
436 hr = cohr = S_OK;
437 if(++deviceCount == 1)
438 hr = cohr = CoInitialize(NULL);
439 if(SUCCEEDED(hr))
440 hr = V0(proxy,openProxy)();
441 if(FAILED(hr))
443 if(--deviceCount == 0 && SUCCEEDED(cohr))
444 CoUninitialize();
447 ReturnMsgResponse(req, hr);
448 continue;
450 case WM_USER_ResetDevice:
451 req = (ThreadRequest*)msg.wParam;
452 proxy = (ALCmmdevProxy*)msg.lParam;
454 hr = V0(proxy,resetProxy)();
455 ReturnMsgResponse(req, hr);
456 continue;
458 case WM_USER_StartDevice:
459 req = (ThreadRequest*)msg.wParam;
460 proxy = (ALCmmdevProxy*)msg.lParam;
462 hr = V0(proxy,startProxy)();
463 ReturnMsgResponse(req, hr);
464 continue;
466 case WM_USER_StopDevice:
467 req = (ThreadRequest*)msg.wParam;
468 proxy = (ALCmmdevProxy*)msg.lParam;
470 V0(proxy,stopProxy)();
471 ReturnMsgResponse(req, S_OK);
472 continue;
474 case WM_USER_CloseDevice:
475 req = (ThreadRequest*)msg.wParam;
476 proxy = (ALCmmdevProxy*)msg.lParam;
478 V0(proxy,closeProxy)();
479 if(--deviceCount == 0)
480 CoUninitialize();
482 ReturnMsgResponse(req, S_OK);
483 continue;
485 case WM_USER_Enumerate:
486 req = (ThreadRequest*)msg.wParam;
488 hr = cohr = S_OK;
489 if(++deviceCount == 1)
490 hr = cohr = CoInitialize(NULL);
491 if(SUCCEEDED(hr))
492 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
493 if(SUCCEEDED(hr))
495 Enumerator = ptr;
497 if(msg.lParam == ALL_DEVICE_PROBE)
498 hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
499 else if(msg.lParam == CAPTURE_DEVICE_PROBE)
500 hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
502 IMMDeviceEnumerator_Release(Enumerator);
503 Enumerator = NULL;
506 if(--deviceCount == 0 && SUCCEEDED(cohr))
507 CoUninitialize();
509 ReturnMsgResponse(req, hr);
510 continue;
512 default:
513 ERR("Unexpected message: %u\n", msg.message);
514 continue;
517 TRACE("Message loop finished\n");
519 return 0;
523 typedef struct ALCmmdevPlayback {
524 DERIVE_FROM_TYPE(ALCbackend);
525 DERIVE_FROM_TYPE(ALCmmdevProxy);
527 WCHAR *devid;
529 IMMDevice *mmdev;
530 IAudioClient *client;
531 IAudioRenderClient *render;
532 HANDLE NotifyEvent;
534 HANDLE MsgEvent;
536 ATOMIC(UINT32) Padding;
538 ATOMIC(int) killNow;
539 althrd_t thread;
540 } ALCmmdevPlayback;
542 static int ALCmmdevPlayback_mixerProc(void *arg);
544 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
545 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
546 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
547 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
548 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
549 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
550 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
551 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
552 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
553 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
554 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
555 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
556 static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
557 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
558 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self);
559 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
560 static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
561 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
563 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
564 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
567 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
569 SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
570 SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
571 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
572 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
574 self->devid = NULL;
576 self->mmdev = NULL;
577 self->client = NULL;
578 self->render = NULL;
579 self->NotifyEvent = NULL;
581 self->MsgEvent = NULL;
583 ATOMIC_INIT(&self->Padding, 0);
585 ATOMIC_INIT(&self->killNow, 0);
588 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
590 ALCmmdevPlayback_close(self);
592 if(self->NotifyEvent != NULL)
593 CloseHandle(self->NotifyEvent);
594 self->NotifyEvent = NULL;
595 if(self->MsgEvent != NULL)
596 CloseHandle(self->MsgEvent);
597 self->MsgEvent = NULL;
599 free(self->devid);
600 self->devid = NULL;
602 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
603 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
607 FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
609 ALCmmdevPlayback *self = arg;
610 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
611 UINT32 buffer_len, written;
612 ALuint update_size, len;
613 BYTE *buffer;
614 HRESULT hr;
616 hr = CoInitialize(NULL);
617 if(FAILED(hr))
619 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
620 V0(device->Backend,lock)();
621 aluHandleDisconnect(device);
622 V0(device->Backend,unlock)();
623 return 1;
626 SetRTPriority();
627 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
629 update_size = device->UpdateSize;
630 buffer_len = update_size * device->NumUpdates;
631 while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
633 hr = IAudioClient_GetCurrentPadding(self->client, &written);
634 if(FAILED(hr))
636 ERR("Failed to get padding: 0x%08lx\n", hr);
637 V0(device->Backend,lock)();
638 aluHandleDisconnect(device);
639 V0(device->Backend,unlock)();
640 break;
642 ATOMIC_STORE(&self->Padding, written, almemory_order_relaxed);
644 len = buffer_len - written;
645 if(len < update_size)
647 DWORD res;
648 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
649 if(res != WAIT_OBJECT_0)
650 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
651 continue;
653 len -= len%update_size;
655 hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
656 if(SUCCEEDED(hr))
658 ALCmmdevPlayback_lock(self);
659 aluMixData(device, buffer, len);
660 ATOMIC_STORE(&self->Padding, written + len, almemory_order_relaxed);
661 ALCmmdevPlayback_unlock(self);
662 hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
664 if(FAILED(hr))
666 ERR("Failed to buffer data: 0x%08lx\n", hr);
667 V0(device->Backend,lock)();
668 aluHandleDisconnect(device);
669 V0(device->Backend,unlock)();
670 break;
673 ATOMIC_STORE(&self->Padding, 0, almemory_order_release);
675 CoUninitialize();
676 return 0;
680 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
682 memset(out, 0, sizeof(*out));
683 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
684 *out = *(const WAVEFORMATEXTENSIBLE*)in;
685 else if(in->wFormatTag == WAVE_FORMAT_PCM)
687 out->Format = *in;
688 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
689 out->Format.cbSize = sizeof(*out) - sizeof(*in);
690 if(out->Format.nChannels == 1)
691 out->dwChannelMask = MONO;
692 else if(out->Format.nChannels == 2)
693 out->dwChannelMask = STEREO;
694 else
695 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
696 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
698 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
700 out->Format = *in;
701 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
702 out->Format.cbSize = sizeof(*out) - sizeof(*in);
703 if(out->Format.nChannels == 1)
704 out->dwChannelMask = MONO;
705 else if(out->Format.nChannels == 2)
706 out->dwChannelMask = STEREO;
707 else
708 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
709 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
711 else
713 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
714 return ALC_FALSE;
716 return ALC_TRUE;
719 static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
721 HRESULT hr = S_OK;
723 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
724 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
725 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
727 ERR("Failed to create message events: %lu\n", GetLastError());
728 hr = E_FAIL;
731 if(SUCCEEDED(hr))
733 if(deviceName)
735 const DevMap *iter;
737 if(VECTOR_SIZE(PlaybackDevices) == 0)
739 ThreadRequest req = { self->MsgEvent, 0 };
740 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
741 (void)WaitForResponse(&req);
744 hr = E_FAIL;
745 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
746 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
747 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
748 #undef MATCH_NAME
749 if(iter == VECTOR_END(PlaybackDevices))
751 int len;
752 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
754 WCHAR *wname = calloc(sizeof(WCHAR), len);
755 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
756 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
757 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
758 #undef MATCH_NAME
759 free(wname);
762 if(iter == VECTOR_END(PlaybackDevices))
763 WARN("Failed to find device name matching \"%s\"\n", deviceName);
764 else
766 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
767 self->devid = strdupW(iter->devid);
768 alstr_copy(&device->DeviceName, iter->name);
769 hr = S_OK;
774 if(SUCCEEDED(hr))
776 ThreadRequest req = { self->MsgEvent, 0 };
778 hr = E_FAIL;
779 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
780 hr = WaitForResponse(&req);
781 else
782 ERR("Failed to post thread message: %lu\n", GetLastError());
785 if(FAILED(hr))
787 if(self->NotifyEvent != NULL)
788 CloseHandle(self->NotifyEvent);
789 self->NotifyEvent = NULL;
790 if(self->MsgEvent != NULL)
791 CloseHandle(self->MsgEvent);
792 self->MsgEvent = NULL;
794 free(self->devid);
795 self->devid = NULL;
797 ERR("Device init failed: 0x%08lx\n", hr);
798 return ALC_INVALID_VALUE;
801 return ALC_NO_ERROR;
804 static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
806 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
807 void *ptr;
808 HRESULT hr;
810 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
811 if(SUCCEEDED(hr))
813 IMMDeviceEnumerator *Enumerator = ptr;
814 if(!self->devid)
815 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
816 else
817 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
818 IMMDeviceEnumerator_Release(Enumerator);
819 Enumerator = NULL;
821 if(SUCCEEDED(hr))
822 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
823 if(SUCCEEDED(hr))
825 self->client = ptr;
826 if(alstr_empty(device->DeviceName))
827 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
830 if(FAILED(hr))
832 if(self->mmdev)
833 IMMDevice_Release(self->mmdev);
834 self->mmdev = NULL;
837 return hr;
841 static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
843 ThreadRequest req = { self->MsgEvent, 0 };
845 if(!req.FinishedEvt)
846 return;
848 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
849 (void)WaitForResponse(&req);
851 CloseHandle(self->MsgEvent);
852 self->MsgEvent = NULL;
854 CloseHandle(self->NotifyEvent);
855 self->NotifyEvent = NULL;
857 free(self->devid);
858 self->devid = NULL;
861 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
863 if(self->client)
864 IAudioClient_Release(self->client);
865 self->client = NULL;
867 if(self->mmdev)
868 IMMDevice_Release(self->mmdev);
869 self->mmdev = NULL;
873 static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
875 ThreadRequest req = { self->MsgEvent, 0 };
876 HRESULT hr = E_FAIL;
878 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
879 hr = WaitForResponse(&req);
881 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
884 static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
886 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
887 EndpointFormFactor formfactor = UnknownFormFactor;
888 WAVEFORMATEXTENSIBLE OutputType;
889 WAVEFORMATEX *wfx = NULL;
890 REFERENCE_TIME min_per, buf_time;
891 UINT32 buffer_len, min_len;
892 void *ptr = NULL;
893 HRESULT hr;
895 if(self->client)
896 IAudioClient_Release(self->client);
897 self->client = NULL;
899 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
900 if(FAILED(hr))
902 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
903 return hr;
905 self->client = ptr;
907 hr = IAudioClient_GetMixFormat(self->client, &wfx);
908 if(FAILED(hr))
910 ERR("Failed to get mix format: 0x%08lx\n", hr);
911 return hr;
914 if(!MakeExtensible(&OutputType, wfx))
916 CoTaskMemFree(wfx);
917 return E_FAIL;
919 CoTaskMemFree(wfx);
920 wfx = NULL;
922 buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
923 device->Frequency);
925 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
926 device->Frequency = OutputType.Format.nSamplesPerSec;
927 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
929 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
930 device->FmtChans = DevFmtMono;
931 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
932 device->FmtChans = DevFmtStereo;
933 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
934 device->FmtChans = DevFmtQuad;
935 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
936 device->FmtChans = DevFmtX51;
937 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
938 device->FmtChans = DevFmtX51Rear;
939 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
940 device->FmtChans = DevFmtX61;
941 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
942 device->FmtChans = DevFmtX71;
943 else
944 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
947 switch(device->FmtChans)
949 case DevFmtMono:
950 OutputType.Format.nChannels = 1;
951 OutputType.dwChannelMask = MONO;
952 break;
953 case DevFmtAmbi3D:
954 device->FmtChans = DevFmtStereo;
955 /*fall-through*/
956 case DevFmtStereo:
957 OutputType.Format.nChannels = 2;
958 OutputType.dwChannelMask = STEREO;
959 break;
960 case DevFmtQuad:
961 OutputType.Format.nChannels = 4;
962 OutputType.dwChannelMask = QUAD;
963 break;
964 case DevFmtX51:
965 OutputType.Format.nChannels = 6;
966 OutputType.dwChannelMask = X5DOT1;
967 break;
968 case DevFmtX51Rear:
969 OutputType.Format.nChannels = 6;
970 OutputType.dwChannelMask = X5DOT1REAR;
971 break;
972 case DevFmtX61:
973 OutputType.Format.nChannels = 7;
974 OutputType.dwChannelMask = X6DOT1;
975 break;
976 case DevFmtX71:
977 OutputType.Format.nChannels = 8;
978 OutputType.dwChannelMask = X7DOT1;
979 break;
981 switch(device->FmtType)
983 case DevFmtByte:
984 device->FmtType = DevFmtUByte;
985 /* fall-through */
986 case DevFmtUByte:
987 OutputType.Format.wBitsPerSample = 8;
988 OutputType.Samples.wValidBitsPerSample = 8;
989 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
990 break;
991 case DevFmtUShort:
992 device->FmtType = DevFmtShort;
993 /* fall-through */
994 case DevFmtShort:
995 OutputType.Format.wBitsPerSample = 16;
996 OutputType.Samples.wValidBitsPerSample = 16;
997 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
998 break;
999 case DevFmtUInt:
1000 device->FmtType = DevFmtInt;
1001 /* fall-through */
1002 case DevFmtInt:
1003 OutputType.Format.wBitsPerSample = 32;
1004 OutputType.Samples.wValidBitsPerSample = 32;
1005 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1006 break;
1007 case DevFmtFloat:
1008 OutputType.Format.wBitsPerSample = 32;
1009 OutputType.Samples.wValidBitsPerSample = 32;
1010 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1011 break;
1013 OutputType.Format.nSamplesPerSec = device->Frequency;
1015 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1016 OutputType.Format.wBitsPerSample / 8;
1017 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1018 OutputType.Format.nBlockAlign;
1020 hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
1021 if(FAILED(hr))
1023 ERR("Failed to check format support: 0x%08lx\n", hr);
1024 hr = IAudioClient_GetMixFormat(self->client, &wfx);
1026 if(FAILED(hr))
1028 ERR("Failed to find a supported format: 0x%08lx\n", hr);
1029 return hr;
1032 if(wfx != NULL)
1034 if(!MakeExtensible(&OutputType, wfx))
1036 CoTaskMemFree(wfx);
1037 return E_FAIL;
1039 CoTaskMemFree(wfx);
1040 wfx = NULL;
1042 device->Frequency = OutputType.Format.nSamplesPerSec;
1043 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
1044 device->FmtChans = DevFmtMono;
1045 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
1046 device->FmtChans = DevFmtStereo;
1047 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
1048 device->FmtChans = DevFmtQuad;
1049 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
1050 device->FmtChans = DevFmtX51;
1051 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
1052 device->FmtChans = DevFmtX51Rear;
1053 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
1054 device->FmtChans = DevFmtX61;
1055 else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
1056 device->FmtChans = DevFmtX71;
1057 else
1059 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
1060 device->FmtChans = DevFmtStereo;
1061 OutputType.Format.nChannels = 2;
1062 OutputType.dwChannelMask = STEREO;
1065 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
1067 if(OutputType.Format.wBitsPerSample == 8)
1068 device->FmtType = DevFmtUByte;
1069 else if(OutputType.Format.wBitsPerSample == 16)
1070 device->FmtType = DevFmtShort;
1071 else if(OutputType.Format.wBitsPerSample == 32)
1072 device->FmtType = DevFmtInt;
1073 else
1075 device->FmtType = DevFmtShort;
1076 OutputType.Format.wBitsPerSample = 16;
1079 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1081 device->FmtType = DevFmtFloat;
1082 OutputType.Format.wBitsPerSample = 32;
1084 else
1086 ERR("Unhandled format sub-type\n");
1087 device->FmtType = DevFmtShort;
1088 OutputType.Format.wBitsPerSample = 16;
1089 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1091 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1093 get_device_formfactor(self->mmdev, &formfactor);
1094 device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
1095 (formfactor == Headphones || formfactor == Headset)
1098 SetDefaultWFXChannelOrder(device);
1100 hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
1101 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1102 buf_time, 0, &OutputType.Format, NULL);
1103 if(FAILED(hr))
1105 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1106 return hr;
1109 hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
1110 if(SUCCEEDED(hr))
1112 min_len = (UINT32)ScaleCeil(min_per, device->Frequency, REFTIME_PER_SEC);
1113 /* Find the nearest multiple of the period size to the update size */
1114 if(min_len < device->UpdateSize)
1115 min_len *= (device->UpdateSize + min_len/2)/min_len;
1116 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1118 if(FAILED(hr))
1120 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
1121 return hr;
1124 device->UpdateSize = min_len;
1125 device->NumUpdates = buffer_len / device->UpdateSize;
1126 if(device->NumUpdates <= 1)
1128 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1129 device->NumUpdates = 2;
1130 device->UpdateSize = buffer_len / device->NumUpdates;
1133 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1134 if(FAILED(hr))
1136 ERR("Failed to set event handle: 0x%08lx\n", hr);
1137 return hr;
1140 return hr;
1144 static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
1146 ThreadRequest req = { self->MsgEvent, 0 };
1147 HRESULT hr = E_FAIL;
1149 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1150 hr = WaitForResponse(&req);
1152 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1155 static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
1157 HRESULT hr;
1158 void *ptr;
1160 ResetEvent(self->NotifyEvent);
1161 hr = IAudioClient_Start(self->client);
1162 if(FAILED(hr))
1163 ERR("Failed to start audio client: 0x%08lx\n", hr);
1165 if(SUCCEEDED(hr))
1166 hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
1167 if(SUCCEEDED(hr))
1169 self->render = ptr;
1170 ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
1171 if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
1173 if(self->render)
1174 IAudioRenderClient_Release(self->render);
1175 self->render = NULL;
1176 IAudioClient_Stop(self->client);
1177 ERR("Failed to start thread\n");
1178 hr = E_FAIL;
1182 return hr;
1186 static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
1188 ThreadRequest req = { self->MsgEvent, 0 };
1189 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1190 (void)WaitForResponse(&req);
1193 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
1195 int res;
1197 if(!self->render)
1198 return;
1200 ATOMIC_STORE_SEQ(&self->killNow, 1);
1201 althrd_join(self->thread, &res);
1203 IAudioRenderClient_Release(self->render);
1204 self->render = NULL;
1205 IAudioClient_Stop(self->client);
1209 static ClockLatency ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback *self)
1211 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1212 ClockLatency ret;
1214 ALCmmdevPlayback_lock(self);
1215 ret.ClockTime = GetDeviceClockTime(device);
1216 ret.Latency = ATOMIC_LOAD(&self->Padding, almemory_order_relaxed) * DEVICE_CLOCK_RES /
1217 device->Frequency;
1218 ALCmmdevPlayback_unlock(self);
1220 return ret;
1224 typedef struct ALCmmdevCapture {
1225 DERIVE_FROM_TYPE(ALCbackend);
1226 DERIVE_FROM_TYPE(ALCmmdevProxy);
1228 WCHAR *devid;
1230 IMMDevice *mmdev;
1231 IAudioClient *client;
1232 IAudioCaptureClient *capture;
1233 HANDLE NotifyEvent;
1235 HANDLE MsgEvent;
1237 ChannelConverter *ChannelConv;
1238 SampleConverter *SampleConv;
1239 ll_ringbuffer_t *Ring;
1241 ATOMIC(int) killNow;
1242 althrd_t thread;
1243 } ALCmmdevCapture;
1245 static int ALCmmdevCapture_recordProc(void *arg);
1247 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
1248 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
1249 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
1250 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
1251 static void ALCmmdevCapture_close(ALCmmdevCapture *self);
1252 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
1253 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
1254 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
1255 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
1256 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
1257 static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
1258 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
1259 static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
1260 static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
1261 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ClockLatency, getClockLatency)
1262 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
1263 static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
1264 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
1266 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
1267 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
1270 static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
1272 SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
1273 SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
1274 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
1275 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
1277 self->devid = NULL;
1279 self->mmdev = NULL;
1280 self->client = NULL;
1281 self->capture = NULL;
1282 self->NotifyEvent = NULL;
1284 self->MsgEvent = NULL;
1286 self->ChannelConv = NULL;
1287 self->SampleConv = NULL;
1288 self->Ring = NULL;
1290 ATOMIC_INIT(&self->killNow, 0);
1293 static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
1295 ALCmmdevCapture_close(self);
1297 ll_ringbuffer_free(self->Ring);
1298 self->Ring = NULL;
1300 DestroySampleConverter(&self->SampleConv);
1301 DestroyChannelConverter(&self->ChannelConv);
1303 if(self->NotifyEvent != NULL)
1304 CloseHandle(self->NotifyEvent);
1305 self->NotifyEvent = NULL;
1306 if(self->MsgEvent != NULL)
1307 CloseHandle(self->MsgEvent);
1308 self->MsgEvent = NULL;
1310 free(self->devid);
1311 self->devid = NULL;
1313 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
1314 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
1318 FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
1320 ALCmmdevCapture *self = arg;
1321 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1322 ALfloat *samples = NULL;
1323 size_t samplesmax = 0;
1324 HRESULT hr;
1326 hr = CoInitialize(NULL);
1327 if(FAILED(hr))
1329 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
1330 V0(device->Backend,lock)();
1331 aluHandleDisconnect(device);
1332 V0(device->Backend,unlock)();
1333 return 1;
1336 althrd_setname(althrd_current(), RECORD_THREAD_NAME);
1338 while(!ATOMIC_LOAD(&self->killNow, almemory_order_relaxed))
1340 UINT32 avail;
1341 DWORD res;
1343 hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
1344 if(FAILED(hr))
1345 ERR("Failed to get next packet size: 0x%08lx\n", hr);
1346 else if(avail > 0)
1348 UINT32 numsamples;
1349 DWORD flags;
1350 BYTE *rdata;
1352 hr = IAudioCaptureClient_GetBuffer(self->capture,
1353 &rdata, &numsamples, &flags, NULL, NULL
1355 if(FAILED(hr))
1356 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
1357 else
1359 ll_ringbuffer_data_t data[2];
1360 size_t dstframes = 0;
1362 if(self->ChannelConv)
1364 if(samplesmax < numsamples)
1366 size_t newmax = RoundUp(numsamples, 4096);
1367 ALfloat *tmp = al_calloc(DEF_ALIGN, newmax*2*sizeof(ALfloat));
1368 al_free(samples);
1369 samples = tmp;
1370 samplesmax = newmax;
1372 ChannelConverterInput(self->ChannelConv, rdata, samples, numsamples);
1373 rdata = (BYTE*)samples;
1376 ll_ringbuffer_get_write_vector(self->Ring, data);
1378 if(self->SampleConv)
1380 const ALvoid *srcdata = rdata;
1381 ALsizei srcframes = numsamples;
1383 dstframes = SampleConverterInput(self->SampleConv,
1384 &srcdata, &srcframes, data[0].buf, (ALsizei)minz(data[0].len, INT_MAX)
1386 if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0)
1388 /* If some source samples remain, all of the first dest
1389 * block was filled, and there's space in the second
1390 * dest block, do another run for the second block.
1392 dstframes += SampleConverterInput(self->SampleConv,
1393 &srcdata, &srcframes, data[1].buf, (ALsizei)minz(data[1].len, INT_MAX)
1397 else
1399 ALuint framesize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType,
1400 device->AmbiOrder);
1401 size_t len1 = minz(data[0].len, numsamples);
1402 size_t len2 = minz(data[1].len, numsamples-len1);
1404 memcpy(data[0].buf, rdata, len1*framesize);
1405 if(len2 > 0)
1406 memcpy(data[1].buf, rdata+len1*framesize, len2*framesize);
1407 dstframes = len1 + len2;
1410 ll_ringbuffer_write_advance(self->Ring, dstframes);
1412 hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
1413 if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
1417 if(FAILED(hr))
1419 V0(device->Backend,lock)();
1420 aluHandleDisconnect(device);
1421 V0(device->Backend,unlock)();
1422 break;
1425 res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
1426 if(res != WAIT_OBJECT_0)
1427 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
1430 al_free(samples);
1431 samples = NULL;
1432 samplesmax = 0;
1434 CoUninitialize();
1435 return 0;
1439 static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
1441 HRESULT hr = S_OK;
1443 self->NotifyEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1444 self->MsgEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1445 if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
1447 ERR("Failed to create message events: %lu\n", GetLastError());
1448 hr = E_FAIL;
1451 if(SUCCEEDED(hr))
1453 if(deviceName)
1455 const DevMap *iter;
1457 if(VECTOR_SIZE(CaptureDevices) == 0)
1459 ThreadRequest req = { self->MsgEvent, 0 };
1460 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
1461 (void)WaitForResponse(&req);
1464 hr = E_FAIL;
1465 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
1466 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1467 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1468 #undef MATCH_NAME
1469 if(iter == VECTOR_END(CaptureDevices))
1471 int len;
1472 if((len=MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, NULL, 0)) > 0)
1474 WCHAR *wname = calloc(sizeof(WCHAR), len);
1475 MultiByteToWideChar(CP_UTF8, 0, deviceName, -1, wname, len);
1476 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1477 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1478 #undef MATCH_NAME
1479 free(wname);
1482 if(iter == VECTOR_END(CaptureDevices))
1483 WARN("Failed to find device name matching \"%s\"\n", deviceName);
1484 else
1486 ALCdevice *device = STATIC_CAST(ALCbackend,self)->mDevice;
1487 self->devid = strdupW(iter->devid);
1488 alstr_copy(&device->DeviceName, iter->name);
1489 hr = S_OK;
1494 if(SUCCEEDED(hr))
1496 ThreadRequest req = { self->MsgEvent, 0 };
1498 hr = E_FAIL;
1499 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1500 hr = WaitForResponse(&req);
1501 else
1502 ERR("Failed to post thread message: %lu\n", GetLastError());
1505 if(FAILED(hr))
1507 if(self->NotifyEvent != NULL)
1508 CloseHandle(self->NotifyEvent);
1509 self->NotifyEvent = NULL;
1510 if(self->MsgEvent != NULL)
1511 CloseHandle(self->MsgEvent);
1512 self->MsgEvent = NULL;
1514 free(self->devid);
1515 self->devid = NULL;
1517 ERR("Device init failed: 0x%08lx\n", hr);
1518 return ALC_INVALID_VALUE;
1520 else
1522 ThreadRequest req = { self->MsgEvent, 0 };
1524 hr = E_FAIL;
1525 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1526 hr = WaitForResponse(&req);
1527 else
1528 ERR("Failed to post thread message: %lu\n", GetLastError());
1530 if(FAILED(hr))
1532 ALCmmdevCapture_close(self);
1533 if(hr == E_OUTOFMEMORY)
1534 return ALC_OUT_OF_MEMORY;
1535 return ALC_INVALID_VALUE;
1539 return ALC_NO_ERROR;
1542 static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
1544 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1545 void *ptr;
1546 HRESULT hr;
1548 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
1549 if(SUCCEEDED(hr))
1551 IMMDeviceEnumerator *Enumerator = ptr;
1552 if(!self->devid)
1553 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
1554 else
1555 hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
1556 IMMDeviceEnumerator_Release(Enumerator);
1557 Enumerator = NULL;
1559 if(SUCCEEDED(hr))
1560 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1561 if(SUCCEEDED(hr))
1563 self->client = ptr;
1564 if(alstr_empty(device->DeviceName))
1565 get_device_name_and_guid(self->mmdev, &device->DeviceName, NULL);
1568 if(FAILED(hr))
1570 if(self->mmdev)
1571 IMMDevice_Release(self->mmdev);
1572 self->mmdev = NULL;
1575 return hr;
1579 static void ALCmmdevCapture_close(ALCmmdevCapture *self)
1581 ThreadRequest req = { self->MsgEvent, 0 };
1583 if(!req.FinishedEvt)
1584 return;
1586 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1587 (void)WaitForResponse(&req);
1589 ll_ringbuffer_free(self->Ring);
1590 self->Ring = NULL;
1592 CloseHandle(self->MsgEvent);
1593 self->MsgEvent = NULL;
1595 CloseHandle(self->NotifyEvent);
1596 self->NotifyEvent = NULL;
1598 free(self->devid);
1599 self->devid = NULL;
1602 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
1604 if(self->client)
1605 IAudioClient_Release(self->client);
1606 self->client = NULL;
1608 if(self->mmdev)
1609 IMMDevice_Release(self->mmdev);
1610 self->mmdev = NULL;
1614 static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
1616 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1617 WAVEFORMATEXTENSIBLE OutputType;
1618 WAVEFORMATEX *wfx = NULL;
1619 enum DevFmtType srcType;
1620 REFERENCE_TIME buf_time;
1621 UINT32 buffer_len;
1622 void *ptr = NULL;
1623 HRESULT hr;
1625 if(self->client)
1626 IAudioClient_Release(self->client);
1627 self->client = NULL;
1629 hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
1630 if(FAILED(hr))
1632 ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
1633 return hr;
1635 self->client = ptr;
1637 buf_time = ScaleCeil(device->UpdateSize*device->NumUpdates, REFTIME_PER_SEC,
1638 device->Frequency);
1639 // Make sure buffer is at least 100ms in size
1640 buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
1641 device->UpdateSize = (ALuint)ScaleCeil(buf_time, device->Frequency, REFTIME_PER_SEC) /
1642 device->NumUpdates;
1644 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1645 switch(device->FmtChans)
1647 case DevFmtMono:
1648 OutputType.Format.nChannels = 1;
1649 OutputType.dwChannelMask = MONO;
1650 break;
1651 case DevFmtStereo:
1652 OutputType.Format.nChannels = 2;
1653 OutputType.dwChannelMask = STEREO;
1654 break;
1655 case DevFmtQuad:
1656 OutputType.Format.nChannels = 4;
1657 OutputType.dwChannelMask = QUAD;
1658 break;
1659 case DevFmtX51:
1660 OutputType.Format.nChannels = 6;
1661 OutputType.dwChannelMask = X5DOT1;
1662 break;
1663 case DevFmtX51Rear:
1664 OutputType.Format.nChannels = 6;
1665 OutputType.dwChannelMask = X5DOT1REAR;
1666 break;
1667 case DevFmtX61:
1668 OutputType.Format.nChannels = 7;
1669 OutputType.dwChannelMask = X6DOT1;
1670 break;
1671 case DevFmtX71:
1672 OutputType.Format.nChannels = 8;
1673 OutputType.dwChannelMask = X7DOT1;
1674 break;
1676 case DevFmtAmbi3D:
1677 return E_FAIL;
1679 switch(device->FmtType)
1681 /* NOTE: Signedness doesn't matter, the converter will handle it. */
1682 case DevFmtByte:
1683 case DevFmtUByte:
1684 OutputType.Format.wBitsPerSample = 8;
1685 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1686 break;
1687 case DevFmtShort:
1688 case DevFmtUShort:
1689 OutputType.Format.wBitsPerSample = 16;
1690 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1691 break;
1692 case DevFmtInt:
1693 case DevFmtUInt:
1694 OutputType.Format.wBitsPerSample = 32;
1695 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1696 break;
1697 case DevFmtFloat:
1698 OutputType.Format.wBitsPerSample = 32;
1699 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1700 break;
1702 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1703 OutputType.Format.nSamplesPerSec = device->Frequency;
1705 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
1706 OutputType.Format.wBitsPerSample / 8;
1707 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1708 OutputType.Format.nBlockAlign;
1709 OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
1711 hr = IAudioClient_IsFormatSupported(self->client,
1712 AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx
1714 if(FAILED(hr))
1716 ERR("Failed to check format support: 0x%08lx\n", hr);
1717 return hr;
1720 DestroySampleConverter(&self->SampleConv);
1721 DestroyChannelConverter(&self->ChannelConv);
1723 if(wfx != NULL)
1725 if(!(wfx->nChannels == OutputType.Format.nChannels ||
1726 (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
1727 (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
1729 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1730 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1731 device->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
1732 wfx->nSamplesPerSec);
1733 CoTaskMemFree(wfx);
1734 return E_FAIL;
1737 if(!MakeExtensible(&OutputType, wfx))
1739 CoTaskMemFree(wfx);
1740 return E_FAIL;
1742 CoTaskMemFree(wfx);
1743 wfx = NULL;
1746 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
1748 if(OutputType.Format.wBitsPerSample == 8)
1749 srcType = DevFmtUByte;
1750 else if(OutputType.Format.wBitsPerSample == 16)
1751 srcType = DevFmtShort;
1752 else if(OutputType.Format.wBitsPerSample == 32)
1753 srcType = DevFmtInt;
1754 else
1756 ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
1757 return E_FAIL;
1760 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1762 if(OutputType.Format.wBitsPerSample == 32)
1763 srcType = DevFmtFloat;
1764 else
1766 ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
1767 return E_FAIL;
1770 else
1772 ERR("Unhandled format sub-type\n");
1773 return E_FAIL;
1776 if(device->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
1778 self->ChannelConv = CreateChannelConverter(srcType, DevFmtStereo,
1779 device->FmtChans);
1780 if(!self->ChannelConv)
1782 ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
1783 return E_FAIL;
1785 TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
1786 /* The channel converter always outputs float, so change the input type
1787 * for the resampler/type-converter.
1789 srcType = DevFmtFloat;
1791 else if(device->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
1793 self->ChannelConv = CreateChannelConverter(srcType, DevFmtMono,
1794 device->FmtChans);
1795 if(!self->ChannelConv)
1797 ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
1798 return E_FAIL;
1800 TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
1801 srcType = DevFmtFloat;
1804 if(device->Frequency != OutputType.Format.nSamplesPerSec || device->FmtType != srcType)
1806 self->SampleConv = CreateSampleConverter(
1807 srcType, device->FmtType, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder),
1808 OutputType.Format.nSamplesPerSec, device->Frequency
1810 if(!self->SampleConv)
1812 ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1813 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1814 device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
1815 return E_FAIL;
1817 TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1818 DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
1819 device->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
1822 hr = IAudioClient_Initialize(self->client,
1823 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1824 buf_time, 0, &OutputType.Format, NULL
1826 if(FAILED(hr))
1828 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1829 return hr;
1832 hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
1833 if(FAILED(hr))
1835 ERR("Failed to get buffer size: 0x%08lx\n", hr);
1836 return hr;
1839 buffer_len = maxu(device->UpdateSize*device->NumUpdates + 1, buffer_len);
1840 ll_ringbuffer_free(self->Ring);
1841 self->Ring = ll_ringbuffer_create(buffer_len,
1842 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
1844 if(!self->Ring)
1846 ERR("Failed to allocate capture ring buffer\n");
1847 return E_OUTOFMEMORY;
1850 hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
1851 if(FAILED(hr))
1853 ERR("Failed to set event handle: 0x%08lx\n", hr);
1854 return hr;
1857 return hr;
1861 static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
1863 ThreadRequest req = { self->MsgEvent, 0 };
1864 HRESULT hr = E_FAIL;
1866 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1867 hr = WaitForResponse(&req);
1869 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
1872 static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
1874 HRESULT hr;
1875 void *ptr;
1877 ResetEvent(self->NotifyEvent);
1878 hr = IAudioClient_Start(self->client);
1879 if(FAILED(hr))
1881 ERR("Failed to start audio client: 0x%08lx\n", hr);
1882 return hr;
1885 hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
1886 if(SUCCEEDED(hr))
1888 self->capture = ptr;
1889 ATOMIC_STORE(&self->killNow, 0, almemory_order_release);
1890 if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
1892 ERR("Failed to start thread\n");
1893 IAudioCaptureClient_Release(self->capture);
1894 self->capture = NULL;
1895 hr = E_FAIL;
1899 if(FAILED(hr))
1901 IAudioClient_Stop(self->client);
1902 IAudioClient_Reset(self->client);
1905 return hr;
1909 static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
1911 ThreadRequest req = { self->MsgEvent, 0 };
1912 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
1913 (void)WaitForResponse(&req);
1916 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
1918 int res;
1920 if(!self->capture)
1921 return;
1923 ATOMIC_STORE_SEQ(&self->killNow, 1);
1924 althrd_join(self->thread, &res);
1926 IAudioCaptureClient_Release(self->capture);
1927 self->capture = NULL;
1928 IAudioClient_Stop(self->client);
1929 IAudioClient_Reset(self->client);
1933 ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
1935 return (ALuint)ll_ringbuffer_read_space(self->Ring);
1938 ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
1940 if(ALCmmdevCapture_availableSamples(self) < samples)
1941 return ALC_INVALID_VALUE;
1942 ll_ringbuffer_read(self->Ring, buffer, samples);
1943 return ALC_NO_ERROR;
1947 static inline void AppendAllDevicesList2(const DevMap *entry)
1948 { AppendAllDevicesList(alstr_get_cstr(entry->name)); }
1949 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1950 { AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
1952 typedef struct ALCmmdevBackendFactory {
1953 DERIVE_FROM_TYPE(ALCbackendFactory);
1954 } ALCmmdevBackendFactory;
1955 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1957 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
1958 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
1959 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
1960 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
1961 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
1963 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
1966 static BOOL MMDevApiLoad(void)
1968 static HRESULT InitResult;
1969 if(!ThreadHdl)
1971 ThreadRequest req;
1972 InitResult = E_FAIL;
1974 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
1975 if(req.FinishedEvt == NULL)
1976 ERR("Failed to create event: %lu\n", GetLastError());
1977 else
1979 ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
1980 if(ThreadHdl != NULL)
1981 InitResult = WaitForResponse(&req);
1982 CloseHandle(req.FinishedEvt);
1985 return SUCCEEDED(InitResult);
1988 static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
1990 VECTOR_INIT(PlaybackDevices);
1991 VECTOR_INIT(CaptureDevices);
1993 if(!MMDevApiLoad())
1994 return ALC_FALSE;
1995 return ALC_TRUE;
1998 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
2000 clear_devlist(&PlaybackDevices);
2001 VECTOR_DEINIT(PlaybackDevices);
2003 clear_devlist(&CaptureDevices);
2004 VECTOR_DEINIT(CaptureDevices);
2006 if(ThreadHdl)
2008 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
2009 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
2010 CloseHandle(ThreadHdl);
2011 ThreadHdl = NULL;
2015 static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
2017 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
2018 * rechanneling or resampling; if the device is configured for 48000hz
2019 * stereo input, for example, and the app asks for 22050hz mono,
2020 * initialization will fail.
2022 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
2023 return ALC_TRUE;
2024 return ALC_FALSE;
2027 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
2029 ThreadRequest req = { NULL, 0 };
2031 req.FinishedEvt = CreateEventW(NULL, FALSE, FALSE, NULL);
2032 if(req.FinishedEvt == NULL)
2033 ERR("Failed to create event: %lu\n", GetLastError());
2034 else
2036 HRESULT hr = E_FAIL;
2037 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
2038 hr = WaitForResponse(&req);
2039 if(SUCCEEDED(hr)) switch(type)
2041 case ALL_DEVICE_PROBE:
2042 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
2043 break;
2045 case CAPTURE_DEVICE_PROBE:
2046 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
2047 break;
2049 CloseHandle(req.FinishedEvt);
2050 req.FinishedEvt = NULL;
2054 static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
2056 if(type == ALCbackend_Playback)
2058 ALCmmdevPlayback *backend;
2059 NEW_OBJ(backend, ALCmmdevPlayback)(device);
2060 if(!backend) return NULL;
2061 return STATIC_CAST(ALCbackend, backend);
2063 if(type == ALCbackend_Capture)
2065 ALCmmdevCapture *backend;
2066 NEW_OBJ(backend, ALCmmdevCapture)(device);
2067 if(!backend) return NULL;
2068 return STATIC_CAST(ALCbackend, backend);
2071 return NULL;
2075 ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
2077 static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
2078 return STATIC_CAST(ALCbackendFactory, &factory);