view Alc/backends/dsound.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
line wrap: on
line source
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.
8 *
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.
13 *
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
19 */
21 #include "config.h"
23 #define _WIN32_WINNT 0x0500
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <dsound.h>
29 #include <cguid.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);
51 static HMODULE ds_handle;
52 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
53 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
55 #define DirectSoundCreate pDirectSoundCreate
56 #define DirectSoundEnumerateA pDirectSoundEnumerateA
59 typedef struct {
60 // DirectSound Playback Device
61 IDirectSound *lpDS;
62 IDirectSoundBuffer *DSpbuffer;
63 IDirectSoundBuffer *DSsbuffer;
64 IDirectSoundNotify *DSnotify;
65 HANDLE hNotifyEvent;
67 volatile int killNow;
68 ALvoid *thread;
69 } DSoundData;
72 typedef struct {
73 ALCchar *name;
74 GUID guid;
75 } DevMap;
77 static const ALCchar dsDevice[] = "DirectSound Default";
78 static DevMap *DeviceList;
79 static ALuint NumDevices;
81 #define MAX_UPDATES 128
83 static ALCboolean DSoundLoad(void)
84 {
85 ALCboolean ok = ALC_TRUE;
86 if(!ds_handle)
87 {
88 ds_handle = LoadLibraryA("dsound.dll");
89 if(ds_handle == NULL)
90 {
91 ERR("Failed to load dsound.dll\n");
92 return ALC_FALSE;
93 }
95 #define LOAD_FUNC(x) do { \
96 if((p##x = (void*)GetProcAddress(ds_handle, #x)) == NULL) { \
97 ERR("Could not load %s from dsound.dll\n", #x); \
98 ok = ALC_FALSE; \
99 } \
100 } while(0)
101 LOAD_FUNC(DirectSoundCreate);
102 LOAD_FUNC(DirectSoundEnumerateA);
103 #undef LOAD_FUNC
105 if(!ok)
106 {
107 FreeLibrary(ds_handle);
108 ds_handle = NULL;
109 }
110 }
111 return ok;
112 }
115 static BOOL CALLBACK DSoundEnumDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
116 {
117 char str[1024];
118 void *temp;
119 int count;
120 ALuint i;
122 (void)data;
123 (void)drvname;
125 if(!guid)
126 return TRUE;
128 count = 0;
129 do {
130 if(count == 0)
131 snprintf(str, sizeof(str), "%s", desc);
132 else
133 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
134 count++;
136 for(i = 0;i < NumDevices;i++)
137 {
138 if(strcmp(str, DeviceList[i].name) == 0)
139 break;
140 }
141 } while(i != NumDevices);
143 temp = realloc(DeviceList, sizeof(DevMap) * (NumDevices+1));
144 if(temp)
145 {
146 DeviceList = temp;
147 DeviceList[NumDevices].name = strdup(str);
148 DeviceList[NumDevices].guid = *guid;
149 NumDevices++;
150 }
152 return TRUE;
153 }
156 static ALuint DSoundProc(ALvoid *ptr)
157 {
158 ALCdevice *pDevice = (ALCdevice*)ptr;
159 DSoundData *pData = (DSoundData*)pDevice->ExtraData;
160 DSBCAPS DSBCaps;
161 DWORD LastCursor = 0;
162 DWORD PlayCursor;
163 VOID *WritePtr1, *WritePtr2;
164 DWORD WriteCnt1, WriteCnt2;
165 BOOL Playing = FALSE;
166 DWORD FrameSize;
167 DWORD FragSize;
168 DWORD avail;
169 HRESULT err;
171 SetRTPriority();
173 memset(&DSBCaps, 0, sizeof(DSBCaps));
174 DSBCaps.dwSize = sizeof(DSBCaps);
175 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
176 if(FAILED(err))
177 {
178 ERR("Failed to get buffer caps: 0x%lx\n", err);
179 aluHandleDisconnect(pDevice);
180 return 1;
181 }
183 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
184 FragSize = pDevice->UpdateSize * FrameSize;
186 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
187 while(!pData->killNow)
188 {
189 // Get current play cursor
190 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
191 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
193 if(avail < FragSize)
194 {
195 if(!Playing)
196 {
197 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
198 if(FAILED(err))
199 {
200 ERR("Failed to play buffer: 0x%lx\n", err);
201 aluHandleDisconnect(pDevice);
202 return 1;
203 }
204 Playing = TRUE;
205 }
207 avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);
208 if(avail != WAIT_OBJECT_0)
209 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
210 continue;
211 }
212 avail -= avail%FragSize;
214 // Lock output buffer
215 WriteCnt1 = 0;
216 WriteCnt2 = 0;
217 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
219 // If the buffer is lost, restore it and lock
220 if(err == DSERR_BUFFERLOST)
221 {
222 WARN("Buffer lost, restoring...\n");
223 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
224 if(SUCCEEDED(err))
225 {
226 Playing = FALSE;
227 LastCursor = 0;
228 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
229 }
230 }
232 // Successfully locked the output buffer
233 if(SUCCEEDED(err))
234 {
235 // If we have an active context, mix data directly into output buffer otherwise fill with silence
236 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
237 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
239 // Unlock output buffer only when successfully locked
240 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
241 }
242 else
243 {
244 ERR("Buffer lock error: %#lx\n", err);
245 aluHandleDisconnect(pDevice);
246 return 1;
247 }
249 // Update old write cursor location
250 LastCursor += WriteCnt1+WriteCnt2;
251 LastCursor %= DSBCaps.dwBufferBytes;
252 }
254 return 0;
255 }
257 static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
258 {
259 DSoundData *pData = NULL;
260 LPGUID guid = NULL;
261 HRESULT hr;
263 if(!deviceName)
264 deviceName = dsDevice;
265 else if(strcmp(deviceName, dsDevice) != 0)
266 {
267 ALuint i;
269 if(!DeviceList)
270 {
271 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
272 if(FAILED(hr))
273 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
274 }
276 for(i = 0;i < NumDevices;i++)
277 {
278 if(strcmp(deviceName, DeviceList[i].name) == 0)
279 {
280 guid = &DeviceList[i].guid;
281 break;
282 }
283 }
284 if(i == NumDevices)
285 return ALC_FALSE;
286 }
288 //Initialise requested device
289 pData = calloc(1, sizeof(DSoundData));
290 if(!pData)
291 {
292 alcSetError(device, ALC_OUT_OF_MEMORY);
293 return ALC_FALSE;
294 }
296 hr = DS_OK;
297 pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
298 if(pData->hNotifyEvent == NULL)
299 hr = E_FAIL;
301 //DirectSound Init code
302 if(SUCCEEDED(hr))
303 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
304 if(SUCCEEDED(hr))
305 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
306 if(FAILED(hr))
307 {
308 if(pData->lpDS)
309 IDirectSound_Release(pData->lpDS);
310 if(pData->hNotifyEvent)
311 CloseHandle(pData->hNotifyEvent);
312 free(pData);
313 ERR("Device init failed: 0x%08lx\n", hr);
314 return ALC_FALSE;
315 }
317 device->szDeviceName = strdup(deviceName);
318 device->ExtraData = pData;
319 return ALC_TRUE;
320 }
322 static void DSoundClosePlayback(ALCdevice *device)
323 {
324 DSoundData *pData = device->ExtraData;
326 IDirectSound_Release(pData->lpDS);
327 CloseHandle(pData->hNotifyEvent);
328 free(pData);
329 device->ExtraData = NULL;
330 }
332 static ALCboolean DSoundResetPlayback(ALCdevice *device)
333 {
334 DSoundData *pData = (DSoundData*)device->ExtraData;
335 DSBUFFERDESC DSBDescription;
336 WAVEFORMATEXTENSIBLE OutputType;
337 DWORD speakers;
338 HRESULT hr;
340 memset(&OutputType, 0, sizeof(OutputType));
342 switch(device->FmtType)
343 {
344 case DevFmtByte:
345 device->FmtType = DevFmtUByte;
346 break;
347 case DevFmtUShort:
348 device->FmtType = DevFmtShort;
349 break;
350 case DevFmtUByte:
351 case DevFmtShort:
352 case DevFmtFloat:
353 break;
354 }
356 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
357 if(SUCCEEDED(hr))
358 {
359 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
360 {
361 speakers = DSSPEAKER_CONFIG(speakers);
362 if(speakers == DSSPEAKER_MONO)
363 device->FmtChans = DevFmtMono;
364 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
365 device->FmtChans = DevFmtStereo;
366 else if(speakers == DSSPEAKER_QUAD)
367 device->FmtChans = DevFmtQuad;
368 else if(speakers == DSSPEAKER_5POINT1)
369 device->FmtChans = DevFmtX51;
370 else if(speakers == DSSPEAKER_7POINT1)
371 device->FmtChans = DevFmtX71;
372 else
373 ERR("Unknown system speaker config: 0x%lx\n", speakers);
374 }
376 switch(device->FmtChans)
377 {
378 case DevFmtMono:
379 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
380 break;
381 case DevFmtStereo:
382 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
383 SPEAKER_FRONT_RIGHT;
384 break;
385 case DevFmtQuad:
386 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
387 SPEAKER_FRONT_RIGHT |
388 SPEAKER_BACK_LEFT |
389 SPEAKER_BACK_RIGHT;
390 break;
391 case DevFmtX51:
392 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
393 SPEAKER_FRONT_RIGHT |
394 SPEAKER_FRONT_CENTER |
395 SPEAKER_LOW_FREQUENCY |
396 SPEAKER_BACK_LEFT |
397 SPEAKER_BACK_RIGHT;
398 break;
399 case DevFmtX51Side:
400 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
401 SPEAKER_FRONT_RIGHT |
402 SPEAKER_FRONT_CENTER |
403 SPEAKER_LOW_FREQUENCY |
404 SPEAKER_SIDE_LEFT |
405 SPEAKER_SIDE_RIGHT;
406 break;
407 case DevFmtX61:
408 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
409 SPEAKER_FRONT_RIGHT |
410 SPEAKER_FRONT_CENTER |
411 SPEAKER_LOW_FREQUENCY |
412 SPEAKER_BACK_CENTER |
413 SPEAKER_SIDE_LEFT |
414 SPEAKER_SIDE_RIGHT;
415 break;
416 case DevFmtX71:
417 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
418 SPEAKER_FRONT_RIGHT |
419 SPEAKER_FRONT_CENTER |
420 SPEAKER_LOW_FREQUENCY |
421 SPEAKER_BACK_LEFT |
422 SPEAKER_BACK_RIGHT |
423 SPEAKER_SIDE_LEFT |
424 SPEAKER_SIDE_RIGHT;
425 break;
426 }
428 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
429 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
430 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
431 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
432 OutputType.Format.nSamplesPerSec = device->Frequency;
433 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
434 OutputType.Format.cbSize = 0;
435 }
437 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
438 {
439 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
440 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
441 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
442 if(device->FmtType == DevFmtFloat)
443 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
444 else
445 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
446 }
447 else
448 {
449 if(SUCCEEDED(hr))
450 {
451 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
452 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
453 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
454 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
455 }
456 if(SUCCEEDED(hr))
457 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
458 }
460 if(SUCCEEDED(hr))
461 {
462 if(device->NumUpdates > MAX_UPDATES)
463 {
464 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
465 MAX_UPDATES-1) / MAX_UPDATES;
466 device->NumUpdates = MAX_UPDATES;
467 }
469 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
470 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
471 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
472 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
473 OutputType.Format.nBlockAlign;
474 DSBDescription.lpwfxFormat=&OutputType.Format;
475 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
476 }
478 if(SUCCEEDED(hr))
479 {
480 hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
481 if(SUCCEEDED(hr))
482 {
483 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
484 ALuint i;
486 for(i = 0;i < device->NumUpdates;++i)
487 {
488 notifies[i].dwOffset = i * device->UpdateSize *
489 OutputType.Format.nBlockAlign;
490 notifies[i].hEventNotify = pData->hNotifyEvent;
491 }
492 if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
493 hr = E_FAIL;
494 }
495 }
497 if(SUCCEEDED(hr))
498 {
499 ResetEvent(pData->hNotifyEvent);
500 SetDefaultWFXChannelOrder(device);
501 pData->thread = StartThread(DSoundProc, device);
502 if(pData->thread == NULL)
503 hr = E_FAIL;
504 }
506 if(FAILED(hr))
507 {
508 if(pData->DSnotify != NULL)
509 IDirectSoundNotify_Release(pData->DSnotify);
510 pData->DSnotify = NULL;
511 if(pData->DSsbuffer != NULL)
512 IDirectSoundBuffer_Release(pData->DSsbuffer);
513 pData->DSsbuffer = NULL;
514 if(pData->DSpbuffer != NULL)
515 IDirectSoundBuffer_Release(pData->DSpbuffer);
516 pData->DSpbuffer = NULL;
517 return ALC_FALSE;
518 }
520 return ALC_TRUE;
521 }
523 static void DSoundStopPlayback(ALCdevice *device)
524 {
525 DSoundData *pData = device->ExtraData;
527 if(!pData->thread)
528 return;
530 pData->killNow = 1;
531 StopThread(pData->thread);
532 pData->thread = NULL;
534 pData->killNow = 0;
536 IDirectSoundNotify_Release(pData->DSnotify);
537 pData->DSnotify = NULL;
538 IDirectSoundBuffer_Release(pData->DSsbuffer);
539 pData->DSsbuffer = NULL;
540 if(pData->DSpbuffer != NULL)
541 IDirectSoundBuffer_Release(pData->DSpbuffer);
542 pData->DSpbuffer = NULL;
543 }
546 static const BackendFuncs DSoundFuncs = {
547 DSoundOpenPlayback,
548 DSoundClosePlayback,
549 DSoundResetPlayback,
550 DSoundStopPlayback,
551 NULL,
552 NULL,
553 NULL,
554 NULL,
555 NULL,
556 NULL
557 };
560 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
561 {
562 if(!DSoundLoad())
563 return ALC_FALSE;
564 *FuncList = DSoundFuncs;
565 return ALC_TRUE;
566 }
568 void alcDSoundDeinit(void)
569 {
570 ALuint i;
572 for(i = 0;i < NumDevices;++i)
573 free(DeviceList[i].name);
574 free(DeviceList);
575 DeviceList = NULL;
576 NumDevices = 0;
578 if(ds_handle)
579 FreeLibrary(ds_handle);
580 ds_handle = NULL;
581 }
583 void alcDSoundProbe(enum DevProbe type)
584 {
585 HRESULT hr;
586 ALuint i;
588 switch(type)
589 {
590 case DEVICE_PROBE:
591 AppendDeviceList(dsDevice);
592 break;
594 case ALL_DEVICE_PROBE:
595 for(i = 0;i < NumDevices;++i)
596 free(DeviceList[i].name);
597 free(DeviceList);
598 DeviceList = NULL;
599 NumDevices = 0;
601 hr = DirectSoundEnumerateA(DSoundEnumDevices, NULL);
602 if(FAILED(hr))
603 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
604 else
605 {
606 for(i = 0;i < NumDevices;i++)
607 AppendAllDeviceList(DeviceList[i].name);
608 }
609 break;
611 case CAPTURE_DEVICE_PROBE:
612 break;
613 }
614 }