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
30 #ifndef _WAVEFORMATEXTENSIBLE_
39 #ifndef DSSPEAKER_5POINT1
40 #define DSSPEAKER_5POINT1 6
42 #ifndef DSSPEAKER_7POINT1
43 #define DSSPEAKER_7POINT1 7
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
59 // DirectSound Playback Device
61 IDirectSoundBuffer
*DSpbuffer
;
62 IDirectSoundBuffer
*DSsbuffer
;
63 IDirectSoundNotify
*DSnotify
;
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
;
87 ds_handle
= LoadLibraryA("dsound.dll");
90 ERR("Failed to load dsound.dll\n");
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); \
100 LOAD_FUNC(DirectSoundCreate
);
101 LOAD_FUNC(DirectSoundEnumerateA
);
106 FreeLibrary(ds_handle
);
114 static BOOL CALLBACK
DSoundEnumDevices(LPGUID guid
, LPCSTR desc
, LPCSTR drvname
, LPVOID data
)
130 snprintf(str
, sizeof(str
), "%s", desc
);
132 snprintf(str
, sizeof(str
), "%s #%d", desc
, count
+1);
135 for(i
= 0;i
< NumDevices
;i
++)
137 if(strcmp(str
, DeviceList
[i
].name
) == 0)
140 } while(i
!= NumDevices
);
142 temp
= realloc(DeviceList
, sizeof(DevMap
) * (NumDevices
+1));
146 DeviceList
[NumDevices
].name
= strdup(str
);
147 DeviceList
[NumDevices
].guid
= *guid
;
155 static ALuint
DSoundProc(ALvoid
*ptr
)
157 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
158 DSoundData
*pData
= (DSoundData
*)pDevice
->ExtraData
;
160 DWORD LastCursor
= 0;
162 VOID
*WritePtr1
, *WritePtr2
;
163 DWORD WriteCnt1
, WriteCnt2
;
164 BOOL Playing
= FALSE
;
172 memset(&DSBCaps
, 0, sizeof(DSBCaps
));
173 DSBCaps
.dwSize
= sizeof(DSBCaps
);
174 err
= IDirectSoundBuffer_GetCaps(pData
->DSsbuffer
, &DSBCaps
);
177 ERR("Failed to get buffer caps: 0x%lx\n", err
);
178 aluHandleDisconnect(pDevice
);
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
;
196 err
= IDirectSoundBuffer_Play(pData
->DSsbuffer
, 0, 0, DSBPLAY_LOOPING
);
199 ERR("Failed to play buffer: 0x%lx\n", err
);
200 aluHandleDisconnect(pDevice
);
206 avail
= WaitForSingleObjectEx(pData
->hNotifyEvent
, 2000, FALSE
);
207 if(avail
!= WAIT_OBJECT_0
)
208 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
211 avail
-= avail
%FragSize
;
213 // Lock output buffer
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
);
227 err
= IDirectSoundBuffer_Lock(pData
->DSsbuffer
, 0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
231 // Successfully locked the output buffer
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
);
243 ERR("Buffer lock error: %#lx\n", err
);
244 aluHandleDisconnect(pDevice
);
248 // Update old write cursor location
249 LastCursor
+= WriteCnt1
+WriteCnt2
;
250 LastCursor
%= DSBCaps
.dwBufferBytes
;
256 static ALCenum
DSoundOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
258 DSoundData
*pData
= NULL
;
263 deviceName
= dsDevice
;
264 else if(strcmp(deviceName
, dsDevice
) != 0)
270 hr
= DirectSoundEnumerateA(DSoundEnumDevices
, NULL
);
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
;
284 return ALC_INVALID_VALUE
;
287 //Initialise requested device
288 pData
= calloc(1, sizeof(DSoundData
));
290 return ALC_OUT_OF_MEMORY
;
293 pData
->hNotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
294 if(pData
->hNotifyEvent
== NULL
)
297 //DirectSound Init code
299 hr
= DirectSoundCreate(guid
, &pData
->lpDS
, NULL
);
301 hr
= IDirectSound_SetCooperativeLevel(pData
->lpDS
, GetForegroundWindow(), DSSCL_PRIORITY
);
305 IDirectSound_Release(pData
->lpDS
);
306 if(pData
->hNotifyEvent
)
307 CloseHandle(pData
->hNotifyEvent
);
309 ERR("Device init failed: 0x%08lx\n", hr
);
310 return ALC_INVALID_VALUE
;
313 device
->szDeviceName
= strdup(deviceName
);
314 device
->ExtraData
= pData
;
318 static void DSoundClosePlayback(ALCdevice
*device
)
320 DSoundData
*pData
= device
->ExtraData
;
322 IDirectSound_Release(pData
->lpDS
);
323 CloseHandle(pData
->hNotifyEvent
);
325 device
->ExtraData
= NULL
;
328 static ALCboolean
DSoundResetPlayback(ALCdevice
*device
)
330 DSoundData
*pData
= (DSoundData
*)device
->ExtraData
;
331 DSBUFFERDESC DSBDescription
;
332 WAVEFORMATEXTENSIBLE OutputType
;
336 memset(&OutputType
, 0, sizeof(OutputType
));
338 switch(device
->FmtType
)
341 device
->FmtType
= DevFmtUByte
;
344 device
->FmtType
= DevFmtShort
;
352 hr
= IDirectSound_GetSpeakerConfig(pData
->lpDS
, &speakers
);
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
;
369 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
372 switch(device
->FmtChans
)
375 OutputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
378 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
382 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
383 SPEAKER_FRONT_RIGHT
|
388 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
389 SPEAKER_FRONT_RIGHT
|
390 SPEAKER_FRONT_CENTER
|
391 SPEAKER_LOW_FREQUENCY
|
396 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
397 SPEAKER_FRONT_RIGHT
|
398 SPEAKER_FRONT_CENTER
|
399 SPEAKER_LOW_FREQUENCY
|
404 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
405 SPEAKER_FRONT_RIGHT
|
406 SPEAKER_FRONT_CENTER
|
407 SPEAKER_LOW_FREQUENCY
|
408 SPEAKER_BACK_CENTER
|
413 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
414 SPEAKER_FRONT_RIGHT
|
415 SPEAKER_FRONT_CENTER
|
416 SPEAKER_LOW_FREQUENCY
|
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
;
441 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
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
);
453 hr
= IDirectSoundBuffer_SetFormat(pData
->DSpbuffer
,&OutputType
.Format
);
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
);
476 hr
= IDirectSoundBuffer_QueryInterface(pData
->DSsbuffer
, &IID_IDirectSoundNotify
, (LPVOID
*)&pData
->DSnotify
);
479 DSBPOSITIONNOTIFY notifies
[MAX_UPDATES
];
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
)
495 ResetEvent(pData
->hNotifyEvent
);
496 SetDefaultWFXChannelOrder(device
);
497 pData
->thread
= StartThread(DSoundProc
, device
);
498 if(pData
->thread
== NULL
)
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
;
519 static void DSoundStopPlayback(ALCdevice
*device
)
521 DSoundData
*pData
= device
->ExtraData
;
527 StopThread(pData
->thread
);
528 pData
->thread
= NULL
;
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
= {
556 ALCboolean
alcDSoundInit(BackendFuncs
*FuncList
)
560 *FuncList
= DSoundFuncs
;
564 void alcDSoundDeinit(void)
568 for(i
= 0;i
< NumDevices
;++i
)
569 free(DeviceList
[i
].name
);
575 FreeLibrary(ds_handle
);
579 void alcDSoundProbe(enum DevProbe type
)
587 AppendDeviceList(dsDevice
);
590 case ALL_DEVICE_PROBE
:
591 for(i
= 0;i
< NumDevices
;++i
)
592 free(DeviceList
[i
].name
);
597 hr
= DirectSoundEnumerateA(DSoundEnumDevices
, NULL
);
599 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr
);
602 for(i
= 0;i
< NumDevices
;i
++)
603 AppendAllDeviceList(DeviceList
[i
].name
);
607 case CAPTURE_DEVICE_PROBE
: