Add a method to deinitialize backends
[openal-soft.git] / Alc / dsound.c
blobd50ecadb0026bc3b3d305885ea3691818d26616f
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);
54 // Since DSound doesn't report the fragment size, emulate it
55 static int num_frags;
57 typedef struct {
58 // DirectSound Playback Device
59 LPDIRECTSOUND lpDS;
60 LPDIRECTSOUNDBUFFER DSpbuffer;
61 LPDIRECTSOUNDBUFFER DSsbuffer;
63 volatile int killNow;
64 ALvoid *thread;
65 } DSoundData;
68 typedef struct {
69 ALCchar *name;
70 GUID guid;
71 } DevMap;
72 static DevMap DeviceList[16];
75 static ALuint DSoundProc(ALvoid *ptr)
77 ALCdevice *pDevice = (ALCdevice*)ptr;
78 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
79 DSBCAPS DSBCaps;
80 DWORD LastCursor = 0;
81 DWORD PlayCursor;
82 VOID *WritePtr1, *WritePtr2;
83 DWORD WriteCnt1, WriteCnt2;
84 DWORD FragSize;
85 DWORD avail;
86 HRESULT err;
88 memset(&DSBCaps, 0, sizeof(DSBCaps));
89 DSBCaps.dwSize = sizeof(DSBCaps);
90 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
91 if(FAILED(err))
93 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
94 return 1;
96 FragSize = DSBCaps.dwBufferBytes / num_frags;
98 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
99 while(!pData->killNow)
101 // Get current play and write cursors
102 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
103 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
105 if(avail < FragSize)
107 Sleep(1);
108 continue;
110 avail -= avail%FragSize;
112 // Lock output buffer
113 WriteCnt1 = 0;
114 WriteCnt2 = 0;
115 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
117 // If the buffer is lost, restore it, play and lock
118 if(err == DSERR_BUFFERLOST)
120 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
121 if(SUCCEEDED(err))
122 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
123 if(SUCCEEDED(err))
124 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
127 // Successfully locked the output buffer
128 if(SUCCEEDED(err))
130 // If we have an active context, mix data directly into output buffer otherwise fill with silence
131 SuspendContext(NULL);
132 aluMixData(pDevice->Context, WritePtr1, WriteCnt1, pDevice->Format);
133 aluMixData(pDevice->Context, WritePtr2, WriteCnt2, pDevice->Format);
134 ProcessContext(NULL);
136 // Unlock output buffer only when successfully locked
137 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
139 else
140 AL_PRINT("Buffer lock error: %#lx\n", err);
142 // Update old write cursor location
143 LastCursor += WriteCnt1+WriteCnt2;
144 LastCursor %= DSBCaps.dwBufferBytes;
147 return 0;
150 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
152 DSoundData *pData = NULL;
153 LPGUID guid = NULL;
154 HRESULT hr;
156 if(ds_handle == NULL)
157 return ALC_FALSE;
159 if(deviceName)
161 int i;
162 for(i = 0;DeviceList[i].name;i++)
164 if(strcmp(deviceName, DeviceList[i].name) == 0)
166 device->szDeviceName = DeviceList[i].name;
167 if(i > 0)
168 guid = &DeviceList[i].guid;
169 break;
172 if(!DeviceList[i].name)
173 return ALC_FALSE;
175 else
176 device->szDeviceName = DeviceList[0].name;
178 //Initialise requested device
180 pData = calloc(1, sizeof(DSoundData));
181 if(!pData)
183 SetALCError(ALC_OUT_OF_MEMORY);
184 return ALC_FALSE;
187 //DirectSound Init code
188 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
189 if(SUCCEEDED(hr))
190 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
191 if(FAILED(hr))
193 if(pData->lpDS)
194 IDirectSound_Release(pData->lpDS);
195 free(pData);
196 return ALC_FALSE;
199 device->ExtraData = pData;
200 return ALC_TRUE;
203 static void DSoundClosePlayback(ALCdevice *device)
205 DSoundData *pData = device->ExtraData;
207 IDirectSound_Release(pData->lpDS);
208 free(pData);
209 device->ExtraData = NULL;
212 static ALCboolean DSoundStartContext(ALCdevice *device, ALCcontext *context)
214 DSoundData *pData = (DSoundData*)device->ExtraData;
215 DSBUFFERDESC DSBDescription;
216 WAVEFORMATEXTENSIBLE OutputType;
217 DWORD frameSize = 0;
218 ALenum format = 0;
219 DWORD speakers;
220 HRESULT hr;
221 (void)context;
223 memset(&OutputType, 0, sizeof(OutputType));
225 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
226 if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
228 if(device->Format == AL_FORMAT_MONO8 || device->Format == AL_FORMAT_MONO16)
229 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
230 else if(device->Format == AL_FORMAT_STEREO8 || device->Format == AL_FORMAT_STEREO16)
231 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
232 else if(device->Format == AL_FORMAT_QUAD8 || device->Format == AL_FORMAT_QUAD16)
233 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
234 else if(device->Format == AL_FORMAT_51CHN8 || device->Format == AL_FORMAT_51CHN16)
235 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
236 else if(device->Format == AL_FORMAT_71CHN8 || device->Format == AL_FORMAT_71CHN16)
237 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
239 if(SUCCEEDED(hr))
241 speakers = DSSPEAKER_CONFIG(speakers);
242 if(speakers == DSSPEAKER_MONO)
244 if(aluBytesFromFormat(device->Format) == 1)
245 format = AL_FORMAT_MONO8;
246 else if(aluBytesFromFormat(device->Format) == 2)
247 format = AL_FORMAT_MONO16;
248 else if(aluBytesFromFormat(device->Format) == 4)
249 format = AL_FORMAT_MONO_FLOAT32;
251 else if(speakers == DSSPEAKER_STEREO)
253 if(aluBytesFromFormat(device->Format) == 1)
254 format = AL_FORMAT_STEREO8;
255 else if(aluBytesFromFormat(device->Format) == 2)
256 format = AL_FORMAT_STEREO16;
257 else if(aluBytesFromFormat(device->Format) == 4)
258 format = AL_FORMAT_STEREO_FLOAT32;
260 else if(speakers == DSSPEAKER_QUAD)
262 if(aluBytesFromFormat(device->Format) == 1)
263 format = AL_FORMAT_QUAD8;
264 else if(aluBytesFromFormat(device->Format) == 2)
265 format = AL_FORMAT_QUAD16;
266 else if(aluBytesFromFormat(device->Format) == 4)
267 format = AL_FORMAT_QUAD32;
268 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
269 SPEAKER_FRONT_RIGHT |
270 SPEAKER_BACK_LEFT |
271 SPEAKER_BACK_RIGHT;
273 else if(speakers == DSSPEAKER_5POINT1)
275 if(aluBytesFromFormat(device->Format) == 1)
276 format = AL_FORMAT_51CHN8;
277 else if(aluBytesFromFormat(device->Format) == 2)
278 format = AL_FORMAT_51CHN16;
279 else if(aluBytesFromFormat(device->Format) == 4)
280 format = AL_FORMAT_51CHN32;
281 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
282 SPEAKER_FRONT_RIGHT |
283 SPEAKER_FRONT_CENTER |
284 SPEAKER_LOW_FREQUENCY |
285 SPEAKER_BACK_LEFT |
286 SPEAKER_BACK_RIGHT;
288 else if(speakers == DSSPEAKER_7POINT1)
290 if(aluBytesFromFormat(device->Format) == 1)
291 format = AL_FORMAT_71CHN8;
292 else if(aluBytesFromFormat(device->Format) == 2)
293 format = AL_FORMAT_71CHN16;
294 else if(aluBytesFromFormat(device->Format) == 4)
295 format = AL_FORMAT_71CHN32;
296 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
297 SPEAKER_FRONT_RIGHT |
298 SPEAKER_FRONT_CENTER |
299 SPEAKER_LOW_FREQUENCY |
300 SPEAKER_BACK_LEFT |
301 SPEAKER_BACK_RIGHT |
302 SPEAKER_SIDE_LEFT |
303 SPEAKER_SIDE_RIGHT;
305 else
306 format = device->Format;
307 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
309 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
310 OutputType.Format.nChannels = aluChannelsFromFormat(format);
311 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
312 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
313 OutputType.Format.nSamplesPerSec = device->Frequency;
314 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
315 OutputType.Format.cbSize = 0;
318 if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
320 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
321 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
322 OutputType.Format.cbSize = 22;
323 if(OutputType.Format.wBitsPerSample == 32)
324 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
325 else
326 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
328 else
330 if(SUCCEEDED(hr))
332 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
333 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
334 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
335 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
337 if(SUCCEEDED(hr))
338 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
341 if(SUCCEEDED(hr))
343 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
344 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
345 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
346 DSBDescription.dwBufferBytes=(device->BufferSize/num_frags) * num_frags * frameSize;
347 DSBDescription.lpwfxFormat=&OutputType.Format;
348 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
351 if(SUCCEEDED(hr))
352 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
354 if(SUCCEEDED(hr))
356 device->ExtraData = pData;
357 pData->thread = StartThread(DSoundProc, device);
358 if(!pData->thread)
359 hr = E_FAIL;
362 if(FAILED(hr))
364 if (pData->DSsbuffer)
365 IDirectSoundBuffer_Release(pData->DSsbuffer);
366 pData->DSsbuffer = NULL;
367 if (pData->DSpbuffer)
368 IDirectSoundBuffer_Release(pData->DSpbuffer);
369 pData->DSpbuffer = NULL;
370 return ALC_FALSE;
373 device->Format = format;
374 device->UpdateSize = device->BufferSize/num_frags;
376 return ALC_TRUE;
379 static void DSoundStopContext(ALCdevice *device, ALCcontext *context)
381 DSoundData *pData = device->ExtraData;
382 (void)context;
384 if(!pData->thread)
385 return;
387 pData->killNow = 1;
388 StopThread(pData->thread);
389 pData->thread = NULL;
391 IDirectSoundBuffer_Release(pData->DSsbuffer);
392 pData->DSsbuffer = NULL;
393 if (pData->DSpbuffer)
394 IDirectSoundBuffer_Release(pData->DSpbuffer);
395 pData->DSpbuffer = NULL;
399 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
401 (void)pDevice;
402 (void)deviceName;
403 return ALC_FALSE;
406 static void DSoundCloseCapture(ALCdevice *pDevice)
408 (void)pDevice;
411 static void DSoundStartCapture(ALCdevice *pDevice)
413 (void)pDevice;
416 static void DSoundStopCapture(ALCdevice *pDevice)
418 (void)pDevice;
421 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
423 (void)pDevice;
424 (void)pBuffer;
425 (void)lSamples;
428 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
430 (void)pDevice;
431 return 0;
435 BackendFuncs DSoundFuncs = {
436 DSoundOpenPlayback,
437 DSoundClosePlayback,
438 DSoundStartContext,
439 DSoundStopContext,
440 DSoundOpenCapture,
441 DSoundCloseCapture,
442 DSoundStartCapture,
443 DSoundStopCapture,
444 DSoundCaptureSamples,
445 DSoundAvailableSamples
448 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
450 size_t *iter = data;
451 (void)drvname;
453 if(guid)
455 char str[128];
456 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
457 DeviceList[*iter].name = AppendAllDeviceList(str);
458 DeviceList[*iter].guid = *guid;
459 (*iter)++;
461 else
462 DeviceList[0].name = AppendDeviceList("DirectSound Software");
464 return TRUE;
467 void alcDSoundInit(BackendFuncs *FuncList)
469 size_t iter = 1;
470 HRESULT hr;
472 *FuncList = DSoundFuncs;
474 #ifdef _WIN32
475 ds_handle = LoadLibraryA("dsound.dll");
476 if(ds_handle == NULL)
478 AL_PRINT("Failed to load dsound.dll\n");
479 return;
482 #define LOAD_FUNC(f) do { \
483 p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
484 if(p##f == NULL) \
486 FreeLibrary(ds_handle); \
487 ds_handle = NULL; \
488 AL_PRINT("Could not load %s from dsound.dll\n", #f); \
489 return; \
491 } while(0)
492 #else
493 ds_handle = (void*)0xDEADBEEF;
494 #define LOAD_FUNC(f) p##f = f
495 #endif
497 LOAD_FUNC(DirectSoundCreate);
498 LOAD_FUNC(DirectSoundEnumerateA);
499 #undef LOAD_FUNC
501 num_frags = GetConfigValueInt("dsound", "periods", 4);
502 if(num_frags < 2) num_frags = 2;
504 hr = pDirectSoundEnumerateA(DSoundEnumDevices, &iter);
505 if(FAILED(hr))
506 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
509 void alcDSoundDeinit(void)