dsound.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  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 "dsound.h"
  22. #define WIN32_LEAN_AND_MEAN
  23. #include <windows.h>
  24. #include <cguid.h>
  25. #include <mmreg.h>
  26. #ifndef _WAVEFORMATEXTENSIBLE_
  27. #include <ks.h>
  28. #include <ksmedia.h>
  29. #endif
  30. #include <algorithm>
  31. #include <atomic>
  32. #include <cassert>
  33. #include <cstdio>
  34. #include <cstdlib>
  35. #include <memory.h>
  36. #include <string>
  37. #include <thread>
  38. #include <vector>
  39. #include "alnumeric.h"
  40. #include "alspan.h"
  41. #include "althrd_setname.h"
  42. #include "comptr.h"
  43. #include "core/device.h"
  44. #include "core/helpers.h"
  45. #include "core/logging.h"
  46. #include "dynload.h"
  47. #include "fmt/core.h"
  48. #include "ringbuffer.h"
  49. #include "strutils.h"
  50. /* MinGW-w64 needs this for some unknown reason now. */
  51. using LPCWAVEFORMATEX = const WAVEFORMATEX*;
  52. #include <dsound.h>
  53. #ifndef DSSPEAKER_5POINT1
  54. # define DSSPEAKER_5POINT1 0x00000006
  55. #endif
  56. #ifndef DSSPEAKER_5POINT1_BACK
  57. # define DSSPEAKER_5POINT1_BACK 0x00000006
  58. #endif
  59. #ifndef DSSPEAKER_7POINT1
  60. # define DSSPEAKER_7POINT1 0x00000007
  61. #endif
  62. #ifndef DSSPEAKER_7POINT1_SURROUND
  63. # define DSSPEAKER_7POINT1_SURROUND 0x00000008
  64. #endif
  65. #ifndef DSSPEAKER_5POINT1_SURROUND
  66. # define DSSPEAKER_5POINT1_SURROUND 0x00000009
  67. #endif
  68. /* Some headers seem to define these as macros for __uuidof, which is annoying
  69. * since some headers don't declare them at all. Hopefully the ifdef is enough
  70. * to tell if they need to be declared.
  71. */
  72. #ifndef KSDATAFORMAT_SUBTYPE_PCM
  73. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  74. #endif
  75. #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
  76. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  77. #endif
  78. namespace {
  79. #if HAVE_DYNLOAD
  80. void *ds_handle;
  81. HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
  82. HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  83. HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
  84. HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  85. #ifndef IN_IDE_PARSER
  86. #define DirectSoundCreate pDirectSoundCreate
  87. #define DirectSoundEnumerateW pDirectSoundEnumerateW
  88. #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
  89. #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
  90. #endif
  91. #endif
  92. #define MONO SPEAKER_FRONT_CENTER
  93. #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
  94. #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
  95. #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
  96. #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
  97. #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
  98. #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)
  99. #define X7DOT1DOT4 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT|SPEAKER_TOP_FRONT_LEFT|SPEAKER_TOP_FRONT_RIGHT|SPEAKER_TOP_BACK_LEFT|SPEAKER_TOP_BACK_RIGHT)
  100. #define MAX_UPDATES 128
  101. struct DevMap {
  102. std::string name;
  103. GUID guid;
  104. template<typename T0, typename T1>
  105. DevMap(T0&& name_, T1&& guid_)
  106. : name{std::forward<T0>(name_)}, guid{std::forward<T1>(guid_)}
  107. { }
  108. };
  109. std::vector<DevMap> PlaybackDevices;
  110. std::vector<DevMap> CaptureDevices;
  111. bool checkName(const al::span<DevMap> list, const std::string &name)
  112. {
  113. auto match_name = [&name](const DevMap &entry) -> bool
  114. { return entry.name == name; };
  115. return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
  116. }
  117. BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, void *data) noexcept
  118. {
  119. if(!guid)
  120. return TRUE;
  121. auto& devices = *static_cast<std::vector<DevMap>*>(data);
  122. const auto basename = wstr_to_utf8(desc);
  123. auto count = 1;
  124. auto newname = basename;
  125. while(checkName(devices, newname))
  126. newname = fmt::format("{} #{}", basename, ++count);
  127. const DevMap &newentry = devices.emplace_back(std::move(newname), *guid);
  128. OLECHAR *guidstr{nullptr};
  129. HRESULT hr{StringFromCLSID(*guid, &guidstr)};
  130. if(SUCCEEDED(hr))
  131. {
  132. TRACE("Got device \"{}\", GUID \"{}\"", newentry.name, wstr_to_utf8(guidstr));
  133. CoTaskMemFree(guidstr);
  134. }
  135. return TRUE;
  136. }
  137. struct DSoundPlayback final : public BackendBase {
  138. explicit DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
  139. ~DSoundPlayback() override;
  140. int mixerProc();
  141. void open(std::string_view name) override;
  142. bool reset() override;
  143. void start() override;
  144. void stop() override;
  145. ComPtr<IDirectSound> mDS;
  146. ComPtr<IDirectSoundBuffer> mPrimaryBuffer;
  147. ComPtr<IDirectSoundBuffer> mBuffer;
  148. ComPtr<IDirectSoundNotify> mNotifies;
  149. HANDLE mNotifyEvent{nullptr};
  150. std::atomic<bool> mKillNow{true};
  151. std::thread mThread;
  152. };
  153. DSoundPlayback::~DSoundPlayback()
  154. {
  155. mNotifies = nullptr;
  156. mBuffer = nullptr;
  157. mPrimaryBuffer = nullptr;
  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(GetMixerThreadName());
  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: {:#x}", as_unsigned(err));
  173. mDevice->handleDisconnect("Failure retrieving playback buffer info: {:#x}",
  174. as_unsigned(err));
  175. return 1;
  176. }
  177. const size_t FrameStep{mDevice->channelsFromFmt()};
  178. uint FrameSize{mDevice->frameSizeFromFmt()};
  179. DWORD FragSize{mDevice->mUpdateSize * FrameSize};
  180. bool Playing{false};
  181. DWORD LastCursor{0u};
  182. mBuffer->GetCurrentPosition(&LastCursor, nullptr);
  183. while(!mKillNow.load(std::memory_order_acquire)
  184. && mDevice->Connected.load(std::memory_order_acquire))
  185. {
  186. // Get current play cursor
  187. DWORD PlayCursor;
  188. mBuffer->GetCurrentPosition(&PlayCursor, nullptr);
  189. DWORD avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
  190. if(avail < FragSize)
  191. {
  192. if(!Playing)
  193. {
  194. err = mBuffer->Play(0, 0, DSBPLAY_LOOPING);
  195. if(FAILED(err))
  196. {
  197. ERR("Failed to play buffer: {:#x}", as_unsigned(err));
  198. mDevice->handleDisconnect("Failure starting playback: {:#x}",
  199. as_unsigned(err));
  200. return 1;
  201. }
  202. Playing = true;
  203. }
  204. avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE);
  205. if(avail != WAIT_OBJECT_0)
  206. ERR("WaitForSingleObjectEx error: {:#x}", avail);
  207. continue;
  208. }
  209. avail -= avail%FragSize;
  210. // Lock output buffer
  211. void *WritePtr1, *WritePtr2;
  212. DWORD WriteCnt1{0u}, WriteCnt2{0u};
  213. err = mBuffer->Lock(LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  214. // If the buffer is lost, restore it and lock
  215. if(err == DSERR_BUFFERLOST)
  216. {
  217. WARN("Buffer lost, restoring...");
  218. err = mBuffer->Restore();
  219. if(SUCCEEDED(err))
  220. {
  221. Playing = false;
  222. LastCursor = 0;
  223. err = mBuffer->Lock(0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1,
  224. &WritePtr2, &WriteCnt2, 0);
  225. }
  226. }
  227. if(FAILED(err))
  228. {
  229. ERR("Buffer lock error: {:#x}", as_unsigned(err));
  230. mDevice->handleDisconnect("Failed to lock output buffer: {:#x}", as_unsigned(err));
  231. return 1;
  232. }
  233. mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep);
  234. if(WriteCnt2 > 0)
  235. mDevice->renderSamples(WritePtr2, WriteCnt2/FrameSize, FrameStep);
  236. mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
  237. // Update old write cursor location
  238. LastCursor += WriteCnt1+WriteCnt2;
  239. LastCursor %= DSBCaps.dwBufferBytes;
  240. }
  241. return 0;
  242. }
  243. void DSoundPlayback::open(std::string_view name)
  244. {
  245. HRESULT hr;
  246. if(PlaybackDevices.empty())
  247. {
  248. /* Initialize COM to prevent name truncation */
  249. ComWrapper com{};
  250. hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
  251. if(FAILED(hr))
  252. ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr));
  253. }
  254. const GUID *guid{nullptr};
  255. if(name.empty() && !PlaybackDevices.empty())
  256. {
  257. name = PlaybackDevices[0].name;
  258. guid = &PlaybackDevices[0].guid;
  259. }
  260. else
  261. {
  262. auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  263. [name](const DevMap &entry) -> bool { return entry.name == name; });
  264. if(iter == PlaybackDevices.cend())
  265. {
  266. GUID id{};
  267. hr = CLSIDFromString(utf8_to_wstr(name).c_str(), &id);
  268. if(SUCCEEDED(hr))
  269. iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
  270. [&id](const DevMap &entry) -> bool { return entry.guid == id; });
  271. if(iter == PlaybackDevices.cend())
  272. throw al::backend_exception{al::backend_error::NoDevice,
  273. "Device name \"{}\" not found", name};
  274. }
  275. guid = &iter->guid;
  276. }
  277. hr = DS_OK;
  278. if(!mNotifyEvent)
  279. {
  280. mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
  281. if(!mNotifyEvent) hr = E_FAIL;
  282. }
  283. //DirectSound Init code
  284. ComPtr<IDirectSound> ds;
  285. if(SUCCEEDED(hr))
  286. hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr);
  287. if(SUCCEEDED(hr))
  288. hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
  289. if(FAILED(hr))
  290. throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
  291. as_unsigned(hr)};
  292. mNotifies = nullptr;
  293. mBuffer = nullptr;
  294. mPrimaryBuffer = nullptr;
  295. mDS = std::move(ds);
  296. mDeviceName = name;
  297. }
  298. bool DSoundPlayback::reset()
  299. {
  300. mNotifies = nullptr;
  301. mBuffer = nullptr;
  302. mPrimaryBuffer = nullptr;
  303. switch(mDevice->FmtType)
  304. {
  305. case DevFmtByte:
  306. mDevice->FmtType = DevFmtUByte;
  307. break;
  308. case DevFmtFloat:
  309. if(mDevice->Flags.test(SampleTypeRequest))
  310. break;
  311. /* fall-through */
  312. case DevFmtUShort:
  313. mDevice->FmtType = DevFmtShort;
  314. break;
  315. case DevFmtUInt:
  316. mDevice->FmtType = DevFmtInt;
  317. break;
  318. case DevFmtUByte:
  319. case DevFmtShort:
  320. case DevFmtInt:
  321. break;
  322. }
  323. WAVEFORMATEXTENSIBLE OutputType{};
  324. DWORD speakers{};
  325. HRESULT hr{mDS->GetSpeakerConfig(&speakers)};
  326. if(FAILED(hr))
  327. throw al::backend_exception{al::backend_error::DeviceError,
  328. "Failed to get speaker config: {:#x}", as_unsigned(hr)};
  329. speakers = DSSPEAKER_CONFIG(speakers);
  330. if(!mDevice->Flags.test(ChannelsRequest))
  331. {
  332. if(speakers == DSSPEAKER_MONO)
  333. mDevice->FmtChans = DevFmtMono;
  334. else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
  335. mDevice->FmtChans = DevFmtStereo;
  336. else if(speakers == DSSPEAKER_QUAD)
  337. mDevice->FmtChans = DevFmtQuad;
  338. else if(speakers == DSSPEAKER_5POINT1_SURROUND || speakers == DSSPEAKER_5POINT1_BACK)
  339. mDevice->FmtChans = DevFmtX51;
  340. else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
  341. mDevice->FmtChans = DevFmtX71;
  342. else
  343. ERR("Unknown system speaker config: {:#x}", speakers);
  344. }
  345. mDevice->Flags.set(DirectEar, (speakers == DSSPEAKER_HEADPHONE));
  346. const bool isRear51{speakers == DSSPEAKER_5POINT1_BACK};
  347. switch(mDevice->FmtChans)
  348. {
  349. case DevFmtMono: OutputType.dwChannelMask = MONO; break;
  350. case DevFmtAmbi3D: mDevice->FmtChans = DevFmtStereo;
  351. /* fall-through */
  352. case DevFmtStereo: OutputType.dwChannelMask = STEREO; break;
  353. case DevFmtQuad: OutputType.dwChannelMask = QUAD; break;
  354. case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
  355. case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
  356. case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
  357. case DevFmtX7144: mDevice->FmtChans = DevFmtX714;
  358. /* fall-through */
  359. case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
  360. case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break;
  361. }
  362. do {
  363. hr = S_OK;
  364. OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  365. OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
  366. OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
  367. OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
  368. OutputType.Format.wBitsPerSample / 8);
  369. OutputType.Format.nSamplesPerSec = mDevice->mSampleRate;
  370. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
  371. OutputType.Format.nBlockAlign;
  372. OutputType.Format.cbSize = 0;
  373. if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
  374. {
  375. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  376. /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
  377. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  378. OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  379. if(mDevice->FmtType == DevFmtFloat)
  380. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  381. else
  382. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  383. mPrimaryBuffer = nullptr;
  384. }
  385. else
  386. {
  387. if(SUCCEEDED(hr) && !mPrimaryBuffer)
  388. {
  389. DSBUFFERDESC DSBDescription{};
  390. DSBDescription.dwSize = sizeof(DSBDescription);
  391. DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
  392. hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
  393. }
  394. if(SUCCEEDED(hr))
  395. hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
  396. }
  397. if(FAILED(hr))
  398. break;
  399. uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
  400. if(num_updates > MAX_UPDATES)
  401. num_updates = MAX_UPDATES;
  402. mDevice->mBufferSize = mDevice->mUpdateSize * num_updates;
  403. DSBUFFERDESC DSBDescription{};
  404. DSBDescription.dwSize = sizeof(DSBDescription);
  405. DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2
  406. | DSBCAPS_GLOBALFOCUS;
  407. DSBDescription.dwBufferBytes = mDevice->mBufferSize * OutputType.Format.nBlockAlign;
  408. DSBDescription.lpwfxFormat = &OutputType.Format;
  409. hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
  410. if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
  411. break;
  412. mDevice->FmtType = DevFmtShort;
  413. } while(FAILED(hr));
  414. if(SUCCEEDED(hr))
  415. {
  416. hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
  417. if(SUCCEEDED(hr))
  418. {
  419. uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize};
  420. assert(num_updates <= MAX_UPDATES);
  421. std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots{};
  422. for(uint i{0};i < num_updates;++i)
  423. {
  424. nots[i].dwOffset = i * mDevice->mUpdateSize * OutputType.Format.nBlockAlign;
  425. nots[i].hEventNotify = mNotifyEvent;
  426. }
  427. if(mNotifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK)
  428. hr = E_FAIL;
  429. }
  430. }
  431. if(FAILED(hr))
  432. {
  433. mNotifies = nullptr;
  434. mBuffer = nullptr;
  435. mPrimaryBuffer = nullptr;
  436. return false;
  437. }
  438. ResetEvent(mNotifyEvent);
  439. setDefaultWFXChannelOrder();
  440. return true;
  441. }
  442. void DSoundPlayback::start()
  443. {
  444. try {
  445. mKillNow.store(false, std::memory_order_release);
  446. mThread = std::thread{&DSoundPlayback::mixerProc, this};
  447. }
  448. catch(std::exception& e) {
  449. throw al::backend_exception{al::backend_error::DeviceError,
  450. "Failed to start mixing thread: {}", e.what()};
  451. }
  452. }
  453. void DSoundPlayback::stop()
  454. {
  455. if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
  456. return;
  457. mThread.join();
  458. mBuffer->Stop();
  459. }
  460. struct DSoundCapture final : public BackendBase {
  461. explicit DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
  462. ~DSoundCapture() override;
  463. void open(std::string_view name) override;
  464. void start() override;
  465. void stop() override;
  466. void captureSamples(std::byte *buffer, uint samples) override;
  467. uint availableSamples() override;
  468. ComPtr<IDirectSoundCapture> mDSC;
  469. ComPtr<IDirectSoundCaptureBuffer> mDSCbuffer;
  470. DWORD mBufferBytes{0u};
  471. DWORD mCursor{0u};
  472. RingBufferPtr mRing;
  473. };
  474. DSoundCapture::~DSoundCapture()
  475. {
  476. if(mDSCbuffer)
  477. {
  478. mDSCbuffer->Stop();
  479. mDSCbuffer = nullptr;
  480. }
  481. mDSC = nullptr;
  482. }
  483. void DSoundCapture::open(std::string_view name)
  484. {
  485. HRESULT hr;
  486. if(CaptureDevices.empty())
  487. {
  488. /* Initialize COM to prevent name truncation */
  489. ComWrapper com{};
  490. hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
  491. if(FAILED(hr))
  492. ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr));
  493. }
  494. const GUID *guid{nullptr};
  495. if(name.empty() && !CaptureDevices.empty())
  496. {
  497. name = CaptureDevices[0].name;
  498. guid = &CaptureDevices[0].guid;
  499. }
  500. else
  501. {
  502. auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  503. [name](const DevMap &entry) -> bool { return entry.name == name; });
  504. if(iter == CaptureDevices.cend())
  505. {
  506. GUID id{};
  507. hr = CLSIDFromString(utf8_to_wstr(name).c_str(), &id);
  508. if(SUCCEEDED(hr))
  509. iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
  510. [&id](const DevMap &entry) -> bool { return entry.guid == id; });
  511. if(iter == CaptureDevices.cend())
  512. throw al::backend_exception{al::backend_error::NoDevice,
  513. "Device name \"{}\" not found", name};
  514. }
  515. guid = &iter->guid;
  516. }
  517. switch(mDevice->FmtType)
  518. {
  519. case DevFmtByte:
  520. case DevFmtUShort:
  521. case DevFmtUInt:
  522. WARN("{} capture samples not supported", DevFmtTypeString(mDevice->FmtType));
  523. throw al::backend_exception{al::backend_error::DeviceError,
  524. "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)};
  525. case DevFmtUByte:
  526. case DevFmtShort:
  527. case DevFmtInt:
  528. case DevFmtFloat:
  529. break;
  530. }
  531. WAVEFORMATEXTENSIBLE InputType{};
  532. switch(mDevice->FmtChans)
  533. {
  534. case DevFmtMono: InputType.dwChannelMask = MONO; break;
  535. case DevFmtStereo: InputType.dwChannelMask = STEREO; break;
  536. case DevFmtQuad: InputType.dwChannelMask = QUAD; break;
  537. case DevFmtX51: InputType.dwChannelMask = X5DOT1; break;
  538. case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
  539. case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
  540. case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
  541. case DevFmtX7144:
  542. case DevFmtX3D71:
  543. case DevFmtAmbi3D:
  544. WARN("{} capture not supported", DevFmtChannelsString(mDevice->FmtChans));
  545. throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported",
  546. DevFmtChannelsString(mDevice->FmtChans)};
  547. }
  548. InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  549. InputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
  550. InputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
  551. InputType.Format.nBlockAlign = static_cast<WORD>(InputType.Format.nChannels *
  552. InputType.Format.wBitsPerSample / 8);
  553. InputType.Format.nSamplesPerSec = mDevice->mSampleRate;
  554. InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
  555. InputType.Format.nBlockAlign;
  556. InputType.Format.cbSize = 0;
  557. /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
  558. InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
  559. if(mDevice->FmtType == DevFmtFloat)
  560. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  561. else
  562. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  563. if(InputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
  564. {
  565. InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  566. InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  567. }
  568. const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)};
  569. DSCBUFFERDESC DSCBDescription{};
  570. DSCBDescription.dwSize = sizeof(DSCBDescription);
  571. DSCBDescription.dwFlags = 0;
  572. DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
  573. DSCBDescription.lpwfxFormat = &InputType.Format;
  574. //DirectSoundCapture Init code
  575. hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr);
  576. if(SUCCEEDED(hr))
  577. mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
  578. if(SUCCEEDED(hr))
  579. mRing = RingBuffer::Create(mDevice->mBufferSize, InputType.Format.nBlockAlign, false);
  580. if(FAILED(hr))
  581. {
  582. mRing = nullptr;
  583. mDSCbuffer = nullptr;
  584. mDSC = nullptr;
  585. throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}",
  586. as_unsigned(hr)};
  587. }
  588. mBufferBytes = DSCBDescription.dwBufferBytes;
  589. setDefaultWFXChannelOrder();
  590. mDeviceName = name;
  591. }
  592. void DSoundCapture::start()
  593. {
  594. const HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)};
  595. if(FAILED(hr))
  596. throw al::backend_exception{al::backend_error::DeviceError,
  597. "Failure starting capture: {:#x}", as_unsigned(hr)};
  598. }
  599. void DSoundCapture::stop()
  600. {
  601. HRESULT hr{mDSCbuffer->Stop()};
  602. if(FAILED(hr))
  603. {
  604. ERR("stop failed: {:#x}", as_unsigned(hr));
  605. mDevice->handleDisconnect("Failure stopping capture: {:#x}", as_unsigned(hr));
  606. }
  607. }
  608. void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
  609. { std::ignore = mRing->read(buffer, samples); }
  610. uint DSoundCapture::availableSamples()
  611. {
  612. if(!mDevice->Connected.load(std::memory_order_acquire))
  613. return static_cast<uint>(mRing->readSpace());
  614. const uint FrameSize{mDevice->frameSizeFromFmt()};
  615. const DWORD BufferBytes{mBufferBytes};
  616. const DWORD LastCursor{mCursor};
  617. DWORD ReadCursor{};
  618. void *ReadPtr1{}, *ReadPtr2{};
  619. DWORD ReadCnt1{}, ReadCnt2{};
  620. HRESULT hr{mDSCbuffer->GetCurrentPosition(nullptr, &ReadCursor)};
  621. if(SUCCEEDED(hr))
  622. {
  623. const DWORD NumBytes{(BufferBytes+ReadCursor-LastCursor) % BufferBytes};
  624. if(!NumBytes) return static_cast<uint>(mRing->readSpace());
  625. hr = mDSCbuffer->Lock(LastCursor, NumBytes, &ReadPtr1, &ReadCnt1, &ReadPtr2, &ReadCnt2, 0);
  626. }
  627. if(SUCCEEDED(hr))
  628. {
  629. std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize);
  630. if(ReadPtr2 != nullptr && ReadCnt2 > 0)
  631. std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize);
  632. hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
  633. mCursor = ReadCursor;
  634. }
  635. if(FAILED(hr))
  636. {
  637. ERR("update failed: {:#x}", as_unsigned(hr));
  638. mDevice->handleDisconnect("Failure retrieving capture data: {:#x}", as_unsigned(hr));
  639. }
  640. return static_cast<uint>(mRing->readSpace());
  641. }
  642. } // namespace
  643. BackendFactory &DSoundBackendFactory::getFactory()
  644. {
  645. static DSoundBackendFactory factory{};
  646. return factory;
  647. }
  648. bool DSoundBackendFactory::init()
  649. {
  650. #if HAVE_DYNLOAD
  651. if(!ds_handle)
  652. {
  653. ds_handle = LoadLib("dsound.dll");
  654. if(!ds_handle)
  655. {
  656. ERR("Failed to load dsound.dll");
  657. return false;
  658. }
  659. #define LOAD_FUNC(f) do { \
  660. p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
  661. if(!p##f) \
  662. { \
  663. CloseLib(ds_handle); \
  664. ds_handle = nullptr; \
  665. return false; \
  666. } \
  667. } while(0)
  668. LOAD_FUNC(DirectSoundCreate);
  669. LOAD_FUNC(DirectSoundEnumerateW);
  670. LOAD_FUNC(DirectSoundCaptureCreate);
  671. LOAD_FUNC(DirectSoundCaptureEnumerateW);
  672. #undef LOAD_FUNC
  673. }
  674. #endif
  675. return true;
  676. }
  677. bool DSoundBackendFactory::querySupport(BackendType type)
  678. { return (type == BackendType::Playback || type == BackendType::Capture); }
  679. auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
  680. {
  681. std::vector<std::string> outnames;
  682. auto add_device = [&outnames](const DevMap &entry) -> void
  683. { outnames.emplace_back(entry.name); };
  684. /* Initialize COM to prevent name truncation */
  685. ComWrapper com{};
  686. switch(type)
  687. {
  688. case BackendType::Playback:
  689. PlaybackDevices.clear();
  690. if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
  691. ERR("Error enumerating DirectSound playback devices: {:#x}", as_unsigned(hr));
  692. outnames.reserve(PlaybackDevices.size());
  693. std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
  694. break;
  695. case BackendType::Capture:
  696. CaptureDevices.clear();
  697. if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
  698. ERR("Error enumerating DirectSound capture devices: {:#x}", as_unsigned(hr));
  699. outnames.reserve(CaptureDevices.size());
  700. std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
  701. break;
  702. }
  703. return outnames;
  704. }
  705. BackendPtr DSoundBackendFactory::createBackend(DeviceBase *device, BackendType type)
  706. {
  707. if(type == BackendType::Playback)
  708. return BackendPtr{new DSoundPlayback{device}};
  709. if(type == BackendType::Capture)
  710. return BackendPtr{new DSoundCapture{device}};
  711. return nullptr;
  712. }