Allow delaying playback start until context creation, and don't use UpdateSize to...
[openal-soft.git] / Alc / dsound.c
blob92ee80731e0def6055fc2fc486369631d6fc0ccd
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);
49 static void *ds_handle;
50 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
51 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
53 // Since DSound doesn't report the fragment size, emulate it
54 static int num_frags;
56 typedef struct {
57 // DirectSound Playback Device
58 LPDIRECTSOUND lpDS;
59 LPDIRECTSOUNDBUFFER DSpbuffer;
60 LPDIRECTSOUNDBUFFER DSsbuffer;
62 volatile int killNow;
63 ALvoid *thread;
64 } DSoundData;
67 typedef struct {
68 ALCchar *name;
69 GUID guid;
70 } DevMap;
71 static DevMap DeviceList[16];
74 static ALuint DSoundProc(ALvoid *ptr)
76 ALCdevice *pDevice = (ALCdevice*)ptr;
77 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
78 DSBCAPS DSBCaps;
79 DWORD LastCursor = 0;
80 DWORD PlayCursor;
81 VOID *WritePtr1, *WritePtr2;
82 DWORD WriteCnt1, WriteCnt2;
83 DWORD FragSize;
84 DWORD avail;
85 HRESULT err;
87 memset(&DSBCaps, 0, sizeof(DSBCaps));
88 DSBCaps.dwSize = sizeof(DSBCaps);
89 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
90 if(FAILED(err))
92 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
93 return 1;
95 FragSize = DSBCaps.dwBufferBytes / num_frags;
97 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
98 while(!pData->killNow)
100 // Get current play and write cursors
101 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
102 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
104 if(avail < FragSize)
106 Sleep(1);
107 continue;
109 avail -= avail%FragSize;
111 // Lock output buffer
112 WriteCnt1 = 0;
113 WriteCnt2 = 0;
114 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
116 // If the buffer is lost, restore it, play and lock
117 if(err == DSERR_BUFFERLOST)
119 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
120 if(SUCCEEDED(err))
121 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
122 if(SUCCEEDED(err))
123 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
126 // Successfully locked the output buffer
127 if(SUCCEEDED(err))
129 // If we have an active context, mix data directly into output buffer otherwise fill with silence
130 SuspendContext(NULL);
131 aluMixData(pDevice->Context, WritePtr1, WriteCnt1, pDevice->Format);
132 aluMixData(pDevice->Context, WritePtr2, WriteCnt2, pDevice->Format);
133 ProcessContext(NULL);
135 // Unlock output buffer only when successfully locked
136 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
138 else
139 AL_PRINT("Buffer lock error: %#lx\n", err);
141 // Update old write cursor location
142 LastCursor += WriteCnt1+WriteCnt2;
143 LastCursor %= DSBCaps.dwBufferBytes;
146 return 0;
149 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
151 DSBUFFERDESC DSBDescription;
152 DSoundData *pData = NULL;
153 WAVEFORMATEXTENSIBLE OutputType;
154 DWORD frameSize = 0;
155 LPGUID guid = NULL;
156 ALenum format = 0;
157 DWORD speakers;
158 HRESULT hr;
160 if(ds_handle == NULL)
161 return ALC_FALSE;
163 if(deviceName)
165 int i;
166 for(i = 0;DeviceList[i].name;i++)
168 if(strcmp(deviceName, DeviceList[i].name) == 0)
170 device->szDeviceName = DeviceList[i].name;
171 if(i > 0)
172 guid = &DeviceList[i].guid;
173 break;
176 if(!DeviceList[i].name)
177 return ALC_FALSE;
179 else
180 device->szDeviceName = DeviceList[0].name;
182 memset(&OutputType, 0, sizeof(OutputType));
184 //Initialise requested device
186 pData = calloc(1, sizeof(DSoundData));
187 if(!pData)
189 SetALCError(ALC_OUT_OF_MEMORY);
190 return ALC_FALSE;
193 //DirectSound Init code
194 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
195 if(SUCCEEDED(hr))
196 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
198 if(SUCCEEDED(hr))
200 if(*(GetConfigValue(NULL, "format", "")) == 0)
201 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
202 else
204 if(device->Format == AL_FORMAT_MONO8 || device->Format == AL_FORMAT_MONO16)
205 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
206 else if(device->Format == AL_FORMAT_STEREO8 || device->Format == AL_FORMAT_STEREO16)
207 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
208 else if(device->Format == AL_FORMAT_QUAD8 || device->Format == AL_FORMAT_QUAD16)
209 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
210 else if(device->Format == AL_FORMAT_51CHN8 || device->Format == AL_FORMAT_51CHN16)
211 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
212 else if(device->Format == AL_FORMAT_71CHN8 || device->Format == AL_FORMAT_71CHN16)
213 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
214 else
215 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
218 if(SUCCEEDED(hr))
220 speakers = DSSPEAKER_CONFIG(speakers);
221 if(speakers == DSSPEAKER_MONO)
223 if(aluBytesFromFormat(device->Format) == 1)
224 format = AL_FORMAT_MONO8;
225 else
226 format = AL_FORMAT_MONO16;
228 else if(speakers == DSSPEAKER_STEREO)
230 if(aluBytesFromFormat(device->Format) == 1)
231 format = AL_FORMAT_STEREO8;
232 else
233 format = AL_FORMAT_STEREO16;
235 else if(speakers == DSSPEAKER_QUAD)
237 if(aluBytesFromFormat(device->Format) == 1)
238 format = AL_FORMAT_QUAD8;
239 else
240 format = AL_FORMAT_QUAD16;
241 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
242 SPEAKER_FRONT_RIGHT |
243 SPEAKER_BACK_LEFT |
244 SPEAKER_BACK_RIGHT;
246 else if(speakers == DSSPEAKER_5POINT1)
248 if(aluBytesFromFormat(device->Format) == 1)
249 format = AL_FORMAT_51CHN8;
250 else
251 format = AL_FORMAT_51CHN16;
252 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
253 SPEAKER_FRONT_RIGHT |
254 SPEAKER_FRONT_CENTER |
255 SPEAKER_LOW_FREQUENCY |
256 SPEAKER_BACK_LEFT |
257 SPEAKER_BACK_RIGHT;
259 else if(speakers == DSSPEAKER_7POINT1)
261 if(aluBytesFromFormat(device->Format) == 1)
262 format = AL_FORMAT_71CHN8;
263 else
264 format = AL_FORMAT_71CHN16;
265 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
266 SPEAKER_FRONT_RIGHT |
267 SPEAKER_FRONT_CENTER |
268 SPEAKER_LOW_FREQUENCY |
269 SPEAKER_BACK_LEFT |
270 SPEAKER_BACK_RIGHT |
271 SPEAKER_SIDE_LEFT |
272 SPEAKER_SIDE_RIGHT;
274 else
275 format = device->Format;
276 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
278 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
279 OutputType.Format.nChannels = aluChannelsFromFormat(format);
280 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
281 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
282 OutputType.Format.nSamplesPerSec = device->Frequency;
283 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
284 OutputType.Format.cbSize = 0;
287 if(OutputType.Format.nChannels > 2)
289 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
290 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
291 OutputType.Format.cbSize = 22;
292 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
294 else
296 if(SUCCEEDED(hr))
298 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
299 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
300 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
301 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
303 if(SUCCEEDED(hr))
304 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
307 if(SUCCEEDED(hr))
309 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
310 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
311 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
312 DSBDescription.dwBufferBytes=(device->BufferSize/num_frags) * num_frags * frameSize;
313 DSBDescription.lpwfxFormat=&OutputType.Format;
314 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
317 if(SUCCEEDED(hr))
318 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
320 if(SUCCEEDED(hr))
322 device->ExtraData = pData;
323 pData->thread = StartThread(DSoundProc, device);
324 if(!pData->thread)
325 hr = E_FAIL;
328 if(FAILED(hr))
330 if (pData->DSsbuffer)
331 IDirectSoundBuffer_Release(pData->DSsbuffer);
332 if (pData->DSpbuffer)
333 IDirectSoundBuffer_Release(pData->DSpbuffer);
334 if (pData->lpDS)
335 IDirectSound_Release(pData->lpDS);
337 free(pData);
338 return ALC_FALSE;
341 device->Format = format;
342 device->UpdateSize = device->BufferSize/num_frags;
344 return ALC_TRUE;
347 static void DSoundClosePlayback(ALCdevice *device)
349 DSoundData *pData = device->ExtraData;
351 pData->killNow = 1;
352 StopThread(pData->thread);
354 IDirectSoundBuffer_Release(pData->DSsbuffer);
355 if (pData->DSpbuffer)
356 IDirectSoundBuffer_Release(pData->DSpbuffer);
357 IDirectSound_Release(pData->lpDS);
359 free(pData);
360 device->ExtraData = NULL;
363 static ALCboolean DSoundStartContext(ALCdevice *device, ALCcontext *context)
365 return ALC_TRUE;
366 (void)device;
367 (void)context;
370 static void DSoundStopContext(ALCdevice *device, ALCcontext *context)
372 (void)device;
373 (void)context;
377 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
379 (void)pDevice;
380 (void)deviceName;
381 (void)frequency;
382 (void)format;
383 (void)SampleSize;
384 return ALC_FALSE;
387 static void DSoundCloseCapture(ALCdevice *pDevice)
389 (void)pDevice;
392 static void DSoundStartCapture(ALCdevice *pDevice)
394 (void)pDevice;
397 static void DSoundStopCapture(ALCdevice *pDevice)
399 (void)pDevice;
402 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
404 (void)pDevice;
405 (void)pBuffer;
406 (void)lSamples;
409 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
411 (void)pDevice;
412 return 0;
416 BackendFuncs DSoundFuncs = {
417 DSoundOpenPlayback,
418 DSoundClosePlayback,
419 DSoundStartContext,
420 DSoundStopContext,
421 DSoundOpenCapture,
422 DSoundCloseCapture,
423 DSoundStartCapture,
424 DSoundStopCapture,
425 DSoundCaptureSamples,
426 DSoundAvailableSamples
429 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
431 size_t *iter = data;
432 (void)drvname;
434 if(guid)
436 char str[128];
437 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
438 DeviceList[*iter].name = AppendAllDeviceList(str);
439 DeviceList[*iter].guid = *guid;
440 (*iter)++;
442 else
443 DeviceList[0].name = AppendDeviceList("DirectSound Software");
445 return TRUE;
448 void alcDSoundInit(BackendFuncs *FuncList)
450 size_t iter = 1;
451 HRESULT hr;
453 *FuncList = DSoundFuncs;
455 #ifdef _WIN32
456 ds_handle = LoadLibraryA("dsound.dll");
457 if(ds_handle == NULL)
459 AL_PRINT("Failed to load dsound.dll\n");
460 return;
463 #define LOAD_FUNC(f) do { \
464 p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
465 if(p##f == NULL) \
467 FreeLibrary(ds_handle); \
468 ds_handle = NULL; \
469 AL_PRINT("Could not load %s from dsound.dll\n", #f); \
470 return; \
472 } while(0)
473 #else
474 ds_handle = (void*)0xDEADBEEF;
475 #define LOAD_FUNC(f) p##f = f
476 #endif
478 LOAD_FUNC(DirectSoundCreate);
479 LOAD_FUNC(DirectSoundEnumerateA);
480 #undef LOAD_FUNC
482 num_frags = GetConfigValueInt("dsound", "periods", 4);
483 if(num_frags < 2) num_frags = 2;
485 hr = pDirectSoundEnumerateA(DSoundEnumDevices, &iter);
486 if(FAILED(hr))
487 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);