Define _WIN32_WINNT on the command line with _WIN32
[openal-soft/android.git] / Alc / backends / dsound.c
blobf1dd07622eed16f897bc0ca2cc50b7104fd87a7d
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 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
27 #include <dsound.h>
28 #include <cguid.h>
29 #include <mmreg.h>
30 #ifndef _WAVEFORMATEXTENSIBLE_
31 #include <ks.h>
32 #include <ksmedia.h>
33 #endif
35 #include "alMain.h"
36 #include "AL/al.h"
37 #include "AL/alc.h"
39 #ifndef DSSPEAKER_5POINT1
40 #define DSSPEAKER_5POINT1 6
41 #endif
42 #ifndef DSSPEAKER_7POINT1
43 #define DSSPEAKER_7POINT1 7
44 #endif
46 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
47 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
50 static HMODULE ds_handle;
51 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
52 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
54 #define DirectSoundCreate pDirectSoundCreate
55 #define DirectSoundEnumerateA pDirectSoundEnumerateA
58 typedef struct {
59 // DirectSound Playback Device
60 IDirectSound *lpDS;
61 IDirectSoundBuffer *DSpbuffer;
62 IDirectSoundBuffer *DSsbuffer;
63 IDirectSoundNotify *DSnotify;
64 HANDLE hNotifyEvent;
66 volatile int killNow;
67 ALvoid *thread;
68 } DSoundData;
71 typedef struct {
72 ALCchar *name;
73 GUID guid;
74 } DevMap;
76 static const ALCchar dsDevice[] = "DirectSound Default";
77 static DevMap *DeviceList;
78 static ALuint NumDevices;
80 #define MAX_UPDATES 128
82 static ALCboolean DSoundLoad(void)
84 ALCboolean ok = ALC_TRUE;
85 if(!ds_handle)
87 ds_handle = LoadLibraryA("dsound.dll");
88 if(ds_handle == NULL)
90 ERR("Failed to load dsound.dll\n");
91 return ALC_FALSE;
94 #define LOAD_FUNC(x) do { \
95 if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \
96 ERR("Could not load %s from dsound.dll\n", #x); \
97 ok = ALC_FALSE; \
98 } \
99 } while(0)
100 LOAD_FUNC(DirectSoundCreate);
101 LOAD_FUNC(DirectSoundEnumerateA);
102 #undef LOAD_FUNC
104 if(!ok)
106 FreeLibrary(ds_handle);
107 ds_handle = NULL;
110 return ok;
114 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
116 char str[1024];
117 void *temp;
118 int count;
119 ALuint i;
121 (void)data;
122 (void)drvname;
124 if(!guid)
125 return TRUE;
127 count = 0;
128 do {
129 if(count == 0)
130 snprintf(str, sizeof(str), "%s", desc);
131 else
132 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
133 count++;
135 for(i = 0;i < NumDevices;i++)
137 if(strcmp(str, DeviceList[i].name) == 0)
138 break;
140 } while(i != NumDevices);
142 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
143 if(temp)
145 DeviceList = temp;
146 DeviceList[NumDevices].name = strdup(str);
147 DeviceList[NumDevices].guid = *guid;
148 NumDevices++;
151 return TRUE;
155 static ALuint DSoundProc(ALvoid *ptr)
157 ALCdevice *pDevice = (ALCdevice*)ptr;
158 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
159 DSBCAPS DSBCaps;
160 DWORD LastCursor = 0;
161 DWORD PlayCursor;
162 VOID *WritePtr1, *WritePtr2;
163 DWORD WriteCnt1, WriteCnt2;
164 BOOL Playing = FALSE;
165 DWORD FrameSize;
166 DWORD FragSize;
167 DWORD avail;
168 HRESULT err;
170 SetRTPriority();
172 memset(&DSBCaps, 0, sizeof(DSBCaps));
173 DSBCaps.dwSize = sizeof(DSBCaps);
174 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
175 if(FAILED(err))
177 ERR("Failed to get buffer caps: 0x%lx\n", err);
178 aluHandleDisconnect(pDevice);
179 return 1;
182 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
183 FragSize = pDevice->UpdateSize * FrameSize;
185 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
186 while(!pData->killNow)
188 // Get current play cursor
189 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
190 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
192 if(avail < FragSize)
194 if(!Playing)
196 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
197 if(FAILED(err))
199 ERR("Failed to play buffer: 0x%lx\n", err);
200 aluHandleDisconnect(pDevice);
201 return 1;
203 Playing = TRUE;
206 avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);
207 if(avail != WAIT_OBJECT_0)
208 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
209 continue;
211 avail -= avail%FragSize;
213 // Lock output buffer
214 WriteCnt1 = 0;
215 WriteCnt2 = 0;
216 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
218 // If the buffer is lost, restore it and lock
219 if(err == DSERR_BUFFERLOST)
221 WARN("Buffer lost, restoring...\n");
222 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
223 if(SUCCEEDED(err))
225 Playing = FALSE;
226 LastCursor = 0;
227 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
231 // Successfully locked the output buffer
232 if(SUCCEEDED(err))
234 // If we have an active context, mix data directly into output buffer otherwise fill with silence
235 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
236 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
238 // Unlock output buffer only when successfully locked
239 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
241 else
243 ERR("Buffer lock error: %#lx\n", err);
244 aluHandleDisconnect(pDevice);
245 return 1;
248 // Update old write cursor location
249 LastCursor += WriteCnt1+WriteCnt2;
250 LastCursor %= DSBCaps.dwBufferBytes;
253 return 0;
256 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
258 DSoundData *pData = NULL;
259 LPGUID guid = NULL;
260 HRESULT hr;
262 if(!deviceName)
263 deviceName = dsDevice;
264 else if(strcmp(deviceName, dsDevice) != 0)
266 ALuint i;
268 if(!DeviceList)
270 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
271 if(FAILED(hr))
272 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
275 for(i = 0;i < NumDevices;i++)
277 if(strcmp(deviceName, DeviceList[i].name) == 0)
279 guid = &DeviceList[i].guid;
280 break;
283 if(i == NumDevices)
284 return ALC_INVALID_VALUE;
287 //Initialise requested device
288 pData = calloc(1, sizeof(DSoundData));
289 if(!pData)
290 return ALC_OUT_OF_MEMORY;
292 hr = DS_OK;
293 pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
294 if(pData->hNotifyEvent == NULL)
295 hr = E_FAIL;
297 //DirectSound Init code
298 if(SUCCEEDED(hr))
299 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
300 if(SUCCEEDED(hr))
301 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
302 if(FAILED(hr))
304 if(pData->lpDS)
305 IDirectSound_Release(pData->lpDS);
306 if(pData->hNotifyEvent)
307 CloseHandle(pData->hNotifyEvent);
308 free(pData);
309 ERR("Device init failed: 0x%08lx\n", hr);
310 return ALC_INVALID_VALUE;
313 device->szDeviceName = strdup(deviceName);
314 device->ExtraData = pData;
315 return ALC_NO_ERROR;
318 static void DSoundClosePlayback(ALCdevice *device)
320 DSoundData *pData = device->ExtraData;
322 IDirectSound_Release(pData->lpDS);
323 CloseHandle(pData->hNotifyEvent);
324 free(pData);
325 device->ExtraData = NULL;
328 static ALCboolean DSoundResetPlayback(ALCdevice *device)
330 DSoundData *pData = (DSoundData*)device->ExtraData;
331 DSBUFFERDESC DSBDescription;
332 WAVEFORMATEXTENSIBLE OutputType;
333 DWORD speakers;
334 HRESULT hr;
336 memset(&OutputType, 0, sizeof(OutputType));
338 switch(device->FmtType)
340 case DevFmtByte:
341 device->FmtType = DevFmtUByte;
342 break;
343 case DevFmtUShort:
344 device->FmtType = DevFmtShort;
345 break;
346 case DevFmtUByte:
347 case DevFmtShort:
348 case DevFmtFloat:
349 break;
352 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
353 if(SUCCEEDED(hr))
355 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
357 speakers = DSSPEAKER_CONFIG(speakers);
358 if(speakers == DSSPEAKER_MONO)
359 device->FmtChans = DevFmtMono;
360 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
361 device->FmtChans = DevFmtStereo;
362 else if(speakers == DSSPEAKER_QUAD)
363 device->FmtChans = DevFmtQuad;
364 else if(speakers == DSSPEAKER_5POINT1)
365 device->FmtChans = DevFmtX51;
366 else if(speakers == DSSPEAKER_7POINT1)
367 device->FmtChans = DevFmtX71;
368 else
369 ERR("Unknown system speaker config: 0x%lx\n", speakers);
372 switch(device->FmtChans)
374 case DevFmtMono:
375 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
376 break;
377 case DevFmtStereo:
378 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
379 SPEAKER_FRONT_RIGHT;
380 break;
381 case DevFmtQuad:
382 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
383 SPEAKER_FRONT_RIGHT |
384 SPEAKER_BACK_LEFT |
385 SPEAKER_BACK_RIGHT;
386 break;
387 case DevFmtX51:
388 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
389 SPEAKER_FRONT_RIGHT |
390 SPEAKER_FRONT_CENTER |
391 SPEAKER_LOW_FREQUENCY |
392 SPEAKER_BACK_LEFT |
393 SPEAKER_BACK_RIGHT;
394 break;
395 case DevFmtX51Side:
396 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
397 SPEAKER_FRONT_RIGHT |
398 SPEAKER_FRONT_CENTER |
399 SPEAKER_LOW_FREQUENCY |
400 SPEAKER_SIDE_LEFT |
401 SPEAKER_SIDE_RIGHT;
402 break;
403 case DevFmtX61:
404 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
405 SPEAKER_FRONT_RIGHT |
406 SPEAKER_FRONT_CENTER |
407 SPEAKER_LOW_FREQUENCY |
408 SPEAKER_BACK_CENTER |
409 SPEAKER_SIDE_LEFT |
410 SPEAKER_SIDE_RIGHT;
411 break;
412 case DevFmtX71:
413 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
414 SPEAKER_FRONT_RIGHT |
415 SPEAKER_FRONT_CENTER |
416 SPEAKER_LOW_FREQUENCY |
417 SPEAKER_BACK_LEFT |
418 SPEAKER_BACK_RIGHT |
419 SPEAKER_SIDE_LEFT |
420 SPEAKER_SIDE_RIGHT;
421 break;
424 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
425 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
426 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
427 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
428 OutputType.Format.nSamplesPerSec = device->Frequency;
429 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
430 OutputType.Format.cbSize = 0;
433 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
435 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
436 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
437 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
438 if(device->FmtType == DevFmtFloat)
439 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
440 else
441 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
443 else
445 if(SUCCEEDED(hr))
447 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
448 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
449 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
450 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
452 if(SUCCEEDED(hr))
453 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
456 if(SUCCEEDED(hr))
458 if(device->NumUpdates > MAX_UPDATES)
460 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
461 MAX_UPDATES-1) / MAX_UPDATES;
462 device->NumUpdates = MAX_UPDATES;
465 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
466 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
467 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
468 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
469 OutputType.Format.nBlockAlign;
470 DSBDescription.lpwfxFormat=&OutputType.Format;
471 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
474 if(SUCCEEDED(hr))
476 hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
477 if(SUCCEEDED(hr))
479 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
480 ALuint i;
482 for(i = 0;i < device->NumUpdates;++i)
484 notifies[i].dwOffset = i * device->UpdateSize *
485 OutputType.Format.nBlockAlign;
486 notifies[i].hEventNotify = pData->hNotifyEvent;
488 if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
489 hr = E_FAIL;
493 if(SUCCEEDED(hr))
495 ResetEvent(pData->hNotifyEvent);
496 SetDefaultWFXChannelOrder(device);
497 pData->thread = StartThread(DSoundProc, device);
498 if(pData->thread == NULL)
499 hr = E_FAIL;
502 if(FAILED(hr))
504 if(pData->DSnotify != NULL)
505 IDirectSoundNotify_Release(pData->DSnotify);
506 pData->DSnotify = NULL;
507 if(pData->DSsbuffer != NULL)
508 IDirectSoundBuffer_Release(pData->DSsbuffer);
509 pData->DSsbuffer = NULL;
510 if(pData->DSpbuffer != NULL)
511 IDirectSoundBuffer_Release(pData->DSpbuffer);
512 pData->DSpbuffer = NULL;
513 return ALC_FALSE;
516 return ALC_TRUE;
519 static void DSoundStopPlayback(ALCdevice *device)
521 DSoundData *pData = device->ExtraData;
523 if(!pData->thread)
524 return;
526 pData->killNow = 1;
527 StopThread(pData->thread);
528 pData->thread = NULL;
530 pData->killNow = 0;
532 IDirectSoundNotify_Release(pData->DSnotify);
533 pData->DSnotify = NULL;
534 IDirectSoundBuffer_Release(pData->DSsbuffer);
535 pData->DSsbuffer = NULL;
536 if(pData->DSpbuffer != NULL)
537 IDirectSoundBuffer_Release(pData->DSpbuffer);
538 pData->DSpbuffer = NULL;
542 static const BackendFuncs DSoundFuncs = {
543 DSoundOpenPlayback,
544 DSoundClosePlayback,
545 DSoundResetPlayback,
546 DSoundStopPlayback,
547 NULL,
548 NULL,
549 NULL,
550 NULL,
551 NULL,
552 NULL
556 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
558 if(!DSoundLoad())
559 return ALC_FALSE;
560 *FuncList = DSoundFuncs;
561 return ALC_TRUE;
564 void alcDSoundDeinit(void)
566 ALuint i;
568 for(i = 0;i < NumDevices;++i)
569 free(DeviceList[i].name);
570 free(DeviceList);
571 DeviceList = NULL;
572 NumDevices = 0;
574 if(ds_handle)
575 FreeLibrary(ds_handle);
576 ds_handle = NULL;
579 void alcDSoundProbe(enum DevProbe type)
581 HRESULT hr;
582 ALuint i;
584 switch(type)
586 case DEVICE_PROBE:
587 AppendDeviceList(dsDevice);
588 break;
590 case ALL_DEVICE_PROBE:
591 for(i = 0;i < NumDevices;++i)
592 free(DeviceList[i].name);
593 free(DeviceList);
594 DeviceList = NULL;
595 NumDevices = 0;
597 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
598 if(FAILED(hr))
599 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
600 else
602 for(i = 0;i < NumDevices;i++)
603 AppendAllDeviceList(DeviceList[i].name);
605 break;
607 case CAPTURE_DEVICE_PROBE:
608 break;