Hold the default context extensions globally
[openal-soft.git] / Alc / dsound.c
blob43935defa8dac99b25b6c9a9b2e455314b7ff387
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 NULL;
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 NULL; \
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;
110 return ds_handle;
113 void DSoundUnload(void)
115 if(load_count == 0 || --load_count > 0)
116 return;
118 #ifdef _WIN32
119 FreeLibrary(ds_handle);
120 #endif
121 ds_handle = NULL;
125 static ALuint DSoundProc(ALvoid *ptr)
127 ALCdevice *pDevice = (ALCdevice*)ptr;
128 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
129 DSBCAPS DSBCaps;
130 DWORD LastCursor = 0;
131 DWORD PlayCursor;
132 VOID *WritePtr1, *WritePtr2;
133 DWORD WriteCnt1, WriteCnt2;
134 DWORD FrameSize;
135 DWORD FragSize;
136 DWORD avail;
137 HRESULT err;
139 EnableRTPrio(RTPrioLevel);
141 memset(&DSBCaps, 0, sizeof(DSBCaps));
142 DSBCaps.dwSize = sizeof(DSBCaps);
143 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
144 if(FAILED(err))
146 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
147 aluHandleDisconnect(pDevice);
148 return 1;
151 FrameSize = aluChannelsFromFormat(pDevice->Format) *
152 aluBytesFromFormat(pDevice->Format);
153 FragSize = pDevice->UpdateSize * FrameSize;
155 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
156 while(!pData->killNow)
158 // Get current play and write cursors
159 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
160 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
162 if(avail < FragSize)
164 Sleep(1);
165 continue;
167 avail -= avail%FragSize;
169 // Lock output buffer
170 WriteCnt1 = 0;
171 WriteCnt2 = 0;
172 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
174 // If the buffer is lost, restore it, play and lock
175 if(err == DSERR_BUFFERLOST)
177 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
178 if(SUCCEEDED(err))
179 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
180 if(SUCCEEDED(err))
181 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
184 // Successfully locked the output buffer
185 if(SUCCEEDED(err))
187 // If we have an active context, mix data directly into output buffer otherwise fill with silence
188 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
189 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
191 // Unlock output buffer only when successfully locked
192 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
194 else
195 AL_PRINT("Buffer lock error: %#lx\n", err);
197 // Update old write cursor location
198 LastCursor += WriteCnt1+WriteCnt2;
199 LastCursor %= DSBCaps.dwBufferBytes;
202 return 0;
205 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
207 DSoundData *pData = NULL;
208 LPGUID guid = NULL;
209 HRESULT hr;
211 if(!deviceName)
212 deviceName = dsDevice;
213 else if(strcmp(deviceName, dsDevice) != 0)
215 ALuint i;
216 for(i = 0;i < NumDevices;i++)
218 if(strcmp(deviceName, DeviceList[i].name) == 0)
220 guid = &DeviceList[i].guid;
221 break;
224 if(i == NumDevices)
225 return ALC_FALSE;
228 if(!DSoundLoad())
229 return ALC_FALSE;
231 //Initialise requested device
233 pData = calloc(1, sizeof(DSoundData));
234 if(!pData)
236 alcSetError(device, ALC_OUT_OF_MEMORY);
237 DSoundUnload();
238 return ALC_FALSE;
241 //DirectSound Init code
242 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
243 if(SUCCEEDED(hr))
244 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
245 if(FAILED(hr))
247 if(pData->lpDS)
248 IDirectSound_Release(pData->lpDS);
249 free(pData);
250 DSoundUnload();
251 return ALC_FALSE;
254 device->szDeviceName = strdup(deviceName);
255 device->ExtraData = pData;
256 return ALC_TRUE;
259 static void DSoundClosePlayback(ALCdevice *device)
261 DSoundData *pData = device->ExtraData;
263 IDirectSound_Release(pData->lpDS);
264 free(pData);
265 device->ExtraData = NULL;
267 DSoundUnload();
270 static ALCboolean DSoundResetPlayback(ALCdevice *device)
272 DSoundData *pData = (DSoundData*)device->ExtraData;
273 DSBUFFERDESC DSBDescription;
274 WAVEFORMATEXTENSIBLE OutputType;
275 DWORD frameSize = 0;
276 ALenum format = 0;
277 DWORD speakers;
278 HRESULT hr;
280 memset(&OutputType, 0, sizeof(OutputType));
282 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
283 if(SUCCEEDED(hr) && ConfigValueExists(NULL, "format"))
285 if(aluChannelsFromFormat(device->Format) == 1)
286 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
287 else if(aluChannelsFromFormat(device->Format) == 2)
288 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
289 else if(aluChannelsFromFormat(device->Format) == 4)
290 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
291 else if(aluChannelsFromFormat(device->Format) == 6)
292 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
293 else if(aluChannelsFromFormat(device->Format) == 8)
294 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
295 else
297 AL_PRINT("Unknown format: 0x%x\n", device->Format);
298 return ALC_FALSE;
301 if(SUCCEEDED(hr))
303 speakers = DSSPEAKER_CONFIG(speakers);
304 if(speakers == DSSPEAKER_MONO)
306 if(aluBytesFromFormat(device->Format) == 1)
307 format = AL_FORMAT_MONO8;
308 else if(aluBytesFromFormat(device->Format) == 2)
309 format = AL_FORMAT_MONO16;
310 else if(aluBytesFromFormat(device->Format) == 4)
311 format = AL_FORMAT_MONO_FLOAT32;
313 else if(speakers == DSSPEAKER_STEREO)
315 if(aluBytesFromFormat(device->Format) == 1)
316 format = AL_FORMAT_STEREO8;
317 else if(aluBytesFromFormat(device->Format) == 2)
318 format = AL_FORMAT_STEREO16;
319 else if(aluBytesFromFormat(device->Format) == 4)
320 format = AL_FORMAT_STEREO_FLOAT32;
322 else if(speakers == DSSPEAKER_QUAD)
324 if(aluBytesFromFormat(device->Format) == 1)
325 format = AL_FORMAT_QUAD8;
326 else if(aluBytesFromFormat(device->Format) == 2)
327 format = AL_FORMAT_QUAD16;
328 else if(aluBytesFromFormat(device->Format) == 4)
329 format = AL_FORMAT_QUAD32;
330 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
331 SPEAKER_FRONT_RIGHT |
332 SPEAKER_BACK_LEFT |
333 SPEAKER_BACK_RIGHT;
335 else if(speakers == DSSPEAKER_5POINT1)
337 if(aluBytesFromFormat(device->Format) == 1)
338 format = AL_FORMAT_51CHN8;
339 else if(aluBytesFromFormat(device->Format) == 2)
340 format = AL_FORMAT_51CHN16;
341 else if(aluBytesFromFormat(device->Format) == 4)
342 format = AL_FORMAT_51CHN32;
343 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
344 SPEAKER_FRONT_RIGHT |
345 SPEAKER_FRONT_CENTER |
346 SPEAKER_LOW_FREQUENCY |
347 SPEAKER_BACK_LEFT |
348 SPEAKER_BACK_RIGHT;
350 else if(speakers == DSSPEAKER_7POINT1)
352 if(aluBytesFromFormat(device->Format) == 1)
353 format = AL_FORMAT_71CHN8;
354 else if(aluBytesFromFormat(device->Format) == 2)
355 format = AL_FORMAT_71CHN16;
356 else if(aluBytesFromFormat(device->Format) == 4)
357 format = AL_FORMAT_71CHN32;
358 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
359 SPEAKER_FRONT_RIGHT |
360 SPEAKER_FRONT_CENTER |
361 SPEAKER_LOW_FREQUENCY |
362 SPEAKER_BACK_LEFT |
363 SPEAKER_BACK_RIGHT |
364 SPEAKER_SIDE_LEFT |
365 SPEAKER_SIDE_RIGHT;
367 else
368 format = device->Format;
369 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
371 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
372 OutputType.Format.nChannels = aluChannelsFromFormat(format);
373 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
374 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
375 OutputType.Format.nSamplesPerSec = device->Frequency;
376 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
377 OutputType.Format.cbSize = 0;
380 if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
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;
390 else
392 if(SUCCEEDED(hr))
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);
399 if(SUCCEEDED(hr))
400 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
403 if(SUCCEEDED(hr))
405 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
406 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
407 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
408 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
409 DSBDescription.lpwfxFormat=&OutputType.Format;
410 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
413 if(SUCCEEDED(hr))
414 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
416 if(SUCCEEDED(hr))
418 device->Format = format;
419 SetDefaultWFXChannelOrder(device);
420 pData->thread = StartThread(DSoundProc, device);
421 if(!pData->thread)
422 hr = E_FAIL;
425 if(FAILED(hr))
427 if (pData->DSsbuffer)
428 IDirectSoundBuffer_Release(pData->DSsbuffer);
429 pData->DSsbuffer = NULL;
430 if (pData->DSpbuffer)
431 IDirectSoundBuffer_Release(pData->DSpbuffer);
432 pData->DSpbuffer = NULL;
433 return ALC_FALSE;
436 return ALC_TRUE;
439 static void DSoundStopPlayback(ALCdevice *device)
441 DSoundData *pData = device->ExtraData;
443 if(!pData->thread)
444 return;
446 pData->killNow = 1;
447 StopThread(pData->thread);
448 pData->thread = NULL;
450 pData->killNow = 0;
452 IDirectSoundBuffer_Release(pData->DSsbuffer);
453 pData->DSsbuffer = NULL;
454 if (pData->DSpbuffer)
455 IDirectSoundBuffer_Release(pData->DSpbuffer);
456 pData->DSpbuffer = NULL;
460 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
462 (void)pDevice;
463 (void)deviceName;
464 return ALC_FALSE;
467 static void DSoundCloseCapture(ALCdevice *pDevice)
469 (void)pDevice;
472 static void DSoundStartCapture(ALCdevice *pDevice)
474 (void)pDevice;
477 static void DSoundStopCapture(ALCdevice *pDevice)
479 (void)pDevice;
482 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
484 (void)pDevice;
485 (void)pBuffer;
486 (void)lSamples;
489 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
491 (void)pDevice;
492 return 0;
496 BackendFuncs DSoundFuncs = {
497 DSoundOpenPlayback,
498 DSoundClosePlayback,
499 DSoundResetPlayback,
500 DSoundStopPlayback,
501 DSoundOpenCapture,
502 DSoundCloseCapture,
503 DSoundStartCapture,
504 DSoundStopCapture,
505 DSoundCaptureSamples,
506 DSoundAvailableSamples
509 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
511 (void)data;
512 (void)drvname;
514 if(guid)
516 char str[128];
517 void *temp;
519 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
520 if(temp)
522 DeviceList = temp;
524 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
525 AppendAllDeviceList(str);
527 DeviceList[NumDevices].name = strdup(str);
528 DeviceList[NumDevices].guid = *guid;
529 NumDevices++;
533 return TRUE;
536 void alcDSoundInit(BackendFuncs *FuncList)
538 *FuncList = DSoundFuncs;
541 void alcDSoundDeinit(void)
543 ALuint i;
545 for(i = 0;i < NumDevices;++i)
546 free(DeviceList[i].name);
547 free(DeviceList);
548 DeviceList = NULL;
549 NumDevices = 0;
552 void alcDSoundProbe(int type)
554 if(!DSoundLoad()) return;
556 if(type == DEVICE_PROBE)
557 AppendDeviceList(dsDevice);
558 else if(type == ALL_DEVICE_PROBE)
560 HRESULT hr;
561 ALuint i;
563 for(i = 0;i < NumDevices;++i)
564 free(DeviceList[i].name);
565 free(DeviceList);
566 DeviceList = NULL;
567 NumDevices = 0;
569 hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
570 if(FAILED(hr))
571 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
574 DSoundUnload();