🛠️🐜 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.

644 lines
19 KiB

  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 1999-2007 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/winmm.h"
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <memory.h>
  25. #include <windows.h>
  26. #include <mmsystem.h>
  27. #include <array>
  28. #include <atomic>
  29. #include <thread>
  30. #include <vector>
  31. #include <string>
  32. #include <algorithm>
  33. #include <functional>
  34. #include "alMain.h"
  35. #include "alu.h"
  36. #include "ringbuffer.h"
  37. #include "threads.h"
  38. #include "compat.h"
  39. #ifndef WAVE_FORMAT_IEEE_FLOAT
  40. #define WAVE_FORMAT_IEEE_FLOAT 0x0003
  41. #endif
  42. namespace {
  43. #define DEVNAME_HEAD "OpenAL Soft on "
  44. al::vector<std::string> PlaybackDevices;
  45. al::vector<std::string> CaptureDevices;
  46. bool checkName(const al::vector<std::string> &list, const std::string &name)
  47. { return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
  48. void ProbePlaybackDevices(void)
  49. {
  50. PlaybackDevices.clear();
  51. ALuint numdevs{waveOutGetNumDevs()};
  52. PlaybackDevices.reserve(numdevs);
  53. for(ALuint i{0};i < numdevs;i++)
  54. {
  55. std::string dname;
  56. WAVEOUTCAPSW WaveCaps{};
  57. if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
  58. {
  59. const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
  60. int count{1};
  61. std::string newname{basename};
  62. while(checkName(PlaybackDevices, newname))
  63. {
  64. newname = basename;
  65. newname += " #";
  66. newname += std::to_string(++count);
  67. }
  68. dname = std::move(newname);
  69. TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
  70. }
  71. PlaybackDevices.emplace_back(std::move(dname));
  72. }
  73. }
  74. void ProbeCaptureDevices(void)
  75. {
  76. CaptureDevices.clear();
  77. ALuint numdevs{waveInGetNumDevs()};
  78. CaptureDevices.reserve(numdevs);
  79. for(ALuint i{0};i < numdevs;i++)
  80. {
  81. std::string dname;
  82. WAVEINCAPSW WaveCaps{};
  83. if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
  84. {
  85. const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
  86. int count{1};
  87. std::string newname{basename};
  88. while(checkName(CaptureDevices, newname))
  89. {
  90. newname = basename;
  91. newname += " #";
  92. newname += std::to_string(++count);
  93. }
  94. dname = std::move(newname);
  95. TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
  96. }
  97. CaptureDevices.emplace_back(std::move(dname));
  98. }
  99. }
  100. struct WinMMPlayback final : public BackendBase {
  101. WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
  102. ~WinMMPlayback() override;
  103. static void CALLBACK waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
  104. void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
  105. int mixerProc();
  106. ALCenum open(const ALCchar *name) override;
  107. ALCboolean reset() override;
  108. ALCboolean start() override;
  109. void stop() override;
  110. std::atomic<ALuint> mWritable{0u};
  111. al::semaphore mSem;
  112. int mIdx{0};
  113. std::array<WAVEHDR,4> mWaveBuffer{};
  114. HWAVEOUT mOutHdl{nullptr};
  115. WAVEFORMATEX mFormat{};
  116. std::atomic<bool> mKillNow{true};
  117. std::thread mThread;
  118. static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMPlayback::"; }
  119. DEF_NEWDEL(WinMMPlayback)
  120. };
  121. WinMMPlayback::~WinMMPlayback()
  122. {
  123. if(mOutHdl)
  124. waveOutClose(mOutHdl);
  125. mOutHdl = nullptr;
  126. al_free(mWaveBuffer[0].lpData);
  127. std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
  128. }
  129. void CALLBACK WinMMPlayback::waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
  130. { reinterpret_cast<WinMMPlayback*>(instance)->waveOutProc(device, msg, param1, param2); }
  131. /* WinMMPlayback::waveOutProc
  132. *
  133. * Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
  134. * completed and returns to the application (for more data)
  135. */
  136. void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT UNUSED(device), UINT msg,
  137. DWORD_PTR UNUSED(param1), DWORD_PTR UNUSED(param2))
  138. {
  139. if(msg != WOM_DONE) return;
  140. mWritable.fetch_add(1, std::memory_order_acq_rel);
  141. mSem.post();
  142. }
  143. FORCE_ALIGN int WinMMPlayback::mixerProc()
  144. {
  145. SetRTPriority();
  146. althrd_setname(MIXER_THREAD_NAME);
  147. lock();
  148. while(!mKillNow.load(std::memory_order_acquire) &&
  149. mDevice->Connected.load(std::memory_order_acquire))
  150. {
  151. ALsizei todo = mWritable.load(std::memory_order_acquire);
  152. if(todo < 1)
  153. {
  154. unlock();
  155. mSem.wait();
  156. lock();
  157. continue;
  158. }
  159. int widx{mIdx};
  160. do {
  161. WAVEHDR &waveHdr = mWaveBuffer[widx];
  162. widx = (widx+1) % mWaveBuffer.size();
  163. aluMixData(mDevice, waveHdr.lpData, mDevice->UpdateSize);
  164. mWritable.fetch_sub(1, std::memory_order_acq_rel);
  165. waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
  166. } while(--todo);
  167. mIdx = widx;
  168. }
  169. unlock();
  170. return 0;
  171. }
  172. ALCenum WinMMPlayback::open(const ALCchar *name)
  173. {
  174. if(PlaybackDevices.empty())
  175. ProbePlaybackDevices();
  176. // Find the Device ID matching the deviceName if valid
  177. auto iter = name ?
  178. std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
  179. PlaybackDevices.cbegin();
  180. if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE;
  181. auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
  182. retry_open:
  183. mFormat = WAVEFORMATEX{};
  184. if(mDevice->FmtType == DevFmtFloat)
  185. {
  186. mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
  187. mFormat.wBitsPerSample = 32;
  188. }
  189. else
  190. {
  191. mFormat.wFormatTag = WAVE_FORMAT_PCM;
  192. if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
  193. mFormat.wBitsPerSample = 8;
  194. else
  195. mFormat.wBitsPerSample = 16;
  196. }
  197. mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
  198. mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
  199. mFormat.nSamplesPerSec = mDevice->Frequency;
  200. mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
  201. mFormat.cbSize = 0;
  202. MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMPlayback::waveOutProcC,
  203. reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
  204. if(res != MMSYSERR_NOERROR)
  205. {
  206. if(mDevice->FmtType == DevFmtFloat)
  207. {
  208. mDevice->FmtType = DevFmtShort;
  209. goto retry_open;
  210. }
  211. ERR("waveOutOpen failed: %u\n", res);
  212. return ALC_INVALID_VALUE;
  213. }
  214. mDevice->DeviceName = PlaybackDevices[DeviceID];
  215. return ALC_NO_ERROR;
  216. }
  217. ALCboolean WinMMPlayback::reset()
  218. {
  219. mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} *
  220. mFormat.nSamplesPerSec / mDevice->Frequency);
  221. mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3;
  222. mDevice->UpdateSize = mDevice->BufferSize / 4;
  223. mDevice->Frequency = mFormat.nSamplesPerSec;
  224. if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
  225. {
  226. if(mFormat.wBitsPerSample == 32)
  227. mDevice->FmtType = DevFmtFloat;
  228. else
  229. {
  230. ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
  231. return ALC_FALSE;
  232. }
  233. }
  234. else if(mFormat.wFormatTag == WAVE_FORMAT_PCM)
  235. {
  236. if(mFormat.wBitsPerSample == 16)
  237. mDevice->FmtType = DevFmtShort;
  238. else if(mFormat.wBitsPerSample == 8)
  239. mDevice->FmtType = DevFmtUByte;
  240. else
  241. {
  242. ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
  243. return ALC_FALSE;
  244. }
  245. }
  246. else
  247. {
  248. ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
  249. return ALC_FALSE;
  250. }
  251. if(mFormat.nChannels == 2)
  252. mDevice->FmtChans = DevFmtStereo;
  253. else if(mFormat.nChannels == 1)
  254. mDevice->FmtChans = DevFmtMono;
  255. else
  256. {
  257. ERR("Unhandled channel count: %d\n", mFormat.nChannels);
  258. return ALC_FALSE;
  259. }
  260. SetDefaultWFXChannelOrder(mDevice);
  261. ALuint BufferSize{mDevice->UpdateSize * mDevice->frameSizeFromFmt()};
  262. al_free(mWaveBuffer[0].lpData);
  263. mWaveBuffer[0] = WAVEHDR{};
  264. mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
  265. mWaveBuffer[0].dwBufferLength = BufferSize;
  266. for(size_t i{1};i < mWaveBuffer.size();i++)
  267. {
  268. mWaveBuffer[i] = WAVEHDR{};
  269. mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
  270. mWaveBuffer[i].dwBufferLength = BufferSize;
  271. }
  272. mIdx = 0;
  273. return ALC_TRUE;
  274. }
  275. ALCboolean WinMMPlayback::start()
  276. {
  277. try {
  278. std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
  279. [this](WAVEHDR &waveHdr) -> void
  280. { waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
  281. );
  282. mWritable.store(static_cast<ALuint>(mWaveBuffer.size()), std::memory_order_release);
  283. mKillNow.store(false, std::memory_order_release);
  284. mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
  285. return ALC_TRUE;
  286. }
  287. catch(std::exception& e) {
  288. ERR("Failed to start mixing thread: %s\n", e.what());
  289. }
  290. catch(...) {
  291. }
  292. return ALC_FALSE;
  293. }
  294. void WinMMPlayback::stop()
  295. {
  296. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  297. return;
  298. mThread.join();
  299. while(mWritable.load(std::memory_order_acquire) < mWaveBuffer.size())
  300. mSem.wait();
  301. std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
  302. [this](WAVEHDR &waveHdr) -> void
  303. { waveOutUnprepareHeader(mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
  304. );
  305. mWritable.store(0, std::memory_order_release);
  306. }
  307. struct WinMMCapture final : public BackendBase {
  308. WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
  309. ~WinMMCapture() override;
  310. static void CALLBACK waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
  311. void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
  312. int captureProc();
  313. ALCenum open(const ALCchar *name) override;
  314. ALCboolean start() override;
  315. void stop() override;
  316. ALCenum captureSamples(void *buffer, ALCuint samples) override;
  317. ALCuint availableSamples() override;
  318. std::atomic<ALuint> mReadable{0u};
  319. al::semaphore mSem;
  320. int mIdx{0};
  321. std::array<WAVEHDR,4> mWaveBuffer{};
  322. HWAVEIN mInHdl{nullptr};
  323. RingBufferPtr mRing{nullptr};
  324. WAVEFORMATEX mFormat{};
  325. std::atomic<bool> mKillNow{true};
  326. std::thread mThread;
  327. static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMCapture::"; }
  328. DEF_NEWDEL(WinMMCapture)
  329. };
  330. WinMMCapture::~WinMMCapture()
  331. {
  332. // Close the Wave device
  333. if(mInHdl)
  334. waveInClose(mInHdl);
  335. mInHdl = nullptr;
  336. al_free(mWaveBuffer[0].lpData);
  337. std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
  338. }
  339. void CALLBACK WinMMCapture::waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
  340. { reinterpret_cast<WinMMCapture*>(instance)->waveInProc(device, msg, param1, param2); }
  341. /* WinMMCapture::waveInProc
  342. *
  343. * Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
  344. * completed and returns to the application (with more data).
  345. */
  346. void CALLBACK WinMMCapture::waveInProc(HWAVEIN UNUSED(device), UINT msg,
  347. DWORD_PTR UNUSED(param1), DWORD_PTR UNUSED(param2))
  348. {
  349. if(msg != WIM_DATA) return;
  350. mReadable.fetch_add(1, std::memory_order_acq_rel);
  351. mSem.post();
  352. }
  353. int WinMMCapture::captureProc()
  354. {
  355. althrd_setname(RECORD_THREAD_NAME);
  356. lock();
  357. while(!mKillNow.load(std::memory_order_acquire) &&
  358. mDevice->Connected.load(std::memory_order_acquire))
  359. {
  360. ALuint todo{mReadable.load(std::memory_order_acquire)};
  361. if(todo < 1)
  362. {
  363. unlock();
  364. mSem.wait();
  365. lock();
  366. continue;
  367. }
  368. int widx{mIdx};
  369. do {
  370. WAVEHDR &waveHdr = mWaveBuffer[widx];
  371. widx = (widx+1) % mWaveBuffer.size();
  372. mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
  373. mReadable.fetch_sub(1, std::memory_order_acq_rel);
  374. waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
  375. } while(--todo);
  376. mIdx = widx;
  377. }
  378. unlock();
  379. return 0;
  380. }
  381. ALCenum WinMMCapture::open(const ALCchar *name)
  382. {
  383. if(CaptureDevices.empty())
  384. ProbeCaptureDevices();
  385. // Find the Device ID matching the deviceName if valid
  386. auto iter = name ?
  387. std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
  388. CaptureDevices.cbegin();
  389. if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE;
  390. auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
  391. switch(mDevice->FmtChans)
  392. {
  393. case DevFmtMono:
  394. case DevFmtStereo:
  395. break;
  396. case DevFmtQuad:
  397. case DevFmtX51:
  398. case DevFmtX51Rear:
  399. case DevFmtX61:
  400. case DevFmtX71:
  401. case DevFmtAmbi3D:
  402. return ALC_INVALID_ENUM;
  403. }
  404. switch(mDevice->FmtType)
  405. {
  406. case DevFmtUByte:
  407. case DevFmtShort:
  408. case DevFmtInt:
  409. case DevFmtFloat:
  410. break;
  411. case DevFmtByte:
  412. case DevFmtUShort:
  413. case DevFmtUInt:
  414. return ALC_INVALID_ENUM;
  415. }
  416. mFormat = WAVEFORMATEX{};
  417. mFormat.wFormatTag = (mDevice->FmtType == DevFmtFloat) ?
  418. WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
  419. mFormat.nChannels = mDevice->channelsFromFmt();
  420. mFormat.wBitsPerSample = mDevice->bytesFromFmt() * 8;
  421. mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
  422. mFormat.nSamplesPerSec = mDevice->Frequency;
  423. mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
  424. mFormat.cbSize = 0;
  425. MMRESULT res{waveInOpen(&mInHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMCapture::waveInProcC,
  426. reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
  427. if(res != MMSYSERR_NOERROR)
  428. {
  429. ERR("waveInOpen failed: %u\n", res);
  430. return ALC_INVALID_VALUE;
  431. }
  432. // Ensure each buffer is 50ms each
  433. DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
  434. BufferSize -= (BufferSize % mFormat.nBlockAlign);
  435. // Allocate circular memory buffer for the captured audio
  436. // Make sure circular buffer is at least 100ms in size
  437. ALuint CapturedDataSize{mDevice->BufferSize};
  438. CapturedDataSize = static_cast<ALuint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
  439. mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false);
  440. if(!mRing) return ALC_INVALID_VALUE;
  441. al_free(mWaveBuffer[0].lpData);
  442. mWaveBuffer[0] = WAVEHDR{};
  443. mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
  444. mWaveBuffer[0].dwBufferLength = BufferSize;
  445. for(size_t i{1};i < mWaveBuffer.size();++i)
  446. {
  447. mWaveBuffer[i] = WAVEHDR{};
  448. mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
  449. mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
  450. }
  451. mDevice->DeviceName = CaptureDevices[DeviceID];
  452. return ALC_NO_ERROR;
  453. }
  454. ALCboolean WinMMCapture::start()
  455. {
  456. try {
  457. for(size_t i{0};i < mWaveBuffer.size();++i)
  458. {
  459. waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
  460. waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
  461. }
  462. mKillNow.store(false, std::memory_order_release);
  463. mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
  464. waveInStart(mInHdl);
  465. return ALC_TRUE;
  466. }
  467. catch(std::exception& e) {
  468. ERR("Failed to start mixing thread: %s\n", e.what());
  469. }
  470. catch(...) {
  471. }
  472. return ALC_FALSE;
  473. }
  474. void WinMMCapture::stop()
  475. {
  476. waveInStop(mInHdl);
  477. mKillNow.store(true, std::memory_order_release);
  478. if(mThread.joinable())
  479. {
  480. mSem.post();
  481. mThread.join();
  482. }
  483. waveInReset(mInHdl);
  484. for(size_t i{0};i < mWaveBuffer.size();++i)
  485. waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
  486. mReadable.store(0, std::memory_order_release);
  487. mIdx = 0;
  488. }
  489. ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples)
  490. {
  491. mRing->read(buffer, samples);
  492. return ALC_NO_ERROR;
  493. }
  494. ALCuint WinMMCapture::availableSamples()
  495. { return (ALCuint)mRing->readSpace(); }
  496. } // namespace
  497. bool WinMMBackendFactory::init()
  498. { return true; }
  499. bool WinMMBackendFactory::querySupport(BackendType type)
  500. { return type == BackendType::Playback || type == BackendType::Capture; }
  501. void WinMMBackendFactory::probe(DevProbe type, std::string *outnames)
  502. {
  503. auto add_device = [outnames](const std::string &dname) -> void
  504. {
  505. /* +1 to also append the null char (to ensure a null-separated list and
  506. * double-null terminated list).
  507. */
  508. if(!dname.empty())
  509. outnames->append(dname.c_str(), dname.length()+1);
  510. };
  511. switch(type)
  512. {
  513. case DevProbe::Playback:
  514. ProbePlaybackDevices();
  515. std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
  516. break;
  517. case DevProbe::Capture:
  518. ProbeCaptureDevices();
  519. std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
  520. break;
  521. }
  522. }
  523. BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
  524. {
  525. if(type == BackendType::Playback)
  526. return BackendPtr{new WinMMPlayback{device}};
  527. if(type == BackendType::Capture)
  528. return BackendPtr{new WinMMCapture{device}};
  529. return nullptr;
  530. }
  531. BackendFactory &WinMMBackendFactory::getFactory()
  532. {
  533. static WinMMBackendFactory factory{};
  534. return factory;
  535. }