dsound.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  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., 59 Temple Place - Suite 330,
  17. * Boston, MA 02111-1307, USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #define _WIN32_WINNT 0x0500
  22. #define INITGUID
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <memory.h>
  26. #include <dsound.h>
  27. #include <cguid.h>
  28. #include <mmreg.h>
  29. #ifndef _WAVEFORMATEXTENSIBLE_
  30. #include <ks.h>
  31. #include <ksmedia.h>
  32. #endif
  33. #include "alMain.h"
  34. #include "AL/al.h"
  35. #include "AL/alc.h"
  36. #ifndef DSSPEAKER_5POINT1
  37. #define DSSPEAKER_5POINT1 6
  38. #endif
  39. #ifndef DSSPEAKER_7POINT1
  40. #define DSSPEAKER_7POINT1 7
  41. #endif
  42. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  43. DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
  44. static void *ds_handle;
  45. static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
  46. static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
  47. typedef struct {
  48. // DirectSound Playback Device
  49. LPDIRECTSOUND lpDS;
  50. LPDIRECTSOUNDBUFFER DSpbuffer;
  51. LPDIRECTSOUNDBUFFER DSsbuffer;
  52. volatile int killNow;
  53. ALvoid *thread;
  54. } DSoundData;
  55. typedef struct {
  56. ALCchar *name;
  57. GUID guid;
  58. } DevMap;
  59. static const ALCchar dsDevice[] = "DirectSound Default";
  60. static DevMap *DeviceList;
  61. static ALuint NumDevices;
  62. void *DSoundLoad(void)
  63. {
  64. if(!ds_handle)
  65. {
  66. #ifdef _WIN32
  67. ds_handle = LoadLibraryA("dsound.dll");
  68. if(ds_handle == NULL)
  69. {
  70. AL_PRINT("Failed to load dsound.dll\n");
  71. return NULL;
  72. }
  73. #define LOAD_FUNC(f) do { \
  74. p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
  75. if(p##f == NULL) \
  76. { \
  77. FreeLibrary(ds_handle); \
  78. ds_handle = NULL; \
  79. AL_PRINT("Could not load %s from dsound.dll\n", #f); \
  80. return NULL; \
  81. } \
  82. } while(0)
  83. #else
  84. ds_handle = (void*)0xDEADBEEF;
  85. #define LOAD_FUNC(f) p##f = f
  86. #endif
  87. LOAD_FUNC(DirectSoundCreate);
  88. LOAD_FUNC(DirectSoundEnumerateA);
  89. #undef LOAD_FUNC
  90. }
  91. return ds_handle;
  92. }
  93. static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
  94. {
  95. char str[1024];
  96. void *temp;
  97. int count;
  98. ALuint i;
  99. (void)data;
  100. (void)drvname;
  101. if(NumDevices == 0)
  102. {
  103. temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
  104. if(temp)
  105. {
  106. DeviceList = temp;
  107. DeviceList[NumDevices].name = strdup(dsDevice);
  108. DeviceList[NumDevices].guid = GUID_NULL;
  109. NumDevices++;
  110. }
  111. }
  112. if(!guid)
  113. return TRUE;
  114. count = 0;
  115. do {
  116. if(count == 0)
  117. snprintf(str, sizeof(str), "%s via DirectSound", desc);
  118. else
  119. snprintf(str, sizeof(str), "%s #%d via DirectSound", desc, count+1);
  120. count++;
  121. for(i = 0;i < NumDevices;i++)
  122. {
  123. if(strcmp(str, DeviceList[i].name) == 0)
  124. break;
  125. }
  126. } while(i != NumDevices);
  127. temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
  128. if(temp)
  129. {
  130. DeviceList = temp;
  131. DeviceList[NumDevices].name = strdup(str);
  132. DeviceList[NumDevices].guid = *guid;
  133. NumDevices++;
  134. }
  135. return TRUE;
  136. }
  137. static ALuint DSoundProc(ALvoid *ptr)
  138. {
  139. ALCdevice *pDevice = (ALCdevice*)ptr;
  140. DSoundData *pData = (DSoundData*)pDevice->ExtraData;
  141. DSBCAPS DSBCaps;
  142. DWORD LastCursor = 0;
  143. DWORD PlayCursor;
  144. VOID *WritePtr1, *WritePtr2;
  145. DWORD WriteCnt1, WriteCnt2;
  146. BOOL Playing = FALSE;
  147. DWORD FrameSize;
  148. DWORD FragSize;
  149. DWORD avail;
  150. HRESULT err;
  151. SetRTPriority();
  152. memset(&DSBCaps, 0, sizeof(DSBCaps));
  153. DSBCaps.dwSize = sizeof(DSBCaps);
  154. err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
  155. if(FAILED(err))
  156. {
  157. AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
  158. aluHandleDisconnect(pDevice);
  159. return 1;
  160. }
  161. FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
  162. FragSize = pDevice->UpdateSize * FrameSize;
  163. IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
  164. while(!pData->killNow)
  165. {
  166. // Get current play and write cursors
  167. IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
  168. avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
  169. if(avail < FragSize)
  170. {
  171. if(!Playing)
  172. {
  173. err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
  174. if(FAILED(err))
  175. {
  176. AL_PRINT("Failed to play buffer: 0x%lx\n", err);
  177. aluHandleDisconnect(pDevice);
  178. return 1;
  179. }
  180. Playing = TRUE;
  181. }
  182. Sleep(1);
  183. continue;
  184. }
  185. avail -= avail%FragSize;
  186. // Lock output buffer
  187. WriteCnt1 = 0;
  188. WriteCnt2 = 0;
  189. err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  190. // If the buffer is lost, restore it and lock
  191. if(err == DSERR_BUFFERLOST)
  192. {
  193. err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
  194. if(SUCCEEDED(err))
  195. {
  196. Playing = FALSE;
  197. LastCursor = 0;
  198. err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
  199. }
  200. }
  201. // Successfully locked the output buffer
  202. if(SUCCEEDED(err))
  203. {
  204. // If we have an active context, mix data directly into output buffer otherwise fill with silence
  205. aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
  206. aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
  207. // Unlock output buffer only when successfully locked
  208. IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
  209. }
  210. else
  211. {
  212. AL_PRINT("Buffer lock error: %#lx\n", err);
  213. aluHandleDisconnect(pDevice);
  214. return 1;
  215. }
  216. // Update old write cursor location
  217. LastCursor += WriteCnt1+WriteCnt2;
  218. LastCursor %= DSBCaps.dwBufferBytes;
  219. }
  220. return 0;
  221. }
  222. static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
  223. {
  224. DSoundData *pData = NULL;
  225. LPGUID guid = NULL;
  226. HRESULT hr;
  227. if(!DSoundLoad())
  228. return ALC_FALSE;
  229. if(!deviceName)
  230. deviceName = dsDevice;
  231. else if(strcmp(deviceName, dsDevice) != 0)
  232. {
  233. ALuint i;
  234. if(!DeviceList)
  235. {
  236. hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
  237. if(FAILED(hr))
  238. AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
  239. }
  240. for(i = 0;i < NumDevices;i++)
  241. {
  242. if(strcmp(deviceName, DeviceList[i].name) == 0)
  243. {
  244. if(i > 0)
  245. guid = &DeviceList[i].guid;
  246. break;
  247. }
  248. }
  249. if(i == NumDevices)
  250. return ALC_FALSE;
  251. }
  252. //Initialise requested device
  253. pData = calloc(1, sizeof(DSoundData));
  254. if(!pData)
  255. {
  256. alcSetError(device, ALC_OUT_OF_MEMORY);
  257. return ALC_FALSE;
  258. }
  259. //DirectSound Init code
  260. hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
  261. if(SUCCEEDED(hr))
  262. hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
  263. if(FAILED(hr))
  264. {
  265. if(pData->lpDS)
  266. IDirectSound_Release(pData->lpDS);
  267. free(pData);
  268. AL_PRINT("Device init failed: 0x%08lx\n", hr);
  269. return ALC_FALSE;
  270. }
  271. device->szDeviceName = strdup(deviceName);
  272. device->ExtraData = pData;
  273. return ALC_TRUE;
  274. }
  275. static void DSoundClosePlayback(ALCdevice *device)
  276. {
  277. DSoundData *pData = device->ExtraData;
  278. IDirectSound_Release(pData->lpDS);
  279. free(pData);
  280. device->ExtraData = NULL;
  281. }
  282. static ALCboolean DSoundResetPlayback(ALCdevice *device)
  283. {
  284. DSoundData *pData = (DSoundData*)device->ExtraData;
  285. DSBUFFERDESC DSBDescription;
  286. WAVEFORMATEXTENSIBLE OutputType;
  287. DWORD speakers;
  288. HRESULT hr;
  289. memset(&OutputType, 0, sizeof(OutputType));
  290. switch(device->FmtType)
  291. {
  292. case DevFmtByte:
  293. device->FmtType = DevFmtUByte;
  294. break;
  295. case DevFmtUShort:
  296. device->FmtType = DevFmtShort;
  297. break;
  298. case DevFmtUByte:
  299. case DevFmtShort:
  300. case DevFmtFloat:
  301. break;
  302. }
  303. hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
  304. if(SUCCEEDED(hr) && ConfigValueExists(NULL, "format"))
  305. {
  306. switch(device->FmtChans)
  307. {
  308. case DevFmtMono:
  309. speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
  310. break;
  311. case DevFmtStereo:
  312. speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
  313. break;
  314. case DevFmtQuad:
  315. speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
  316. break;
  317. case DevFmtX51:
  318. speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
  319. break;
  320. case DevFmtX61:
  321. /* ??? */;
  322. break;
  323. case DevFmtX71:
  324. speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
  325. break;
  326. }
  327. }
  328. if(SUCCEEDED(hr))
  329. {
  330. speakers = DSSPEAKER_CONFIG(speakers);
  331. if(speakers == DSSPEAKER_MONO)
  332. {
  333. device->FmtChans = DevFmtMono;
  334. OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
  335. }
  336. else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
  337. {
  338. device->FmtChans = DevFmtStereo;
  339. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  340. SPEAKER_FRONT_RIGHT;
  341. }
  342. else if(speakers == DSSPEAKER_QUAD)
  343. {
  344. device->FmtChans = DevFmtQuad;
  345. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  346. SPEAKER_FRONT_RIGHT |
  347. SPEAKER_BACK_LEFT |
  348. SPEAKER_BACK_RIGHT;
  349. }
  350. else if(speakers == DSSPEAKER_5POINT1)
  351. {
  352. device->FmtChans = DevFmtX51;
  353. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  354. SPEAKER_FRONT_RIGHT |
  355. SPEAKER_FRONT_CENTER |
  356. SPEAKER_LOW_FREQUENCY |
  357. SPEAKER_BACK_LEFT |
  358. SPEAKER_BACK_RIGHT;
  359. }
  360. else if(speakers == DSSPEAKER_7POINT1)
  361. {
  362. device->FmtChans = DevFmtX71;
  363. OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
  364. SPEAKER_FRONT_RIGHT |
  365. SPEAKER_FRONT_CENTER |
  366. SPEAKER_LOW_FREQUENCY |
  367. SPEAKER_BACK_LEFT |
  368. SPEAKER_BACK_RIGHT |
  369. SPEAKER_SIDE_LEFT |
  370. SPEAKER_SIDE_RIGHT;
  371. }
  372. OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
  373. OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
  374. OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
  375. OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
  376. OutputType.Format.nSamplesPerSec = device->Frequency;
  377. OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
  378. OutputType.Format.cbSize = 0;
  379. }
  380. if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
  381. {
  382. OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  383. OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
  384. OutputType.Format.cbSize = 22;
  385. if(OutputType.Format.wBitsPerSample == 32)
  386. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  387. else
  388. OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  389. }
  390. else
  391. {
  392. if(SUCCEEDED(hr))
  393. {
  394. memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
  395. DSBDescription.dwSize=sizeof(DSBUFFERDESC);
  396. DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
  397. hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
  398. }
  399. if(SUCCEEDED(hr))
  400. hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
  401. }
  402. if(SUCCEEDED(hr))
  403. {
  404. memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
  405. DSBDescription.dwSize=sizeof(DSBUFFERDESC);
  406. DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
  407. DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
  408. OutputType.Format.nBlockAlign;
  409. DSBDescription.lpwfxFormat=&OutputType.Format;
  410. hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
  411. }
  412. if(SUCCEEDED(hr))
  413. {
  414. SetDefaultWFXChannelOrder(device);
  415. pData->thread = StartThread(DSoundProc, device);
  416. if(!pData->thread)
  417. hr = E_FAIL;
  418. }
  419. if(FAILED(hr))
  420. {
  421. if (pData->DSsbuffer)
  422. IDirectSoundBuffer_Release(pData->DSsbuffer);
  423. pData->DSsbuffer = NULL;
  424. if (pData->DSpbuffer)
  425. IDirectSoundBuffer_Release(pData->DSpbuffer);
  426. pData->DSpbuffer = NULL;
  427. return ALC_FALSE;
  428. }
  429. return ALC_TRUE;
  430. }
  431. static void DSoundStopPlayback(ALCdevice *device)
  432. {
  433. DSoundData *pData = device->ExtraData;
  434. if(!pData->thread)
  435. return;
  436. pData->killNow = 1;
  437. StopThread(pData->thread);
  438. pData->thread = NULL;
  439. pData->killNow = 0;
  440. IDirectSoundBuffer_Release(pData->DSsbuffer);
  441. pData->DSsbuffer = NULL;
  442. if (pData->DSpbuffer)
  443. IDirectSoundBuffer_Release(pData->DSpbuffer);
  444. pData->DSpbuffer = NULL;
  445. }
  446. static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
  447. {
  448. (void)pDevice;
  449. (void)deviceName;
  450. return ALC_FALSE;
  451. }
  452. static void DSoundCloseCapture(ALCdevice *pDevice)
  453. {
  454. (void)pDevice;
  455. }
  456. static void DSoundStartCapture(ALCdevice *pDevice)
  457. {
  458. (void)pDevice;
  459. }
  460. static void DSoundStopCapture(ALCdevice *pDevice)
  461. {
  462. (void)pDevice;
  463. }
  464. static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
  465. {
  466. (void)pDevice;
  467. (void)pBuffer;
  468. (void)lSamples;
  469. }
  470. static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
  471. {
  472. (void)pDevice;
  473. return 0;
  474. }
  475. BackendFuncs DSoundFuncs = {
  476. DSoundOpenPlayback,
  477. DSoundClosePlayback,
  478. DSoundResetPlayback,
  479. DSoundStopPlayback,
  480. DSoundOpenCapture,
  481. DSoundCloseCapture,
  482. DSoundStartCapture,
  483. DSoundStopCapture,
  484. DSoundCaptureSamples,
  485. DSoundAvailableSamples
  486. };
  487. void alcDSoundInit(BackendFuncs *FuncList)
  488. {
  489. *FuncList = DSoundFuncs;
  490. }
  491. void alcDSoundDeinit(void)
  492. {
  493. ALuint i;
  494. for(i = 0;i < NumDevices;++i)
  495. free(DeviceList[i].name);
  496. free(DeviceList);
  497. DeviceList = NULL;
  498. NumDevices = 0;
  499. if(ds_handle)
  500. {
  501. #ifdef _WIN32
  502. FreeLibrary(ds_handle);
  503. #endif
  504. ds_handle = NULL;
  505. }
  506. }
  507. void alcDSoundProbe(int type)
  508. {
  509. if(!DSoundLoad()) return;
  510. if(type == DEVICE_PROBE)
  511. AppendDeviceList(dsDevice);
  512. else if(type == ALL_DEVICE_PROBE)
  513. {
  514. HRESULT hr;
  515. ALuint i;
  516. for(i = 0;i < NumDevices;++i)
  517. free(DeviceList[i].name);
  518. free(DeviceList);
  519. DeviceList = NULL;
  520. NumDevices = 0;
  521. hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
  522. if(FAILED(hr))
  523. AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
  524. else
  525. {
  526. for(i = 0;i < NumDevices;i++)
  527. AppendAllDeviceList(DeviceList[i].name);
  528. }
  529. }
  530. }