Pretend DSound buffer fragment size is 1/4th the total buffer size
[openal-soft.git] / Alc / dsound.c
blob98c50e8379b7b4d71f9cb32498a3ebd901ce84d2
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 INITGUID
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <windows.h>
29 #include <mmsystem.h>
30 #include <mmreg.h>
31 #include <dsound.h>
33 #include "alMain.h"
34 #include "AL/al.h"
35 #include "AL/alc.h"
37 #ifndef DSSPEAKER_7POINT1
38 #define DSSPEAKER_7POINT1 7
39 #endif
41 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
43 // Since DSound doesn't report the fragment size, just assume 4 fragments
44 #define DS_FRAGS 4
46 typedef struct {
47 // DirectSound Playback Device
48 LPDIRECTSOUND lpDS;
49 LPDIRECTSOUNDBUFFER DSpbuffer;
50 LPDIRECTSOUNDBUFFER DSsbuffer;
52 int killNow;
53 ALvoid *thread;
54 } DSoundData;
57 typedef struct {
58 ALCchar *name;
59 GUID guid;
60 } DevMap;
61 static DevMap DeviceList[16];
64 static ALuint DSoundProc(ALvoid *ptr)
66 ALCdevice *pDevice = (ALCdevice*)ptr;
67 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
68 DWORD LastCursor = 0;
69 DWORD PlayCursor;
70 VOID *WritePtr1, *WritePtr2;
71 DWORD WriteCnt1, WriteCnt2;
72 DWORD BufferSize;
73 DWORD avail;
74 HRESULT err;
76 BufferSize = pDevice->UpdateFreq*DS_FRAGS*pDevice->FrameSize;
78 while(!pData->killNow)
80 // Get current play and write cursors
81 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
82 avail = (PlayCursor-LastCursor+BufferSize) % BufferSize;
84 if(avail == 0)
86 Sleep(1);
87 continue;
90 // Lock output buffer
91 WriteCnt1 = 0;
92 WriteCnt2 = 0;
93 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
95 // If the buffer is lost, restore it, play and lock
96 if(err == DSERR_BUFFERLOST)
98 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
99 if(SUCCEEDED(err))
100 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
101 if(SUCCEEDED(err))
102 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
105 // Successfully locked the output buffer
106 if(SUCCEEDED(err))
108 // If we have an active context, mix data directly into output buffer otherwise fill with silence
109 SuspendContext(NULL);
110 aluMixData(pDevice->Context, WritePtr1, WriteCnt1, pDevice->Format);
111 aluMixData(pDevice->Context, WritePtr2, WriteCnt2, pDevice->Format);
112 ProcessContext(NULL);
114 // Unlock output buffer only when successfully locked
115 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
117 else
118 AL_PRINT("Buffer lock error: %#lx\n", err);
120 // Update old write cursor location
121 LastCursor += WriteCnt1+WriteCnt2;
122 LastCursor %= BufferSize;
125 return 0;
128 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
130 DSBUFFERDESC DSBDescription;
131 DSoundData *pData = NULL;
132 WAVEFORMATEXTENSIBLE OutputType;
133 LPGUID guid = NULL;
134 DWORD speakers;
135 HRESULT hr;
137 if(deviceName)
139 int i;
140 for(i = 0;DeviceList[i].name;i++)
142 if(strcmp(deviceName, DeviceList[i].name) == 0)
144 device->szDeviceName = DeviceList[i].name;
145 if(i > 0)
146 guid = &DeviceList[i].guid;
147 break;
150 if(!DeviceList[i].name)
151 return ALC_FALSE;
153 else
154 device->szDeviceName = DeviceList[0].name;
156 memset(&OutputType, 0, sizeof(OutputType));
158 //Initialise requested device
160 pData = calloc(1, sizeof(DSoundData));
161 if(!pData)
163 SetALCError(ALC_OUT_OF_MEMORY);
164 return ALC_FALSE;
167 //DirectSound Init code
168 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
169 if(SUCCEEDED(hr))
170 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
172 if(SUCCEEDED(hr))
173 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
174 if(SUCCEEDED(hr))
176 speakers = DSSPEAKER_CONFIG(speakers);
177 if(speakers == DSSPEAKER_MONO)
179 if(aluBytesFromFormat(device->Format) == 1)
180 device->Format = AL_FORMAT_MONO8;
181 else
182 device->Format = AL_FORMAT_MONO16;
184 else if(speakers == DSSPEAKER_STEREO)
186 if(aluBytesFromFormat(device->Format) == 1)
187 device->Format = AL_FORMAT_STEREO8;
188 else
189 device->Format = AL_FORMAT_STEREO16;
191 else if(speakers == DSSPEAKER_QUAD)
193 if(aluBytesFromFormat(device->Format) == 1)
194 device->Format = AL_FORMAT_QUAD8;
195 else
196 device->Format = AL_FORMAT_QUAD16;
197 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
198 SPEAKER_FRONT_RIGHT |
199 SPEAKER_BACK_LEFT |
200 SPEAKER_BACK_RIGHT;
202 else if(speakers == DSSPEAKER_5POINT1)
204 if(aluBytesFromFormat(device->Format) == 1)
205 device->Format = AL_FORMAT_51CHN8;
206 else
207 device->Format = AL_FORMAT_51CHN16;
208 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
209 SPEAKER_FRONT_RIGHT |
210 SPEAKER_FRONT_CENTER |
211 SPEAKER_LOW_FREQUENCY |
212 SPEAKER_BACK_LEFT |
213 SPEAKER_BACK_RIGHT;
215 else if(speakers == DSSPEAKER_7POINT1)
217 if(aluBytesFromFormat(device->Format) == 1)
218 device->Format = AL_FORMAT_71CHN8;
219 else
220 device->Format = AL_FORMAT_71CHN16;
221 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
222 SPEAKER_FRONT_RIGHT |
223 SPEAKER_FRONT_CENTER |
224 SPEAKER_LOW_FREQUENCY |
225 SPEAKER_BACK_LEFT |
226 SPEAKER_BACK_RIGHT |
227 SPEAKER_SIDE_LEFT |
228 SPEAKER_SIDE_RIGHT;
230 device->FrameSize = aluBytesFromFormat(device->Format) *
231 aluChannelsFromFormat(device->Format);
233 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
234 OutputType.Format.nChannels = aluChannelsFromFormat(device->Format);
235 OutputType.Format.wBitsPerSample = aluBytesFromFormat(device->Format) * 8;
236 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
237 OutputType.Format.nSamplesPerSec = device->Frequency;
238 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
239 OutputType.Format.cbSize = 0;
241 device->UpdateFreq /= DS_FRAGS;
244 if(OutputType.Format.nChannels > 2)
246 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
247 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
248 OutputType.Format.cbSize = 22;
249 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
251 else
253 if(SUCCEEDED(hr))
255 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
256 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
257 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
258 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
260 if(SUCCEEDED(hr))
261 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
264 if(SUCCEEDED(hr))
266 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
267 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
268 DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
269 DSBDescription.dwBufferBytes=device->UpdateFreq * DS_FRAGS * device->FrameSize;
270 DSBDescription.lpwfxFormat=&OutputType.Format;
271 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
274 if(SUCCEEDED(hr))
275 hr = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
277 device->ExtraData = pData;
278 pData->thread = StartThread(DSoundProc, device);
279 if(!pData->thread)
280 hr = E_FAIL;
282 if(FAILED(hr))
284 if (pData->DSsbuffer)
285 IDirectSoundBuffer_Release(pData->DSsbuffer);
286 if (pData->DSpbuffer)
287 IDirectSoundBuffer_Release(pData->DSpbuffer);
288 if (pData->lpDS)
289 IDirectSound_Release(pData->lpDS);
291 free(pData);
292 return ALC_FALSE;
295 return ALC_TRUE;
298 static void DSoundClosePlayback(ALCdevice *device)
300 DSoundData *pData = device->ExtraData;
302 pData->killNow = 1;
303 StopThread(pData->thread);
305 IDirectSoundBuffer_Release(pData->DSsbuffer);
306 if (pData->DSpbuffer)
307 IDirectSoundBuffer_Release(pData->DSpbuffer);
308 IDirectSound_Release(pData->lpDS);
310 free(pData);
311 device->ExtraData = NULL;
315 static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
317 (void)pDevice;
318 (void)deviceName;
319 (void)frequency;
320 (void)format;
321 (void)SampleSize;
322 return ALC_FALSE;
325 static void DSoundCloseCapture(ALCdevice *pDevice)
327 (void)pDevice;
330 static void DSoundStartCapture(ALCdevice *pDevice)
332 (void)pDevice;
335 static void DSoundStopCapture(ALCdevice *pDevice)
337 (void)pDevice;
340 static void DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
342 (void)pDevice;
343 (void)pBuffer;
344 (void)lSamples;
347 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
349 (void)pDevice;
350 return 0;
354 BackendFuncs DSoundFuncs = {
355 DSoundOpenPlayback,
356 DSoundClosePlayback,
357 DSoundOpenCapture,
358 DSoundCloseCapture,
359 DSoundStartCapture,
360 DSoundStopCapture,
361 DSoundCaptureSamples,
362 DSoundAvailableSamples
365 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
367 static size_t i = 1;
368 (void)drvname;
369 (void)data;
371 if(guid)
373 char str[128];
374 snprintf(str, sizeof(str), "DirectSound Software on %s", desc);
375 DeviceList[i].name = AppendAllDeviceList(str);
376 DeviceList[i].guid = *guid;
377 i++;
379 else
380 DeviceList[0].name = AppendDeviceList("DirectSound Software");
382 return TRUE;
385 void alcDSoundInit(BackendFuncs *FuncList)
387 HRESULT hr;
389 *FuncList = DSoundFuncs;
391 hr = DirectSoundEnumerate(DSoundEnumDevices, NULL);
392 if(FAILED(hr))
393 AL_PRINT("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);