dsound.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  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 <stdlib.h>
  22. #include <stdio.h>
  23. #include <memory.h>
  24. #include <dsound.h>
  25. #include <cguid.h>
  26. #include <mmreg.h>
  27. #ifndef _WAVEFORMATEXTENSIBLE_
  28. #include <ks.h>
  29. #include <ksmedia.h>
  30. #endif
  31. #include "alMain.h"
  32. #include "alu.h"
  33. #include "threads.h"
  34. #include "compat.h"
  35. #include "alstring.h"
  36. #include "backends/base.h"
  37. #ifndef DSSPEAKER_5POINT1
  38. # define DSSPEAKER_5POINT1 0x00000006
  39. #endif
  40. #ifndef DSSPEAKER_5POINT1_BACK
  41. # define DSSPEAKER_5POINT1_BACK 0x00000006
  42. #endif
  43. #ifndef DSSPEAKER_7POINT1
  44. # define DSSPEAKER_7POINT1 0x00000007
  45. #endif
  46. #ifndef DSSPEAKER_7POINT1_SURROUND
  47. # define DSSPEAKER_7POINT1_SURROUND 0x00000008
  48. #endif
  49. #ifndef DSSPEAKER_5POINT1_SURROUND
  50. # define DSSPEAKER_5POINT1_SURROUND 0x00000009
  51. #endif
  52. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  53. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  54. #define DEVNAME_TAIL " on OpenAL Soft"
  55. #ifdef HAVE_DYNLOAD
  56. static void *ds_handle;
  57. static HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
  58. static HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  59. static HRESULT (WINAPI *pDirectSoundCaptureCreate)(const GUID *pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
  60. static HRESULT (WINAPI *pDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext);
  61. #define DirectSoundCreate pDirectSoundCreate
  62. #define DirectSoundEnumerateW pDirectSoundEnumerateW
  63. #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
  64. #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
  65. #endif
  66. static ALCboolean DSoundLoad(void)
  67. {
  68. #ifdef HAVE_DYNLOAD
  69. if(!ds_handle)
  70. {
  71. ds_handle = LoadLib("dsound.dll");
  72. if(ds_handle == NULL)
  73. {
  74. ERR("Failed to load dsound.dll\n");
  75. return ALC_FALSE;
  76. }
  77. #define LOAD_FUNC(f) do { \
  78. p##f = GetSymbol(ds_handle, #f); \
  79. if(p##f == NULL) { \
  80. CloseLib(ds_handle); \
  81. ds_handle = NULL; \
  82. return ALC_FALSE; \
  83. } \
  84. } while(0)
  85. LOAD_FUNC(DirectSoundCreate);
  86. LOAD_FUNC(DirectSoundEnumerateW);
  87. LOAD_FUNC(DirectSoundCaptureCreate);
  88. LOAD_FUNC(DirectSoundCaptureEnumerateW);
  89. #undef LOAD_FUNC
  90. }
  91. #endif
  92. return ALC_TRUE;
  93. }
  94. #define MAX_UPDATES 128
  95. typedef struct {
  96. al_string name;
  97. GUID guid;
  98. } DevMap;
  99. TYPEDEF_VECTOR(DevMap, vector_DevMap)
  100. static vector_DevMap PlaybackDevices;
  101. static vector_DevMap CaptureDevices;
  102. static void clear_devlist(vector_DevMap *list)
  103. {
  104. #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
  105. VECTOR_FOR_EACH(DevMap, *list, DEINIT_STR);
  106. VECTOR_RESIZE(*list, 0);
  107. #undef DEINIT_STR
  108. }
  109. static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
  110. {
  111. vector_DevMap *devices = data;
  112. OLECHAR *guidstr = NULL;
  113. DevMap entry;
  114. HRESULT hr;
  115. int count;
  116. if(!guid)
  117. return TRUE;
  118. AL_STRING_INIT(entry.name);
  119. count = 0;
  120. while(1)
  121. {
  122. const DevMap *iter;
  123. al_string_copy_wcstr(&entry.name, desc);
  124. if(count == 0)
  125. al_string_append_cstr(&entry.name, DEVNAME_TAIL);
  126. else
  127. {
  128. char str[64];
  129. snprintf(str, sizeof(str), " #%d"DEVNAME_TAIL, count+1);
  130. al_string_append_cstr(&entry.name, str);
  131. }
  132. #define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
  133. VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
  134. if(iter == VECTOR_ITER_END(*devices)) break;
  135. #undef MATCH_ENTRY
  136. count++;
  137. }
  138. entry.guid = *guid;
  139. hr = StringFromCLSID(guid, &guidstr);
  140. if(SUCCEEDED(hr))
  141. {
  142. TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
  143. CoTaskMemFree(guidstr);
  144. }
  145. VECTOR_PUSH_BACK(*devices, entry);
  146. return TRUE;
  147. }
  148. typedef struct ALCdsoundPlayback {
  149. DERIVE_FROM_TYPE(ALCbackend);
  150. IDirectSound *DS;
  151. IDirectSoundBuffer *PrimaryBuffer;
  152. IDirectSoundBuffer *Buffer;
  153. IDirectSoundNotify *Notifies;
  154. HANDLE NotifyEvent;
  155. volatile int killNow;
  156. althrd_t thread;
  157. } ALCdsoundPlayback;
  158. static int ALCdsoundPlayback_mixerProc(void *ptr);
  159. static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device);
  160. static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, Destruct)
  161. static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *name);
  162. static void ALCdsoundPlayback_close(ALCdsoundPlayback *self);
  163. static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self);
  164. static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self);
  165. static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self);
  166. static DECLARE_FORWARD2(ALCdsoundPlayback, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
  167. static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALCuint, availableSamples)
  168. static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, ALint64, getLatency)
  169. static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, lock)
  170. static DECLARE_FORWARD(ALCdsoundPlayback, ALCbackend, void, unlock)
  171. DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback)
  172. DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback);
  173. static void ALCdsoundPlayback_Construct(ALCdsoundPlayback *self, ALCdevice *device)
  174. {
  175. ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
  176. SET_VTABLE2(ALCdsoundPlayback, ALCbackend, self);
  177. }
  178. FORCE_ALIGN static int ALCdsoundPlayback_mixerProc(void *ptr)
  179. {
  180. ALCdsoundPlayback *self = ptr;
  181. ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
  182. DSBCAPS DSBCaps;
  183. DWORD LastCursor = 0;
  184. DWORD PlayCursor;
  185. void *WritePtr1, *WritePtr2;
  186. DWORD WriteCnt1, WriteCnt2;
  187. BOOL Playing = FALSE;
  188. DWORD FrameSize;
  189. DWORD FragSize;
  190. DWORD avail;
  191. HRESULT err;
  192. SetRTPriority();
  193. althrd_setname(althrd_current(), MIXER_THREAD_NAME);
  194. memset(&DSBCaps, 0, sizeof(DSBCaps));
  195. DSBCaps.dwSize = sizeof(DSBCaps);
  196. err = IDirectSoundBuffer_GetCaps(self->Buffer, &DSBCaps);
  197. if(FAILED(err))
  198. {
  199. ERR("Failed to get buffer caps: 0x%lx\n", err);
  200. ALCdevice_Lock(device);
  201. aluHandleDisconnect(device);
  202. ALCdevice_Unlock(device);
  203. return 1;
  204. }
  205. FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
  206. FragSize = device->UpdateSize * FrameSize;
  207. IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &LastCursor, NULL);
  208. while(!self->killNow)
  209. {
  210. // Get current play cursor
  211. IDirectSoundBuffer_GetCurrentPosition(self->Buffer, &PlayCursor, NULL);
  212. avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
  213. if(avail < FragSize)
  214. {
  215. if(!Playing)
  216. {
  217. err = IDirectSoundBuffer_Play(self->Buffer, 0, 0, DSBPLAY_LOOPING);
  218. if(FAILED(err))
  219. {
  220. ERR("Failed to play buffer: 0x%lx\n", err);
  221. ALCdevice_Lock(device);
  222. aluHandleDisconnect(device);
  223. ALCdevice_Unlock(device);
  224. return 1;
  225. }
  226. Playing = TRUE;
  227. }
  228. avail = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
  229. if(avail != WAIT_OBJECT_0)
  230. ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
  231. continue;
  232. }
  233. avail -= avail%FragSize;
  234. // Lock output buffer
  235. WriteCnt1 = 0;
  236. WriteCnt2 = 0;
  237. err = IDirectSoundBuffer_Lock(self->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  238. // If the buffer is lost, restore it and lock
  239. if(err == DSERR_BUFFERLOST)
  240. {
  241. WARN("Buffer lost, restoring...\n");
  242. err = IDirectSoundBuffer_Restore(self->Buffer);
  243. if(SUCCEEDED(err))
  244. {
  245. Playing = FALSE;
  246. LastCursor = 0;
  247. err = IDirectSoundBuffer_Lock(self->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  248. }
  249. }
  250. // Successfully locked the output buffer
  251. if(SUCCEEDED(err))
  252. {
  253. // If we have an active context, mix data directly into output buffer otherwise fill with silence
  254. aluMixData(device, WritePtr1, WriteCnt1/FrameSize);
  255. aluMixData(device, WritePtr2, WriteCnt2/FrameSize);
  256. // Unlock output buffer only when successfully locked
  257. IDirectSoundBuffer_Unlock(self->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
  258. }
  259. else
  260. {
  261. ERR("Buffer lock error: %#lx\n", err);
  262. ALCdevice_Lock(device);
  263. aluHandleDisconnect(device);
  264. ALCdevice_Unlock(device);
  265. return 1;
  266. }
  267. // Update old write cursor location
  268. LastCursor += WriteCnt1+WriteCnt2;
  269. LastCursor %= DSBCaps.dwBufferBytes;
  270. }
  271. return 0;
  272. }
  273. static ALCenum ALCdsoundPlayback_open(ALCdsoundPlayback *self, const ALCchar *deviceName)
  274. {
  275. ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
  276. const GUID *guid = NULL;
  277. HRESULT hr, hrcom;
  278. if(VECTOR_SIZE(PlaybackDevices) == 0)
  279. {
  280. /* Initialize COM to prevent name truncation */
  281. hrcom = CoInitialize(NULL);
  282. hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
  283. if(FAILED(hr))
  284. ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
  285. if(SUCCEEDED(hrcom))
  286. CoUninitialize();
  287. }
  288. if(!deviceName && VECTOR_SIZE(PlaybackDevices) > 0)
  289. {
  290. deviceName = al_string_get_cstr(VECTOR_FRONT(PlaybackDevices).name);
  291. guid = &VECTOR_FRONT(PlaybackDevices).guid;
  292. }
  293. else
  294. {
  295. const DevMap *iter;
  296. #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
  297. VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
  298. #undef MATCH_NAME
  299. if(iter == VECTOR_ITER_END(PlaybackDevices))
  300. return ALC_INVALID_VALUE;
  301. guid = &iter->guid;
  302. }
  303. hr = DS_OK;
  304. self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  305. if(self->NotifyEvent == NULL)
  306. hr = E_FAIL;
  307. //DirectSound Init code
  308. if(SUCCEEDED(hr))
  309. hr = DirectSoundCreate(guid, &self->DS, NULL);
  310. if(SUCCEEDED(hr))
  311. hr = IDirectSound_SetCooperativeLevel(self->DS, GetForegroundWindow(), DSSCL_PRIORITY);
  312. if(FAILED(hr))
  313. {
  314. if(self->DS)
  315. IDirectSound_Release(self->DS);
  316. self->DS = NULL;
  317. if(self->NotifyEvent)
  318. CloseHandle(self->NotifyEvent);
  319. self->NotifyEvent = NULL;
  320. ERR("Device init failed: 0x%08lx\n", hr);
  321. return ALC_INVALID_VALUE;
  322. }
  323. al_string_copy_cstr(&device->DeviceName, deviceName);
  324. return ALC_NO_ERROR;
  325. }
  326. static void ALCdsoundPlayback_close(ALCdsoundPlayback *self)
  327. {
  328. if(self->Notifies)
  329. IDirectSoundNotify_Release(self->Notifies);
  330. self->Notifies = NULL;
  331. if(self->Buffer)
  332. IDirectSoundBuffer_Release(self->Buffer);
  333. self->Buffer = NULL;
  334. if(self->PrimaryBuffer != NULL)
  335. IDirectSoundBuffer_Release(self->PrimaryBuffer);
  336. self->PrimaryBuffer = NULL;
  337. IDirectSound_Release(self->DS);
  338. self->DS = NULL;
  339. CloseHandle(self->NotifyEvent);
  340. self->NotifyEvent = NULL;
  341. }
  342. static ALCboolean ALCdsoundPlayback_reset(ALCdsoundPlayback *self)
  343. {
  344. ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
  345. DSBUFFERDESC DSBDescription;
  346. WAVEFORMATEXTENSIBLE OutputType;
  347. DWORD speakers;
  348. HRESULT hr;
  349. memset(&OutputType, 0, sizeof(OutputType));
  350. if(self->Notifies)
  351. IDirectSoundNotify_Release(self->Notifies);
  352. self->Notifies = NULL;
  353. if(self->Buffer)
  354. IDirectSoundBuffer_Release(self->Buffer);
  355. self->Buffer = NULL;
  356. if(self->PrimaryBuffer != NULL)
  357. IDirectSoundBuffer_Release(self->PrimaryBuffer);
  358. self->PrimaryBuffer = NULL;
  359. switch(device->FmtType)
  360. {
  361. case DevFmtByte:
  362. device->FmtType = DevFmtUByte;
  363. break;
  364. case DevFmtFloat:
  365. if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
  366. break;
  367. /* fall-through */
  368. case DevFmtUShort:
  369. device->FmtType = DevFmtShort;
  370. break;
  371. case DevFmtUInt:
  372. device->FmtType = DevFmtInt;
  373. break;
  374. case DevFmtUByte:
  375. case DevFmtShort:
  376. case DevFmtInt:
  377. break;
  378. }
  379. hr = IDirectSound_GetSpeakerConfig(self->DS, &speakers);
  380. if(SUCCEEDED(hr))
  381. {
  382. speakers = DSSPEAKER_CONFIG(speakers);
  383. if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
  384. {
  385. if(speakers == DSSPEAKER_MONO)
  386. device->FmtChans = DevFmtMono;
  387. else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
  388. device->FmtChans = DevFmtStereo;
  389. else if(speakers == DSSPEAKER_QUAD)
  390. device->FmtChans = DevFmtQuad;
  391. else if(speakers == DSSPEAKER_5POINT1_SURROUND)
  392. device->FmtChans = DevFmtX51;
  393. else if(speakers == DSSPEAKER_5POINT1_BACK)
  394. device->FmtChans = DevFmtX51Rear;
  395. else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND)
  396. device->FmtChans = DevFmtX71;
  397. else
  398. ERR("Unknown system speaker config: 0x%lx\n", speakers);
  399. }
  400. device->IsHeadphones = (device->FmtChans == DevFmtStereo &&
  401. speakers == DSSPEAKER_HEADPHONE);
  402. switch(device->FmtChans)
  403. {
  404. case DevFmtMono:
  405. OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
  406. break;
  407. case DevFmtBFormat3D:
  408. device->FmtChans = DevFmtStereo;
  409. /*fall-through*/
  410. case DevFmtStereo:
  411. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  412. SPEAKER_FRONT_RIGHT;
  413. break;
  414. case DevFmtQuad:
  415. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  416. SPEAKER_FRONT_RIGHT |
  417. SPEAKER_BACK_LEFT |
  418. SPEAKER_BACK_RIGHT;
  419. break;
  420. case DevFmtX51:
  421. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  422. SPEAKER_FRONT_RIGHT |
  423. SPEAKER_FRONT_CENTER |
  424. SPEAKER_LOW_FREQUENCY |
  425. SPEAKER_SIDE_LEFT |
  426. SPEAKER_SIDE_RIGHT;
  427. break;
  428. case DevFmtX51Rear:
  429. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  430. SPEAKER_FRONT_RIGHT |
  431. SPEAKER_FRONT_CENTER |
  432. SPEAKER_LOW_FREQUENCY |
  433. SPEAKER_BACK_LEFT |
  434. SPEAKER_BACK_RIGHT;
  435. break;
  436. case DevFmtX61:
  437. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  438. SPEAKER_FRONT_RIGHT |
  439. SPEAKER_FRONT_CENTER |
  440. SPEAKER_LOW_FREQUENCY |
  441. SPEAKER_BACK_CENTER |
  442. SPEAKER_SIDE_LEFT |
  443. SPEAKER_SIDE_RIGHT;
  444. break;
  445. case DevFmtX71:
  446. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  447. SPEAKER_FRONT_RIGHT |
  448. SPEAKER_FRONT_CENTER |
  449. SPEAKER_LOW_FREQUENCY |
  450. SPEAKER_BACK_LEFT |
  451. SPEAKER_BACK_RIGHT |
  452. SPEAKER_SIDE_LEFT |
  453. SPEAKER_SIDE_RIGHT;
  454. break;
  455. }
  456. retry_open:
  457. hr = S_OK;
  458. OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  459. OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
  460. OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
  461. OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
  462. OutputType.Format.nSamplesPerSec = device->Frequency;
  463. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
  464. OutputType.Format.cbSize = 0;
  465. }
  466. if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
  467. {
  468. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  469. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  470. OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  471. if(device->FmtType == DevFmtFloat)
  472. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  473. else
  474. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  475. if(self->PrimaryBuffer)
  476. IDirectSoundBuffer_Release(self->PrimaryBuffer);
  477. self->PrimaryBuffer = NULL;
  478. }
  479. else
  480. {
  481. if(SUCCEEDED(hr) && !self->PrimaryBuffer)
  482. {
  483. memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
  484. DSBDescription.dwSize=sizeof(DSBUFFERDESC);
  485. DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
  486. hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->PrimaryBuffer, NULL);
  487. }
  488. if(SUCCEEDED(hr))
  489. hr = IDirectSoundBuffer_SetFormat(self->PrimaryBuffer,&OutputType.Format);
  490. }
  491. if(SUCCEEDED(hr))
  492. {
  493. if(device->NumUpdates > MAX_UPDATES)
  494. {
  495. device->UpdateSize = (device->UpdateSize*device->NumUpdates +
  496. MAX_UPDATES-1) / MAX_UPDATES;
  497. device->NumUpdates = MAX_UPDATES;
  498. }
  499. memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
  500. DSBDescription.dwSize=sizeof(DSBUFFERDESC);
  501. DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
  502. DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
  503. OutputType.Format.nBlockAlign;
  504. DSBDescription.lpwfxFormat=&OutputType.Format;
  505. hr = IDirectSound_CreateSoundBuffer(self->DS, &DSBDescription, &self->Buffer, NULL);
  506. if(FAILED(hr) && device->FmtType == DevFmtFloat)
  507. {
  508. device->FmtType = DevFmtShort;
  509. goto retry_open;
  510. }
  511. }
  512. if(SUCCEEDED(hr))
  513. {
  514. hr = IDirectSoundBuffer_QueryInterface(self->Buffer, &IID_IDirectSoundNotify, (void**)&self->Notifies);
  515. if(SUCCEEDED(hr))
  516. {
  517. DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
  518. ALuint i;
  519. for(i = 0;i < device->NumUpdates;++i)
  520. {
  521. notifies[i].dwOffset = i * device->UpdateSize *
  522. OutputType.Format.nBlockAlign;
  523. notifies[i].hEventNotify = self->NotifyEvent;
  524. }
  525. if(IDirectSoundNotify_SetNotificationPositions(self->Notifies, device->NumUpdates, notifies) != DS_OK)
  526. hr = E_FAIL;
  527. }
  528. }
  529. if(FAILED(hr))
  530. {
  531. if(self->Notifies != NULL)
  532. IDirectSoundNotify_Release(self->Notifies);
  533. self->Notifies = NULL;
  534. if(self->Buffer != NULL)
  535. IDirectSoundBuffer_Release(self->Buffer);
  536. self->Buffer = NULL;
  537. if(self->PrimaryBuffer != NULL)
  538. IDirectSoundBuffer_Release(self->PrimaryBuffer);
  539. self->PrimaryBuffer = NULL;
  540. return ALC_FALSE;
  541. }
  542. ResetEvent(self->NotifyEvent);
  543. SetDefaultWFXChannelOrder(device);
  544. return ALC_TRUE;
  545. }
  546. static ALCboolean ALCdsoundPlayback_start(ALCdsoundPlayback *self)
  547. {
  548. self->killNow = 0;
  549. if(althrd_create(&self->thread, ALCdsoundPlayback_mixerProc, self) != althrd_success)
  550. return ALC_FALSE;
  551. return ALC_TRUE;
  552. }
  553. static void ALCdsoundPlayback_stop(ALCdsoundPlayback *self)
  554. {
  555. int res;
  556. if(self->killNow)
  557. return;
  558. self->killNow = 1;
  559. althrd_join(self->thread, &res);
  560. IDirectSoundBuffer_Stop(self->Buffer);
  561. }
  562. typedef struct ALCdsoundCapture {
  563. DERIVE_FROM_TYPE(ALCbackend);
  564. IDirectSoundCapture *DSC;
  565. IDirectSoundCaptureBuffer *DSCbuffer;
  566. DWORD BufferBytes;
  567. DWORD Cursor;
  568. RingBuffer *Ring;
  569. } ALCdsoundCapture;
  570. static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device);
  571. static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, Destruct)
  572. static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *name);
  573. static void ALCdsoundCapture_close(ALCdsoundCapture *self);
  574. static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALCboolean, reset)
  575. static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self);
  576. static void ALCdsoundCapture_stop(ALCdsoundCapture *self);
  577. static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples);
  578. static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self);
  579. static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, ALint64, getLatency)
  580. static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, lock)
  581. static DECLARE_FORWARD(ALCdsoundCapture, ALCbackend, void, unlock)
  582. DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture)
  583. DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture);
  584. static void ALCdsoundCapture_Construct(ALCdsoundCapture *self, ALCdevice *device)
  585. {
  586. ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
  587. SET_VTABLE2(ALCdsoundCapture, ALCbackend, self);
  588. }
  589. static ALCenum ALCdsoundCapture_open(ALCdsoundCapture *self, const ALCchar *deviceName)
  590. {
  591. ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
  592. WAVEFORMATEXTENSIBLE InputType;
  593. DSCBUFFERDESC DSCBDescription;
  594. const GUID *guid = NULL;
  595. HRESULT hr, hrcom;
  596. ALuint samples;
  597. if(VECTOR_SIZE(CaptureDevices) == 0)
  598. {
  599. /* Initialize COM to prevent name truncation */
  600. hrcom = CoInitialize(NULL);
  601. hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
  602. if(FAILED(hr))
  603. ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
  604. if(SUCCEEDED(hrcom))
  605. CoUninitialize();
  606. }
  607. if(!deviceName && VECTOR_SIZE(CaptureDevices) > 0)
  608. {
  609. deviceName = al_string_get_cstr(VECTOR_FRONT(CaptureDevices).name);
  610. guid = &VECTOR_FRONT(CaptureDevices).guid;
  611. }
  612. else
  613. {
  614. const DevMap *iter;
  615. #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
  616. VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
  617. #undef MATCH_NAME
  618. if(iter == VECTOR_ITER_END(CaptureDevices))
  619. return ALC_INVALID_VALUE;
  620. guid = &iter->guid;
  621. }
  622. switch(device->FmtType)
  623. {
  624. case DevFmtByte:
  625. case DevFmtUShort:
  626. case DevFmtUInt:
  627. WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
  628. return ALC_INVALID_ENUM;
  629. case DevFmtUByte:
  630. case DevFmtShort:
  631. case DevFmtInt:
  632. case DevFmtFloat:
  633. break;
  634. }
  635. //DirectSoundCapture Init code
  636. hr = DirectSoundCaptureCreate(guid, &self->DSC, NULL);
  637. if(SUCCEEDED(hr))
  638. {
  639. memset(&InputType, 0, sizeof(InputType));
  640. switch(device->FmtChans)
  641. {
  642. case DevFmtMono:
  643. InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
  644. break;
  645. case DevFmtStereo:
  646. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  647. SPEAKER_FRONT_RIGHT;
  648. break;
  649. case DevFmtQuad:
  650. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  651. SPEAKER_FRONT_RIGHT |
  652. SPEAKER_BACK_LEFT |
  653. SPEAKER_BACK_RIGHT;
  654. break;
  655. case DevFmtX51:
  656. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  657. SPEAKER_FRONT_RIGHT |
  658. SPEAKER_FRONT_CENTER |
  659. SPEAKER_LOW_FREQUENCY |
  660. SPEAKER_SIDE_LEFT |
  661. SPEAKER_SIDE_RIGHT;
  662. break;
  663. case DevFmtX51Rear:
  664. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  665. SPEAKER_FRONT_RIGHT |
  666. SPEAKER_FRONT_CENTER |
  667. SPEAKER_LOW_FREQUENCY |
  668. SPEAKER_BACK_LEFT |
  669. SPEAKER_BACK_RIGHT;
  670. break;
  671. case DevFmtX61:
  672. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  673. SPEAKER_FRONT_RIGHT |
  674. SPEAKER_FRONT_CENTER |
  675. SPEAKER_LOW_FREQUENCY |
  676. SPEAKER_BACK_CENTER |
  677. SPEAKER_SIDE_LEFT |
  678. SPEAKER_SIDE_RIGHT;
  679. break;
  680. case DevFmtX71:
  681. InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  682. SPEAKER_FRONT_RIGHT |
  683. SPEAKER_FRONT_CENTER |
  684. SPEAKER_LOW_FREQUENCY |
  685. SPEAKER_BACK_LEFT |
  686. SPEAKER_BACK_RIGHT |
  687. SPEAKER_SIDE_LEFT |
  688. SPEAKER_SIDE_RIGHT;
  689. break;
  690. case DevFmtBFormat3D:
  691. break;
  692. }
  693. InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  694. InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
  695. InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
  696. InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
  697. InputType.Format.nSamplesPerSec = device->Frequency;
  698. InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
  699. InputType.Format.cbSize = 0;
  700. if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
  701. {
  702. InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  703. InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
  704. InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
  705. if(device->FmtType == DevFmtFloat)
  706. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  707. else
  708. InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  709. }
  710. samples = device->UpdateSize * device->NumUpdates;
  711. samples = maxu(samples, 100 * device->Frequency / 1000);
  712. memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
  713. DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
  714. DSCBDescription.dwFlags = 0;
  715. DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
  716. DSCBDescription.lpwfxFormat = &InputType.Format;
  717. hr = IDirectSoundCapture_CreateCaptureBuffer(self->DSC, &DSCBDescription, &self->DSCbuffer, NULL);
  718. }
  719. if(SUCCEEDED(hr))
  720. {
  721. self->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
  722. if(self->Ring == NULL)
  723. hr = DSERR_OUTOFMEMORY;
  724. }
  725. if(FAILED(hr))
  726. {
  727. ERR("Device init failed: 0x%08lx\n", hr);
  728. DestroyRingBuffer(self->Ring);
  729. self->Ring = NULL;
  730. if(self->DSCbuffer != NULL)
  731. IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
  732. self->DSCbuffer = NULL;
  733. if(self->DSC)
  734. IDirectSoundCapture_Release(self->DSC);
  735. self->DSC = NULL;
  736. return ALC_INVALID_VALUE;
  737. }
  738. self->BufferBytes = DSCBDescription.dwBufferBytes;
  739. SetDefaultWFXChannelOrder(device);
  740. al_string_copy_cstr(&device->DeviceName, deviceName);
  741. return ALC_NO_ERROR;
  742. }
  743. static void ALCdsoundCapture_close(ALCdsoundCapture *self)
  744. {
  745. DestroyRingBuffer(self->Ring);
  746. self->Ring = NULL;
  747. if(self->DSCbuffer != NULL)
  748. {
  749. IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
  750. IDirectSoundCaptureBuffer_Release(self->DSCbuffer);
  751. self->DSCbuffer = NULL;
  752. }
  753. IDirectSoundCapture_Release(self->DSC);
  754. self->DSC = NULL;
  755. }
  756. static ALCboolean ALCdsoundCapture_start(ALCdsoundCapture *self)
  757. {
  758. HRESULT hr;
  759. hr = IDirectSoundCaptureBuffer_Start(self->DSCbuffer, DSCBSTART_LOOPING);
  760. if(FAILED(hr))
  761. {
  762. ERR("start failed: 0x%08lx\n", hr);
  763. aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
  764. return ALC_FALSE;
  765. }
  766. return ALC_TRUE;
  767. }
  768. static void ALCdsoundCapture_stop(ALCdsoundCapture *self)
  769. {
  770. HRESULT hr;
  771. hr = IDirectSoundCaptureBuffer_Stop(self->DSCbuffer);
  772. if(FAILED(hr))
  773. {
  774. ERR("stop failed: 0x%08lx\n", hr);
  775. aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
  776. }
  777. }
  778. static ALCenum ALCdsoundCapture_captureSamples(ALCdsoundCapture *self, ALCvoid *buffer, ALCuint samples)
  779. {
  780. ReadRingBuffer(self->Ring, buffer, samples);
  781. return ALC_NO_ERROR;
  782. }
  783. static ALCuint ALCdsoundCapture_availableSamples(ALCdsoundCapture *self)
  784. {
  785. ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
  786. DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
  787. void *ReadPtr1, *ReadPtr2;
  788. DWORD ReadCnt1, ReadCnt2;
  789. DWORD FrameSize;
  790. HRESULT hr;
  791. if(!device->Connected)
  792. goto done;
  793. FrameSize = FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
  794. BufferBytes = self->BufferBytes;
  795. LastCursor = self->Cursor;
  796. hr = IDirectSoundCaptureBuffer_GetCurrentPosition(self->DSCbuffer, NULL, &ReadCursor);
  797. if(SUCCEEDED(hr))
  798. {
  799. NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
  800. if(NumBytes == 0)
  801. goto done;
  802. hr = IDirectSoundCaptureBuffer_Lock(self->DSCbuffer, LastCursor, NumBytes,
  803. &ReadPtr1, &ReadCnt1,
  804. &ReadPtr2, &ReadCnt2, 0);
  805. }
  806. if(SUCCEEDED(hr))
  807. {
  808. WriteRingBuffer(self->Ring, ReadPtr1, ReadCnt1/FrameSize);
  809. if(ReadPtr2 != NULL)
  810. WriteRingBuffer(self->Ring, ReadPtr2, ReadCnt2/FrameSize);
  811. hr = IDirectSoundCaptureBuffer_Unlock(self->DSCbuffer,
  812. ReadPtr1, ReadCnt1,
  813. ReadPtr2, ReadCnt2);
  814. self->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
  815. }
  816. if(FAILED(hr))
  817. {
  818. ERR("update failed: 0x%08lx\n", hr);
  819. aluHandleDisconnect(device);
  820. }
  821. done:
  822. return RingBufferSize(self->Ring);
  823. }
  824. static inline void AppendAllDevicesList2(const DevMap *entry)
  825. { AppendAllDevicesList(al_string_get_cstr(entry->name)); }
  826. static inline void AppendCaptureDeviceList2(const DevMap *entry)
  827. { AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
  828. typedef struct ALCdsoundBackendFactory {
  829. DERIVE_FROM_TYPE(ALCbackendFactory);
  830. } ALCdsoundBackendFactory;
  831. #define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } }
  832. ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void);
  833. static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory *self);
  834. static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory *self);
  835. static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory *self, ALCbackend_Type type);
  836. static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory *self, enum DevProbe type);
  837. static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
  838. DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory);
  839. ALCbackendFactory *ALCdsoundBackendFactory_getFactory(void)
  840. {
  841. static ALCdsoundBackendFactory factory = ALCDSOUNDBACKENDFACTORY_INITIALIZER;
  842. return STATIC_CAST(ALCbackendFactory, &factory);
  843. }
  844. static ALCboolean ALCdsoundBackendFactory_init(ALCdsoundBackendFactory* UNUSED(self))
  845. {
  846. VECTOR_INIT(PlaybackDevices);
  847. VECTOR_INIT(CaptureDevices);
  848. if(!DSoundLoad())
  849. return ALC_FALSE;
  850. return ALC_TRUE;
  851. }
  852. static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory* UNUSED(self))
  853. {
  854. clear_devlist(&PlaybackDevices);
  855. VECTOR_DEINIT(PlaybackDevices);
  856. clear_devlist(&CaptureDevices);
  857. VECTOR_DEINIT(CaptureDevices);
  858. #ifdef HAVE_DYNLOAD
  859. if(ds_handle)
  860. CloseLib(ds_handle);
  861. ds_handle = NULL;
  862. #endif
  863. }
  864. static ALCboolean ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory* UNUSED(self), ALCbackend_Type type)
  865. {
  866. if(type == ALCbackend_Playback || type == ALCbackend_Capture)
  867. return ALC_TRUE;
  868. return ALC_FALSE;
  869. }
  870. static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory* UNUSED(self), enum DevProbe type)
  871. {
  872. HRESULT hr, hrcom;
  873. /* Initialize COM to prevent name truncation */
  874. hrcom = CoInitialize(NULL);
  875. switch(type)
  876. {
  877. case ALL_DEVICE_PROBE:
  878. clear_devlist(&PlaybackDevices);
  879. hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
  880. if(FAILED(hr))
  881. ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
  882. VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
  883. break;
  884. case CAPTURE_DEVICE_PROBE:
  885. clear_devlist(&CaptureDevices);
  886. hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
  887. if(FAILED(hr))
  888. ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
  889. VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
  890. break;
  891. }
  892. if(SUCCEEDED(hrcom))
  893. CoUninitialize();
  894. }
  895. static ALCbackend* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
  896. {
  897. if(type == ALCbackend_Playback)
  898. {
  899. ALCdsoundPlayback *backend;
  900. NEW_OBJ(backend, ALCdsoundPlayback)(device);
  901. if(!backend) return NULL;
  902. return STATIC_CAST(ALCbackend, backend);
  903. }
  904. if(type == ALCbackend_Capture)
  905. {
  906. ALCdsoundCapture *backend;
  907. NEW_OBJ(backend, ALCdsoundCapture)(device);
  908. if(!backend) return NULL;
  909. return STATIC_CAST(ALCbackend, backend);
  910. }
  911. return NULL;
  912. }