Don't force latency adjustment with PulseAudio
[openal-soft.git] / Alc / dsound.c
blob966b62361901cc681e2f7f4878d7135230771f00
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;
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; \
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;
111 void DSoundUnload(void)
113 if(load_count == 0 || --load_count > 0)
114 return;
116 #ifdef _WIN32
117 FreeLibrary(ds_handle);
118 #endif
119 ds_handle = NULL;
123 static ALuint DSoundProc(ALvoid *ptr)
125 ALCdevice *pDevice = (ALCdevice*)ptr;
126 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
127 DSBCAPS DSBCaps;
128 DWORD LastCursor = 0;
129 DWORD PlayCursor;
130 VOID *WritePtr1, *WritePtr2;
131 DWORD WriteCnt1, WriteCnt2;
132 DWORD FrameSize;
133 DWORD FragSize;
134 DWORD avail;
135 HRESULT err;
137 EnableRTPrio(RTPrioLevel);
139 memset(&DSBCaps, 0, sizeof(DSBCaps));
140 DSBCaps.dwSize = sizeof(DSBCaps);
141 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
142 if(FAILED(err))
144 AL_PRINT("Failed to get buffer caps: 0x%lx\n", err);
145 aluHandleDisconnect(pDevice);
146 return 1;
149 FrameSize = aluChannelsFromFormat(pDevice->Format) *
150 aluBytesFromFormat(pDevice->Format);
151 FragSize = pDevice->UpdateSize * FrameSize;
153 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
154 while(!pData->killNow)
156 // Get current play and write cursors
157 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
158 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
160 if(avail < FragSize)
162 Sleep(1);
163 continue;
165 avail -= avail%FragSize;
167 // Lock output buffer
168 WriteCnt1 = 0;
169 WriteCnt2 = 0;
170 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
172 // If the buffer is lost, restore it, play and lock
173 if(err == DSERR_BUFFERLOST)
175 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
176 if(SUCCEEDED(err))
177 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
178 if(SUCCEEDED(err))
179 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
182 // Successfully locked the output buffer
183 if(SUCCEEDED(err))
185 // If we have an active context, mix data directly into output buffer otherwise fill with silence
186 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
187 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
189 // Unlock output buffer only when successfully locked
190 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
192 else
193 AL_PRINT("Buffer lock error: %#lx\n", err);
195 // Update old write cursor location
196 LastCursor += WriteCnt1+WriteCnt2;
197 LastCursor %= DSBCaps.dwBufferBytes;
200 return 0;
203 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
205 DSoundData *pData = NULL;
206 LPGUID guid = NULL;
207 HRESULT hr;
209 if(!deviceName)
210 deviceName = dsDevice;
211 else if(strcmp(deviceName, dsDevice) != 0)
213 ALuint i;
214 for(i = 0;i < NumDevices;i++)
216 if(strcmp(deviceName, DeviceList[i].name) == 0)
218 guid = &DeviceList[i].guid;
219 break;
222 if(i == NumDevices)
223 return ALC_FALSE;
226 DSoundLoad();
227 if(ds_handle == NULL)
228 return ALC_FALSE;
230 //Initialise requested device
232 pData = calloc(1, sizeof(DSoundData));
233 if(!pData)
235 alcSetError(ALC_OUT_OF_MEMORY);
236 DSoundUnload();
237 return ALC_FALSE;
240 //DirectSound Init code
241 hr = pDirectSoundCreate(guid, &pData->lpDS, NULL);
242 if(SUCCEEDED(hr))
243 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
244 if(FAILED(hr))
246 if(pData->lpDS)
247 IDirectSound_Release(pData->lpDS);
248 free(pData);
249 DSoundUnload();
250 return ALC_FALSE;
253 device->szDeviceName = strdup(deviceName);
254 device->ExtraData = pData;
255 return ALC_TRUE;
258 static void DSoundClosePlayback(ALCdevice *device)
260 DSoundData *pData = device->ExtraData;
262 IDirectSound_Release(pData->lpDS);
263 free(pData);
264 device->ExtraData = NULL;
266 DSoundUnload();
269 static ALCboolean DSoundResetPlayback(ALCdevice *device)
271 DSoundData *pData = (DSoundData*)device->ExtraData;
272 DSBUFFERDESC DSBDescription;
273 WAVEFORMATEXTENSIBLE OutputType;
274 DWORD frameSize = 0;
275 ALenum format = 0;
276 DWORD speakers;
277 HRESULT hr;
279 memset(&OutputType, 0, sizeof(OutputType));
281 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
282 if(SUCCEEDED(hr) && *(GetConfigValue(NULL, "format", "")) != 0)
284 if(aluChannelsFromFormat(device->Format) == 1)
285 speakers = DSSPEAKER_COMBINED(DSSPEAKER_MONO, 0);
286 else if(aluChannelsFromFormat(device->Format) == 2)
287 speakers = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, 0);
288 else if(aluChannelsFromFormat(device->Format) == 4)
289 speakers = DSSPEAKER_COMBINED(DSSPEAKER_QUAD, 0);
290 else if(aluChannelsFromFormat(device->Format) == 6)
291 speakers = DSSPEAKER_COMBINED(DSSPEAKER_5POINT1, 0);
292 else if(aluChannelsFromFormat(device->Format) == 8)
293 speakers = DSSPEAKER_COMBINED(DSSPEAKER_7POINT1, 0);
294 else
296 AL_PRINT("Unknown format: 0x%x\n", device->Format);
297 return ALC_FALSE;
300 if(SUCCEEDED(hr))
302 speakers = DSSPEAKER_CONFIG(speakers);
303 if(speakers == DSSPEAKER_MONO)
305 if(aluBytesFromFormat(device->Format) == 1)
306 format = AL_FORMAT_MONO8;
307 else if(aluBytesFromFormat(device->Format) == 2)
308 format = AL_FORMAT_MONO16;
309 else if(aluBytesFromFormat(device->Format) == 4)
310 format = AL_FORMAT_MONO_FLOAT32;
312 else if(speakers == DSSPEAKER_STEREO)
314 if(aluBytesFromFormat(device->Format) == 1)
315 format = AL_FORMAT_STEREO8;
316 else if(aluBytesFromFormat(device->Format) == 2)
317 format = AL_FORMAT_STEREO16;
318 else if(aluBytesFromFormat(device->Format) == 4)
319 format = AL_FORMAT_STEREO_FLOAT32;
321 else if(speakers == DSSPEAKER_QUAD)
323 if(aluBytesFromFormat(device->Format) == 1)
324 format = AL_FORMAT_QUAD8;
325 else if(aluBytesFromFormat(device->Format) == 2)
326 format = AL_FORMAT_QUAD16;
327 else if(aluBytesFromFormat(device->Format) == 4)
328 format = AL_FORMAT_QUAD32;
329 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
330 SPEAKER_FRONT_RIGHT |
331 SPEAKER_BACK_LEFT |
332 SPEAKER_BACK_RIGHT;
334 else if(speakers == DSSPEAKER_5POINT1)
336 if(aluBytesFromFormat(device->Format) == 1)
337 format = AL_FORMAT_51CHN8;
338 else if(aluBytesFromFormat(device->Format) == 2)
339 format = AL_FORMAT_51CHN16;
340 else if(aluBytesFromFormat(device->Format) == 4)
341 format = AL_FORMAT_51CHN32;
342 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
343 SPEAKER_FRONT_RIGHT |
344 SPEAKER_FRONT_CENTER |
345 SPEAKER_LOW_FREQUENCY |
346 SPEAKER_BACK_LEFT |
347 SPEAKER_BACK_RIGHT;
349 else if(speakers == DSSPEAKER_7POINT1)
351 if(aluBytesFromFormat(device->Format) == 1)
352 format = AL_FORMAT_71CHN8;
353 else if(aluBytesFromFormat(device->Format) == 2)
354 format = AL_FORMAT_71CHN16;
355 else if(aluBytesFromFormat(device->Format) == 4)
356 format = AL_FORMAT_71CHN32;
357 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
358 SPEAKER_FRONT_RIGHT |
359 SPEAKER_FRONT_CENTER |
360 SPEAKER_LOW_FREQUENCY |
361 SPEAKER_BACK_LEFT |
362 SPEAKER_BACK_RIGHT |
363 SPEAKER_SIDE_LEFT |
364 SPEAKER_SIDE_RIGHT;
366 else
367 format = device->Format;
368 frameSize = aluBytesFromFormat(format) * aluChannelsFromFormat(format);
370 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
371 OutputType.Format.nChannels = aluChannelsFromFormat(format);
372 OutputType.Format.wBitsPerSample = aluBytesFromFormat(format) * 8;
373 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
374 OutputType.Format.nSamplesPerSec = device->Frequency;
375 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
376 OutputType.Format.cbSize = 0;
379 if(OutputType.Format.nChannels > 2 || OutputType.Format.wBitsPerSample > 16)
381 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
382 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
383 OutputType.Format.cbSize = 22;
384 if(OutputType.Format.wBitsPerSample == 32)
385 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
386 else
387 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
389 else
391 if(SUCCEEDED(hr))
393 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
394 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
395 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
396 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
398 if(SUCCEEDED(hr))
399 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
402 if(SUCCEEDED(hr))
404 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
405 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
406 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
407 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates * frameSize;
408 DSBDescription.lpwfxFormat=&OutputType.Format;
409 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
412 if(SUCCEEDED(hr))
413 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
415 if(SUCCEEDED(hr))
417 device->Format = format;
418 SetDefaultWFXChannelOrder(device);
419 pData->thread = StartThread(DSoundProc, device);
420 if(!pData->thread)
421 hr = E_FAIL;
424 if(FAILED(hr))
426 if (pData->DSsbuffer)
427 IDirectSoundBuffer_Release(pData->DSsbuffer);
428 pData->DSsbuffer = NULL;
429 if (pData->DSpbuffer)
430 IDirectSoundBuffer_Release(pData->DSpbuffer);
431 pData->DSpbuffer = NULL;
432 return ALC_FALSE;
435 return ALC_TRUE;
438 static void DSoundStopPlayback(ALCdevice *device)
440 DSoundData *pData = device->ExtraData;
442 if(!pData->thread)
443 return;
445 pData->killNow = 1;
446 StopThread(pData->thread);
447 pData->thread = NULL;
449 pData->killNow = 0;
451 IDirectSoundBuffer_Release(pData->DSsbuffer);
452 pData->DSsbuffer = NULL;
453 if (pData->DSpbuffer)
454 IDirectSoundBuffer_Release(pData->DSpbuffer);
455 pData->DSpbuffer = NULL;
459 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
461 (void)pDevice;
462 (void)deviceName;
463 return ALC_FALSE;
466 static void DSoundCloseCapture(ALCdevice *pDevice)
468 (void)pDevice;
471 static void DSoundStartCapture(ALCdevice *pDevice)
473 (void)pDevice;
476 static void DSoundStopCapture(ALCdevice *pDevice)
478 (void)pDevice;
481 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
483 (void)pDevice;
484 (void)pBuffer;
485 (void)lSamples;
488 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
490 (void)pDevice;
491 return 0;
495 BackendFuncs DSoundFuncs = {
496 DSoundOpenPlayback,
497 DSoundClosePlayback,
498 DSoundResetPlayback,
499 DSoundStopPlayback,
500 DSoundOpenCapture,
501 DSoundCloseCapture,
502 DSoundStartCapture,
503 DSoundStopCapture,
504 DSoundCaptureSamples,
505 DSoundAvailableSamples
508 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
510 (void)data;
511 (void)drvname;
513 if(guid)
515 char str[128];
516 void *temp;
518 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
519 if(temp)
521 DeviceList = temp;
523 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
524 AppendAllDeviceList(str);
526 DeviceList[NumDevices].name = strdup(str);
527 DeviceList[NumDevices].guid = *guid;
528 NumDevices++;
532 return TRUE;
535 void alcDSoundInit(BackendFuncs *FuncList)
537 *FuncList = DSoundFuncs;
540 void alcDSoundDeinit(void)
542 ALuint i;
544 for(i = 0;i < NumDevices;++i)
545 free(DeviceList[i].name);
546 free(DeviceList);
547 DeviceList = NULL;
548 NumDevices = 0;
551 void alcDSoundProbe(int type)
553 DSoundLoad();
554 if(!ds_handle) 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();