dsound.cpp 29 KB

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