🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1703 lines
51 KiB

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