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

935 lines
30 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/dsound.h"
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <memory.h>
  25. #include <cguid.h>
  26. #include <mmreg.h>
  27. #ifndef _WAVEFORMATEXTENSIBLE_
  28. #include <ks.h>
  29. #include <ksmedia.h>
  30. #endif
  31. #include <atomic>
  32. #include <thread>
  33. #include <string>
  34. #include <vector>
  35. #include <algorithm>
  36. #include <functional>
  37. #include "alMain.h"
  38. #include "alu.h"
  39. #include "ringbuffer.h"
  40. #include "compat.h"
  41. /* MinGW-w64 needs this for some unknown reason now. */
  42. using LPCWAVEFORMATEX = const WAVEFORMATEX*;
  43. #include <dsound.h>
  44. #ifndef DSSPEAKER_5POINT1
  45. # define DSSPEAKER_5POINT1 0x00000006
  46. #endif
  47. #ifndef DSSPEAKER_5POINT1_BACK
  48. # define DSSPEAKER_5POINT1_BACK 0x00000006
  49. #endif
  50. #ifndef DSSPEAKER_7POINT1
  51. # define DSSPEAKER_7POINT1 0x00000007
  52. #endif
  53. #ifndef DSSPEAKER_7POINT1_SURROUND
  54. # define DSSPEAKER_7POINT1_SURROUND 0x00000008
  55. #endif
  56. #ifndef DSSPEAKER_5POINT1_SURROUND
  57. # define DSSPEAKER_5POINT1_SURROUND 0x00000009
  58. #endif
  59. /* Some headers seem to define these as macros for __uuidof, which is annoying
  60. * since some headers don't declare them at all. Hopefully the ifdef is enough
  61. * to tell if they need to be declared.
  62. */
  63. #ifndef KSDATAFORMAT_SUBTYPE_PCM
  64. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  65. #endif
  66. #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
  67. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  68. #endif
  69. namespace {
  70. #define DEVNAME_HEAD "OpenAL Soft on "
  71. #ifdef HAVE_DYNLOAD
  72. void *ds_handle;
  73. HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
  74. HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  75. HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
  76. HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  77. #ifndef IN_IDE_PARSER
  78. #define DirectSoundCreate pDirectSoundCreate
  79. #define DirectSoundEnumerateW pDirectSoundEnumerateW
  80. #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
  81. #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
  82. #endif
  83. #endif
  84. #define MAX_UPDATES 128
  85. struct DevMap {
  86. std::string name;
  87. GUID guid;
  88. template<typename T0, typename T1>
  89. DevMap(T0&& name_, T1&& guid_)
  90. : name{std::forward<T0>(name_)}, guid{std::forward<T1>(guid_)}
  91. { }
  92. };
  93. al::vector<DevMap> PlaybackDevices;
  94. al::vector<DevMap> CaptureDevices;
  95. bool checkName(const al::vector<DevMap> &list, const std::string &name)
  96. {
  97. return std::find_if(list.cbegin(), list.cend(),
  98. [&name](const DevMap &entry) -> bool
  99. { return entry.name == name; }
  100. ) != list.cend();
  101. }
  102. BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
  103. {
  104. if(!guid)
  105. return TRUE;
  106. auto& devices = *static_cast<al::vector<DevMap>*>(data);
  107. const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
  108. int count{1};
  109. std::string newname{basename};
  110. while(checkName(devices, newname))
  111. {
  112. newname = basename;
  113. newname += " #";
  114. newname += std::to_string(++count);
  115. }
  116. devices.emplace_back(std::move(newname), *guid);
  117. const DevMap &newentry = devices.back();
  118. OLECHAR *guidstr{nullptr};
  119. HRESULT hr{StringFromCLSID(*guid, &guidstr)};
  120. if(SUCCEEDED(hr))
  121. {
  122. TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr);
  123. CoTaskMemFree(guidstr);
  124. }
  125. return TRUE;
  126. }
  127. struct DSoundPlayback final : public BackendBase {
  128. DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
  129. ~DSoundPlayback() override;
  130. int mixerProc();
  131. ALCenum open(const ALCchar *name) override;
  132. ALCboolean reset() override;
  133. ALCboolean start() override;
  134. void stop() override;
  135. IDirectSound *mDS{nullptr};
  136. IDirectSoundBuffer *mPrimaryBuffer{nullptr};
  137. IDirectSoundBuffer *mBuffer{nullptr};
  138. IDirectSoundNotify *mNotifies{nullptr};
  139. HANDLE mNotifyEvent{nullptr};
  140. std::atomic<bool> mKillNow{true};
  141. std::thread mThread;
  142. static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundPlayback::"; }
  143. DEF_NEWDEL(DSoundPlayback)
  144. };
  145. DSoundPlayback::~DSoundPlayback()
  146. {
  147. if(mNotifies)
  148. mNotifies->Release();
  149. mNotifies = nullptr;
  150. if(mBuffer)
  151. mBuffer->Release();
  152. mBuffer = nullptr;
  153. if(mPrimaryBuffer)
  154. mPrimaryBuffer->Release();
  155. mPrimaryBuffer = nullptr;
  156. if(mDS)
  157. mDS->Release();
  158. mDS = nullptr;
  159. if(mNotifyEvent)
  160. CloseHandle(mNotifyEvent);
  161. mNotifyEvent = nullptr;
  162. }
  163. FORCE_ALIGN int DSoundPlayback::mixerProc()
  164. {
  165. SetRTPriority();
  166. althrd_setname(MIXER_THREAD_NAME);
  167. DSBCAPS DSBCaps{};
  168. DSBCaps.dwSize = sizeof(DSBCaps);
  169. HRESULT err{mBuffer->GetCaps(&DSBCaps)};
  170. if(FAILED(err))
  171. {
  172. ERR("Failed to get buffer caps: 0x%lx\n", err);
  173. aluHandleDisconnect(mDevice, "Failure retrieving playback buffer info: 0x%lx", err);
  174. return 1;
  175. }
  176. ALsizei FrameSize{mDevice->frameSizeFromFmt()};
  177. DWORD FragSize{mDevice->UpdateSize * FrameSize};
  178. bool Playing{false};
  179. DWORD LastCursor{0u};
  180. mBuffer->GetCurrentPosition(&LastCursor, nullptr);
  181. while(!mKillNow.load(std::memory_order_acquire) &&
  182. mDevice->Connected.load(std::memory_order_acquire))
  183. {
  184. // Get current play cursor
  185. DWORD PlayCursor;
  186. mBuffer->GetCurrentPosition(&PlayCursor, nullptr);
  187. DWORD avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
  188. if(avail < FragSize)
  189. {
  190. if(!Playing)
  191. {
  192. err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
  193. if(FAILED(err))
  194. {
  195. ERR("Failed to play buffer: 0x%lx\n", err);
  196. aluHandleDisconnect(mDevice, "Failure starting playback: 0x%lx", err);
  197. return 1;
  198. }
  199. Playing = true;
  200. }
  201. avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
  202. if(avail != WAIT_OBJECT_0)
  203. ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
  204. continue;
  205. }
  206. avail -= avail%FragSize;
  207. // Lock output buffer
  208. void *WritePtr1, *WritePtr2;
  209. DWORD WriteCnt1{0u}, WriteCnt2{0u};
  210. err = mBuffer->Lock(LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  211. // If the buffer is lost, restore it and lock
  212. if(err == DSERR_BUFFERLOST)
  213. {
  214. WARN("Buffer lost, restoring...\n");
  215. err = mBuffer->Restore();
  216. if(SUCCEEDED(err))
  217. {
  218. Playing = false;
  219. LastCursor = 0;
  220. err = mBuffer->Lock(0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1,
  221. &WritePtr2, &WriteCnt2, 0);
  222. }
  223. }
  224. if(SUCCEEDED(err))
  225. {
  226. lock();
  227. aluMixData(mDevice, WritePtr1, WriteCnt1/FrameSize);
  228. if(WriteCnt2 > 0)
  229. aluMixData(mDevice, WritePtr2, WriteCnt2/FrameSize);
  230. unlock();
  231. mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
  232. }
  233. else
  234. {
  235. ERR("Buffer lock error: %#lx\n", err);
  236. aluHandleDisconnect(mDevice, "Failed to lock output buffer: 0x%lx", err);
  237. return 1;
  238. }
  239. // Update old write cursor location
  240. LastCursor += WriteCnt1+WriteCnt2;
  241. LastCursor %= DSBCaps.dwBufferBytes;
  242. }
  243. return 0;
  244. }
  245. ALCenum DSoundPlayback::open(const ALCchar *name)
  246. {
  247. HRESULT hr;
  248. if(PlaybackDevices.empty())
  249. {
  250. /* Initialize COM to prevent name truncation */
  251. HRESULT hrcom{CoInitialize(nullptr)};
  252. hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
  253. if(FAILED(hr))
  254. ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
  255. if(SUCCEEDED(hrcom))
  256. CoUninitialize();
  257. }
  258. const GUID *guid{nullptr};
  259. if(!name && !PlaybackDevices.empty())
  260. {
  261. name = PlaybackDevices[0].name.c_str();
  262. guid = &PlaybackDevices[0].guid;
  263. }
  264. else
  265. {
  266. auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  267. [name](const DevMap &entry) -> bool
  268. { return entry.name == name; }
  269. );
  270. if(iter == PlaybackDevices.cend())
  271. return ALC_INVALID_VALUE;
  272. guid = &iter->guid;
  273. }
  274. hr = DS_OK;
  275. mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
  276. if(!mNotifyEvent) hr = E_FAIL;
  277. //DirectSound Init code
  278. if(SUCCEEDED(hr))
  279. hr = DirectSoundCreate(guid, &mDS, nullptr);
  280. if(SUCCEEDED(hr))
  281. hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
  282. if(FAILED(hr))
  283. {
  284. ERR("Device init failed: 0x%08lx\n", hr);
  285. return ALC_INVALID_VALUE;
  286. }
  287. mDevice->DeviceName = name;
  288. return ALC_NO_ERROR;
  289. }
  290. ALCboolean DSoundPlayback::reset()
  291. {
  292. if(mNotifies)
  293. mNotifies->Release();
  294. mNotifies = nullptr;
  295. if(mBuffer)
  296. mBuffer->Release();
  297. mBuffer = nullptr;
  298. if(mPrimaryBuffer)
  299. mPrimaryBuffer->Release();
  300. mPrimaryBuffer = nullptr;
  301. switch(mDevice->FmtType)
  302. {
  303. case DevFmtByte:
  304. mDevice->FmtType = DevFmtUByte;
  305. break;
  306. case DevFmtFloat:
  307. if((mDevice->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
  308. break;
  309. /* fall-through */
  310. case DevFmtUShort:
  311. mDevice->FmtType = DevFmtShort;
  312. break;
  313. case DevFmtUInt:
  314. mDevice->FmtType = DevFmtInt;
  315. break;
  316. case DevFmtUByte:
  317. case DevFmtShort:
  318. case DevFmtInt:
  319. break;
  320. }
  321. WAVEFORMATEXTENSIBLE OutputType{};
  322. DWORD speakers;
  323. HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
  324. if(SUCCEEDED(hr))
  325. {
  326. speakers = DSSPEAKER_CONFIG(speakers);
  327. if(!(mDevice->Flags&DEVICE_CHANNELS_REQUEST))
  328. {
  329. if(speakers == DSSPEAKER_MONO)
  330. mDevice->FmtChans = DevFmtMono;
  331. else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
  332. mDevice->FmtChans = DevFmtStereo;
  333. else if(speakers == DSSPEAKER_QUAD)
  334. mDevice->FmtChans = DevFmtQuad;
  335. else if(speakers == DSSPEAKER_5POINT1_SURROUND)
  336. mDevice->FmtChans = DevFmtX51;
  337. else if(speakers == DSSPEAKER_5POINT1_BACK)
  338. mDevice->FmtChans = DevFmtX51Rear;
  339. else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
  340. mDevice->FmtChans = DevFmtX71;
  341. else
  342. ERR("Unknown system speaker config: 0x%lx\n", speakers);
  343. }
  344. mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo &&
  345. speakers == DSSPEAKER_HEADPHONE);
  346. switch(mDevice->FmtChans)
  347. {
  348. case DevFmtMono:
  349. OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
  350. break;
  351. case DevFmtAmbi3D:
  352. mDevice->FmtChans = DevFmtStereo;
  353. /*fall-through*/
  354. case DevFmtStereo:
  355. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  356. SPEAKER_FRONT_RIGHT;
  357. break;
  358. case DevFmtQuad:
  359. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  360. SPEAKER_FRONT_RIGHT |
  361. SPEAKER_BACK_LEFT |
  362. SPEAKER_BACK_RIGHT;
  363. break;
  364. case DevFmtX51:
  365. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  366. SPEAKER_FRONT_RIGHT |
  367. SPEAKER_FRONT_CENTER |
  368. SPEAKER_LOW_FREQUENCY |
  369. SPEAKER_SIDE_LEFT |
  370. SPEAKER_SIDE_RIGHT;
  371. break;
  372. case DevFmtX51Rear:
  373. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  374. SPEAKER_FRONT_RIGHT |
  375. SPEAKER_FRONT_CENTER |
  376. SPEAKER_LOW_FREQUENCY |
  377. SPEAKER_BACK_LEFT |
  378. SPEAKER_BACK_RIGHT;
  379. break;
  380. case DevFmtX61:
  381. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  382. SPEAKER_FRONT_RIGHT |
  383. SPEAKER_FRONT_CENTER |
  384. SPEAKER_LOW_FREQUENCY |
  385. SPEAKER_BACK_CENTER |
  386. SPEAKER_SIDE_LEFT |
  387. SPEAKER_SIDE_RIGHT;
  388. break;
  389. case DevFmtX71:
  390. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  391. SPEAKER_FRONT_RIGHT |
  392. SPEAKER_FRONT_CENTER |
  393. SPEAKER_LOW_FREQUENCY |
  394. SPEAKER_BACK_LEFT |
  395. SPEAKER_BACK_RIGHT |
  396. SPEAKER_SIDE_LEFT |
  397. SPEAKER_SIDE_RIGHT;
  398. break;
  399. }
  400. retry_open:
  401. hr = S_OK;
  402. OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  403. OutputType.Format.nChannels = mDevice->channelsFromFmt();
  404. OutputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
  405. OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
  406. OutputType.Format.nSamplesPerSec = mDevice->Frequency;
  407. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
  408. OutputType.Format.cbSize = 0;
  409. }
  410. if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
  411. {
  412. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  413. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  414. OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  415. if(mDevice->FmtType == DevFmtFloat)
  416. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  417. else
  418. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  419. if(mPrimaryBuffer)
  420. mPrimaryBuffer->Release();
  421. mPrimaryBuffer = nullptr;
  422. }
  423. else
  424. {
  425. if(SUCCEEDED(hr) && !mPrimaryBuffer)
  426. {
  427. DSBUFFERDESC DSBDescription{};
  428. DSBDescription.dwSize = sizeof(DSBDescription);
  429. DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
  430. hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
  431. }
  432. if(SUCCEEDED(hr))
  433. hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
  434. }
  435. if(SUCCEEDED(hr))
  436. {
  437. ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
  438. if(num_updates > MAX_UPDATES)
  439. num_updates = MAX_UPDATES;
  440. mDevice->BufferSize = mDevice->UpdateSize * num_updates;
  441. DSBUFFERDESC DSBDescription{};
  442. DSBDescription.dwSize = sizeof(DSBDescription);
  443. DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 |
  444. DSBCAPS_GLOBALFOCUS;
  445. DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
  446. DSBDescription.lpwfxFormat = &OutputType.Format;
  447. hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
  448. if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
  449. {
  450. mDevice->FmtType = DevFmtShort;
  451. goto retry_open;
  452. }
  453. }
  454. if(SUCCEEDED(hr))
  455. {
  456. void *ptr;
  457. hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
  458. if(SUCCEEDED(hr))
  459. {
  460. auto Notifies = static_cast<IDirectSoundNotify*>(ptr);
  461. mNotifies = Notifies;
  462. ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
  463. assert(num_updates <= MAX_UPDATES);
  464. std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
  465. for(ALuint i{0};i < num_updates;++i)
  466. {
  467. nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
  468. nots[i].hEventNotify = mNotifyEvent;
  469. }
  470. if(Notifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
  471. hr = E_FAIL;
  472. }
  473. }
  474. if(FAILED(hr))
  475. {
  476. if(mNotifies)
  477. mNotifies->Release();
  478. mNotifies = nullptr;
  479. if(mBuffer)
  480. mBuffer->Release();
  481. mBuffer = nullptr;
  482. if(mPrimaryBuffer)
  483. mPrimaryBuffer->Release();
  484. mPrimaryBuffer = nullptr;
  485. return ALC_FALSE;
  486. }
  487. ResetEvent(mNotifyEvent);
  488. SetDefaultWFXChannelOrder(mDevice);
  489. return ALC_TRUE;
  490. }
  491. ALCboolean DSoundPlayback::start()
  492. {
  493. try {
  494. mKillNow.store(false, std::memory_order_release);
  495. mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this};
  496. return ALC_TRUE;
  497. }
  498. catch(std::exception& e) {
  499. ERR("Failed to start mixing thread: %s\n", e.what());
  500. }
  501. catch(...) {
  502. }
  503. return ALC_FALSE;
  504. }
  505. void DSoundPlayback::stop()
  506. {
  507. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  508. return;
  509. mThread.join();
  510. mBuffer->Stop();
  511. }
  512. struct DSoundCapture final : public BackendBase {
  513. DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
  514. ~DSoundCapture() override;
  515. ALCenum open(const ALCchar *name) override;
  516. ALCboolean start() override;
  517. void stop() override;
  518. ALCenum captureSamples(void *buffer, ALCuint samples) override;
  519. ALCuint availableSamples() override;
  520. IDirectSoundCapture *mDSC{nullptr};
  521. IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
  522. DWORD mBufferBytes{0u};
  523. DWORD mCursor{0u};
  524. RingBufferPtr mRing;
  525. static constexpr inline const char *CurrentPrefix() noexcept { return "DSoundCapture::"; }
  526. DEF_NEWDEL(DSoundCapture)
  527. };
  528. DSoundCapture::~DSoundCapture()
  529. {
  530. if(mDSCbuffer)
  531. {
  532. mDSCbuffer->Stop();
  533. mDSCbuffer->Release();
  534. mDSCbuffer = nullptr;
  535. }
  536. if(mDSC)
  537. mDSC->Release();
  538. mDSC = nullptr;
  539. }
  540. ALCenum DSoundCapture::open(const ALCchar *name)
  541. {
  542. HRESULT hr;
  543. if(CaptureDevices.empty())
  544. {
  545. /* Initialize COM to prevent name truncation */
  546. HRESULT hrcom{CoInitialize(nullptr)};
  547. hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
  548. if(FAILED(hr))
  549. ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
  550. if(SUCCEEDED(hrcom))
  551. CoUninitialize();
  552. }
  553. const GUID *guid{nullptr};
  554. if(!name && !CaptureDevices.empty())
  555. {
  556. name = CaptureDevices[0].name.c_str();
  557. guid = &CaptureDevices[0].guid;
  558. }
  559. else
  560. {
  561. auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  562. [name](const DevMap &entry) -> bool
  563. { return entry.name == name; }
  564. );
  565. if(iter == CaptureDevices.cend())
  566. return ALC_INVALID_VALUE;
  567. guid = &iter->guid;
  568. }
  569. switch(mDevice->FmtType)
  570. {
  571. case DevFmtByte:
  572. case DevFmtUShort:
  573. case DevFmtUInt:
  574. WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
  575. return ALC_INVALID_ENUM;
  576. case DevFmtUByte:
  577. case DevFmtShort:
  578. case DevFmtInt:
  579. case DevFmtFloat:
  580. break;
  581. }
  582. WAVEFORMATEXTENSIBLE InputType{};
  583. switch(mDevice->FmtChans)
  584. {
  585. case DevFmtMono:
  586. InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
  587. break;
  588. case DevFmtStereo:
  589. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  590. SPEAKER_FRONT_RIGHT;
  591. break;
  592. case DevFmtQuad:
  593. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  594. SPEAKER_FRONT_RIGHT |
  595. SPEAKER_BACK_LEFT |
  596. SPEAKER_BACK_RIGHT;
  597. break;
  598. case DevFmtX51:
  599. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  600. SPEAKER_FRONT_RIGHT |
  601. SPEAKER_FRONT_CENTER |
  602. SPEAKER_LOW_FREQUENCY |
  603. SPEAKER_SIDE_LEFT |
  604. SPEAKER_SIDE_RIGHT;
  605. break;
  606. case DevFmtX51Rear:
  607. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  608. SPEAKER_FRONT_RIGHT |
  609. SPEAKER_FRONT_CENTER |
  610. SPEAKER_LOW_FREQUENCY |
  611. SPEAKER_BACK_LEFT |
  612. SPEAKER_BACK_RIGHT;
  613. break;
  614. case DevFmtX61:
  615. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  616. SPEAKER_FRONT_RIGHT |
  617. SPEAKER_FRONT_CENTER |
  618. SPEAKER_LOW_FREQUENCY |
  619. SPEAKER_BACK_CENTER |
  620. SPEAKER_SIDE_LEFT |
  621. SPEAKER_SIDE_RIGHT;
  622. break;
  623. case DevFmtX71:
  624. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  625. SPEAKER_FRONT_RIGHT |
  626. SPEAKER_FRONT_CENTER |
  627. SPEAKER_LOW_FREQUENCY |
  628. SPEAKER_BACK_LEFT |
  629. SPEAKER_BACK_RIGHT |
  630. SPEAKER_SIDE_LEFT |
  631. SPEAKER_SIDE_RIGHT;
  632. break;
  633. case DevFmtAmbi3D:
  634. WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
  635. return ALC_INVALID_ENUM;
  636. }
  637. InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  638. InputType.Format.nChannels = mDevice->channelsFromFmt();
  639. InputType.Format.wBitsPerSample = mDevice->bytesFromFmt() * 8;
  640. InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
  641. InputType.Format.nSamplesPerSec = mDevice->Frequency;
  642. InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
  643. InputType.Format.cbSize = 0;
  644. InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
  645. if(mDevice->FmtType == DevFmtFloat)
  646. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  647. else
  648. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  649. if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
  650. {
  651. InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  652. InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  653. }
  654. ALuint samples{mDevice->BufferSize};
  655. samples = maxu(samples, 100 * mDevice->Frequency / 1000);
  656. DSCBUFFERDESC DSCBDescription{};
  657. DSCBDescription.dwSize = sizeof(DSCBDescription);
  658. DSCBDescription.dwFlags = 0;
  659. DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
  660. DSCBDescription.lpwfxFormat = &InputType.Format;
  661. //DirectSoundCapture Init code
  662. hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
  663. if(SUCCEEDED(hr))
  664. mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
  665. if(SUCCEEDED(hr))
  666. {
  667. mRing = CreateRingBuffer(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
  668. if(!mRing) hr = DSERR_OUTOFMEMORY;
  669. }
  670. if(FAILED(hr))
  671. {
  672. ERR("Device init failed: 0x%08lx\n", hr);
  673. mRing = nullptr;
  674. if(mDSCbuffer)
  675. mDSCbuffer->Release();
  676. mDSCbuffer = nullptr;
  677. if(mDSC)
  678. mDSC->Release();
  679. mDSC = nullptr;
  680. return ALC_INVALID_VALUE;
  681. }
  682. mBufferBytes = DSCBDescription.dwBufferBytes;
  683. SetDefaultWFXChannelOrder(mDevice);
  684. mDevice->DeviceName = name;
  685. return ALC_NO_ERROR;
  686. }
  687. ALCboolean DSoundCapture::start()
  688. {
  689. HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
  690. if(FAILED(hr))
  691. {
  692. ERR("start failed: 0x%08lx\n", hr);
  693. aluHandleDisconnect(mDevice, "Failure starting capture: 0x%lx", hr);
  694. return ALC_FALSE;
  695. }
  696. return ALC_TRUE;
  697. }
  698. void DSoundCapture::stop()
  699. {
  700. HRESULT hr{mDSCbuffer->Stop()};
  701. if(FAILED(hr))
  702. {
  703. ERR("stop failed: 0x%08lx\n", hr);
  704. aluHandleDisconnect(mDevice, "Failure stopping capture: 0x%lx", hr);
  705. }
  706. }
  707. ALCenum DSoundCapture::captureSamples(void *buffer, ALCuint samples)
  708. {
  709. mRing->read(buffer, samples);
  710. return ALC_NO_ERROR;
  711. }
  712. ALCuint DSoundCapture::availableSamples()
  713. {
  714. if(!mDevice->Connected.load(std::memory_order_acquire))
  715. return static_cast<ALCuint>(mRing->readSpace());
  716. ALsizei FrameSize{mDevice->frameSizeFromFmt()};
  717. DWORD BufferBytes{mBufferBytes};
  718. DWORD LastCursor{mCursor};
  719. DWORD ReadCursor;
  720. void *ReadPtr1, *ReadPtr2;
  721. DWORD ReadCnt1, ReadCnt2;
  722. HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
  723. if(SUCCEEDED(hr))
  724. {
  725. DWORD NumBytes{(ReadCursor-LastCursor + BufferBytes) % BufferBytes};
  726. if(!NumBytes) return static_cast<ALCubyte>(mRing->readSpace());
  727. hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
  728. }
  729. if(SUCCEEDED(hr))
  730. {
  731. mRing->write(ReadPtr1, ReadCnt1/FrameSize);
  732. if(ReadPtr2 != nullptr && ReadCnt2 > 0)
  733. mRing->write(ReadPtr2, ReadCnt2/FrameSize);
  734. hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
  735. mCursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
  736. }
  737. if(FAILED(hr))
  738. {
  739. ERR("update failed: 0x%08lx\n", hr);
  740. aluHandleDisconnect(mDevice, "Failure retrieving capture data: 0x%lx", hr);
  741. }
  742. return static_cast<ALCuint>(mRing->readSpace());
  743. }
  744. } // namespace
  745. BackendFactory &DSoundBackendFactory::getFactory()
  746. {
  747. static DSoundBackendFactory factory{};
  748. return factory;
  749. }
  750. bool DSoundBackendFactory::init()
  751. {
  752. #ifdef HAVE_DYNLOAD
  753. if(!ds_handle)
  754. {
  755. ds_handle = LoadLib("dsound.dll");
  756. if(!ds_handle)
  757. {
  758. ERR("Failed to load dsound.dll\n");
  759. return false;
  760. }
  761. #define LOAD_FUNC(f) do { \
  762. p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
  763. if(!p##f) \
  764. { \
  765. CloseLib(ds_handle); \
  766. ds_handle = nullptr; \
  767. return false; \
  768. } \
  769. } while(0)
  770. LOAD_FUNC(DirectSoundCreate);
  771. LOAD_FUNC(DirectSoundEnumerateW);
  772. LOAD_FUNC(DirectSoundCaptureCreate);
  773. LOAD_FUNC(DirectSoundCaptureEnumerateW);
  774. #undef LOAD_FUNC
  775. }
  776. #endif
  777. return true;
  778. }
  779. bool DSoundBackendFactory::querySupport(BackendType type)
  780. { return (type == BackendType::Playback || type == BackendType::Capture); }
  781. void DSoundBackendFactory::probe(DevProbe type, std::string *outnames)
  782. {
  783. auto add_device = [outnames](const DevMap &entry) -> void
  784. {
  785. /* +1 to also append the null char (to ensure a null-separated list and
  786. * double-null terminated list).
  787. */
  788. outnames->append(entry.name.c_str(), entry.name.length()+1);
  789. };
  790. /* Initialize COM to prevent name truncation */
  791. HRESULT hr;
  792. HRESULT hrcom{CoInitialize(nullptr)};
  793. switch(type)
  794. {
  795. case DevProbe::Playback:
  796. PlaybackDevices.clear();
  797. hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
  798. if(FAILED(hr))
  799. ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
  800. std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
  801. break;
  802. case DevProbe::Capture:
  803. CaptureDevices.clear();
  804. hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
  805. if(FAILED(hr))
  806. ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
  807. std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
  808. break;
  809. }
  810. if(SUCCEEDED(hrcom))
  811. CoUninitialize();
  812. }
  813. BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
  814. {
  815. if(type == BackendType::Playback)
  816. return BackendPtr{new DSoundPlayback{device}};
  817. if(type == BackendType::Capture)
  818. return BackendPtr{new DSoundCapture{device}};
  819. return nullptr;
  820. }