dsound.c 36 KB

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