Mix the proper size for every update, and notify disconnects
[openal-soft.git] / Alc / dsound.c
blobdcad35efaabf2800d789befd118c33307d0ca8e4
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;
73 static const ALCchar dsDevice[] = "DirectSound Software";
74 static DevMap *DeviceList;
75 static ALuint NumDevices;
78 static ALuint DSoundProc(ALvoid *ptr)
80 ALCdevice *pDevice = (ALCdevice*)ptr;
81 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
82 DSBCAPS DSBCaps;
83 DWORD LastCursor = 0;
84 DWORD PlayCursor;
85 VOID *WritePtr1, *WritePtr2;
86 DWORD WriteCnt1, WriteCnt2;
87 DWORD FragSize;
88 DWORD avail;
89 HRESULT err;
91 memset(&DSBCaps, 0, sizeof(DSBCaps));
92 DSBCaps.dwSize = sizeof(DSBCaps);
93 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
94 if(FAILED(err))
96 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
97 return 1;
99 FragSize = DSBCaps.dwBufferBytes / num_frags;
101 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
102 while(!pData->killNow)
104 // Get current play and write cursors
105 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
106 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
108 if(avail < FragSize)
110 Sleep(1);
111 continue;
113 avail -= avail%FragSize;
115 // Lock output buffer
116 WriteCnt1 = 0;
117 WriteCnt2 = 0;
118 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
120 // If the buffer is lost, restore it, play and lock
121 if(err == DSERR_BUFFERLOST)
123 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
124 if(SUCCEEDED(err))
125 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
126 if(SUCCEEDED(err))
127 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
130 // Successfully locked the output buffer
131 if(SUCCEEDED(err))
133 // If we have an active context, mix data directly into output buffer otherwise fill with silence
134 SuspendContext(NULL);
135 aluMixData(pDevice->Context, WritePtr1, WriteCnt1, pDevice->Format);
136 aluMixData(pDevice->Context, WritePtr2, WriteCnt2, pDevice->Format);
137 ProcessContext(NULL);
139 // Unlock output buffer only when successfully locked
140 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
142 else
143 AL_PRINT("Buffer lock error: %#lx\n", err);
145 // Update old write cursor location
146 LastCursor += WriteCnt1+WriteCnt2;
147 LastCursor %= DSBCaps.dwBufferBytes;
150 return 0;
153 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
155 DSoundData *pData = NULL;
156 LPGUID guid = NULL;
157 HRESULT hr;
159 if(ds_handle == NULL)
160 return ALC_FALSE;
162 if(!deviceName)
163 deviceName = dsDevice;
164 else if(strcmp(deviceName, dsDevice) != 0)
166 ALuint i;
167 for(i = 0;i < NumDevices;i++)
169 if(strcmp(deviceName, DeviceList[i].name) == 0)
171 guid = &DeviceList[i].guid;
172 break;
175 if(i == NumDevices)
176 return ALC_FALSE;
179 //Initialise requested device
181 pData = calloc(1, sizeof(DSoundData));
182 if(!pData)
184 SetALCError(ALC_OUT_OF_MEMORY);
185 return ALC_FALSE;
188 //DirectSound Init code
189 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
190 if(SUCCEEDED(hr))
191 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
192 if(FAILED(hr))
194 if(pData->lpDS)
195 IDirectSound_Release(pData->lpDS);
196 free(pData);
197 return ALC_FALSE;
200 device->szDeviceName = strdup(deviceName);
201 device->ExtraData = pData;
202 return ALC_TRUE;
205 static void DSoundClosePlayback(ALCdevice *device)
207 DSoundData *pData = device->ExtraData;
209 IDirectSound_Release(pData->lpDS);
210 free(pData);
211 device->ExtraData = NULL;
214 static ALCboolean DSoundStartContext(ALCdevice *device, ALCcontext *context)
216 DSoundData *pData = (DSoundData*)device->ExtraData;
217 DSBUFFERDESC DSBDescription;
218 WAVEFORMATEXTENSIBLE OutputType;
219 DWORD frameSize = 0;
220 ALenum format = 0;
221 DWORD speakers;
222 HRESULT hr;
223 (void)context;
225 memset(&OutputType, 0, sizeof(OutputType));
227 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
228 if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
230 if(device->Format == AL_FORMAT_MONO8 || device->Format == AL_FORMAT_MONO16)
231 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
232 else if(device->Format == AL_FORMAT_STEREO8 || device->Format == AL_FORMAT_STEREO16)
233 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
234 else if(device->Format == AL_FORMAT_QUAD8 || device->Format == AL_FORMAT_QUAD16)
235 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
236 else if(device->Format == AL_FORMAT_51CHN8 || device->Format == AL_FORMAT_51CHN16)
237 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
238 else if(device->Format == AL_FORMAT_71CHN8 || device->Format == AL_FORMAT_71CHN16)
239 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
241 if(SUCCEEDED(hr))
243 speakers = DSSPEAKER_CONFIG(speakers);
244 if(speakers == DSSPEAKER_MONO)
246 if(aluBytesFromFormat(device->Format) == 1)
247 format = AL_FORMAT_MONO8;
248 else if(aluBytesFromFormat(device->Format) == 2)
249 format = AL_FORMAT_MONO16;
250 else if(aluBytesFromFormat(device->Format) == 4)
251 format = AL_FORMAT_MONO_FLOAT32;
253 else if(speakers == DSSPEAKER_STEREO)
255 if(aluBytesFromFormat(device->Format) == 1)
256 format = AL_FORMAT_STEREO8;
257 else if(aluBytesFromFormat(device->Format) == 2)
258 format = AL_FORMAT_STEREO16;
259 else if(aluBytesFromFormat(device->Format) == 4)
260 format = AL_FORMAT_STEREO_FLOAT32;
262 else if(speakers == DSSPEAKER_QUAD)
264 if(aluBytesFromFormat(device->Format) == 1)
265 format = AL_FORMAT_QUAD8;
266 else if(aluBytesFromFormat(device->Format) == 2)
267 format = AL_FORMAT_QUAD16;
268 else if(aluBytesFromFormat(device->Format) == 4)
269 format = AL_FORMAT_QUAD32;
270 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
271 SPEAKER_FRONT_RIGHT |
272 SPEAKER_BACK_LEFT |
273 SPEAKER_BACK_RIGHT;
275 else if(speakers == DSSPEAKER_5POINT1)
277 if(aluBytesFromFormat(device->Format) == 1)
278 format = AL_FORMAT_51CHN8;
279 else if(aluBytesFromFormat(device->Format) == 2)
280 format = AL_FORMAT_51CHN16;
281 else if(aluBytesFromFormat(device->Format) == 4)
282 format = AL_FORMAT_51CHN32;
283 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
284 SPEAKER_FRONT_RIGHT |
285 SPEAKER_FRONT_CENTER |
286 SPEAKER_LOW_FREQUENCY |
287 SPEAKER_BACK_LEFT |
288 SPEAKER_BACK_RIGHT;
290 else if(speakers == DSSPEAKER_7POINT1)
292 if(aluBytesFromFormat(device->Format) == 1)
293 format = AL_FORMAT_71CHN8;
294 else if(aluBytesFromFormat(device->Format) == 2)
295 format = AL_FORMAT_71CHN16;
296 else if(aluBytesFromFormat(device->Format) == 4)
297 format = AL_FORMAT_71CHN32;
298 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
299 SPEAKER_FRONT_RIGHT |
300 SPEAKER_FRONT_CENTER |
301 SPEAKER_LOW_FREQUENCY |
302 SPEAKER_BACK_LEFT |
303 SPEAKER_BACK_RIGHT |
304 SPEAKER_SIDE_LEFT |
305 SPEAKER_SIDE_RIGHT;
307 else
308 format = device->Format;
309 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
311 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
312 OutputType.Format.nChannels = aluChannelsFromFormat(format);
313 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
314 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
315 OutputType.Format.nSamplesPerSec = device->Frequency;
316 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
317 OutputType.Format.cbSize = 0;
320 if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
322 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
323 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
324 OutputType.Format.cbSize = 22;
325 if(OutputType.Format.wBitsPerSample == 32)
326 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
327 else
328 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
330 else
332 if(SUCCEEDED(hr))
334 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
335 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
336 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
337 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
339 if(SUCCEEDED(hr))
340 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
343 if(SUCCEEDED(hr))
345 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
346 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
347 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
348 DSBDescription.dwBufferBytes=(device->BufferSize/num_frags) * num_frags * frameSize;
349 DSBDescription.lpwfxFormat=&OutputType.Format;
350 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
353 if(SUCCEEDED(hr))
354 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
356 if(SUCCEEDED(hr))
358 device->ExtraData = pData;
359 pData->thread = StartThread(DSoundProc, device);
360 if(!pData->thread)
361 hr = E_FAIL;
364 if(FAILED(hr))
366 if (pData->DSsbuffer)
367 IDirectSoundBuffer_Release(pData->DSsbuffer);
368 pData->DSsbuffer = NULL;
369 if (pData->DSpbuffer)
370 IDirectSoundBuffer_Release(pData->DSpbuffer);
371 pData->DSpbuffer = NULL;
372 return ALC_FALSE;
375 device->Format = format;
376 device->UpdateSize = device->BufferSize/num_frags;
378 return ALC_TRUE;
381 static void DSoundStopContext(ALCdevice *device, ALCcontext *context)
383 DSoundData *pData = device->ExtraData;
384 (void)context;
386 if(!pData->thread)
387 return;
389 pData->killNow = 1;
390 StopThread(pData->thread);
391 pData->thread = NULL;
393 IDirectSoundBuffer_Release(pData->DSsbuffer);
394 pData->DSsbuffer = NULL;
395 if (pData->DSpbuffer)
396 IDirectSoundBuffer_Release(pData->DSpbuffer);
397 pData->DSpbuffer = NULL;
401 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
403 (void)pDevice;
404 (void)deviceName;
405 return ALC_FALSE;
408 static void DSoundCloseCapture(ALCdevice *pDevice)
410 (void)pDevice;
413 static void DSoundStartCapture(ALCdevice *pDevice)
415 (void)pDevice;
418 static void DSoundStopCapture(ALCdevice *pDevice)
420 (void)pDevice;
423 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
425 (void)pDevice;
426 (void)pBuffer;
427 (void)lSamples;
430 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
432 (void)pDevice;
433 return 0;
437 BackendFuncs DSoundFuncs = {
438 DSoundOpenPlayback,
439 DSoundClosePlayback,
440 DSoundStartContext,
441 DSoundStopContext,
442 DSoundOpenCapture,
443 DSoundCloseCapture,
444 DSoundStartCapture,
445 DSoundStopCapture,
446 DSoundCaptureSamples,
447 DSoundAvailableSamples
450 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
452 (void)data;
453 (void)drvname;
455 if(guid)
457 char str[128];
458 void *temp;
460 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
461 if(temp)
463 DeviceList = temp;
465 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
466 AppendAllDeviceList(str);
468 DeviceList[NumDevices].name = strdup(str);
469 DeviceList[NumDevices].guid = *guid;
470 NumDevices++;
474 return TRUE;
477 void alcDSoundInit(BackendFuncs *FuncList)
479 *FuncList = DSoundFuncs;
481 #ifdef _WIN32
482 ds_handle = LoadLibraryA("dsound.dll");
483 if(ds_handle == NULL)
485 AL_PRINT("Failed to load dsound.dll\n");
486 return;
489 #define LOAD_FUNC(f) do { \
490 p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
491 if(p##f == NULL) \
493 FreeLibrary(ds_handle); \
494 ds_handle = NULL; \
495 AL_PRINT("Could not load %s from dsound.dll\n", #f); \
496 return; \
498 } while(0)
499 #else
500 ds_handle = (void*)0xDEADBEEF;
501 #define LOAD_FUNC(f) p##f = f
502 #endif
504 LOAD_FUNC(DirectSoundCreate);
505 LOAD_FUNC(DirectSoundEnumerateA);
506 #undef LOAD_FUNC
508 num_frags = GetConfigValueInt("dsound", "periods", 4);
509 if(num_frags < 2) num_frags = 2;
512 void alcDSoundDeinit(void)
514 ALuint i;
516 for(i = 0;i < NumDevices;++i)
517 free(DeviceList[i].name);
518 free(DeviceList);
519 DeviceList = NULL;
520 NumDevices = 0;
522 #ifdef _WIN32
523 if(ds_handle)
524 FreeLibrary(ds_handle);
525 ds_handle = NULL;
526 #endif
529 void alcDSoundProbe(int type)
531 if(!ds_handle)
532 return;
534 if(type == DEVICE_PROBE)
535 AppendDeviceList(dsDevice);
536 else if(type == ALL_DEVICE_PROBE)
538 HRESULT hr;
539 ALuint i;
541 for(i = 0;i < NumDevices;++i)
542 free(DeviceList[i].name);
543 free(DeviceList);
544 DeviceList = NULL;
545 NumDevices = 0;
547 hr = pDirectSoundEnumerateA(DSoundEnumDevices, NULL);
548 if(FAILED(hr))
549 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);