]> git.tdb.fi Git - ext/openal.git/blob - alc/backends/wasapi.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / backends / wasapi.cpp
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.
8  *
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.
13  *
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
19  */
20
21 #include "config.h"
22
23 #include "wasapi.h"
24
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <memory.h>
31
32 #include <wtypes.h>
33 #include <mmdeviceapi.h>
34 #include <audioclient.h>
35 #include <cguid.h>
36 #include <devpropdef.h>
37 #include <mmreg.h>
38 #include <propsys.h>
39 #include <propkey.h>
40 #include <devpkey.h>
41 #ifndef _WAVEFORMATEXTENSIBLE_
42 #include <ks.h>
43 #include <ksmedia.h>
44 #endif
45
46 #include <algorithm>
47 #include <atomic>
48 #include <chrono>
49 #include <condition_variable>
50 #include <cstring>
51 #include <deque>
52 #include <functional>
53 #include <future>
54 #include <mutex>
55 #include <string>
56 #include <thread>
57 #include <vector>
58
59 #include "albit.h"
60 #include "alc/alconfig.h"
61 #include "alnumeric.h"
62 #include "comptr.h"
63 #include "core/converter.h"
64 #include "core/device.h"
65 #include "core/helpers.h"
66 #include "core/logging.h"
67 #include "ringbuffer.h"
68 #include "strutils.h"
69 #include "threads.h"
70
71
72 /* Some headers seem to define these as macros for __uuidof, which is annoying
73  * since some headers don't declare them at all. Hopefully the ifdef is enough
74  * to tell if they need to be declared.
75  */
76 #ifndef KSDATAFORMAT_SUBTYPE_PCM
77 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
78 #endif
79 #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
80 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
81 #endif
82
83 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
84 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
85 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
86
87
88 namespace {
89
90 using std::chrono::nanoseconds;
91 using std::chrono::milliseconds;
92 using std::chrono::seconds;
93
94 using ReferenceTime = std::chrono::duration<REFERENCE_TIME,std::ratio<1,10000000>>;
95
96 inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept
97 { return ReferenceTime{static_cast<REFERENCE_TIME>(n)}; }
98
99
100 #define MONO SPEAKER_FRONT_CENTER
101 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
102 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
103 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
104 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
105 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
106 #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)
107 #define X7DOT1DOT4 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT|SPEAKER_TOP_FRONT_LEFT|SPEAKER_TOP_FRONT_RIGHT|SPEAKER_TOP_BACK_LEFT|SPEAKER_TOP_BACK_RIGHT)
108
109 constexpr inline DWORD MaskFromTopBits(DWORD b) noexcept
110 {
111     b |= b>>1;
112     b |= b>>2;
113     b |= b>>4;
114     b |= b>>8;
115     b |= b>>16;
116     return b;
117 }
118 constexpr DWORD MonoMask{MaskFromTopBits(MONO)};
119 constexpr DWORD StereoMask{MaskFromTopBits(STEREO)};
120 constexpr DWORD QuadMask{MaskFromTopBits(QUAD)};
121 constexpr DWORD X51Mask{MaskFromTopBits(X5DOT1)};
122 constexpr DWORD X51RearMask{MaskFromTopBits(X5DOT1REAR)};
123 constexpr DWORD X61Mask{MaskFromTopBits(X6DOT1)};
124 constexpr DWORD X71Mask{MaskFromTopBits(X7DOT1)};
125 constexpr DWORD X714Mask{MaskFromTopBits(X7DOT1DOT4)};
126
127 constexpr char DevNameHead[] = "OpenAL Soft on ";
128 constexpr size_t DevNameHeadLen{al::size(DevNameHead) - 1};
129
130
131 /* Scales the given reftime value, rounding the result. */
132 inline uint RefTime2Samples(const ReferenceTime &val, uint srate)
133 {
134     const auto retval = (val*srate + ReferenceTime{seconds{1}}/2) / seconds{1};
135     return static_cast<uint>(mini64(retval, std::numeric_limits<uint>::max()));
136 }
137
138
139 class GuidPrinter {
140     char mMsg[64];
141
142 public:
143     GuidPrinter(const GUID &guid)
144     {
145         std::snprintf(mMsg, al::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
146             DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
147             guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
148     }
149     const char *c_str() const { return mMsg; }
150 };
151
152 struct PropVariant {
153     PROPVARIANT mProp;
154
155 public:
156     PropVariant() { PropVariantInit(&mProp); }
157     ~PropVariant() { clear(); }
158
159     void clear() { PropVariantClear(&mProp); }
160
161     PROPVARIANT* get() noexcept { return &mProp; }
162
163     PROPVARIANT& operator*() noexcept { return mProp; }
164     const PROPVARIANT& operator*() const noexcept { return mProp; }
165
166     PROPVARIANT* operator->() noexcept { return &mProp; }
167     const PROPVARIANT* operator->() const noexcept { return &mProp; }
168 };
169
170 struct DevMap {
171     std::string name;
172     std::string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
173     std::wstring devid;
174
175     template<typename T0, typename T1, typename T2>
176     DevMap(T0&& name_, T1&& guid_, T2&& devid_)
177       : name{std::forward<T0>(name_)}
178       , endpoint_guid{std::forward<T1>(guid_)}
179       , devid{std::forward<T2>(devid_)}
180     { }
181 };
182
183 bool checkName(const al::vector<DevMap> &list, const std::string &name)
184 {
185     auto match_name = [&name](const DevMap &entry) -> bool
186     { return entry.name == name; };
187     return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
188 }
189
190 al::vector<DevMap> PlaybackDevices;
191 al::vector<DevMap> CaptureDevices;
192
193
194 using NameGUIDPair = std::pair<std::string,std::string>;
195 NameGUIDPair get_device_name_and_guid(IMMDevice *device)
196 {
197     static constexpr char UnknownName[]{"Unknown Device Name"};
198     static constexpr char UnknownGuid[]{"Unknown Device GUID"};
199     std::string name, guid;
200
201     ComPtr<IPropertyStore> ps;
202     HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr());
203     if(FAILED(hr))
204     {
205         WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
206         return std::make_pair(UnknownName, UnknownGuid);
207     }
208
209     PropVariant pvprop;
210     hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get());
211     if(FAILED(hr))
212     {
213         WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
214         name += UnknownName;
215     }
216     else if(pvprop->vt == VT_LPWSTR)
217         name += wstr_to_utf8(pvprop->pwszVal);
218     else
219     {
220         WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
221         name += UnknownName;
222     }
223
224     pvprop.clear();
225     hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get());
226     if(FAILED(hr))
227     {
228         WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
229         guid = UnknownGuid;
230     }
231     else if(pvprop->vt == VT_LPWSTR)
232         guid = wstr_to_utf8(pvprop->pwszVal);
233     else
234     {
235         WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
236         guid = UnknownGuid;
237     }
238
239     return std::make_pair(std::move(name), std::move(guid));
240 }
241
242 EndpointFormFactor get_device_formfactor(IMMDevice *device)
243 {
244     ComPtr<IPropertyStore> ps;
245     HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())};
246     if(FAILED(hr))
247     {
248         WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
249         return UnknownFormFactor;
250     }
251
252     EndpointFormFactor formfactor{UnknownFormFactor};
253     PropVariant pvform;
254     hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get());
255     if(FAILED(hr))
256         WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
257     else if(pvform->vt == VT_UI4)
258         formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
259     else if(pvform->vt != VT_EMPTY)
260         WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
261     return formfactor;
262 }
263
264
265 void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list)
266 {
267     for(auto &entry : list)
268     {
269         if(entry.devid == devid)
270             return;
271     }
272
273     auto name_guid = get_device_name_and_guid(device);
274
275     int count{1};
276     std::string newname{name_guid.first};
277     while(checkName(list, newname))
278     {
279         newname = name_guid.first;
280         newname += " #";
281         newname += std::to_string(++count);
282     }
283     list.emplace_back(std::move(newname), std::move(name_guid.second), devid);
284     const DevMap &newentry = list.back();
285
286     TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
287           newentry.endpoint_guid.c_str(), newentry.devid.c_str());
288 }
289
290 WCHAR *get_device_id(IMMDevice *device)
291 {
292     WCHAR *devid;
293
294     const HRESULT hr{device->GetId(&devid)};
295     if(FAILED(hr))
296     {
297         ERR("Failed to get device id: %lx\n", hr);
298         return nullptr;
299     }
300
301     return devid;
302 }
303
304 void probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list)
305 {
306     al::vector<DevMap>{}.swap(list);
307
308     ComPtr<IMMDeviceCollection> coll;
309     HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, coll.getPtr())};
310     if(FAILED(hr))
311     {
312         ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
313         return;
314     }
315
316     UINT count{0};
317     hr = coll->GetCount(&count);
318     if(SUCCEEDED(hr) && count > 0)
319         list.reserve(count);
320
321     ComPtr<IMMDevice> device;
322     hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, device.getPtr());
323     if(SUCCEEDED(hr))
324     {
325         if(WCHAR *devid{get_device_id(device.get())})
326         {
327             add_device(device.get(), devid, list);
328             CoTaskMemFree(devid);
329         }
330         device = nullptr;
331     }
332
333     for(UINT i{0};i < count;++i)
334     {
335         hr = coll->Item(i, device.getPtr());
336         if(FAILED(hr)) continue;
337
338         if(WCHAR *devid{get_device_id(device.get())})
339         {
340             add_device(device.get(), devid, list);
341             CoTaskMemFree(devid);
342         }
343         device = nullptr;
344     }
345 }
346
347
348 bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
349 {
350     *out = WAVEFORMATEXTENSIBLE{};
351     if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
352     {
353         *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format);
354         out->Format.cbSize = sizeof(*out) - sizeof(out->Format);
355     }
356     else if(in->wFormatTag == WAVE_FORMAT_PCM)
357     {
358         out->Format = *in;
359         out->Format.cbSize = 0;
360         out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
361         if(out->Format.nChannels == 1)
362             out->dwChannelMask = MONO;
363         else if(out->Format.nChannels == 2)
364             out->dwChannelMask = STEREO;
365         else
366             ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
367         out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
368     }
369     else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
370     {
371         out->Format = *in;
372         out->Format.cbSize = 0;
373         out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
374         if(out->Format.nChannels == 1)
375             out->dwChannelMask = MONO;
376         else if(out->Format.nChannels == 2)
377             out->dwChannelMask = STEREO;
378         else
379             ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
380         out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
381     }
382     else
383     {
384         ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
385         return false;
386     }
387     return true;
388 }
389
390 void TraceFormat(const char *msg, const WAVEFORMATEX *format)
391 {
392     constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)};
393     if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size)
394     {
395         const WAVEFORMATEXTENSIBLE *fmtex{
396             CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)};
397         TRACE("%s:\n"
398             "    FormatTag      = 0x%04x\n"
399             "    Channels       = %d\n"
400             "    SamplesPerSec  = %lu\n"
401             "    AvgBytesPerSec = %lu\n"
402             "    BlockAlign     = %d\n"
403             "    BitsPerSample  = %d\n"
404             "    Size           = %d\n"
405             "    Samples        = %d\n"
406             "    ChannelMask    = 0x%lx\n"
407             "    SubFormat      = %s\n",
408             msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec,
409             fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample,
410             fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask,
411             GuidPrinter{fmtex->SubFormat}.c_str());
412     }
413     else
414         TRACE("%s:\n"
415             "    FormatTag      = 0x%04x\n"
416             "    Channels       = %d\n"
417             "    SamplesPerSec  = %lu\n"
418             "    AvgBytesPerSec = %lu\n"
419             "    BlockAlign     = %d\n"
420             "    BitsPerSample  = %d\n"
421             "    Size           = %d\n",
422             msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec,
423             format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize);
424 }
425
426
427 enum class MsgType {
428     OpenDevice,
429     ResetDevice,
430     StartDevice,
431     StopDevice,
432     CloseDevice,
433     EnumeratePlayback,
434     EnumerateCapture,
435
436     Count,
437     QuitThread = Count
438 };
439
440 constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
441     "Open Device",
442     "Reset Device",
443     "Start Device",
444     "Stop Device",
445     "Close Device",
446     "Enumerate Playback",
447     "Enumerate Capture"
448 };
449
450
451 /* Proxy interface used by the message handler. */
452 struct WasapiProxy {
453     virtual ~WasapiProxy() = default;
454
455     virtual HRESULT openProxy(const char *name) = 0;
456     virtual void closeProxy() = 0;
457
458     virtual HRESULT resetProxy() = 0;
459     virtual HRESULT startProxy() = 0;
460     virtual void  stopProxy() = 0;
461
462     struct Msg {
463         MsgType mType;
464         WasapiProxy *mProxy;
465         const char *mParam;
466         std::promise<HRESULT> mPromise;
467
468         explicit operator bool() const noexcept { return mType != MsgType::QuitThread; }
469     };
470     static std::thread sThread;
471     static std::deque<Msg> mMsgQueue;
472     static std::mutex mMsgQueueLock;
473     static std::condition_variable mMsgQueueCond;
474     static std::mutex sThreadLock;
475     static size_t sInitCount;
476
477     std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr)
478     {
479         std::promise<HRESULT> promise;
480         std::future<HRESULT> future{promise.get_future()};
481         {
482             std::lock_guard<std::mutex> _{mMsgQueueLock};
483             mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)});
484         }
485         mMsgQueueCond.notify_one();
486         return future;
487     }
488
489     static std::future<HRESULT> pushMessageStatic(MsgType type)
490     {
491         std::promise<HRESULT> promise;
492         std::future<HRESULT> future{promise.get_future()};
493         {
494             std::lock_guard<std::mutex> _{mMsgQueueLock};
495             mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)});
496         }
497         mMsgQueueCond.notify_one();
498         return future;
499     }
500
501     static Msg popMessage()
502     {
503         std::unique_lock<std::mutex> lock{mMsgQueueLock};
504         mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();});
505         Msg msg{std::move(mMsgQueue.front())};
506         mMsgQueue.pop_front();
507         return msg;
508     }
509
510     static int messageHandler(std::promise<HRESULT> *promise);
511
512     static HRESULT InitThread()
513     {
514         std::lock_guard<std::mutex> _{sThreadLock};
515         HRESULT res{S_OK};
516         if(!sThread.joinable())
517         {
518             std::promise<HRESULT> promise;
519             auto future = promise.get_future();
520
521             sThread = std::thread{&WasapiProxy::messageHandler, &promise};
522             res = future.get();
523             if(FAILED(res))
524             {
525                 sThread.join();
526                 return res;
527             }
528         }
529         ++sInitCount;
530         return res;
531     }
532
533     static void DeinitThread()
534     {
535         std::lock_guard<std::mutex> _{sThreadLock};
536         if(!--sInitCount && sThread.joinable())
537         {
538             pushMessageStatic(MsgType::QuitThread);
539             sThread.join();
540         }
541     }
542 };
543 std::thread WasapiProxy::sThread;
544 std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue;
545 std::mutex WasapiProxy::mMsgQueueLock;
546 std::condition_variable WasapiProxy::mMsgQueueCond;
547 std::mutex WasapiProxy::sThreadLock;
548 size_t WasapiProxy::sInitCount{0};
549
550 int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
551 {
552     TRACE("Starting message thread\n");
553
554     HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
555     if(FAILED(hr))
556     {
557         WARN("Failed to initialize COM: 0x%08lx\n", hr);
558         promise->set_value(hr);
559         return 0;
560     }
561     promise->set_value(S_OK);
562     promise = nullptr;
563
564     TRACE("Starting message loop\n");
565     while(Msg msg{popMessage()})
566     {
567         TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n",
568             MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType),
569             static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam));
570
571         switch(msg.mType)
572         {
573         case MsgType::OpenDevice:
574             hr = msg.mProxy->openProxy(msg.mParam);
575             msg.mPromise.set_value(hr);
576             continue;
577
578         case MsgType::ResetDevice:
579             hr = msg.mProxy->resetProxy();
580             msg.mPromise.set_value(hr);
581             continue;
582
583         case MsgType::StartDevice:
584             hr = msg.mProxy->startProxy();
585             msg.mPromise.set_value(hr);
586             continue;
587
588         case MsgType::StopDevice:
589             msg.mProxy->stopProxy();
590             msg.mPromise.set_value(S_OK);
591             continue;
592
593         case MsgType::CloseDevice:
594             msg.mProxy->closeProxy();
595             msg.mPromise.set_value(S_OK);
596             continue;
597
598         case MsgType::EnumeratePlayback:
599         case MsgType::EnumerateCapture:
600             {
601                 void *ptr{};
602                 hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
603                     IID_IMMDeviceEnumerator, &ptr);
604                 if(FAILED(hr))
605                     msg.mPromise.set_value(hr);
606                 else
607                 {
608                     ComPtr<IMMDeviceEnumerator> devenum{static_cast<IMMDeviceEnumerator*>(ptr)};
609
610                     if(msg.mType == MsgType::EnumeratePlayback)
611                         probe_devices(devenum.get(), eRender, PlaybackDevices);
612                     else if(msg.mType == MsgType::EnumerateCapture)
613                         probe_devices(devenum.get(), eCapture, CaptureDevices);
614                     msg.mPromise.set_value(S_OK);
615                 }
616                 continue;
617             }
618
619         case MsgType::QuitThread:
620             break;
621         }
622         ERR("Unexpected message: %u\n", static_cast<uint>(msg.mType));
623         msg.mPromise.set_value(E_FAIL);
624     }
625     TRACE("Message loop finished\n");
626     CoUninitialize();
627
628     return 0;
629 }
630
631
632 struct WasapiPlayback final : public BackendBase, WasapiProxy {
633     WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
634     ~WasapiPlayback() override;
635
636     int mixerProc();
637
638     void open(const char *name) override;
639     HRESULT openProxy(const char *name) override;
640     void closeProxy() override;
641
642     bool reset() override;
643     HRESULT resetProxy() override;
644     void start() override;
645     HRESULT startProxy() override;
646     void stop() override;
647     void stopProxy() override;
648
649     ClockLatency getClockLatency() override;
650
651     HRESULT mOpenStatus{E_FAIL};
652     ComPtr<IMMDevice> mMMDev{nullptr};
653     ComPtr<IAudioClient> mClient{nullptr};
654     ComPtr<IAudioRenderClient> mRender{nullptr};
655     HANDLE mNotifyEvent{nullptr};
656
657     UINT32 mOrigBufferSize{}, mOrigUpdateSize{};
658     std::unique_ptr<char[]> mResampleBuffer{};
659     uint mBufferFilled{0};
660     SampleConverterPtr mResampler;
661
662     WAVEFORMATEXTENSIBLE mFormat{};
663     std::atomic<UINT32> mPadding{0u};
664
665     std::mutex mMutex;
666
667     std::atomic<bool> mKillNow{true};
668     std::thread mThread;
669
670     DEF_NEWDEL(WasapiPlayback)
671 };
672
673 WasapiPlayback::~WasapiPlayback()
674 {
675     if(SUCCEEDED(mOpenStatus))
676     {
677         pushMessage(MsgType::CloseDevice).wait();
678         DeinitThread();
679     }
680     mOpenStatus = E_FAIL;
681
682     if(mNotifyEvent != nullptr)
683         CloseHandle(mNotifyEvent);
684     mNotifyEvent = nullptr;
685 }
686
687
688 FORCE_ALIGN int WasapiPlayback::mixerProc()
689 {
690     HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
691     if(FAILED(hr))
692     {
693         ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
694         mDevice->handleDisconnect("COM init failed: 0x%08lx", hr);
695         return 1;
696     }
697
698     SetRTPriority();
699     althrd_setname(MIXER_THREAD_NAME);
700
701     const uint frame_size{mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8u};
702     const uint update_size{mOrigUpdateSize};
703     const UINT32 buffer_len{mOrigBufferSize};
704     while(!mKillNow.load(std::memory_order_relaxed))
705     {
706         UINT32 written;
707         hr = mClient->GetCurrentPadding(&written);
708         if(FAILED(hr))
709         {
710             ERR("Failed to get padding: 0x%08lx\n", hr);
711             mDevice->handleDisconnect("Failed to retrieve buffer padding: 0x%08lx", hr);
712             break;
713         }
714         mPadding.store(written, std::memory_order_relaxed);
715
716         uint len{buffer_len - written};
717         if(len < update_size)
718         {
719             DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
720             if(res != WAIT_OBJECT_0)
721                 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
722             continue;
723         }
724
725         BYTE *buffer;
726         hr = mRender->GetBuffer(len, &buffer);
727         if(SUCCEEDED(hr))
728         {
729             if(mResampler)
730             {
731                 std::lock_guard<std::mutex> _{mMutex};
732                 for(UINT32 done{0};done < len;)
733                 {
734                     if(mBufferFilled == 0)
735                     {
736                         mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize,
737                             mFormat.Format.nChannels);
738                         mBufferFilled = mDevice->UpdateSize;
739                     }
740
741                     const void *src{mResampleBuffer.get()};
742                     uint srclen{mBufferFilled};
743                     uint got{mResampler->convert(&src, &srclen, buffer, len-done)};
744                     buffer += got*frame_size;
745                     done += got;
746
747                     mPadding.store(written + done, std::memory_order_relaxed);
748                     if(srclen)
749                     {
750                         const char *bsrc{static_cast<const char*>(src)};
751                         std::copy(bsrc, bsrc + srclen*frame_size, mResampleBuffer.get());
752                     }
753                     mBufferFilled = srclen;
754                 }
755             }
756             else
757             {
758                 std::lock_guard<std::mutex> _{mMutex};
759                 mDevice->renderSamples(buffer, len, mFormat.Format.nChannels);
760                 mPadding.store(written + len, std::memory_order_relaxed);
761             }
762             hr = mRender->ReleaseBuffer(len, 0);
763         }
764         if(FAILED(hr))
765         {
766             ERR("Failed to buffer data: 0x%08lx\n", hr);
767             mDevice->handleDisconnect("Failed to send playback samples: 0x%08lx", hr);
768             break;
769         }
770     }
771     mPadding.store(0u, std::memory_order_release);
772
773     CoUninitialize();
774     return 0;
775 }
776
777
778 void WasapiPlayback::open(const char *name)
779 {
780     if(SUCCEEDED(mOpenStatus))
781         throw al::backend_exception{al::backend_error::DeviceError,
782             "Unexpected duplicate open call"};
783
784     mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
785     if(mNotifyEvent == nullptr)
786     {
787         ERR("Failed to create notify events: %lu\n", GetLastError());
788         throw al::backend_exception{al::backend_error::DeviceError,
789             "Failed to create notify events"};
790     }
791
792     HRESULT hr{InitThread()};
793     if(FAILED(hr))
794     {
795         throw al::backend_exception{al::backend_error::DeviceError,
796             "Failed to init COM thread: 0x%08lx", hr};
797     }
798
799     if(name)
800     {
801         if(PlaybackDevices.empty())
802             pushMessage(MsgType::EnumeratePlayback);
803         if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0)
804         {
805             name += DevNameHeadLen;
806             if(*name == '\0')
807                 name = nullptr;
808         }
809     }
810
811     mOpenStatus = pushMessage(MsgType::OpenDevice, name).get();
812     if(FAILED(mOpenStatus))
813     {
814         DeinitThread();
815         throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
816             mOpenStatus};
817     }
818 }
819
820 HRESULT WasapiPlayback::openProxy(const char *name)
821 {
822     const wchar_t *devid{nullptr};
823     if(name)
824     {
825         auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
826             [name](const DevMap &entry) -> bool
827             { return entry.name == name || entry.endpoint_guid == name; });
828         if(iter == PlaybackDevices.cend())
829         {
830             const std::wstring wname{utf8_to_wstr(name)};
831             iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
832                 [&wname](const DevMap &entry) -> bool
833                 { return entry.devid == wname; });
834         }
835         if(iter == PlaybackDevices.cend())
836         {
837             WARN("Failed to find device name matching \"%s\"\n", name);
838             return E_FAIL;
839         }
840         name = iter->name.c_str();
841         devid = iter->devid.c_str();
842     }
843
844     void *ptr;
845     ComPtr<IMMDevice> mmdev;
846     HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
847         IID_IMMDeviceEnumerator, &ptr)};
848     if(SUCCEEDED(hr))
849     {
850         ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
851         if(!devid)
852             hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr());
853         else
854             hr = enumerator->GetDevice(devid, mmdev.getPtr());
855     }
856     if(FAILED(hr))
857     {
858         WARN("Failed to open device \"%s\"\n", name?name:"(default)");
859         return hr;
860     }
861
862     mClient = nullptr;
863     mMMDev = std::move(mmdev);
864     if(name) mDevice->DeviceName = std::string{DevNameHead} + name;
865     else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first;
866
867     return hr;
868 }
869
870 void WasapiPlayback::closeProxy()
871 {
872     mClient = nullptr;
873     mMMDev = nullptr;
874 }
875
876
877 bool WasapiPlayback::reset()
878 {
879     HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
880     if(FAILED(hr))
881         throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr};
882     return true;
883 }
884
885 HRESULT WasapiPlayback::resetProxy()
886 {
887     mClient = nullptr;
888
889     void *ptr;
890     HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
891     if(FAILED(hr))
892     {
893         ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
894         return hr;
895     }
896     mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
897
898     WAVEFORMATEX *wfx;
899     hr = mClient->GetMixFormat(&wfx);
900     if(FAILED(hr))
901     {
902         ERR("Failed to get mix format: 0x%08lx\n", hr);
903         return hr;
904     }
905     TraceFormat("Device mix format", wfx);
906
907     WAVEFORMATEXTENSIBLE OutputType;
908     if(!MakeExtensible(&OutputType, wfx))
909     {
910         CoTaskMemFree(wfx);
911         return E_FAIL;
912     }
913     CoTaskMemFree(wfx);
914     wfx = nullptr;
915
916     const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency};
917     const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency};
918     bool isRear51{false};
919
920     if(!mDevice->Flags.test(FrequencyRequest))
921         mDevice->Frequency = OutputType.Format.nSamplesPerSec;
922     if(!mDevice->Flags.test(ChannelsRequest))
923     {
924         /* If not requesting a channel configuration, auto-select given what
925          * fits the mask's lsb (to ensure no gaps in the output channels). If
926          * there's no mask, we can only assume mono or stereo.
927          */
928         const uint32_t chancount{OutputType.Format.nChannels};
929         const DWORD chanmask{OutputType.dwChannelMask};
930         if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4)
931             mDevice->FmtChans = DevFmtX71;
932         else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1)
933             mDevice->FmtChans = DevFmtX71;
934         else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1)
935             mDevice->FmtChans = DevFmtX61;
936         else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1)
937             mDevice->FmtChans = DevFmtX51;
938         else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR)
939         {
940             mDevice->FmtChans = DevFmtX51;
941             isRear51 = true;
942         }
943         else if(chancount >= 4 && (chanmask&QuadMask) == QUAD)
944             mDevice->FmtChans = DevFmtQuad;
945         else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask))
946             mDevice->FmtChans = DevFmtStereo;
947         else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))
948             mDevice->FmtChans = DevFmtMono;
949         else
950             ERR("Unhandled channel config: %d -- 0x%08lx\n", chancount, chanmask);
951     }
952     else
953     {
954         const uint32_t chancount{OutputType.Format.nChannels};
955         const DWORD chanmask{OutputType.dwChannelMask};
956         isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR);
957     }
958
959     OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
960     switch(mDevice->FmtChans)
961     {
962     case DevFmtMono:
963         OutputType.Format.nChannels = 1;
964         OutputType.dwChannelMask = MONO;
965         break;
966     case DevFmtAmbi3D:
967         mDevice->FmtChans = DevFmtStereo;
968         /*fall-through*/
969     case DevFmtStereo:
970         OutputType.Format.nChannels = 2;
971         OutputType.dwChannelMask = STEREO;
972         break;
973     case DevFmtQuad:
974         OutputType.Format.nChannels = 4;
975         OutputType.dwChannelMask = QUAD;
976         break;
977     case DevFmtX51:
978         OutputType.Format.nChannels = 6;
979         OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1;
980         break;
981     case DevFmtX61:
982         OutputType.Format.nChannels = 7;
983         OutputType.dwChannelMask = X6DOT1;
984         break;
985     case DevFmtX71:
986     case DevFmtX3D71:
987         OutputType.Format.nChannels = 8;
988         OutputType.dwChannelMask = X7DOT1;
989         break;
990     case DevFmtX714:
991         OutputType.Format.nChannels = 12;
992         OutputType.dwChannelMask = X7DOT1DOT4;
993         break;
994     }
995     switch(mDevice->FmtType)
996     {
997     case DevFmtByte:
998         mDevice->FmtType = DevFmtUByte;
999         /* fall-through */
1000     case DevFmtUByte:
1001         OutputType.Format.wBitsPerSample = 8;
1002         OutputType.Samples.wValidBitsPerSample = 8;
1003         OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1004         break;
1005     case DevFmtUShort:
1006         mDevice->FmtType = DevFmtShort;
1007         /* fall-through */
1008     case DevFmtShort:
1009         OutputType.Format.wBitsPerSample = 16;
1010         OutputType.Samples.wValidBitsPerSample = 16;
1011         OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1012         break;
1013     case DevFmtUInt:
1014         mDevice->FmtType = DevFmtInt;
1015         /* fall-through */
1016     case DevFmtInt:
1017         OutputType.Format.wBitsPerSample = 32;
1018         OutputType.Samples.wValidBitsPerSample = 32;
1019         OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1020         break;
1021     case DevFmtFloat:
1022         OutputType.Format.wBitsPerSample = 32;
1023         OutputType.Samples.wValidBitsPerSample = 32;
1024         OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1025         break;
1026     }
1027     OutputType.Format.nSamplesPerSec = mDevice->Frequency;
1028
1029     OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
1030         OutputType.Format.wBitsPerSample / 8);
1031     OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
1032         OutputType.Format.nBlockAlign;
1033
1034     TraceFormat("Requesting playback format", &OutputType.Format);
1035     hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
1036     if(FAILED(hr))
1037     {
1038         WARN("Failed to check format support: 0x%08lx\n", hr);
1039         hr = mClient->GetMixFormat(&wfx);
1040     }
1041     if(FAILED(hr))
1042     {
1043         ERR("Failed to find a supported format: 0x%08lx\n", hr);
1044         return hr;
1045     }
1046
1047     if(wfx != nullptr)
1048     {
1049         TraceFormat("Got playback format", wfx);
1050         if(!MakeExtensible(&OutputType, wfx))
1051         {
1052             CoTaskMemFree(wfx);
1053             return E_FAIL;
1054         }
1055         CoTaskMemFree(wfx);
1056         wfx = nullptr;
1057
1058         if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true))
1059             mDevice->Frequency = OutputType.Format.nSamplesPerSec;
1060         else
1061             mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec);
1062
1063         const uint32_t chancount{OutputType.Format.nChannels};
1064         const DWORD chanmask{OutputType.dwChannelMask};
1065         /* Don't update the channel format if the requested format fits what's
1066          * supported.
1067          */
1068         bool chansok{false};
1069         if(mDevice->Flags.test(ChannelsRequest))
1070         {
1071             /* When requesting a channel configuration, make sure it fits the
1072              * mask's lsb (to ensure no gaps in the output channels). If
1073              * there's no mask, assume the request fits with enough channels.
1074              */
1075             switch(mDevice->FmtChans)
1076             {
1077             case DevFmtMono:
1078                 chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask));
1079                 break;
1080             case DevFmtStereo:
1081                 chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask));
1082                 break;
1083             case DevFmtQuad:
1084                 chansok = (chancount >= 4 && ((chanmask&QuadMask) == QUAD || !chanmask));
1085                 break;
1086             case DevFmtX51:
1087                 chansok = (chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
1088                         || (chanmask&X51RearMask) == X5DOT1REAR || !chanmask));
1089                 break;
1090             case DevFmtX61:
1091                 chansok = (chancount >= 7 && ((chanmask&X61Mask) == X6DOT1 || !chanmask));
1092                 break;
1093             case DevFmtX71:
1094             case DevFmtX3D71:
1095                 chansok = (chancount >= 8 && ((chanmask&X71Mask) == X7DOT1 || !chanmask));
1096                 break;
1097             case DevFmtX714:
1098                 chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask));
1099             case DevFmtAmbi3D:
1100                 break;
1101             }
1102         }
1103         if(!chansok)
1104         {
1105             if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4)
1106                 mDevice->FmtChans = DevFmtX714;
1107             else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1)
1108                 mDevice->FmtChans = DevFmtX71;
1109             else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1)
1110                 mDevice->FmtChans = DevFmtX61;
1111             else if(chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
1112                 || (chanmask&X51RearMask) == X5DOT1REAR))
1113                 mDevice->FmtChans = DevFmtX51;
1114             else if(chancount >= 4 && (chanmask&QuadMask) == QUAD)
1115                 mDevice->FmtChans = DevFmtQuad;
1116             else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask))
1117                 mDevice->FmtChans = DevFmtStereo;
1118             else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))
1119                 mDevice->FmtChans = DevFmtMono;
1120             else
1121             {
1122                 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels,
1123                     OutputType.dwChannelMask);
1124                 mDevice->FmtChans = DevFmtStereo;
1125                 OutputType.Format.nChannels = 2;
1126                 OutputType.dwChannelMask = STEREO;
1127             }
1128         }
1129
1130         if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
1131         {
1132             if(OutputType.Format.wBitsPerSample == 8)
1133                 mDevice->FmtType = DevFmtUByte;
1134             else if(OutputType.Format.wBitsPerSample == 16)
1135                 mDevice->FmtType = DevFmtShort;
1136             else if(OutputType.Format.wBitsPerSample == 32)
1137                 mDevice->FmtType = DevFmtInt;
1138             else
1139             {
1140                 mDevice->FmtType = DevFmtShort;
1141                 OutputType.Format.wBitsPerSample = 16;
1142             }
1143         }
1144         else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1145         {
1146             mDevice->FmtType = DevFmtFloat;
1147             OutputType.Format.wBitsPerSample = 32;
1148         }
1149         else
1150         {
1151             ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str());
1152             mDevice->FmtType = DevFmtShort;
1153             if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
1154                 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
1155             OutputType.Format.wBitsPerSample = 16;
1156             OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1157         }
1158         OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
1159     }
1160     mFormat = OutputType;
1161
1162     const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())};
1163     mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset));
1164
1165     setDefaultWFXChannelOrder();
1166
1167     hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1168         buf_time.count(), 0, &OutputType.Format, nullptr);
1169     if(FAILED(hr))
1170     {
1171         ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1172         return hr;
1173     }
1174
1175     UINT32 buffer_len{};
1176     ReferenceTime min_per{};
1177     hr = mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr);
1178     if(SUCCEEDED(hr))
1179         hr = mClient->GetBufferSize(&buffer_len);
1180     if(FAILED(hr))
1181     {
1182         ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
1183         return hr;
1184     }
1185
1186     /* Find the nearest multiple of the period size to the update size */
1187     if(min_per < per_time)
1188         min_per *= maxi64((per_time + min_per/2) / min_per, 1);
1189
1190     mOrigBufferSize = buffer_len;
1191     mOrigUpdateSize = minu(RefTime2Samples(min_per, mFormat.Format.nSamplesPerSec), buffer_len/2);
1192
1193     mDevice->BufferSize = static_cast<uint>(uint64_t{buffer_len} * mDevice->Frequency /
1194         mFormat.Format.nSamplesPerSec);
1195     mDevice->UpdateSize = minu(RefTime2Samples(min_per, mDevice->Frequency),
1196         mDevice->BufferSize/2);
1197
1198     mResampler = nullptr;
1199     mResampleBuffer = nullptr;
1200     mBufferFilled = 0;
1201     if(mDevice->Frequency != mFormat.Format.nSamplesPerSec)
1202     {
1203         mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
1204             mFormat.Format.nChannels, mDevice->Frequency, mFormat.Format.nSamplesPerSec,
1205             Resampler::FastBSinc24);
1206         mResampleBuffer = std::make_unique<char[]>(size_t{mDevice->UpdateSize} *
1207             mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8);
1208
1209         TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n",
1210             DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
1211             mFormat.Format.nSamplesPerSec, mOrigUpdateSize, mDevice->Frequency,
1212             mDevice->UpdateSize);
1213     }
1214
1215     hr = mClient->SetEventHandle(mNotifyEvent);
1216     if(FAILED(hr))
1217     {
1218         ERR("Failed to set event handle: 0x%08lx\n", hr);
1219         return hr;
1220     }
1221
1222     return hr;
1223 }
1224
1225
1226 void WasapiPlayback::start()
1227 {
1228     const HRESULT hr{pushMessage(MsgType::StartDevice).get()};
1229     if(FAILED(hr))
1230         throw al::backend_exception{al::backend_error::DeviceError,
1231             "Failed to start playback: 0x%lx", hr};
1232 }
1233
1234 HRESULT WasapiPlayback::startProxy()
1235 {
1236     ResetEvent(mNotifyEvent);
1237
1238     HRESULT hr{mClient->Start()};
1239     if(FAILED(hr))
1240     {
1241         ERR("Failed to start audio client: 0x%08lx\n", hr);
1242         return hr;
1243     }
1244
1245     void *ptr;
1246     hr = mClient->GetService(IID_IAudioRenderClient, &ptr);
1247     if(SUCCEEDED(hr))
1248     {
1249         mRender = ComPtr<IAudioRenderClient>{static_cast<IAudioRenderClient*>(ptr)};
1250         try {
1251             mKillNow.store(false, std::memory_order_release);
1252             mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
1253         }
1254         catch(...) {
1255             mRender = nullptr;
1256             ERR("Failed to start thread\n");
1257             hr = E_FAIL;
1258         }
1259     }
1260
1261     if(FAILED(hr))
1262         mClient->Stop();
1263
1264     return hr;
1265 }
1266
1267
1268 void WasapiPlayback::stop()
1269 { pushMessage(MsgType::StopDevice).wait(); }
1270
1271 void WasapiPlayback::stopProxy()
1272 {
1273     if(!mRender || !mThread.joinable())
1274         return;
1275
1276     mKillNow.store(true, std::memory_order_release);
1277     mThread.join();
1278
1279     mRender = nullptr;
1280     mClient->Stop();
1281 }
1282
1283
1284 ClockLatency WasapiPlayback::getClockLatency()
1285 {
1286     ClockLatency ret;
1287
1288     std::lock_guard<std::mutex> _{mMutex};
1289     ret.ClockTime = GetDeviceClockTime(mDevice);
1290     ret.Latency  = seconds{mPadding.load(std::memory_order_relaxed)};
1291     ret.Latency /= mFormat.Format.nSamplesPerSec;
1292     if(mResampler)
1293     {
1294         auto extra = mResampler->currentInputDelay();
1295         ret.Latency += std::chrono::duration_cast<nanoseconds>(extra) / mDevice->Frequency;
1296         ret.Latency += nanoseconds{seconds{mBufferFilled}} / mDevice->Frequency;
1297     }
1298
1299     return ret;
1300 }
1301
1302
1303 struct WasapiCapture final : public BackendBase, WasapiProxy {
1304     WasapiCapture(DeviceBase *device) noexcept : BackendBase{device} { }
1305     ~WasapiCapture() override;
1306
1307     int recordProc();
1308
1309     void open(const char *name) override;
1310     HRESULT openProxy(const char *name) override;
1311     void closeProxy() override;
1312
1313     HRESULT resetProxy() override;
1314     void start() override;
1315     HRESULT startProxy() override;
1316     void stop() override;
1317     void stopProxy() override;
1318
1319     void captureSamples(al::byte *buffer, uint samples) override;
1320     uint availableSamples() override;
1321
1322     HRESULT mOpenStatus{E_FAIL};
1323     ComPtr<IMMDevice> mMMDev{nullptr};
1324     ComPtr<IAudioClient> mClient{nullptr};
1325     ComPtr<IAudioCaptureClient> mCapture{nullptr};
1326     HANDLE mNotifyEvent{nullptr};
1327
1328     ChannelConverter mChannelConv{};
1329     SampleConverterPtr mSampleConv;
1330     RingBufferPtr mRing;
1331
1332     std::atomic<bool> mKillNow{true};
1333     std::thread mThread;
1334
1335     DEF_NEWDEL(WasapiCapture)
1336 };
1337
1338 WasapiCapture::~WasapiCapture()
1339 {
1340     if(SUCCEEDED(mOpenStatus))
1341     {
1342         pushMessage(MsgType::CloseDevice).wait();
1343         DeinitThread();
1344     }
1345     mOpenStatus = E_FAIL;
1346
1347     if(mNotifyEvent != nullptr)
1348         CloseHandle(mNotifyEvent);
1349     mNotifyEvent = nullptr;
1350 }
1351
1352
1353 FORCE_ALIGN int WasapiCapture::recordProc()
1354 {
1355     HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
1356     if(FAILED(hr))
1357     {
1358         ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
1359         mDevice->handleDisconnect("COM init failed: 0x%08lx", hr);
1360         return 1;
1361     }
1362
1363     althrd_setname(RECORD_THREAD_NAME);
1364
1365     al::vector<float> samples;
1366     while(!mKillNow.load(std::memory_order_relaxed))
1367     {
1368         UINT32 avail;
1369         hr = mCapture->GetNextPacketSize(&avail);
1370         if(FAILED(hr))
1371             ERR("Failed to get next packet size: 0x%08lx\n", hr);
1372         else if(avail > 0)
1373         {
1374             UINT32 numsamples;
1375             DWORD flags;
1376             BYTE *rdata;
1377
1378             hr = mCapture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr);
1379             if(FAILED(hr))
1380                 ERR("Failed to get capture buffer: 0x%08lx\n", hr);
1381             else
1382             {
1383                 if(mChannelConv.is_active())
1384                 {
1385                     samples.resize(numsamples*2);
1386                     mChannelConv.convert(rdata, samples.data(), numsamples);
1387                     rdata = reinterpret_cast<BYTE*>(samples.data());
1388                 }
1389
1390                 auto data = mRing->getWriteVector();
1391
1392                 size_t dstframes;
1393                 if(mSampleConv)
1394                 {
1395                     const void *srcdata{rdata};
1396                     uint srcframes{numsamples};
1397
1398                     dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf,
1399                         static_cast<uint>(minz(data.first.len, INT_MAX)));
1400                     if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0)
1401                     {
1402                         /* If some source samples remain, all of the first dest
1403                          * block was filled, and there's space in the second
1404                          * dest block, do another run for the second block.
1405                          */
1406                         dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf,
1407                             static_cast<uint>(minz(data.second.len, INT_MAX)));
1408                     }
1409                 }
1410                 else
1411                 {
1412                     const uint framesize{mDevice->frameSizeFromFmt()};
1413                     size_t len1{minz(data.first.len, numsamples)};
1414                     size_t len2{minz(data.second.len, numsamples-len1)};
1415
1416                     memcpy(data.first.buf, rdata, len1*framesize);
1417                     if(len2 > 0)
1418                         memcpy(data.second.buf, rdata+len1*framesize, len2*framesize);
1419                     dstframes = len1 + len2;
1420                 }
1421
1422                 mRing->writeAdvance(dstframes);
1423
1424                 hr = mCapture->ReleaseBuffer(numsamples);
1425                 if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
1426             }
1427         }
1428
1429         if(FAILED(hr))
1430         {
1431             mDevice->handleDisconnect("Failed to capture samples: 0x%08lx", hr);
1432             break;
1433         }
1434
1435         DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
1436         if(res != WAIT_OBJECT_0)
1437             ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
1438     }
1439
1440     CoUninitialize();
1441     return 0;
1442 }
1443
1444
1445 void WasapiCapture::open(const char *name)
1446 {
1447     if(SUCCEEDED(mOpenStatus))
1448         throw al::backend_exception{al::backend_error::DeviceError,
1449             "Unexpected duplicate open call"};
1450
1451     mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
1452     if(mNotifyEvent == nullptr)
1453     {
1454         ERR("Failed to create notify events: %lu\n", GetLastError());
1455         throw al::backend_exception{al::backend_error::DeviceError,
1456             "Failed to create notify events"};
1457     }
1458
1459     HRESULT hr{InitThread()};
1460     if(FAILED(hr))
1461     {
1462         throw al::backend_exception{al::backend_error::DeviceError,
1463             "Failed to init COM thread: 0x%08lx", hr};
1464     }
1465
1466     if(name)
1467     {
1468         if(CaptureDevices.empty())
1469             pushMessage(MsgType::EnumerateCapture);
1470         if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0)
1471         {
1472             name += DevNameHeadLen;
1473             if(*name == '\0')
1474                 name = nullptr;
1475         }
1476     }
1477
1478     mOpenStatus = pushMessage(MsgType::OpenDevice, name).get();
1479     if(FAILED(mOpenStatus))
1480     {
1481         DeinitThread();
1482         throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
1483             mOpenStatus};
1484     }
1485
1486     hr = pushMessage(MsgType::ResetDevice).get();
1487     if(FAILED(hr))
1488     {
1489         if(hr == E_OUTOFMEMORY)
1490             throw al::backend_exception{al::backend_error::OutOfMemory, "Out of memory"};
1491         throw al::backend_exception{al::backend_error::DeviceError, "Device reset failed"};
1492     }
1493 }
1494
1495 HRESULT WasapiCapture::openProxy(const char *name)
1496 {
1497     const wchar_t *devid{nullptr};
1498     if(name)
1499     {
1500         auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1501             [name](const DevMap &entry) -> bool
1502             { return entry.name == name || entry.endpoint_guid == name; });
1503         if(iter == CaptureDevices.cend())
1504         {
1505             const std::wstring wname{utf8_to_wstr(name)};
1506             iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
1507                 [&wname](const DevMap &entry) -> bool
1508                 { return entry.devid == wname; });
1509         }
1510         if(iter == CaptureDevices.cend())
1511         {
1512             WARN("Failed to find device name matching \"%s\"\n", name);
1513             return E_FAIL;
1514         }
1515         name = iter->name.c_str();
1516         devid = iter->devid.c_str();
1517     }
1518
1519     void *ptr;
1520     HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
1521         IID_IMMDeviceEnumerator, &ptr)};
1522     if(SUCCEEDED(hr))
1523     {
1524         ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
1525         if(!devid)
1526             hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
1527         else
1528             hr = enumerator->GetDevice(devid, mMMDev.getPtr());
1529     }
1530     if(FAILED(hr))
1531     {
1532         WARN("Failed to open device \"%s\"\n", name?name:"(default)");
1533         return hr;
1534     }
1535
1536     mClient = nullptr;
1537     if(name) mDevice->DeviceName = std::string{DevNameHead} + name;
1538     else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first;
1539
1540     return hr;
1541 }
1542
1543 void WasapiCapture::closeProxy()
1544 {
1545     mClient = nullptr;
1546     mMMDev = nullptr;
1547 }
1548
1549 HRESULT WasapiCapture::resetProxy()
1550 {
1551     mClient = nullptr;
1552
1553     void *ptr;
1554     HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
1555     if(FAILED(hr))
1556     {
1557         ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
1558         return hr;
1559     }
1560     mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
1561
1562     WAVEFORMATEX *wfx;
1563     hr = mClient->GetMixFormat(&wfx);
1564     if(FAILED(hr))
1565     {
1566         ERR("Failed to get capture format: 0x%08lx\n", hr);
1567         return hr;
1568     }
1569     TraceFormat("Device capture format", wfx);
1570
1571     WAVEFORMATEXTENSIBLE InputType{};
1572     if(!MakeExtensible(&InputType, wfx))
1573     {
1574         CoTaskMemFree(wfx);
1575         return E_FAIL;
1576     }
1577     CoTaskMemFree(wfx);
1578     wfx = nullptr;
1579
1580     const bool isRear51{InputType.Format.nChannels == 6
1581         && (InputType.dwChannelMask&X51RearMask) == X5DOT1REAR};
1582
1583     // Make sure buffer is at least 100ms in size
1584     ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency};
1585     buf_time = std::max(buf_time, ReferenceTime{milliseconds{100}});
1586
1587     InputType = {};
1588     InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1589     switch(mDevice->FmtChans)
1590     {
1591     case DevFmtMono:
1592         InputType.Format.nChannels = 1;
1593         InputType.dwChannelMask = MONO;
1594         break;
1595     case DevFmtStereo:
1596         InputType.Format.nChannels = 2;
1597         InputType.dwChannelMask = STEREO;
1598         break;
1599     case DevFmtQuad:
1600         InputType.Format.nChannels = 4;
1601         InputType.dwChannelMask = QUAD;
1602         break;
1603     case DevFmtX51:
1604         InputType.Format.nChannels = 6;
1605         InputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1;
1606         break;
1607     case DevFmtX61:
1608         InputType.Format.nChannels = 7;
1609         InputType.dwChannelMask = X6DOT1;
1610         break;
1611     case DevFmtX71:
1612         InputType.Format.nChannels = 8;
1613         InputType.dwChannelMask = X7DOT1;
1614         break;
1615     case DevFmtX714:
1616         InputType.Format.nChannels = 12;
1617         InputType.dwChannelMask = X7DOT1DOT4;
1618         break;
1619
1620     case DevFmtX3D71:
1621     case DevFmtAmbi3D:
1622         return E_FAIL;
1623     }
1624     switch(mDevice->FmtType)
1625     {
1626     /* NOTE: Signedness doesn't matter, the converter will handle it. */
1627     case DevFmtByte:
1628     case DevFmtUByte:
1629         InputType.Format.wBitsPerSample = 8;
1630         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1631         break;
1632     case DevFmtShort:
1633     case DevFmtUShort:
1634         InputType.Format.wBitsPerSample = 16;
1635         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1636         break;
1637     case DevFmtInt:
1638     case DevFmtUInt:
1639         InputType.Format.wBitsPerSample = 32;
1640         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1641         break;
1642     case DevFmtFloat:
1643         InputType.Format.wBitsPerSample = 32;
1644         InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1645         break;
1646     }
1647     InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
1648     InputType.Format.nSamplesPerSec = mDevice->Frequency;
1649
1650     InputType.Format.nBlockAlign = static_cast<WORD>(InputType.Format.nChannels *
1651         InputType.Format.wBitsPerSample / 8);
1652     InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
1653         InputType.Format.nBlockAlign;
1654     InputType.Format.cbSize = sizeof(InputType) - sizeof(InputType.Format);
1655
1656     TraceFormat("Requesting capture format", &InputType.Format);
1657     hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &InputType.Format, &wfx);
1658     if(FAILED(hr))
1659     {
1660         WARN("Failed to check capture format support: 0x%08lx\n", hr);
1661         hr = mClient->GetMixFormat(&wfx);
1662     }
1663     if(FAILED(hr))
1664     {
1665         ERR("Failed to find a supported capture format: 0x%08lx\n", hr);
1666         return hr;
1667     }
1668
1669     mSampleConv = nullptr;
1670     mChannelConv = {};
1671
1672     if(wfx != nullptr)
1673     {
1674         TraceFormat("Got capture format", wfx);
1675         if(!MakeExtensible(&InputType, wfx))
1676         {
1677             CoTaskMemFree(wfx);
1678             return E_FAIL;
1679         }
1680         CoTaskMemFree(wfx);
1681         wfx = nullptr;
1682
1683         auto validate_fmt = [](DeviceBase *device, uint32_t chancount, DWORD chanmask) noexcept
1684             -> bool
1685         {
1686             switch(device->FmtChans)
1687             {
1688             /* If the device wants mono, we can handle any input. */
1689             case DevFmtMono:
1690                 return true;
1691             /* If the device wants stereo, we can handle mono or stereo input. */
1692             case DevFmtStereo:
1693                 return (chancount == 2 && (chanmask == 0 || (chanmask&StereoMask) == STEREO))
1694                     || (chancount == 1 && (chanmask&MonoMask) == MONO);
1695             /* Otherwise, the device must match the input type. */
1696             case DevFmtQuad:
1697                 return (chancount == 4 && (chanmask == 0 || (chanmask&QuadMask) == QUAD));
1698             /* 5.1 (Side) and 5.1 (Rear) are interchangeable here. */
1699             case DevFmtX51:
1700                 return (chancount == 6 && (chanmask == 0 || (chanmask&X51Mask) == X5DOT1
1701                         || (chanmask&X51RearMask) == X5DOT1REAR));
1702             case DevFmtX61:
1703                 return (chancount == 7 && (chanmask == 0 || (chanmask&X61Mask) == X6DOT1));
1704             case DevFmtX71:
1705             case DevFmtX3D71:
1706                 return (chancount == 8 && (chanmask == 0 || (chanmask&X71Mask) == X7DOT1));
1707             case DevFmtX714:
1708                 return (chancount == 12 && (chanmask == 0 || (chanmask&X714Mask) == X7DOT1DOT4));
1709             case DevFmtAmbi3D:
1710                 return (chanmask == 0 && chancount == device->channelsFromFmt());
1711             }
1712             return false;
1713         };
1714         if(!validate_fmt(mDevice, InputType.Format.nChannels, InputType.dwChannelMask))
1715         {
1716             ERR("Failed to match format, wanted: %s %s %uhz, got: 0x%08lx mask %d channel%s %d-bit %luhz\n",
1717                 DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
1718                 mDevice->Frequency, InputType.dwChannelMask, InputType.Format.nChannels,
1719                 (InputType.Format.nChannels==1)?"":"s", InputType.Format.wBitsPerSample,
1720                 InputType.Format.nSamplesPerSec);
1721             return E_FAIL;
1722         }
1723     }
1724
1725     DevFmtType srcType{};
1726     if(IsEqualGUID(InputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
1727     {
1728         if(InputType.Format.wBitsPerSample == 8)
1729             srcType = DevFmtUByte;
1730         else if(InputType.Format.wBitsPerSample == 16)
1731             srcType = DevFmtShort;
1732         else if(InputType.Format.wBitsPerSample == 32)
1733             srcType = DevFmtInt;
1734         else
1735         {
1736             ERR("Unhandled integer bit depth: %d\n", InputType.Format.wBitsPerSample);
1737             return E_FAIL;
1738         }
1739     }
1740     else if(IsEqualGUID(InputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1741     {
1742         if(InputType.Format.wBitsPerSample == 32)
1743             srcType = DevFmtFloat;
1744         else
1745         {
1746             ERR("Unhandled float bit depth: %d\n", InputType.Format.wBitsPerSample);
1747             return E_FAIL;
1748         }
1749     }
1750     else
1751     {
1752         ERR("Unhandled format sub-type: %s\n", GuidPrinter{InputType.SubFormat}.c_str());
1753         return E_FAIL;
1754     }
1755
1756     if(mDevice->FmtChans == DevFmtMono && InputType.Format.nChannels != 1)
1757     {
1758         uint chanmask{(1u<<InputType.Format.nChannels) - 1u};
1759         /* Exclude LFE from the downmix. */
1760         if((InputType.dwChannelMask&SPEAKER_LOW_FREQUENCY))
1761         {
1762             constexpr auto lfemask = MaskFromTopBits(SPEAKER_LOW_FREQUENCY);
1763             const int lfeidx{al::popcount(InputType.dwChannelMask&lfemask) - 1};
1764             chanmask &= ~(1u << lfeidx);
1765         }
1766
1767         mChannelConv = ChannelConverter{srcType, InputType.Format.nChannels, chanmask,
1768             mDevice->FmtChans};
1769         TRACE("Created %s multichannel-to-mono converter\n", DevFmtTypeString(srcType));
1770         /* The channel converter always outputs float, so change the input type
1771          * for the resampler/type-converter.
1772          */
1773         srcType = DevFmtFloat;
1774     }
1775     else if(mDevice->FmtChans == DevFmtStereo && InputType.Format.nChannels == 1)
1776     {
1777         mChannelConv = ChannelConverter{srcType, 1, 0x1, mDevice->FmtChans};
1778         TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
1779         srcType = DevFmtFloat;
1780     }
1781
1782     if(mDevice->Frequency != InputType.Format.nSamplesPerSec || mDevice->FmtType != srcType)
1783     {
1784         mSampleConv = SampleConverter::Create(srcType, mDevice->FmtType,
1785             mDevice->channelsFromFmt(), InputType.Format.nSamplesPerSec, mDevice->Frequency,
1786             Resampler::FastBSinc24);
1787         if(!mSampleConv)
1788         {
1789             ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1790                 DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
1791                 mDevice->Frequency, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec);
1792             return E_FAIL;
1793         }
1794         TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1795             DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
1796             mDevice->Frequency, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec);
1797     }
1798
1799     hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
1800         buf_time.count(), 0, &InputType.Format, nullptr);
1801     if(FAILED(hr))
1802     {
1803         ERR("Failed to initialize audio client: 0x%08lx\n", hr);
1804         return hr;
1805     }
1806
1807     UINT32 buffer_len{};
1808     ReferenceTime min_per{};
1809     hr = mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr);
1810     if(SUCCEEDED(hr))
1811         hr = mClient->GetBufferSize(&buffer_len);
1812     if(FAILED(hr))
1813     {
1814         ERR("Failed to get buffer size: 0x%08lx\n", hr);
1815         return hr;
1816     }
1817     mDevice->UpdateSize = RefTime2Samples(min_per, mDevice->Frequency);
1818     mDevice->BufferSize = buffer_len;
1819
1820     mRing = RingBuffer::Create(buffer_len, mDevice->frameSizeFromFmt(), false);
1821
1822     hr = mClient->SetEventHandle(mNotifyEvent);
1823     if(FAILED(hr))
1824     {
1825         ERR("Failed to set event handle: 0x%08lx\n", hr);
1826         return hr;
1827     }
1828
1829     return hr;
1830 }
1831
1832
1833 void WasapiCapture::start()
1834 {
1835     const HRESULT hr{pushMessage(MsgType::StartDevice).get()};
1836     if(FAILED(hr))
1837         throw al::backend_exception{al::backend_error::DeviceError,
1838             "Failed to start recording: 0x%lx", hr};
1839 }
1840
1841 HRESULT WasapiCapture::startProxy()
1842 {
1843     ResetEvent(mNotifyEvent);
1844
1845     HRESULT hr{mClient->Start()};
1846     if(FAILED(hr))
1847     {
1848         ERR("Failed to start audio client: 0x%08lx\n", hr);
1849         return hr;
1850     }
1851
1852     void *ptr;
1853     hr = mClient->GetService(IID_IAudioCaptureClient, &ptr);
1854     if(SUCCEEDED(hr))
1855     {
1856         mCapture = ComPtr<IAudioCaptureClient>{static_cast<IAudioCaptureClient*>(ptr)};
1857         try {
1858             mKillNow.store(false, std::memory_order_release);
1859             mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this};
1860         }
1861         catch(...) {
1862             mCapture = nullptr;
1863             ERR("Failed to start thread\n");
1864             hr = E_FAIL;
1865         }
1866     }
1867
1868     if(FAILED(hr))
1869     {
1870         mClient->Stop();
1871         mClient->Reset();
1872     }
1873
1874     return hr;
1875 }
1876
1877
1878 void WasapiCapture::stop()
1879 { pushMessage(MsgType::StopDevice).wait(); }
1880
1881 void WasapiCapture::stopProxy()
1882 {
1883     if(!mCapture || !mThread.joinable())
1884         return;
1885
1886     mKillNow.store(true, std::memory_order_release);
1887     mThread.join();
1888
1889     mCapture = nullptr;
1890     mClient->Stop();
1891     mClient->Reset();
1892 }
1893
1894
1895 void WasapiCapture::captureSamples(al::byte *buffer, uint samples)
1896 { mRing->read(buffer, samples); }
1897
1898 uint WasapiCapture::availableSamples()
1899 { return static_cast<uint>(mRing->readSpace()); }
1900
1901 } // namespace
1902
1903
1904 bool WasapiBackendFactory::init()
1905 {
1906     static HRESULT InitResult{E_FAIL};
1907
1908     if(FAILED(InitResult)) try
1909     {
1910         auto res = std::async(std::launch::async, []() -> HRESULT
1911         {
1912             HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
1913             if(FAILED(hr))
1914             {
1915                 WARN("Failed to initialize COM: 0x%08lx\n", hr);
1916                 return hr;
1917             }
1918
1919             void *ptr{};
1920             hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
1921                 IID_IMMDeviceEnumerator, &ptr);
1922             if(FAILED(hr))
1923             {
1924                 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
1925                 CoUninitialize();
1926                 return hr;
1927             }
1928             static_cast<IMMDeviceEnumerator*>(ptr)->Release();
1929             CoUninitialize();
1930
1931             return S_OK;
1932         });
1933
1934         InitResult = res.get();
1935     }
1936     catch(...) {
1937     }
1938
1939     return SUCCEEDED(InitResult);
1940 }
1941
1942 bool WasapiBackendFactory::querySupport(BackendType type)
1943 { return type == BackendType::Playback || type == BackendType::Capture; }
1944
1945 std::string WasapiBackendFactory::probe(BackendType type)
1946 {
1947     struct ProxyControl {
1948         HRESULT mResult{};
1949         ProxyControl() { mResult = WasapiProxy::InitThread(); }
1950         ~ProxyControl() { if(SUCCEEDED(mResult)) WasapiProxy::DeinitThread(); }
1951     };
1952     ProxyControl proxy;
1953
1954     std::string outnames;
1955     if(FAILED(proxy.mResult))
1956         return outnames;
1957
1958     switch(type)
1959     {
1960     case BackendType::Playback:
1961         WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).wait();
1962         for(const DevMap &entry : PlaybackDevices)
1963         {
1964             /* +1 to also append the null char (to ensure a null-separated list
1965              * and double-null terminated list).
1966              */
1967             outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
1968         }
1969         break;
1970
1971     case BackendType::Capture:
1972         WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).wait();
1973         for(const DevMap &entry : CaptureDevices)
1974             outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
1975         break;
1976     }
1977
1978     return outnames;
1979 }
1980
1981 BackendPtr WasapiBackendFactory::createBackend(DeviceBase *device, BackendType type)
1982 {
1983     if(type == BackendType::Playback)
1984         return BackendPtr{new WasapiPlayback{device}};
1985     if(type == BackendType::Capture)
1986         return BackendPtr{new WasapiCapture{device}};
1987     return nullptr;
1988 }
1989
1990 BackendFactory &WasapiBackendFactory::getFactory()
1991 {
1992     static WasapiBackendFactory factory{};
1993     return factory;
1994 }