Start DSound playback at context creation
[openal-soft.git] / Alc / dsound.c
blob34f421017e774fe275026a0d5c261a987ef27eb4
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 DSoundData *pData = NULL;
152 LPGUID guid = NULL;
153 HRESULT hr;
155 if(ds_handle == NULL)
156 return ALC_FALSE;
158 if(deviceName)
160 int i;
161 for(i = 0;DeviceList[i].name;i++)
163 if(strcmp(deviceName, DeviceList[i].name) == 0)
165 device->szDeviceName = DeviceList[i].name;
166 if(i > 0)
167 guid = &DeviceList[i].guid;
168 break;
171 if(!DeviceList[i].name)
172 return ALC_FALSE;
174 else
175 device->szDeviceName = DeviceList[0].name;
177 //Initialise requested device
179 pData = calloc(1, sizeof(DSoundData));
180 if(!pData)
182 SetALCError(ALC_OUT_OF_MEMORY);
183 return ALC_FALSE;
186 //DirectSound Init code
187 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
188 if(SUCCEEDED(hr))
189 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
190 if(FAILED(hr))
192 if(pData->lpDS)
193 IDirectSound_Release(pData->lpDS);
194 free(pData);
195 return ALC_FALSE;
198 device->ExtraData = pData;
199 return ALC_TRUE;
202 static void DSoundClosePlayback(ALCdevice *device)
204 DSoundData *pData = device->ExtraData;
206 IDirectSound_Release(pData->lpDS);
207 free(pData);
208 device->ExtraData = NULL;
211 static ALCboolean DSoundStartContext(ALCdevice *device, ALCcontext *context)
213 DSoundData *pData = (DSoundData*)device->ExtraData;
214 DSBUFFERDESC DSBDescription;
215 WAVEFORMATEXTENSIBLE OutputType;
216 DWORD frameSize = 0;
217 ALenum format = 0;
218 DWORD speakers;
219 HRESULT hr;
220 (void)context;
222 memset(&OutputType, 0, sizeof(OutputType));
224 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
225 if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) == 0)
227 if(device->Format == AL_FORMAT_MONO8 || device->Format == AL_FORMAT_MONO16)
228 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
229 else if(device->Format == AL_FORMAT_STEREO8 || device->Format == AL_FORMAT_STEREO16)
230 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
231 else if(device->Format == AL_FORMAT_QUAD8 || device->Format == AL_FORMAT_QUAD16)
232 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
233 else if(device->Format == AL_FORMAT_51CHN8 || device->Format == AL_FORMAT_51CHN16)
234 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
235 else if(device->Format == AL_FORMAT_71CHN8 || device->Format == AL_FORMAT_71CHN16)
236 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
238 if(SUCCEEDED(hr))
240 speakers = DSSPEAKER_CONFIG(speakers);
241 if(speakers == DSSPEAKER_MONO)
243 if(aluBytesFromFormat(device->Format) == 1)
244 format = AL_FORMAT_MONO8;
245 else
246 format = AL_FORMAT_MONO16;
248 else if(speakers == DSSPEAKER_STEREO)
250 if(aluBytesFromFormat(device->Format) == 1)
251 format = AL_FORMAT_STEREO8;
252 else
253 format = AL_FORMAT_STEREO16;
255 else if(speakers == DSSPEAKER_QUAD)
257 if(aluBytesFromFormat(device->Format) == 1)
258 format = AL_FORMAT_QUAD8;
259 else
260 format = AL_FORMAT_QUAD16;
261 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
262 SPEAKER_FRONT_RIGHT |
263 SPEAKER_BACK_LEFT |
264 SPEAKER_BACK_RIGHT;
266 else if(speakers == DSSPEAKER_5POINT1)
268 if(aluBytesFromFormat(device->Format) == 1)
269 format = AL_FORMAT_51CHN8;
270 else
271 format = AL_FORMAT_51CHN16;
272 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
273 SPEAKER_FRONT_RIGHT |
274 SPEAKER_FRONT_CENTER |
275 SPEAKER_LOW_FREQUENCY |
276 SPEAKER_BACK_LEFT |
277 SPEAKER_BACK_RIGHT;
279 else if(speakers == DSSPEAKER_7POINT1)
281 if(aluBytesFromFormat(device->Format) == 1)
282 format = AL_FORMAT_71CHN8;
283 else
284 format = AL_FORMAT_71CHN16;
285 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
286 SPEAKER_FRONT_RIGHT |
287 SPEAKER_FRONT_CENTER |
288 SPEAKER_LOW_FREQUENCY |
289 SPEAKER_BACK_LEFT |
290 SPEAKER_BACK_RIGHT |
291 SPEAKER_SIDE_LEFT |
292 SPEAKER_SIDE_RIGHT;
294 else
295 format = device->Format;
296 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
298 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
299 OutputType.Format.nChannels = aluChannelsFromFormat(format);
300 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
301 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
302 OutputType.Format.nSamplesPerSec = device->Frequency;
303 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
304 OutputType.Format.cbSize = 0;
307 if(OutputType.Format.nChannels > 2)
309 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
310 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
311 OutputType.Format.cbSize = 22;
312 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
314 else
316 if(SUCCEEDED(hr))
318 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
319 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
320 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
321 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
323 if(SUCCEEDED(hr))
324 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
327 if(SUCCEEDED(hr))
329 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
330 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
331 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
332 DSBDescription.dwBufferBytes=(device->BufferSize/num_frags) * num_frags * frameSize;
333 DSBDescription.lpwfxFormat=&OutputType.Format;
334 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
337 if(SUCCEEDED(hr))
338 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
340 if(SUCCEEDED(hr))
342 device->ExtraData = pData;
343 pData->thread = StartThread(DSoundProc, device);
344 if(!pData->thread)
345 hr = E_FAIL;
348 if(FAILED(hr))
350 if (pData->DSsbuffer)
351 IDirectSoundBuffer_Release(pData->DSsbuffer);
352 pData->DSsbuffer = NULL;
353 if (pData->DSpbuffer)
354 IDirectSoundBuffer_Release(pData->DSpbuffer);
355 pData->DSpbuffer = NULL;
356 return ALC_FALSE;
359 device->Format = format;
360 device->UpdateSize = device->BufferSize/num_frags;
362 return ALC_TRUE;
365 static void DSoundStopContext(ALCdevice *device, ALCcontext *context)
367 DSoundData *pData = device->ExtraData;
368 (void)context;
370 if(!pData->thread)
371 return;
373 pData->killNow = 1;
374 StopThread(pData->thread);
375 pData->thread = NULL;
377 IDirectSoundBuffer_Release(pData->DSsbuffer);
378 pData->DSsbuffer = NULL;
379 if (pData->DSpbuffer)
380 IDirectSoundBuffer_Release(pData->DSpbuffer);
381 pData->DSpbuffer = NULL;
385 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
387 (void)pDevice;
388 (void)deviceName;
389 (void)frequency;
390 (void)format;
391 (void)SampleSize;
392 return ALC_FALSE;
395 static void DSoundCloseCapture(ALCdevice *pDevice)
397 (void)pDevice;
400 static void DSoundStartCapture(ALCdevice *pDevice)
402 (void)pDevice;
405 static void DSoundStopCapture(ALCdevice *pDevice)
407 (void)pDevice;
410 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
412 (void)pDevice;
413 (void)pBuffer;
414 (void)lSamples;
417 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
419 (void)pDevice;
420 return 0;
424 BackendFuncs DSoundFuncs = {
425 DSoundOpenPlayback,
426 DSoundClosePlayback,
427 DSoundStartContext,
428 DSoundStopContext,
429 DSoundOpenCapture,
430 DSoundCloseCapture,
431 DSoundStartCapture,
432 DSoundStopCapture,
433 DSoundCaptureSamples,
434 DSoundAvailableSamples
437 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
439 size_t *iter = data;
440 (void)drvname;
442 if(guid)
444 char str[128];
445 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
446 DeviceList[*iter].name = AppendAllDeviceList(str);
447 DeviceList[*iter].guid = *guid;
448 (*iter)++;
450 else
451 DeviceList[0].name = AppendDeviceList("DirectSound Software");
453 return TRUE;
456 void alcDSoundInit(BackendFuncs *FuncList)
458 size_t iter = 1;
459 HRESULT hr;
461 *FuncList = DSoundFuncs;
463 #ifdef _WIN32
464 ds_handle = LoadLibraryA("dsound.dll");
465 if(ds_handle == NULL)
467 AL_PRINT("Failed to load dsound.dll\n");
468 return;
471 #define LOAD_FUNC(f) do { \
472 p##f = (void*)GetProcAddress((HMODULE)ds_handle, #f); \
473 if(p##f == NULL) \
475 FreeLibrary(ds_handle); \
476 ds_handle = NULL; \
477 AL_PRINT("Could not load %s from dsound.dll\n", #f); \
478 return; \
480 } while(0)
481 #else
482 ds_handle = (void*)0xDEADBEEF;
483 #define LOAD_FUNC(f) p##f = f
484 #endif
486 LOAD_FUNC(DirectSoundCreate);
487 LOAD_FUNC(DirectSoundEnumerateA);
488 #undef LOAD_FUNC
490 num_frags = GetConfigValueInt("dsound", "periods", 4);
491 if(num_frags < 2) num_frags = 2;
493 hr = pDirectSoundEnumerateA(DSoundEnumDevices, &iter);
494 if(FAILED(hr))
495 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);