dsound.c 36 KB

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