Small fixups
[openal-soft.git] / Alc / dsound.c
blob111183f370db61b48520f6fbaa9a32a92f75ef34
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.
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.
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
21 #include "config.h"
23 #define _WIN32_WINNT 0x0500
24 #define INITGUID
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
29 #include <dsound.h>
30 #include <mmreg.h>
31 #ifndef _WAVEFORMATEXTENSIBLE_
32 #include <ks.h>
33 #include <ksmedia.h>
34 #endif
36 #include "alMain.h"
37 #include "AL/al.h"
38 #include "AL/alc.h"
40 #ifndef DSSPEAKER_5POINT1
41 #define DSSPEAKER_5POINT1 6
42 #endif
43 #ifndef DSSPEAKER_7POINT1
44 #define DSSPEAKER_7POINT1 7
45 #endif
47 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
48 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
50 static void *ds_handle;
51 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
52 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
55 typedef struct {
56 // DirectSound Playback Device
57 LPDIRECTSOUND lpDS;
58 LPDIRECTSOUNDBUFFER DSpbuffer;
59 LPDIRECTSOUNDBUFFER DSsbuffer;
61 volatile int killNow;
62 ALvoid *thread;
63 } DSoundData;
66 typedef struct {
67 ALCchar *name;
68 GUID guid;
69 } DevMap;
71 static const ALCchar dsDevice[] = "DirectSound Software";
72 static DevMap *DeviceList;
73 static ALuint NumDevices;
74 static volatile ALuint load_count;
77 void DSoundLoad(void)
79 if(load_count == 0)
81 #ifdef _WIN32
82 ds_handle = LoadLibraryA("dsound.dll");
83 if(ds_handle == NULL)
85 AL_PRINT("Failed to load dsound.dll\n");
86 return;
89 #define LOAD_FUNC(f) do { \
90 p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
91 if(p##f == NULL) \
92 { \
93 FreeLibrary(ds_handle); \
94 ds_handle = NULL; \
95 AL_PRINT("Could not load %s from dsound.dll\n", #f); \
96 return; \
97 } \
98 } while(0)
99 #else
100 ds_handle = (void*)0xDEADBEEF;
101 #define LOAD_FUNC(f) p##f = f
102 #endif
104 LOAD_FUNC(DirectSoundCreate);
105 LOAD_FUNC(DirectSoundEnumerateA);
106 #undef LOAD_FUNC
108 ++load_count;
111 void DSoundUnload(void)
113 if(load_count == 0 || --load_count > 0)
114 return;
116 #ifdef _WIN32
117 FreeLibrary(ds_handle);
118 #endif
119 ds_handle = NULL;
123 static ALuint DSoundProc(ALvoid *ptr)
125 ALCdevice *pDevice = (ALCdevice*)ptr;
126 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
127 DSBCAPS DSBCaps;
128 DWORD LastCursor = 0;
129 DWORD PlayCursor;
130 VOID *WritePtr1, *WritePtr2;
131 DWORD WriteCnt1, WriteCnt2;
132 DWORD FrameSize;
133 DWORD FragSize;
134 DWORD avail;
135 HRESULT err;
137 memset(&DSBCaps, 0, sizeof(DSBCaps));
138 DSBCaps.dwSize = sizeof(DSBCaps);
139 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
140 if(FAILED(err))
142 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
143 aluHandleDisconnect(pDevice);
144 return 1;
147 FrameSize = aluChannelsFromFormat(pDevice->Format) *
148 aluBytesFromFormat(pDevice->Format);
149 FragSize = pDevice->UpdateSize * FrameSize;
151 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
152 while(!pData->killNow)
154 // Get current play and write cursors
155 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
156 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
158 if(avail < FragSize)
160 Sleep(1);
161 continue;
163 avail -= avail%FragSize;
165 // Lock output buffer
166 WriteCnt1 = 0;
167 WriteCnt2 = 0;
168 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
170 // If the buffer is lost, restore it, play and lock
171 if(err == DSERR_BUFFERLOST)
173 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
174 if(SUCCEEDED(err))
175 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
176 if(SUCCEEDED(err))
177 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
180 // Successfully locked the output buffer
181 if(SUCCEEDED(err))
183 // If we have an active context, mix data directly into output buffer otherwise fill with silence
184 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
185 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
187 // Unlock output buffer only when successfully locked
188 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
190 else
191 AL_PRINT("Buffer lock error: %#lx\n", err);
193 // Update old write cursor location
194 LastCursor += WriteCnt1+WriteCnt2;
195 LastCursor %= DSBCaps.dwBufferBytes;
198 return 0;
201 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
203 DSoundData *pData = NULL;
204 LPGUID guid = NULL;
205 HRESULT hr;
207 if(!deviceName)
208 deviceName = dsDevice;
209 else if(strcmp(deviceName, dsDevice) != 0)
211 ALuint i;
212 for(i = 0;i < NumDevices;i++)
214 if(strcmp(deviceName, DeviceList[i].name) == 0)
216 guid = &DeviceList[i].guid;
217 break;
220 if(i == NumDevices)
221 return ALC_FALSE;
224 DSoundLoad();
225 if(ds_handle == NULL)
226 return ALC_FALSE;
228 //Initialise requested device
230 pData = calloc(1, sizeof(DSoundData));
231 if(!pData)
233 alcSetError(ALC_OUT_OF_MEMORY);
234 DSoundUnload();
235 return ALC_FALSE;
238 //DirectSound Init code
239 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
240 if(SUCCEEDED(hr))
241 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
242 if(FAILED(hr))
244 if(pData->lpDS)
245 IDirectSound_Release(pData->lpDS);
246 free(pData);
247 DSoundUnload();
248 return ALC_FALSE;
251 device->szDeviceName = strdup(deviceName);
252 device->ExtraData = pData;
253 return ALC_TRUE;
256 static void DSoundClosePlayback(ALCdevice *device)
258 DSoundData *pData = device->ExtraData;
260 IDirectSound_Release(pData->lpDS);
261 free(pData);
262 device->ExtraData = NULL;
264 DSoundUnload();
267 static ALCboolean DSoundResetPlayback(ALCdevice *device)
269 DSoundData *pData = (DSoundData*)device->ExtraData;
270 DSBUFFERDESC DSBDescription;
271 WAVEFORMATEXTENSIBLE OutputType;
272 DWORD frameSize = 0;
273 ALenum format = 0;
274 DWORD speakers;
275 HRESULT hr;
277 memset(&OutputType, 0, sizeof(OutputType));
279 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
280 if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
282 if(aluChannelsFromFormat(device->Format) == 1)
283 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
284 else if(aluChannelsFromFormat(device->Format) == 2)
285 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
286 else if(aluChannelsFromFormat(device->Format) == 4)
287 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
288 else if(aluChannelsFromFormat(device->Format) == 6)
289 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
290 else if(aluChannelsFromFormat(device->Format) == 8)
291 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
292 else
294 AL_PRINT("Unknown format: 0x%x\n", device->Format);
295 return ALC_FALSE;
298 if(SUCCEEDED(hr))
300 speakers = DSSPEAKER_CONFIG(speakers);
301 if(speakers == DSSPEAKER_MONO)
303 if(aluBytesFromFormat(device->Format) == 1)
304 format = AL_FORMAT_MONO8;
305 else if(aluBytesFromFormat(device->Format) == 2)
306 format = AL_FORMAT_MONO16;
307 else if(aluBytesFromFormat(device->Format) == 4)
308 format = AL_FORMAT_MONO_FLOAT32;
310 else if(speakers == DSSPEAKER_STEREO)
312 if(aluBytesFromFormat(device->Format) == 1)
313 format = AL_FORMAT_STEREO8;
314 else if(aluBytesFromFormat(device->Format) == 2)
315 format = AL_FORMAT_STEREO16;
316 else if(aluBytesFromFormat(device->Format) == 4)
317 format = AL_FORMAT_STEREO_FLOAT32;
319 else if(speakers == DSSPEAKER_QUAD)
321 if(aluBytesFromFormat(device->Format) == 1)
322 format = AL_FORMAT_QUAD8;
323 else if(aluBytesFromFormat(device->Format) == 2)
324 format = AL_FORMAT_QUAD16;
325 else if(aluBytesFromFormat(device->Format) == 4)
326 format = AL_FORMAT_QUAD32;
327 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
328 SPEAKER_FRONT_RIGHT |
329 SPEAKER_BACK_LEFT |
330 SPEAKER_BACK_RIGHT;
332 else if(speakers == DSSPEAKER_5POINT1)
334 if(aluBytesFromFormat(device->Format) == 1)
335 format = AL_FORMAT_51CHN8;
336 else if(aluBytesFromFormat(device->Format) == 2)
337 format = AL_FORMAT_51CHN16;
338 else if(aluBytesFromFormat(device->Format) == 4)
339 format = AL_FORMAT_51CHN32;
340 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
341 SPEAKER_FRONT_RIGHT |
342 SPEAKER_FRONT_CENTER |
343 SPEAKER_LOW_FREQUENCY |
344 SPEAKER_BACK_LEFT |
345 SPEAKER_BACK_RIGHT;
347 else if(speakers == DSSPEAKER_7POINT1)
349 if(aluBytesFromFormat(device->Format) == 1)
350 format = AL_FORMAT_71CHN8;
351 else if(aluBytesFromFormat(device->Format) == 2)
352 format = AL_FORMAT_71CHN16;
353 else if(aluBytesFromFormat(device->Format) == 4)
354 format = AL_FORMAT_71CHN32;
355 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
356 SPEAKER_FRONT_RIGHT |
357 SPEAKER_FRONT_CENTER |
358 SPEAKER_LOW_FREQUENCY |
359 SPEAKER_BACK_LEFT |
360 SPEAKER_BACK_RIGHT |
361 SPEAKER_SIDE_LEFT |
362 SPEAKER_SIDE_RIGHT;
364 else
365 format = device->Format;
366 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
368 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
369 OutputType.Format.nChannels = aluChannelsFromFormat(format);
370 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
371 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
372 OutputType.Format.nSamplesPerSec = device->Frequency;
373 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
374 OutputType.Format.cbSize = 0;
377 if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
379 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
380 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
381 OutputType.Format.cbSize = 22;
382 if(OutputType.Format.wBitsPerSample == 32)
383 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
384 else
385 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
387 else
389 if(SUCCEEDED(hr))
391 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
392 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
393 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
394 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
396 if(SUCCEEDED(hr))
397 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
400 if(SUCCEEDED(hr))
402 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
403 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
404 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
405 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
406 DSBDescription.lpwfxFormat=&OutputType.Format;
407 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
410 if(SUCCEEDED(hr))
411 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
413 if(SUCCEEDED(hr))
415 device->ExtraData = pData;
416 pData->thread = StartThread(DSoundProc, device);
417 if(!pData->thread)
418 hr = E_FAIL;
421 if(FAILED(hr))
423 if (pData->DSsbuffer)
424 IDirectSoundBuffer_Release(pData->DSsbuffer);
425 pData->DSsbuffer = NULL;
426 if (pData->DSpbuffer)
427 IDirectSoundBuffer_Release(pData->DSpbuffer);
428 pData->DSpbuffer = NULL;
429 return ALC_FALSE;
432 device->Format = format;
434 return ALC_TRUE;
437 static void DSoundStopPlayback(ALCdevice *device)
439 DSoundData *pData = device->ExtraData;
441 if(!pData->thread)
442 return;
444 pData->killNow = 1;
445 StopThread(pData->thread);
446 pData->thread = NULL;
448 IDirectSoundBuffer_Release(pData->DSsbuffer);
449 pData->DSsbuffer = NULL;
450 if (pData->DSpbuffer)
451 IDirectSoundBuffer_Release(pData->DSpbuffer);
452 pData->DSpbuffer = NULL;
456 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
458 (void)pDevice;
459 (void)deviceName;
460 return ALC_FALSE;
463 static void DSoundCloseCapture(ALCdevice *pDevice)
465 (void)pDevice;
468 static void DSoundStartCapture(ALCdevice *pDevice)
470 (void)pDevice;
473 static void DSoundStopCapture(ALCdevice *pDevice)
475 (void)pDevice;
478 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
480 (void)pDevice;
481 (void)pBuffer;
482 (void)lSamples;
485 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
487 (void)pDevice;
488 return 0;
492 BackendFuncs DSoundFuncs = {
493 DSoundOpenPlayback,
494 DSoundClosePlayback,
495 DSoundResetPlayback,
496 DSoundStopPlayback,
497 DSoundOpenCapture,
498 DSoundCloseCapture,
499 DSoundStartCapture,
500 DSoundStopCapture,
501 DSoundCaptureSamples,
502 DSoundAvailableSamples
505 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
507 (void)data;
508 (void)drvname;
510 if(guid)
512 char str[128];
513 void *temp;
515 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
516 if(temp)
518 DeviceList = temp;
520 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
521 AppendAllDeviceList(str);
523 DeviceList[NumDevices].name = strdup(str);
524 DeviceList[NumDevices].guid = *guid;
525 NumDevices++;
529 return TRUE;
532 void alcDSoundInit(BackendFuncs *FuncList)
534 *FuncList = DSoundFuncs;
537 void alcDSoundDeinit(void)
539 ALuint i;
541 for(i = 0;i < NumDevices;++i)
542 free(DeviceList[i].name);
543 free(DeviceList);
544 DeviceList = NULL;
545 NumDevices = 0;
548 void alcDSoundProbe(int type)
550 DSoundLoad();
551 if(!ds_handle) return;
553 if(type == DEVICE_PROBE)
554 AppendDeviceList(dsDevice);
555 else if(type == ALL_DEVICE_PROBE)
557 HRESULT hr;
558 ALuint i;
560 for(i = 0;i < NumDevices;++i)
561 free(DeviceList[i].name);
562 free(DeviceList);
563 DeviceList = NULL;
564 NumDevices = 0;
566 hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
567 if(FAILED(hr))
568 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
571 DSoundUnload();