wasapi.cpp 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
  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. #include "config.h"
  21. #include "backends/wasapi.h"
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <memory.h>
  27. #include <wtypes.h>
  28. #include <mmdeviceapi.h>
  29. #include <audioclient.h>
  30. #include <cguid.h>
  31. #include <devpropdef.h>
  32. #include <mmreg.h>
  33. #include <propsys.h>
  34. #include <propkey.h>
  35. #include <devpkey.h>
  36. #ifndef _WAVEFORMATEXTENSIBLE_
  37. #include <ks.h>
  38. #include <ksmedia.h>
  39. #endif
  40. #include <deque>
  41. #include <mutex>
  42. #include <atomic>
  43. #include <thread>
  44. #include <vector>
  45. #include <string>
  46. #include <future>
  47. #include <algorithm>
  48. #include <functional>
  49. #include <condition_variable>
  50. #include "alcmain.h"
  51. #include "alexcpt.h"
  52. #include "alu.h"
  53. #include "ringbuffer.h"
  54. #include "compat.h"
  55. #include "converter.h"
  56. #include "strutils.h"
  57. #include "threads.h"
  58. /* Some headers seem to define these as macros for __uuidof, which is annoying
  59. * since some headers don't declare them at all. Hopefully the ifdef is enough
  60. * to tell if they need to be declared.
  61. */
  62. #ifndef KSDATAFORMAT_SUBTYPE_PCM
  63. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  64. #endif
  65. #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
  66. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  67. #endif
  68. DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
  69. DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
  70. DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
  71. namespace {
  72. inline constexpr REFERENCE_TIME operator "" _reftime(unsigned long long int n) noexcept
  73. { return static_cast<REFERENCE_TIME>(n); }
  74. #define MONO SPEAKER_FRONT_CENTER
  75. #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
  76. #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
  77. #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
  78. #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
  79. #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
  80. #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)
  81. #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)
  82. #define REFTIME_PER_SEC 10000000_reftime
  83. #define DEVNAME_HEAD "OpenAL Soft on "
  84. /* Scales the given value using 64-bit integer math, ceiling the result. */
  85. inline int64_t ScaleCeil(int64_t val, int64_t new_scale, int64_t old_scale)
  86. {
  87. return (val*new_scale + old_scale-1) / old_scale;
  88. }
  89. struct PropVariant {
  90. PROPVARIANT mProp;
  91. public:
  92. PropVariant() { PropVariantInit(&mProp); }
  93. ~PropVariant() { clear(); }
  94. void clear() { PropVariantClear(&mProp); }
  95. PROPVARIANT* get() noexcept { return &mProp; }
  96. PROPVARIANT& operator*() noexcept { return mProp; }
  97. const PROPVARIANT& operator*() const noexcept { return mProp; }
  98. PROPVARIANT* operator->() noexcept { return &mProp; }
  99. const PROPVARIANT* operator->() const noexcept { return &mProp; }
  100. };
  101. struct DevMap {
  102. std::string name;
  103. std::string endpoint_guid; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
  104. std::wstring devid;
  105. template<typename T0, typename T1, typename T2>
  106. DevMap(T0&& name_, T1&& guid_, T2&& devid_)
  107. : name{std::forward<T0>(name_)}
  108. , endpoint_guid{std::forward<T1>(guid_)}
  109. , devid{std::forward<T2>(devid_)}
  110. { }
  111. };
  112. bool checkName(const al::vector<DevMap> &list, const std::string &name)
  113. {
  114. return std::find_if(list.cbegin(), list.cend(),
  115. [&name](const DevMap &entry) -> bool
  116. { return entry.name == name; }
  117. ) != list.cend();
  118. }
  119. al::vector<DevMap> PlaybackDevices;
  120. al::vector<DevMap> CaptureDevices;
  121. using NameGUIDPair = std::pair<std::string,std::string>;
  122. NameGUIDPair get_device_name_and_guid(IMMDevice *device)
  123. {
  124. std::string name{DEVNAME_HEAD};
  125. std::string guid;
  126. IPropertyStore *ps;
  127. HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
  128. if(FAILED(hr))
  129. {
  130. WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
  131. return { name+"Unknown Device Name", "Unknown Device GUID" };
  132. }
  133. PropVariant pvprop;
  134. hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get());
  135. if(FAILED(hr))
  136. {
  137. WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
  138. name += "Unknown Device Name";
  139. }
  140. else if(pvprop->vt == VT_LPWSTR)
  141. name += wstr_to_utf8(pvprop->pwszVal);
  142. else
  143. {
  144. WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
  145. name += "Unknown Device Name";
  146. }
  147. pvprop.clear();
  148. hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get());
  149. if(FAILED(hr))
  150. {
  151. WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
  152. guid = "Unknown Device GUID";
  153. }
  154. else if(pvprop->vt == VT_LPWSTR)
  155. guid = wstr_to_utf8(pvprop->pwszVal);
  156. else
  157. {
  158. WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
  159. guid = "Unknown Device GUID";
  160. }
  161. ps->Release();
  162. return {name, guid};
  163. }
  164. void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
  165. {
  166. IPropertyStore *ps;
  167. HRESULT hr = device->OpenPropertyStore(STGM_READ, &ps);
  168. if(FAILED(hr))
  169. {
  170. WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
  171. return;
  172. }
  173. PropVariant pvform;
  174. hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get());
  175. if(FAILED(hr))
  176. WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
  177. else if(pvform->vt == VT_UI4)
  178. *formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
  179. else if(pvform->vt == VT_EMPTY)
  180. *formfactor = UnknownFormFactor;
  181. else
  182. WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
  183. ps->Release();
  184. }
  185. void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list)
  186. {
  187. std::string basename, guidstr;
  188. std::tie(basename, guidstr) = get_device_name_and_guid(device);
  189. int count{1};
  190. std::string newname{basename};
  191. while(checkName(list, newname))
  192. {
  193. newname = basename;
  194. newname += " #";
  195. newname += std::to_string(++count);
  196. }
  197. list.emplace_back(std::move(newname), std::move(guidstr), devid);
  198. const DevMap &newentry = list.back();
  199. TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
  200. newentry.endpoint_guid.c_str(), newentry.devid.c_str());
  201. }
  202. WCHAR *get_device_id(IMMDevice *device)
  203. {
  204. WCHAR *devid;
  205. HRESULT hr = device->GetId(&devid);
  206. if(FAILED(hr))
  207. {
  208. ERR("Failed to get device id: %lx\n", hr);
  209. return nullptr;
  210. }
  211. return devid;
  212. }
  213. HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list)
  214. {
  215. IMMDeviceCollection *coll;
  216. HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, &coll)};
  217. if(FAILED(hr))
  218. {
  219. ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
  220. return hr;
  221. }
  222. IMMDevice *defdev{nullptr};
  223. WCHAR *defdevid{nullptr};
  224. UINT count{0};
  225. hr = coll->GetCount(&count);
  226. if(SUCCEEDED(hr) && count > 0)
  227. {
  228. list.clear();
  229. list.reserve(count);
  230. hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, &defdev);
  231. }
  232. if(SUCCEEDED(hr) && defdev != nullptr)
  233. {
  234. defdevid = get_device_id(defdev);
  235. if(defdevid)
  236. add_device(defdev, defdevid, list);
  237. }
  238. for(UINT i{0};i < count;++i)
  239. {
  240. IMMDevice *device;
  241. hr = coll->Item(i, &device);
  242. if(FAILED(hr)) continue;
  243. WCHAR *devid{get_device_id(device)};
  244. if(devid)
  245. {
  246. if(!defdevid || wcscmp(devid, defdevid) != 0)
  247. add_device(device, devid, list);
  248. CoTaskMemFree(devid);
  249. }
  250. device->Release();
  251. }
  252. if(defdev) defdev->Release();
  253. if(defdevid) CoTaskMemFree(defdevid);
  254. coll->Release();
  255. return S_OK;
  256. }
  257. bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
  258. {
  259. *out = WAVEFORMATEXTENSIBLE{};
  260. if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
  261. {
  262. *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format);
  263. out->Format.cbSize = sizeof(*out) - sizeof(out->Format);
  264. }
  265. else if(in->wFormatTag == WAVE_FORMAT_PCM)
  266. {
  267. out->Format = *in;
  268. out->Format.cbSize = 0;
  269. out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
  270. if(out->Format.nChannels == 1)
  271. out->dwChannelMask = MONO;
  272. else if(out->Format.nChannels == 2)
  273. out->dwChannelMask = STEREO;
  274. else
  275. ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
  276. out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  277. }
  278. else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
  279. {
  280. out->Format = *in;
  281. out->Format.cbSize = 0;
  282. out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample;
  283. if(out->Format.nChannels == 1)
  284. out->dwChannelMask = MONO;
  285. else if(out->Format.nChannels == 2)
  286. out->dwChannelMask = STEREO;
  287. else
  288. ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
  289. out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  290. }
  291. else
  292. {
  293. ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
  294. return false;
  295. }
  296. return true;
  297. }
  298. void TraceFormat(const char *msg, const WAVEFORMATEX *format)
  299. {
  300. constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)};
  301. if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size)
  302. {
  303. class GuidPrinter {
  304. char mMsg[64];
  305. public:
  306. GuidPrinter(const GUID &guid)
  307. {
  308. std::snprintf(mMsg, al::size(mMsg),
  309. "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
  310. DWORD{guid.Data1}, guid.Data2, guid.Data3,
  311. guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
  312. guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
  313. }
  314. const char *c_str() const { return mMsg; }
  315. };
  316. const WAVEFORMATEXTENSIBLE *fmtex{
  317. CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)};
  318. TRACE("%s:\n"
  319. " FormatTag = 0x%04x\n"
  320. " Channels = %d\n"
  321. " SamplesPerSec = %lu\n"
  322. " AvgBytesPerSec = %lu\n"
  323. " BlockAlign = %d\n"
  324. " BitsPerSample = %d\n"
  325. " Size = %d\n"
  326. " Samples = %d\n"
  327. " ChannelMask = 0x%lx\n"
  328. " SubFormat = %s\n",
  329. msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec,
  330. fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample,
  331. fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask,
  332. GuidPrinter{fmtex->SubFormat}.c_str());
  333. }
  334. else
  335. TRACE("%s:\n"
  336. " FormatTag = 0x%04x\n"
  337. " Channels = %d\n"
  338. " SamplesPerSec = %lu\n"
  339. " AvgBytesPerSec = %lu\n"
  340. " BlockAlign = %d\n"
  341. " BitsPerSample = %d\n"
  342. " Size = %d\n",
  343. msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec,
  344. format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize);
  345. }
  346. enum class MsgType {
  347. OpenDevice,
  348. ResetDevice,
  349. StartDevice,
  350. StopDevice,
  351. CloseDevice,
  352. EnumeratePlayback,
  353. EnumerateCapture,
  354. QuitThread,
  355. Count
  356. };
  357. constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
  358. "Open Device",
  359. "Reset Device",
  360. "Start Device",
  361. "Stop Device",
  362. "Close Device",
  363. "Enumerate Playback",
  364. "Enumerate Capture",
  365. "Quit"
  366. };
  367. /* Proxy interface used by the message handler. */
  368. struct WasapiProxy {
  369. virtual ~WasapiProxy() = default;
  370. virtual HRESULT openProxy() = 0;
  371. virtual void closeProxy() = 0;
  372. virtual HRESULT resetProxy() = 0;
  373. virtual HRESULT startProxy() = 0;
  374. virtual void stopProxy() = 0;
  375. struct Msg {
  376. MsgType mType;
  377. WasapiProxy *mProxy;
  378. std::promise<HRESULT> mPromise;
  379. };
  380. static std::deque<Msg> mMsgQueue;
  381. static std::mutex mMsgQueueLock;
  382. static std::condition_variable mMsgQueueCond;
  383. std::future<HRESULT> pushMessage(MsgType type)
  384. {
  385. std::promise<HRESULT> promise;
  386. std::future<HRESULT> future{promise.get_future()};
  387. { std::lock_guard<std::mutex> _{mMsgQueueLock};
  388. mMsgQueue.emplace_back(Msg{type, this, std::move(promise)});
  389. }
  390. mMsgQueueCond.notify_one();
  391. return future;
  392. }
  393. static std::future<HRESULT> pushMessageStatic(MsgType type)
  394. {
  395. std::promise<HRESULT> promise;
  396. std::future<HRESULT> future{promise.get_future()};
  397. { std::lock_guard<std::mutex> _{mMsgQueueLock};
  398. mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)});
  399. }
  400. mMsgQueueCond.notify_one();
  401. return future;
  402. }
  403. static bool popMessage(Msg &msg)
  404. {
  405. std::unique_lock<std::mutex> lock{mMsgQueueLock};
  406. while(mMsgQueue.empty())
  407. mMsgQueueCond.wait(lock);
  408. msg = std::move(mMsgQueue.front());
  409. mMsgQueue.pop_front();
  410. return msg.mType != MsgType::QuitThread;
  411. }
  412. static int messageHandler(std::promise<HRESULT> *promise);
  413. };
  414. std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue;
  415. std::mutex WasapiProxy::mMsgQueueLock;
  416. std::condition_variable WasapiProxy::mMsgQueueCond;
  417. int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
  418. {
  419. TRACE("Starting message thread\n");
  420. HRESULT cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  421. if(FAILED(cohr))
  422. {
  423. WARN("Failed to initialize COM: 0x%08lx\n", cohr);
  424. promise->set_value(cohr);
  425. return 0;
  426. }
  427. void *ptr{};
  428. HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
  429. IID_IMMDeviceEnumerator, &ptr)};
  430. if(FAILED(hr))
  431. {
  432. WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
  433. promise->set_value(hr);
  434. CoUninitialize();
  435. return 0;
  436. }
  437. auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
  438. Enumerator->Release();
  439. Enumerator = nullptr;
  440. CoUninitialize();
  441. TRACE("Message thread initialization complete\n");
  442. promise->set_value(S_OK);
  443. promise = nullptr;
  444. TRACE("Starting message loop\n");
  445. ALuint deviceCount{0};
  446. Msg msg;
  447. while(popMessage(msg))
  448. {
  449. TRACE("Got message \"%s\" (0x%04x, this=%p)\n",
  450. MessageStr[static_cast<size_t>(msg.mType)], static_cast<int>(msg.mType),
  451. decltype(std::declval<void*>()){msg.mProxy});
  452. switch(msg.mType)
  453. {
  454. case MsgType::OpenDevice:
  455. hr = cohr = S_OK;
  456. if(++deviceCount == 1)
  457. hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  458. if(SUCCEEDED(hr))
  459. hr = msg.mProxy->openProxy();
  460. msg.mPromise.set_value(hr);
  461. if(FAILED(hr))
  462. {
  463. if(--deviceCount == 0 && SUCCEEDED(cohr))
  464. CoUninitialize();
  465. }
  466. continue;
  467. case MsgType::ResetDevice:
  468. hr = msg.mProxy->resetProxy();
  469. msg.mPromise.set_value(hr);
  470. continue;
  471. case MsgType::StartDevice:
  472. hr = msg.mProxy->startProxy();
  473. msg.mPromise.set_value(hr);
  474. continue;
  475. case MsgType::StopDevice:
  476. msg.mProxy->stopProxy();
  477. msg.mPromise.set_value(S_OK);
  478. continue;
  479. case MsgType::CloseDevice:
  480. msg.mProxy->closeProxy();
  481. msg.mPromise.set_value(S_OK);
  482. if(--deviceCount == 0)
  483. CoUninitialize();
  484. continue;
  485. case MsgType::EnumeratePlayback:
  486. case MsgType::EnumerateCapture:
  487. hr = cohr = S_OK;
  488. if(++deviceCount == 1)
  489. hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  490. if(SUCCEEDED(hr))
  491. hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr);
  492. if(FAILED(hr))
  493. msg.mPromise.set_value(hr);
  494. else
  495. {
  496. Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
  497. if(msg.mType == MsgType::EnumeratePlayback)
  498. hr = probe_devices(Enumerator, eRender, PlaybackDevices);
  499. else if(msg.mType == MsgType::EnumerateCapture)
  500. hr = probe_devices(Enumerator, eCapture, CaptureDevices);
  501. msg.mPromise.set_value(hr);
  502. Enumerator->Release();
  503. Enumerator = nullptr;
  504. }
  505. if(--deviceCount == 0 && SUCCEEDED(cohr))
  506. CoUninitialize();
  507. continue;
  508. default:
  509. ERR("Unexpected message: %u\n", static_cast<unsigned int>(msg.mType));
  510. msg.mPromise.set_value(E_FAIL);
  511. continue;
  512. }
  513. }
  514. TRACE("Message loop finished\n");
  515. return 0;
  516. }
  517. struct WasapiPlayback final : public BackendBase, WasapiProxy {
  518. WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
  519. ~WasapiPlayback() override;
  520. int mixerProc();
  521. void open(const ALCchar *name) override;
  522. HRESULT openProxy() override;
  523. void closeProxy() override;
  524. bool reset() override;
  525. HRESULT resetProxy() override;
  526. bool start() override;
  527. HRESULT startProxy() override;
  528. void stop() override;
  529. void stopProxy() override;
  530. ClockLatency getClockLatency() override;
  531. std::wstring mDevId;
  532. IMMDevice *mMMDev{nullptr};
  533. IAudioClient *mClient{nullptr};
  534. IAudioRenderClient *mRender{nullptr};
  535. HANDLE mNotifyEvent{nullptr};
  536. std::atomic<UINT32> mPadding{0u};
  537. std::atomic<bool> mKillNow{true};
  538. std::thread mThread;
  539. DEF_NEWDEL(WasapiPlayback)
  540. };
  541. WasapiPlayback::~WasapiPlayback()
  542. {
  543. pushMessage(MsgType::CloseDevice).wait();
  544. if(mNotifyEvent != nullptr)
  545. CloseHandle(mNotifyEvent);
  546. mNotifyEvent = nullptr;
  547. }
  548. FORCE_ALIGN int WasapiPlayback::mixerProc()
  549. {
  550. HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  551. if(FAILED(hr))
  552. {
  553. ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
  554. aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
  555. return 1;
  556. }
  557. SetRTPriority();
  558. althrd_setname(MIXER_THREAD_NAME);
  559. const ALuint update_size{mDevice->UpdateSize};
  560. const UINT32 buffer_len{mDevice->BufferSize};
  561. while(!mKillNow.load(std::memory_order_relaxed))
  562. {
  563. UINT32 written;
  564. hr = mClient->GetCurrentPadding(&written);
  565. if(FAILED(hr))
  566. {
  567. ERR("Failed to get padding: 0x%08lx\n", hr);
  568. aluHandleDisconnect(mDevice, "Failed to retrieve buffer padding: 0x%08lx", hr);
  569. break;
  570. }
  571. mPadding.store(written, std::memory_order_relaxed);
  572. ALuint len{buffer_len - written};
  573. if(len < update_size)
  574. {
  575. DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
  576. if(res != WAIT_OBJECT_0)
  577. ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
  578. continue;
  579. }
  580. BYTE *buffer;
  581. hr = mRender->GetBuffer(len, &buffer);
  582. if(SUCCEEDED(hr))
  583. {
  584. std::unique_lock<WasapiPlayback> dlock{*this};
  585. aluMixData(mDevice, buffer, len);
  586. mPadding.store(written + len, std::memory_order_relaxed);
  587. dlock.unlock();
  588. hr = mRender->ReleaseBuffer(len, 0);
  589. }
  590. if(FAILED(hr))
  591. {
  592. ERR("Failed to buffer data: 0x%08lx\n", hr);
  593. aluHandleDisconnect(mDevice, "Failed to send playback samples: 0x%08lx", hr);
  594. break;
  595. }
  596. }
  597. mPadding.store(0u, std::memory_order_release);
  598. CoUninitialize();
  599. return 0;
  600. }
  601. void WasapiPlayback::open(const ALCchar *name)
  602. {
  603. HRESULT hr{S_OK};
  604. mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
  605. if(mNotifyEvent == nullptr)
  606. {
  607. ERR("Failed to create notify events: %lu\n", GetLastError());
  608. hr = E_FAIL;
  609. }
  610. if(SUCCEEDED(hr))
  611. {
  612. if(name)
  613. {
  614. if(PlaybackDevices.empty())
  615. pushMessage(MsgType::EnumeratePlayback).wait();
  616. hr = E_FAIL;
  617. auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  618. [name](const DevMap &entry) -> bool
  619. { return entry.name == name || entry.endpoint_guid == name; }
  620. );
  621. if(iter == PlaybackDevices.cend())
  622. {
  623. std::wstring wname{utf8_to_wstr(name)};
  624. iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  625. [&wname](const DevMap &entry) -> bool
  626. { return entry.devid == wname; }
  627. );
  628. }
  629. if(iter == PlaybackDevices.cend())
  630. WARN("Failed to find device name matching \"%s\"\n", name);
  631. else
  632. {
  633. mDevId = iter->devid;
  634. mDevice->DeviceName = iter->name;
  635. hr = S_OK;
  636. }
  637. }
  638. }
  639. if(SUCCEEDED(hr))
  640. hr = pushMessage(MsgType::OpenDevice).get();
  641. if(FAILED(hr))
  642. {
  643. if(mNotifyEvent != nullptr)
  644. CloseHandle(mNotifyEvent);
  645. mNotifyEvent = nullptr;
  646. mDevId.clear();
  647. throw al::backend_exception{ALC_INVALID_VALUE, "Device init failed: 0x%08lx", hr};
  648. }
  649. }
  650. HRESULT WasapiPlayback::openProxy()
  651. {
  652. void *ptr;
  653. HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, &ptr)};
  654. if(SUCCEEDED(hr))
  655. {
  656. auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
  657. if(mDevId.empty())
  658. hr = Enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &mMMDev);
  659. else
  660. hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
  661. Enumerator->Release();
  662. }
  663. if(SUCCEEDED(hr))
  664. hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
  665. if(SUCCEEDED(hr))
  666. {
  667. mClient = static_cast<IAudioClient*>(ptr);
  668. if(mDevice->DeviceName.empty())
  669. mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
  670. }
  671. if(FAILED(hr))
  672. {
  673. if(mMMDev)
  674. mMMDev->Release();
  675. mMMDev = nullptr;
  676. }
  677. return hr;
  678. }
  679. void WasapiPlayback::closeProxy()
  680. {
  681. if(mClient)
  682. mClient->Release();
  683. mClient = nullptr;
  684. if(mMMDev)
  685. mMMDev->Release();
  686. mMMDev = nullptr;
  687. }
  688. bool WasapiPlayback::reset()
  689. {
  690. HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
  691. if(FAILED(hr))
  692. throw al::backend_exception{ALC_INVALID_VALUE, "0x%08lx", hr};
  693. return true;
  694. }
  695. HRESULT WasapiPlayback::resetProxy()
  696. {
  697. if(mClient)
  698. mClient->Release();
  699. mClient = nullptr;
  700. void *ptr;
  701. HRESULT hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
  702. if(FAILED(hr))
  703. {
  704. ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
  705. return hr;
  706. }
  707. mClient = static_cast<IAudioClient*>(ptr);
  708. WAVEFORMATEX *wfx;
  709. hr = mClient->GetMixFormat(&wfx);
  710. if(FAILED(hr))
  711. {
  712. ERR("Failed to get mix format: 0x%08lx\n", hr);
  713. return hr;
  714. }
  715. WAVEFORMATEXTENSIBLE OutputType;
  716. if(!MakeExtensible(&OutputType, wfx))
  717. {
  718. CoTaskMemFree(wfx);
  719. return E_FAIL;
  720. }
  721. CoTaskMemFree(wfx);
  722. wfx = nullptr;
  723. const REFERENCE_TIME per_time{mDevice->UpdateSize * REFTIME_PER_SEC / mDevice->Frequency};
  724. const REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
  725. if(!mDevice->Flags.get<FrequencyRequest>())
  726. mDevice->Frequency = OutputType.Format.nSamplesPerSec;
  727. if(!mDevice->Flags.get<ChannelsRequest>())
  728. {
  729. if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
  730. mDevice->FmtChans = DevFmtMono;
  731. else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
  732. mDevice->FmtChans = DevFmtStereo;
  733. else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
  734. mDevice->FmtChans = DevFmtQuad;
  735. else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
  736. mDevice->FmtChans = DevFmtX51;
  737. else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
  738. mDevice->FmtChans = DevFmtX51Rear;
  739. else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
  740. mDevice->FmtChans = DevFmtX61;
  741. else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
  742. mDevice->FmtChans = DevFmtX71;
  743. else
  744. ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
  745. }
  746. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  747. switch(mDevice->FmtChans)
  748. {
  749. case DevFmtMono:
  750. OutputType.Format.nChannels = 1;
  751. OutputType.dwChannelMask = MONO;
  752. break;
  753. case DevFmtAmbi3D:
  754. mDevice->FmtChans = DevFmtStereo;
  755. /*fall-through*/
  756. case DevFmtStereo:
  757. OutputType.Format.nChannels = 2;
  758. OutputType.dwChannelMask = STEREO;
  759. break;
  760. case DevFmtQuad:
  761. OutputType.Format.nChannels = 4;
  762. OutputType.dwChannelMask = QUAD;
  763. break;
  764. case DevFmtX51:
  765. OutputType.Format.nChannels = 6;
  766. OutputType.dwChannelMask = X5DOT1;
  767. break;
  768. case DevFmtX51Rear:
  769. OutputType.Format.nChannels = 6;
  770. OutputType.dwChannelMask = X5DOT1REAR;
  771. break;
  772. case DevFmtX61:
  773. OutputType.Format.nChannels = 7;
  774. OutputType.dwChannelMask = X6DOT1;
  775. break;
  776. case DevFmtX71:
  777. OutputType.Format.nChannels = 8;
  778. OutputType.dwChannelMask = X7DOT1;
  779. break;
  780. }
  781. switch(mDevice->FmtType)
  782. {
  783. case DevFmtByte:
  784. mDevice->FmtType = DevFmtUByte;
  785. /* fall-through */
  786. case DevFmtUByte:
  787. OutputType.Format.wBitsPerSample = 8;
  788. OutputType.Samples.wValidBitsPerSample = 8;
  789. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  790. break;
  791. case DevFmtUShort:
  792. mDevice->FmtType = DevFmtShort;
  793. /* fall-through */
  794. case DevFmtShort:
  795. OutputType.Format.wBitsPerSample = 16;
  796. OutputType.Samples.wValidBitsPerSample = 16;
  797. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  798. break;
  799. case DevFmtUInt:
  800. mDevice->FmtType = DevFmtInt;
  801. /* fall-through */
  802. case DevFmtInt:
  803. OutputType.Format.wBitsPerSample = 32;
  804. OutputType.Samples.wValidBitsPerSample = 32;
  805. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  806. break;
  807. case DevFmtFloat:
  808. OutputType.Format.wBitsPerSample = 32;
  809. OutputType.Samples.wValidBitsPerSample = 32;
  810. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  811. break;
  812. }
  813. OutputType.Format.nSamplesPerSec = mDevice->Frequency;
  814. OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
  815. OutputType.Format.wBitsPerSample / 8);
  816. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
  817. OutputType.Format.nBlockAlign;
  818. TraceFormat("Requesting playback format", &OutputType.Format);
  819. hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
  820. if(FAILED(hr))
  821. {
  822. ERR("Failed to check format support: 0x%08lx\n", hr);
  823. hr = mClient->GetMixFormat(&wfx);
  824. }
  825. if(FAILED(hr))
  826. {
  827. ERR("Failed to find a supported format: 0x%08lx\n", hr);
  828. return hr;
  829. }
  830. if(wfx != nullptr)
  831. {
  832. TraceFormat("Got playback format", wfx);
  833. if(!MakeExtensible(&OutputType, wfx))
  834. {
  835. CoTaskMemFree(wfx);
  836. return E_FAIL;
  837. }
  838. CoTaskMemFree(wfx);
  839. wfx = nullptr;
  840. mDevice->Frequency = OutputType.Format.nSamplesPerSec;
  841. if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
  842. mDevice->FmtChans = DevFmtMono;
  843. else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
  844. mDevice->FmtChans = DevFmtStereo;
  845. else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
  846. mDevice->FmtChans = DevFmtQuad;
  847. else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
  848. mDevice->FmtChans = DevFmtX51;
  849. else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
  850. mDevice->FmtChans = DevFmtX51Rear;
  851. else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
  852. mDevice->FmtChans = DevFmtX61;
  853. else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
  854. mDevice->FmtChans = DevFmtX71;
  855. else
  856. {
  857. ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
  858. mDevice->FmtChans = DevFmtStereo;
  859. OutputType.Format.nChannels = 2;
  860. OutputType.dwChannelMask = STEREO;
  861. }
  862. if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
  863. {
  864. if(OutputType.Format.wBitsPerSample == 8)
  865. mDevice->FmtType = DevFmtUByte;
  866. else if(OutputType.Format.wBitsPerSample == 16)
  867. mDevice->FmtType = DevFmtShort;
  868. else if(OutputType.Format.wBitsPerSample == 32)
  869. mDevice->FmtType = DevFmtInt;
  870. else
  871. {
  872. mDevice->FmtType = DevFmtShort;
  873. OutputType.Format.wBitsPerSample = 16;
  874. }
  875. }
  876. else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
  877. {
  878. mDevice->FmtType = DevFmtFloat;
  879. OutputType.Format.wBitsPerSample = 32;
  880. }
  881. else
  882. {
  883. ERR("Unhandled format sub-type\n");
  884. mDevice->FmtType = DevFmtShort;
  885. if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
  886. OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  887. OutputType.Format.wBitsPerSample = 16;
  888. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  889. }
  890. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  891. }
  892. EndpointFormFactor formfactor = UnknownFormFactor;
  893. get_device_formfactor(mMMDev, &formfactor);
  894. mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
  895. (formfactor == Headphones || formfactor == Headset));
  896. SetDefaultWFXChannelOrder(mDevice);
  897. hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
  898. 0, &OutputType.Format, nullptr);
  899. if(FAILED(hr))
  900. {
  901. ERR("Failed to initialize audio client: 0x%08lx\n", hr);
  902. return hr;
  903. }
  904. UINT32 buffer_len{}, min_len{};
  905. REFERENCE_TIME min_per{};
  906. hr = mClient->GetDevicePeriod(&min_per, nullptr);
  907. if(SUCCEEDED(hr))
  908. hr = mClient->GetBufferSize(&buffer_len);
  909. if(FAILED(hr))
  910. {
  911. ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
  912. return hr;
  913. }
  914. /* Find the nearest multiple of the period size to the update size */
  915. if(min_per < per_time)
  916. min_per *= maxi64((per_time + min_per/2) / min_per, 1);
  917. min_len = static_cast<UINT32>(ScaleCeil(min_per, mDevice->Frequency, REFTIME_PER_SEC));
  918. min_len = minu(min_len, buffer_len/2);
  919. mDevice->UpdateSize = min_len;
  920. mDevice->BufferSize = buffer_len;
  921. hr = mClient->SetEventHandle(mNotifyEvent);
  922. if(FAILED(hr))
  923. {
  924. ERR("Failed to set event handle: 0x%08lx\n", hr);
  925. return hr;
  926. }
  927. return hr;
  928. }
  929. bool WasapiPlayback::start()
  930. {
  931. HRESULT hr{pushMessage(MsgType::StartDevice).get()};
  932. return SUCCEEDED(hr) ? true : false;
  933. }
  934. HRESULT WasapiPlayback::startProxy()
  935. {
  936. ResetEvent(mNotifyEvent);
  937. HRESULT hr = mClient->Start();
  938. if(FAILED(hr))
  939. {
  940. ERR("Failed to start audio client: 0x%08lx\n", hr);
  941. return hr;
  942. }
  943. void *ptr;
  944. hr = mClient->GetService(IID_IAudioRenderClient, &ptr);
  945. if(SUCCEEDED(hr))
  946. {
  947. mRender = static_cast<IAudioRenderClient*>(ptr);
  948. try {
  949. mKillNow.store(false, std::memory_order_release);
  950. mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
  951. }
  952. catch(...) {
  953. mRender->Release();
  954. mRender = nullptr;
  955. ERR("Failed to start thread\n");
  956. hr = E_FAIL;
  957. }
  958. }
  959. if(FAILED(hr))
  960. mClient->Stop();
  961. return hr;
  962. }
  963. void WasapiPlayback::stop()
  964. { pushMessage(MsgType::StopDevice).wait(); }
  965. void WasapiPlayback::stopProxy()
  966. {
  967. if(!mRender || !mThread.joinable())
  968. return;
  969. mKillNow.store(true, std::memory_order_release);
  970. mThread.join();
  971. mRender->Release();
  972. mRender = nullptr;
  973. mClient->Stop();
  974. }
  975. ClockLatency WasapiPlayback::getClockLatency()
  976. {
  977. ClockLatency ret;
  978. std::lock_guard<WasapiPlayback> _{*this};
  979. ret.ClockTime = GetDeviceClockTime(mDevice);
  980. ret.Latency = std::chrono::seconds{mPadding.load(std::memory_order_relaxed)};
  981. ret.Latency /= mDevice->Frequency;
  982. return ret;
  983. }
  984. struct WasapiCapture final : public BackendBase, WasapiProxy {
  985. WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
  986. ~WasapiCapture() override;
  987. int recordProc();
  988. void open(const ALCchar *name) override;
  989. HRESULT openProxy() override;
  990. void closeProxy() override;
  991. HRESULT resetProxy() override;
  992. bool start() override;
  993. HRESULT startProxy() override;
  994. void stop() override;
  995. void stopProxy() override;
  996. ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
  997. ALCuint availableSamples() override;
  998. std::wstring mDevId;
  999. IMMDevice *mMMDev{nullptr};
  1000. IAudioClient *mClient{nullptr};
  1001. IAudioCaptureClient *mCapture{nullptr};
  1002. HANDLE mNotifyEvent{nullptr};
  1003. ChannelConverter mChannelConv{};
  1004. SampleConverterPtr mSampleConv;
  1005. RingBufferPtr mRing;
  1006. std::atomic<bool> mKillNow{true};
  1007. std::thread mThread;
  1008. DEF_NEWDEL(WasapiCapture)
  1009. };
  1010. WasapiCapture::~WasapiCapture()
  1011. {
  1012. pushMessage(MsgType::CloseDevice).wait();
  1013. if(mNotifyEvent != nullptr)
  1014. CloseHandle(mNotifyEvent);
  1015. mNotifyEvent = nullptr;
  1016. }
  1017. FORCE_ALIGN int WasapiCapture::recordProc()
  1018. {
  1019. HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  1020. if(FAILED(hr))
  1021. {
  1022. ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
  1023. aluHandleDisconnect(mDevice, "COM init failed: 0x%08lx", hr);
  1024. return 1;
  1025. }
  1026. althrd_setname(RECORD_THREAD_NAME);
  1027. al::vector<float> samples;
  1028. while(!mKillNow.load(std::memory_order_relaxed))
  1029. {
  1030. UINT32 avail;
  1031. hr = mCapture->GetNextPacketSize(&avail);
  1032. if(FAILED(hr))
  1033. ERR("Failed to get next packet size: 0x%08lx\n", hr);
  1034. else if(avail > 0)
  1035. {
  1036. UINT32 numsamples;
  1037. DWORD flags;
  1038. BYTE *rdata;
  1039. hr = mCapture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr);
  1040. if(FAILED(hr))
  1041. ERR("Failed to get capture buffer: 0x%08lx\n", hr);
  1042. else
  1043. {
  1044. if(mChannelConv.is_active())
  1045. {
  1046. samples.resize(numsamples*2);
  1047. mChannelConv.convert(rdata, samples.data(), numsamples);
  1048. rdata = reinterpret_cast<BYTE*>(samples.data());
  1049. }
  1050. auto data = mRing->getWriteVector();
  1051. size_t dstframes;
  1052. if(mSampleConv)
  1053. {
  1054. const ALvoid *srcdata{rdata};
  1055. ALuint srcframes{numsamples};
  1056. dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf,
  1057. static_cast<ALuint>(minz(data.first.len, INT_MAX)));
  1058. if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0)
  1059. {
  1060. /* If some source samples remain, all of the first dest
  1061. * block was filled, and there's space in the second
  1062. * dest block, do another run for the second block.
  1063. */
  1064. dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf,
  1065. static_cast<ALuint>(minz(data.second.len, INT_MAX)));
  1066. }
  1067. }
  1068. else
  1069. {
  1070. const auto framesize = static_cast<ALuint>(mDevice->frameSizeFromFmt());
  1071. size_t len1{minz(data.first.len, numsamples)};
  1072. size_t len2{minz(data.second.len, numsamples-len1)};
  1073. memcpy(data.first.buf, rdata, len1*framesize);
  1074. if(len2 > 0)
  1075. memcpy(data.second.buf, rdata+len1*framesize, len2*framesize);
  1076. dstframes = len1 + len2;
  1077. }
  1078. mRing->writeAdvance(dstframes);
  1079. hr = mCapture->ReleaseBuffer(numsamples);
  1080. if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr);
  1081. }
  1082. }
  1083. if(FAILED(hr))
  1084. {
  1085. aluHandleDisconnect(mDevice, "Failed to capture samples: 0x%08lx", hr);
  1086. break;
  1087. }
  1088. DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)};
  1089. if(res != WAIT_OBJECT_0)
  1090. ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
  1091. }
  1092. CoUninitialize();
  1093. return 0;
  1094. }
  1095. void WasapiCapture::open(const ALCchar *name)
  1096. {
  1097. HRESULT hr{S_OK};
  1098. mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
  1099. if(mNotifyEvent == nullptr)
  1100. {
  1101. ERR("Failed to create notify event: %lu\n", GetLastError());
  1102. hr = E_FAIL;
  1103. }
  1104. if(SUCCEEDED(hr))
  1105. {
  1106. if(name)
  1107. {
  1108. if(CaptureDevices.empty())
  1109. pushMessage(MsgType::EnumerateCapture).wait();
  1110. hr = E_FAIL;
  1111. auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  1112. [name](const DevMap &entry) -> bool
  1113. { return entry.name == name || entry.endpoint_guid == name; }
  1114. );
  1115. if(iter == CaptureDevices.cend())
  1116. {
  1117. std::wstring wname{utf8_to_wstr(name)};
  1118. iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  1119. [&wname](const DevMap &entry) -> bool
  1120. { return entry.devid == wname; }
  1121. );
  1122. }
  1123. if(iter == CaptureDevices.cend())
  1124. WARN("Failed to find device name matching \"%s\"\n", name);
  1125. else
  1126. {
  1127. mDevId = iter->devid;
  1128. mDevice->DeviceName = iter->name;
  1129. hr = S_OK;
  1130. }
  1131. }
  1132. }
  1133. if(SUCCEEDED(hr))
  1134. hr = pushMessage(MsgType::OpenDevice).get();
  1135. if(FAILED(hr))
  1136. {
  1137. if(mNotifyEvent != nullptr)
  1138. CloseHandle(mNotifyEvent);
  1139. mNotifyEvent = nullptr;
  1140. mDevId.clear();
  1141. throw al::backend_exception{ALC_INVALID_VALUE, "Device init failed: 0x%08lx", hr};
  1142. }
  1143. hr = pushMessage(MsgType::ResetDevice).get();
  1144. if(FAILED(hr))
  1145. {
  1146. if(hr == E_OUTOFMEMORY)
  1147. throw al::backend_exception{ALC_OUT_OF_MEMORY, "Out of memory"};
  1148. throw al::backend_exception{ALC_INVALID_VALUE, "Device reset failed"};
  1149. }
  1150. }
  1151. HRESULT WasapiCapture::openProxy()
  1152. {
  1153. void *ptr;
  1154. HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
  1155. IID_IMMDeviceEnumerator, &ptr)};
  1156. if(SUCCEEDED(hr))
  1157. {
  1158. auto Enumerator = static_cast<IMMDeviceEnumerator*>(ptr);
  1159. if(mDevId.empty())
  1160. hr = Enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &mMMDev);
  1161. else
  1162. hr = Enumerator->GetDevice(mDevId.c_str(), &mMMDev);
  1163. Enumerator->Release();
  1164. }
  1165. if(SUCCEEDED(hr))
  1166. hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
  1167. if(SUCCEEDED(hr))
  1168. {
  1169. mClient = static_cast<IAudioClient*>(ptr);
  1170. if(mDevice->DeviceName.empty())
  1171. mDevice->DeviceName = get_device_name_and_guid(mMMDev).first;
  1172. }
  1173. if(FAILED(hr))
  1174. {
  1175. if(mMMDev)
  1176. mMMDev->Release();
  1177. mMMDev = nullptr;
  1178. }
  1179. return hr;
  1180. }
  1181. void WasapiCapture::closeProxy()
  1182. {
  1183. if(mClient)
  1184. mClient->Release();
  1185. mClient = nullptr;
  1186. if(mMMDev)
  1187. mMMDev->Release();
  1188. mMMDev = nullptr;
  1189. }
  1190. HRESULT WasapiCapture::resetProxy()
  1191. {
  1192. if(mClient)
  1193. mClient->Release();
  1194. mClient = nullptr;
  1195. void *ptr;
  1196. HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
  1197. if(FAILED(hr))
  1198. {
  1199. ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
  1200. return hr;
  1201. }
  1202. mClient = static_cast<IAudioClient*>(ptr);
  1203. // Make sure buffer is at least 100ms in size
  1204. REFERENCE_TIME buf_time{mDevice->BufferSize * REFTIME_PER_SEC / mDevice->Frequency};
  1205. buf_time = maxu64(buf_time, REFTIME_PER_SEC/10);
  1206. WAVEFORMATEXTENSIBLE OutputType{};
  1207. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  1208. switch(mDevice->FmtChans)
  1209. {
  1210. case DevFmtMono:
  1211. OutputType.Format.nChannels = 1;
  1212. OutputType.dwChannelMask = MONO;
  1213. break;
  1214. case DevFmtStereo:
  1215. OutputType.Format.nChannels = 2;
  1216. OutputType.dwChannelMask = STEREO;
  1217. break;
  1218. case DevFmtQuad:
  1219. OutputType.Format.nChannels = 4;
  1220. OutputType.dwChannelMask = QUAD;
  1221. break;
  1222. case DevFmtX51:
  1223. OutputType.Format.nChannels = 6;
  1224. OutputType.dwChannelMask = X5DOT1;
  1225. break;
  1226. case DevFmtX51Rear:
  1227. OutputType.Format.nChannels = 6;
  1228. OutputType.dwChannelMask = X5DOT1REAR;
  1229. break;
  1230. case DevFmtX61:
  1231. OutputType.Format.nChannels = 7;
  1232. OutputType.dwChannelMask = X6DOT1;
  1233. break;
  1234. case DevFmtX71:
  1235. OutputType.Format.nChannels = 8;
  1236. OutputType.dwChannelMask = X7DOT1;
  1237. break;
  1238. case DevFmtAmbi3D:
  1239. return E_FAIL;
  1240. }
  1241. switch(mDevice->FmtType)
  1242. {
  1243. /* NOTE: Signedness doesn't matter, the converter will handle it. */
  1244. case DevFmtByte:
  1245. case DevFmtUByte:
  1246. OutputType.Format.wBitsPerSample = 8;
  1247. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  1248. break;
  1249. case DevFmtShort:
  1250. case DevFmtUShort:
  1251. OutputType.Format.wBitsPerSample = 16;
  1252. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  1253. break;
  1254. case DevFmtInt:
  1255. case DevFmtUInt:
  1256. OutputType.Format.wBitsPerSample = 32;
  1257. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  1258. break;
  1259. case DevFmtFloat:
  1260. OutputType.Format.wBitsPerSample = 32;
  1261. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  1262. break;
  1263. }
  1264. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  1265. OutputType.Format.nSamplesPerSec = mDevice->Frequency;
  1266. OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
  1267. OutputType.Format.wBitsPerSample / 8);
  1268. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
  1269. OutputType.Format.nBlockAlign;
  1270. OutputType.Format.cbSize = sizeof(OutputType) - sizeof(OutputType.Format);
  1271. TraceFormat("Requesting capture format", &OutputType.Format);
  1272. WAVEFORMATEX *wfx;
  1273. hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
  1274. if(FAILED(hr))
  1275. {
  1276. ERR("Failed to check format support: 0x%08lx\n", hr);
  1277. return hr;
  1278. }
  1279. mSampleConv = nullptr;
  1280. mChannelConv = {};
  1281. if(wfx != nullptr)
  1282. {
  1283. TraceFormat("Got capture format", wfx);
  1284. if(!(wfx->nChannels == OutputType.Format.nChannels ||
  1285. (wfx->nChannels == 1 && OutputType.Format.nChannels == 2) ||
  1286. (wfx->nChannels == 2 && OutputType.Format.nChannels == 1)))
  1287. {
  1288. ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
  1289. DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
  1290. mDevice->Frequency, wfx->nChannels, (wfx->nChannels==1)?"":"s", wfx->wBitsPerSample,
  1291. wfx->nSamplesPerSec);
  1292. CoTaskMemFree(wfx);
  1293. return E_FAIL;
  1294. }
  1295. if(!MakeExtensible(&OutputType, wfx))
  1296. {
  1297. CoTaskMemFree(wfx);
  1298. return E_FAIL;
  1299. }
  1300. CoTaskMemFree(wfx);
  1301. wfx = nullptr;
  1302. }
  1303. DevFmtType srcType;
  1304. if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
  1305. {
  1306. if(OutputType.Format.wBitsPerSample == 8)
  1307. srcType = DevFmtUByte;
  1308. else if(OutputType.Format.wBitsPerSample == 16)
  1309. srcType = DevFmtShort;
  1310. else if(OutputType.Format.wBitsPerSample == 32)
  1311. srcType = DevFmtInt;
  1312. else
  1313. {
  1314. ERR("Unhandled integer bit depth: %d\n", OutputType.Format.wBitsPerSample);
  1315. return E_FAIL;
  1316. }
  1317. }
  1318. else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
  1319. {
  1320. if(OutputType.Format.wBitsPerSample == 32)
  1321. srcType = DevFmtFloat;
  1322. else
  1323. {
  1324. ERR("Unhandled float bit depth: %d\n", OutputType.Format.wBitsPerSample);
  1325. return E_FAIL;
  1326. }
  1327. }
  1328. else
  1329. {
  1330. ERR("Unhandled format sub-type\n");
  1331. return E_FAIL;
  1332. }
  1333. if(mDevice->FmtChans == DevFmtMono && OutputType.Format.nChannels == 2)
  1334. {
  1335. mChannelConv = ChannelConverter{srcType, DevFmtStereo, mDevice->FmtChans};
  1336. TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType));
  1337. /* The channel converter always outputs float, so change the input type
  1338. * for the resampler/type-converter.
  1339. */
  1340. srcType = DevFmtFloat;
  1341. }
  1342. else if(mDevice->FmtChans == DevFmtStereo && OutputType.Format.nChannels == 1)
  1343. {
  1344. mChannelConv = ChannelConverter{srcType, DevFmtMono, mDevice->FmtChans};
  1345. TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType));
  1346. srcType = DevFmtFloat;
  1347. }
  1348. if(mDevice->Frequency != OutputType.Format.nSamplesPerSec || mDevice->FmtType != srcType)
  1349. {
  1350. mSampleConv = CreateSampleConverter(srcType, mDevice->FmtType, mDevice->channelsFromFmt(),
  1351. OutputType.Format.nSamplesPerSec, mDevice->Frequency, Resampler::FastBSinc24);
  1352. if(!mSampleConv)
  1353. {
  1354. ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
  1355. DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
  1356. mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
  1357. return E_FAIL;
  1358. }
  1359. TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
  1360. DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
  1361. mDevice->Frequency, DevFmtTypeString(srcType), OutputType.Format.nSamplesPerSec);
  1362. }
  1363. hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time,
  1364. 0, &OutputType.Format, nullptr);
  1365. if(FAILED(hr))
  1366. {
  1367. ERR("Failed to initialize audio client: 0x%08lx\n", hr);
  1368. return hr;
  1369. }
  1370. UINT32 buffer_len{};
  1371. REFERENCE_TIME min_per{};
  1372. hr = mClient->GetDevicePeriod(&min_per, nullptr);
  1373. if(SUCCEEDED(hr))
  1374. hr = mClient->GetBufferSize(&buffer_len);
  1375. if(FAILED(hr))
  1376. {
  1377. ERR("Failed to get buffer size: 0x%08lx\n", hr);
  1378. return hr;
  1379. }
  1380. mDevice->UpdateSize = static_cast<ALuint>(ScaleCeil(min_per, mDevice->Frequency,
  1381. REFTIME_PER_SEC));
  1382. mDevice->BufferSize = buffer_len;
  1383. buffer_len = maxu(mDevice->BufferSize, buffer_len);
  1384. mRing = CreateRingBuffer(buffer_len, mDevice->frameSizeFromFmt(), false);
  1385. hr = mClient->SetEventHandle(mNotifyEvent);
  1386. if(FAILED(hr))
  1387. {
  1388. ERR("Failed to set event handle: 0x%08lx\n", hr);
  1389. return hr;
  1390. }
  1391. return hr;
  1392. }
  1393. bool WasapiCapture::start()
  1394. {
  1395. HRESULT hr{pushMessage(MsgType::StartDevice).get()};
  1396. return SUCCEEDED(hr) ? true : false;
  1397. }
  1398. HRESULT WasapiCapture::startProxy()
  1399. {
  1400. ResetEvent(mNotifyEvent);
  1401. HRESULT hr{mClient->Start()};
  1402. if(FAILED(hr))
  1403. {
  1404. ERR("Failed to start audio client: 0x%08lx\n", hr);
  1405. return hr;
  1406. }
  1407. void *ptr;
  1408. hr = mClient->GetService(IID_IAudioCaptureClient, &ptr);
  1409. if(SUCCEEDED(hr))
  1410. {
  1411. mCapture = static_cast<IAudioCaptureClient*>(ptr);
  1412. try {
  1413. mKillNow.store(false, std::memory_order_release);
  1414. mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this};
  1415. }
  1416. catch(...) {
  1417. mCapture->Release();
  1418. mCapture = nullptr;
  1419. ERR("Failed to start thread\n");
  1420. hr = E_FAIL;
  1421. }
  1422. }
  1423. if(FAILED(hr))
  1424. {
  1425. mClient->Stop();
  1426. mClient->Reset();
  1427. }
  1428. return hr;
  1429. }
  1430. void WasapiCapture::stop()
  1431. { pushMessage(MsgType::StopDevice).wait(); }
  1432. void WasapiCapture::stopProxy()
  1433. {
  1434. if(!mCapture || !mThread.joinable())
  1435. return;
  1436. mKillNow.store(true, std::memory_order_release);
  1437. mThread.join();
  1438. mCapture->Release();
  1439. mCapture = nullptr;
  1440. mClient->Stop();
  1441. mClient->Reset();
  1442. }
  1443. ALCuint WasapiCapture::availableSamples()
  1444. { return static_cast<ALCuint>(mRing->readSpace()); }
  1445. ALCenum WasapiCapture::captureSamples(al::byte *buffer, ALCuint samples)
  1446. {
  1447. mRing->read(buffer, samples);
  1448. return ALC_NO_ERROR;
  1449. }
  1450. } // namespace
  1451. bool WasapiBackendFactory::init()
  1452. {
  1453. static HRESULT InitResult{E_FAIL};
  1454. if(FAILED(InitResult)) try
  1455. {
  1456. std::promise<HRESULT> promise;
  1457. auto future = promise.get_future();
  1458. std::thread{&WasapiProxy::messageHandler, &promise}.detach();
  1459. InitResult = future.get();
  1460. }
  1461. catch(...) {
  1462. }
  1463. return SUCCEEDED(InitResult) ? ALC_TRUE : ALC_FALSE;
  1464. }
  1465. bool WasapiBackendFactory::querySupport(BackendType type)
  1466. { return type == BackendType::Playback || type == BackendType::Capture; }
  1467. void WasapiBackendFactory::probe(DevProbe type, std::string *outnames)
  1468. {
  1469. auto add_device = [outnames](const DevMap &entry) -> void
  1470. {
  1471. /* +1 to also append the null char (to ensure a null-separated list and
  1472. * double-null terminated list).
  1473. */
  1474. outnames->append(entry.name.c_str(), entry.name.length()+1);
  1475. };
  1476. HRESULT hr{};
  1477. switch(type)
  1478. {
  1479. case DevProbe::Playback:
  1480. hr = WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).get();
  1481. if(SUCCEEDED(hr))
  1482. std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
  1483. break;
  1484. case DevProbe::Capture:
  1485. hr = WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).get();
  1486. if(SUCCEEDED(hr))
  1487. std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
  1488. break;
  1489. }
  1490. }
  1491. BackendPtr WasapiBackendFactory::createBackend(ALCdevice *device, BackendType type)
  1492. {
  1493. if(type == BackendType::Playback)
  1494. return BackendPtr{new WasapiPlayback{device}};
  1495. if(type == BackendType::Capture)
  1496. return BackendPtr{new WasapiCapture{device}};
  1497. return nullptr;
  1498. }
  1499. BackendFactory &WasapiBackendFactory::getFactory()
  1500. {
  1501. static WasapiBackendFactory factory{};
  1502. return factory;
  1503. }