dsound.cpp 28 KB

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