Always use "OpenAL Soft" for the short device enumeration list
[openal-soft.git] / Alc / backends / dsound.c
blobe237513783940b5386afd64a9cd4b60803ea66a7
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 void *ds_handle;
51 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
52 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
53 static HRESULT (WINAPI *pDirectSoundCaptureCreate)(LPCGUID pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
54 static HRESULT (WINAPI *pDirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
56 #define DirectSoundCreate pDirectSoundCreate
57 #define DirectSoundEnumerateA pDirectSoundEnumerateA
58 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
59 #define DirectSoundCaptureEnumerateA pDirectSoundCaptureEnumerateA
62 typedef struct {
63 // DirectSound Playback Device
64 IDirectSound *lpDS;
65 IDirectSoundBuffer *DSpbuffer;
66 IDirectSoundBuffer *DSsbuffer;
67 IDirectSoundNotify *DSnotify;
68 HANDLE hNotifyEvent;
70 volatile int killNow;
71 ALvoid *thread;
72 } DSoundPlaybackData;
74 typedef struct {
75 // DirectSound Capture Device
76 IDirectSoundCapture *lpDSC;
77 IDirectSoundCaptureBuffer *DSCbuffer;
78 DWORD dwBufferBytes;
79 DWORD dwCursor;
80 RingBuffer *pRing;
81 } DSoundCaptureData;
84 typedef struct {
85 ALCchar *name;
86 GUID guid;
87 } DevMap;
89 static const ALCchar dsDevice[] = "DirectSound Default";
90 static DevMap *PlaybackDeviceList;
91 static ALuint NumPlaybackDevices;
92 static DevMap *CaptureDeviceList;
93 static ALuint NumCaptureDevices;
95 #define MAX_UPDATES 128
97 static ALCboolean DSoundLoad(void)
99 if(!ds_handle)
101 ds_handle = LoadLib("dsound.dll");
102 if(ds_handle == NULL)
104 ERR("Failed to load dsound.dll\n");
105 return ALC_FALSE;
108 #define LOAD_FUNC(f) do { \
109 p##f = GetSymbol(ds_handle, #f); \
110 if(p##f == NULL) { \
111 CloseLib(ds_handle); \
112 ds_handle = NULL; \
113 return ALC_FALSE; \
115 } while(0)
116 LOAD_FUNC(DirectSoundCreate);
117 LOAD_FUNC(DirectSoundEnumerateA);
118 LOAD_FUNC(DirectSoundCaptureCreate);
119 LOAD_FUNC(DirectSoundCaptureEnumerateA);
120 #undef LOAD_FUNC
122 return ALC_TRUE;
126 static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
128 char str[1024];
129 void *temp;
130 int count;
131 ALuint i;
133 (void)data;
134 (void)drvname;
136 if(!guid)
137 return TRUE;
139 count = 0;
140 do {
141 if(count == 0)
142 snprintf(str, sizeof(str), "%s", desc);
143 else
144 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
145 count++;
147 for(i = 0;i < NumPlaybackDevices;i++)
149 if(strcmp(str, PlaybackDeviceList[i].name) == 0)
150 break;
152 } while(i != NumPlaybackDevices);
154 temp = realloc(PlaybackDeviceList, sizeof(DevMap) * (NumPlaybackDevices+1));
155 if(temp)
157 PlaybackDeviceList = temp;
158 PlaybackDeviceList[NumPlaybackDevices].name = strdup(str);
159 PlaybackDeviceList[NumPlaybackDevices].guid = *guid;
160 NumPlaybackDevices++;
163 return TRUE;
167 static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
169 char str[1024];
170 void *temp;
171 int count;
172 ALuint i;
174 (void)data;
175 (void)drvname;
177 if(!guid)
178 return TRUE;
180 count = 0;
181 do {
182 if(count == 0)
183 snprintf(str, sizeof(str), "%s", desc);
184 else
185 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
186 count++;
188 for(i = 0;i < NumCaptureDevices;i++)
190 if(strcmp(str, CaptureDeviceList[i].name) == 0)
191 break;
193 } while(i != NumCaptureDevices);
195 temp = realloc(CaptureDeviceList, sizeof(DevMap) * (NumCaptureDevices+1));
196 if(temp)
198 CaptureDeviceList = temp;
199 CaptureDeviceList[NumCaptureDevices].name = strdup(str);
200 CaptureDeviceList[NumCaptureDevices].guid = *guid;
201 NumCaptureDevices++;
204 return TRUE;
208 static ALuint DSoundPlaybackProc(ALvoid *ptr)
210 ALCdevice *pDevice = (ALCdevice*)ptr;
211 DSoundPlaybackData *pData = (DSoundPlaybackData*)pDevice->ExtraData;
212 DSBCAPS DSBCaps;
213 DWORD LastCursor = 0;
214 DWORD PlayCursor;
215 VOID *WritePtr1, *WritePtr2;
216 DWORD WriteCnt1, WriteCnt2;
217 BOOL Playing = FALSE;
218 DWORD FrameSize;
219 DWORD FragSize;
220 DWORD avail;
221 HRESULT err;
223 SetRTPriority();
225 memset(&DSBCaps, 0, sizeof(DSBCaps));
226 DSBCaps.dwSize = sizeof(DSBCaps);
227 err = IDirectSoundBuffer_GetCaps(pData->DSsbuffer, &DSBCaps);
228 if(FAILED(err))
230 ERR("Failed to get buffer caps: 0x%lx\n", err);
231 aluHandleDisconnect(pDevice);
232 return 1;
235 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
236 FragSize = pDevice->UpdateSize * FrameSize;
238 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
239 while(!pData->killNow)
241 // Get current play cursor
242 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
243 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
245 if(avail < FragSize)
247 if(!Playing)
249 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
250 if(FAILED(err))
252 ERR("Failed to play buffer: 0x%lx\n", err);
253 aluHandleDisconnect(pDevice);
254 return 1;
256 Playing = TRUE;
259 avail = WaitForSingleObjectEx(pData->hNotifyEvent, 2000, FALSE);
260 if(avail != WAIT_OBJECT_0)
261 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
262 continue;
264 avail -= avail%FragSize;
266 // Lock output buffer
267 WriteCnt1 = 0;
268 WriteCnt2 = 0;
269 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
271 // If the buffer is lost, restore it and lock
272 if(err == DSERR_BUFFERLOST)
274 WARN("Buffer lost, restoring...\n");
275 err = IDirectSoundBuffer_Restore(pData->DSsbuffer);
276 if(SUCCEEDED(err))
278 Playing = FALSE;
279 LastCursor = 0;
280 err = IDirectSoundBuffer_Lock(pData->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
284 // Successfully locked the output buffer
285 if(SUCCEEDED(err))
287 // If we have an active context, mix data directly into output buffer otherwise fill with silence
288 aluMixData(pDevice, WritePtr1, WriteCnt1/FrameSize);
289 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
291 // Unlock output buffer only when successfully locked
292 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
294 else
296 ERR("Buffer lock error: %#lx\n", err);
297 aluHandleDisconnect(pDevice);
298 return 1;
301 // Update old write cursor location
302 LastCursor += WriteCnt1+WriteCnt2;
303 LastCursor %= DSBCaps.dwBufferBytes;
306 return 0;
309 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
311 DSoundPlaybackData *pData = NULL;
312 LPGUID guid = NULL;
313 HRESULT hr;
315 if(!deviceName)
316 deviceName = dsDevice;
317 else if(strcmp(deviceName, dsDevice) != 0)
319 ALuint i;
321 if(!PlaybackDeviceList)
323 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
324 if(FAILED(hr))
325 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
328 for(i = 0;i < NumPlaybackDevices;i++)
330 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
332 guid = &PlaybackDeviceList[i].guid;
333 break;
336 if(i == NumPlaybackDevices)
337 return ALC_INVALID_VALUE;
340 //Initialise requested device
341 pData = calloc(1, sizeof(DSoundPlaybackData));
342 if(!pData)
343 return ALC_OUT_OF_MEMORY;
345 hr = DS_OK;
346 pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
347 if(pData->hNotifyEvent == NULL)
348 hr = E_FAIL;
350 //DirectSound Init code
351 if(SUCCEEDED(hr))
352 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
353 if(SUCCEEDED(hr))
354 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
355 if(FAILED(hr))
357 if(pData->lpDS)
358 IDirectSound_Release(pData->lpDS);
359 if(pData->hNotifyEvent)
360 CloseHandle(pData->hNotifyEvent);
361 free(pData);
362 ERR("Device init failed: 0x%08lx\n", hr);
363 return ALC_INVALID_VALUE;
366 device->szDeviceName = strdup(deviceName);
367 device->ExtraData = pData;
368 return ALC_NO_ERROR;
371 static void DSoundClosePlayback(ALCdevice *device)
373 DSoundPlaybackData *pData = device->ExtraData;
375 IDirectSound_Release(pData->lpDS);
376 CloseHandle(pData->hNotifyEvent);
377 free(pData);
378 device->ExtraData = NULL;
381 static ALCboolean DSoundResetPlayback(ALCdevice *device)
383 DSoundPlaybackData *pData = (DSoundPlaybackData*)device->ExtraData;
384 DSBUFFERDESC DSBDescription;
385 WAVEFORMATEXTENSIBLE OutputType;
386 DWORD speakers;
387 HRESULT hr;
389 memset(&OutputType, 0, sizeof(OutputType));
391 switch(device->FmtType)
393 case DevFmtByte:
394 device->FmtType = DevFmtUByte;
395 break;
396 case DevFmtUShort:
397 device->FmtType = DevFmtShort;
398 break;
399 case DevFmtUInt:
400 device->FmtType = DevFmtInt;
401 break;
402 case DevFmtUByte:
403 case DevFmtShort:
404 case DevFmtInt:
405 case DevFmtFloat:
406 break;
409 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
410 if(SUCCEEDED(hr))
412 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
414 speakers = DSSPEAKER_CONFIG(speakers);
415 if(speakers == DSSPEAKER_MONO)
416 device->FmtChans = DevFmtMono;
417 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
418 device->FmtChans = DevFmtStereo;
419 else if(speakers == DSSPEAKER_QUAD)
420 device->FmtChans = DevFmtQuad;
421 else if(speakers == DSSPEAKER_5POINT1)
422 device->FmtChans = DevFmtX51;
423 else if(speakers == DSSPEAKER_7POINT1)
424 device->FmtChans = DevFmtX71;
425 else
426 ERR("Unknown system speaker config: 0x%lx\n", speakers);
429 switch(device->FmtChans)
431 case DevFmtMono:
432 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
433 break;
434 case DevFmtStereo:
435 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
436 SPEAKER_FRONT_RIGHT;
437 break;
438 case DevFmtQuad:
439 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
440 SPEAKER_FRONT_RIGHT |
441 SPEAKER_BACK_LEFT |
442 SPEAKER_BACK_RIGHT;
443 break;
444 case DevFmtX51:
445 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
446 SPEAKER_FRONT_RIGHT |
447 SPEAKER_FRONT_CENTER |
448 SPEAKER_LOW_FREQUENCY |
449 SPEAKER_BACK_LEFT |
450 SPEAKER_BACK_RIGHT;
451 break;
452 case DevFmtX51Side:
453 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
454 SPEAKER_FRONT_RIGHT |
455 SPEAKER_FRONT_CENTER |
456 SPEAKER_LOW_FREQUENCY |
457 SPEAKER_SIDE_LEFT |
458 SPEAKER_SIDE_RIGHT;
459 break;
460 case DevFmtX61:
461 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
462 SPEAKER_FRONT_RIGHT |
463 SPEAKER_FRONT_CENTER |
464 SPEAKER_LOW_FREQUENCY |
465 SPEAKER_BACK_CENTER |
466 SPEAKER_SIDE_LEFT |
467 SPEAKER_SIDE_RIGHT;
468 break;
469 case DevFmtX71:
470 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
471 SPEAKER_FRONT_RIGHT |
472 SPEAKER_FRONT_CENTER |
473 SPEAKER_LOW_FREQUENCY |
474 SPEAKER_BACK_LEFT |
475 SPEAKER_BACK_RIGHT |
476 SPEAKER_SIDE_LEFT |
477 SPEAKER_SIDE_RIGHT;
478 break;
481 retry_open:
482 hr = S_OK;
483 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
484 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
485 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
486 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
487 OutputType.Format.nSamplesPerSec = device->Frequency;
488 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
489 OutputType.Format.cbSize = 0;
492 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
494 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
495 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
496 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
497 if(device->FmtType == DevFmtFloat)
498 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
499 else
500 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
502 if(pData->DSpbuffer)
503 IDirectSoundBuffer_Release(pData->DSpbuffer);
504 pData->DSpbuffer = NULL;
506 else
508 if(SUCCEEDED(hr))
510 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
511 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
512 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
513 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
515 if(SUCCEEDED(hr))
516 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
519 if(SUCCEEDED(hr))
521 if(device->NumUpdates > MAX_UPDATES)
523 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
524 MAX_UPDATES-1) / MAX_UPDATES;
525 device->NumUpdates = MAX_UPDATES;
528 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
529 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
530 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
531 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
532 OutputType.Format.nBlockAlign;
533 DSBDescription.lpwfxFormat=&OutputType.Format;
534 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
535 if(FAILED(hr) && device->FmtType == DevFmtFloat)
537 device->FmtType = DevFmtShort;
538 goto retry_open;
542 if(SUCCEEDED(hr))
544 hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
545 if(SUCCEEDED(hr))
547 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
548 ALuint i;
550 for(i = 0;i < device->NumUpdates;++i)
552 notifies[i].dwOffset = i * device->UpdateSize *
553 OutputType.Format.nBlockAlign;
554 notifies[i].hEventNotify = pData->hNotifyEvent;
556 if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
557 hr = E_FAIL;
561 if(SUCCEEDED(hr))
563 ResetEvent(pData->hNotifyEvent);
564 SetDefaultWFXChannelOrder(device);
565 pData->thread = StartThread(DSoundPlaybackProc, device);
566 if(pData->thread == NULL)
567 hr = E_FAIL;
570 if(FAILED(hr))
572 if(pData->DSnotify != NULL)
573 IDirectSoundNotify_Release(pData->DSnotify);
574 pData->DSnotify = NULL;
575 if(pData->DSsbuffer != NULL)
576 IDirectSoundBuffer_Release(pData->DSsbuffer);
577 pData->DSsbuffer = NULL;
578 if(pData->DSpbuffer != NULL)
579 IDirectSoundBuffer_Release(pData->DSpbuffer);
580 pData->DSpbuffer = NULL;
581 return ALC_FALSE;
584 return ALC_TRUE;
587 static void DSoundStopPlayback(ALCdevice *device)
589 DSoundPlaybackData *pData = device->ExtraData;
591 if(!pData->thread)
592 return;
594 pData->killNow = 1;
595 StopThread(pData->thread);
596 pData->thread = NULL;
598 pData->killNow = 0;
600 IDirectSoundNotify_Release(pData->DSnotify);
601 pData->DSnotify = NULL;
602 IDirectSoundBuffer_Release(pData->DSsbuffer);
603 pData->DSsbuffer = NULL;
604 if(pData->DSpbuffer != NULL)
605 IDirectSoundBuffer_Release(pData->DSpbuffer);
606 pData->DSpbuffer = NULL;
610 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
612 DSoundCaptureData *pData = NULL;
613 WAVEFORMATEXTENSIBLE InputType;
614 DSCBUFFERDESC DSCBDescription;
615 LPGUID guid = NULL;
616 HRESULT hr, hrcom;
617 ALuint samples;
619 if(!CaptureDeviceList)
621 /* Initialize COM to prevent name truncation */
622 hrcom = CoInitialize(NULL);
623 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
624 if(FAILED(hr))
625 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
626 if(SUCCEEDED(hrcom))
627 CoUninitialize();
630 if(!deviceName && NumCaptureDevices > 0)
632 deviceName = CaptureDeviceList[0].name;
633 guid = &CaptureDeviceList[0].guid;
635 else
637 ALuint i;
639 for(i = 0;i < NumCaptureDevices;i++)
641 if(strcmp(deviceName, CaptureDeviceList[i].name) == 0)
643 guid = &CaptureDeviceList[i].guid;
644 break;
647 if(i == NumCaptureDevices)
648 return ALC_INVALID_VALUE;
651 //Initialise requested device
652 pData = calloc(1, sizeof(DSoundCaptureData));
653 if(!pData)
654 return ALC_OUT_OF_MEMORY;
656 hr = DS_OK;
658 //DirectSoundCapture Init code
659 if(SUCCEEDED(hr))
660 hr = DirectSoundCaptureCreate(guid, &pData->lpDSC, NULL);
661 if(SUCCEEDED(hr))
663 memset(&InputType, 0, sizeof(InputType));
665 switch(device->FmtChans)
667 case DevFmtMono:
668 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
669 break;
670 case DevFmtStereo:
671 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
672 SPEAKER_FRONT_RIGHT;
673 break;
674 case DevFmtQuad:
675 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
676 SPEAKER_FRONT_RIGHT |
677 SPEAKER_BACK_LEFT |
678 SPEAKER_BACK_RIGHT;
679 break;
680 case DevFmtX51:
681 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
682 SPEAKER_FRONT_RIGHT |
683 SPEAKER_FRONT_CENTER |
684 SPEAKER_LOW_FREQUENCY |
685 SPEAKER_BACK_LEFT |
686 SPEAKER_BACK_RIGHT;
687 break;
688 case DevFmtX51Side:
689 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
690 SPEAKER_FRONT_RIGHT |
691 SPEAKER_FRONT_CENTER |
692 SPEAKER_LOW_FREQUENCY |
693 SPEAKER_SIDE_LEFT |
694 SPEAKER_SIDE_RIGHT;
695 break;
696 case DevFmtX61:
697 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
698 SPEAKER_FRONT_RIGHT |
699 SPEAKER_FRONT_CENTER |
700 SPEAKER_LOW_FREQUENCY |
701 SPEAKER_BACK_CENTER |
702 SPEAKER_SIDE_LEFT |
703 SPEAKER_SIDE_RIGHT;
704 break;
705 case DevFmtX71:
706 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
707 SPEAKER_FRONT_RIGHT |
708 SPEAKER_FRONT_CENTER |
709 SPEAKER_LOW_FREQUENCY |
710 SPEAKER_BACK_LEFT |
711 SPEAKER_BACK_RIGHT |
712 SPEAKER_SIDE_LEFT |
713 SPEAKER_SIDE_RIGHT;
714 break;
717 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
718 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
719 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
720 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
721 InputType.Format.nSamplesPerSec = device->Frequency;
722 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
723 InputType.Format.cbSize = 0;
725 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
727 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
728 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
729 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
730 if(device->FmtType == DevFmtFloat)
731 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
732 else
733 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
736 samples = device->UpdateSize * device->NumUpdates;
737 samples = maxu(samples, 100 * device->Frequency / 1000);
739 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
740 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
741 DSCBDescription.dwFlags = 0;
742 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
743 DSCBDescription.lpwfxFormat = &InputType.Format;
745 hr = IDirectSoundCapture_CreateCaptureBuffer(pData->lpDSC, &DSCBDescription, &pData->DSCbuffer, NULL);
747 if(SUCCEEDED(hr))
749 pData->pRing = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
750 if(pData->pRing == NULL)
751 hr = DSERR_OUTOFMEMORY;
754 if(FAILED(hr))
756 ERR("Device init failed: 0x%08lx\n", hr);
758 DestroyRingBuffer(pData->pRing);
759 pData->pRing = NULL;
760 if(pData->DSCbuffer != NULL)
761 IDirectSoundCaptureBuffer_Release(pData->DSCbuffer);
762 pData->DSCbuffer = NULL;
763 if(pData->lpDSC)
764 IDirectSoundCapture_Release(pData->lpDSC);
765 pData->lpDSC = NULL;
767 free(pData);
768 return ALC_INVALID_VALUE;
771 pData->dwBufferBytes = DSCBDescription.dwBufferBytes;
772 SetDefaultWFXChannelOrder(device);
774 device->szDeviceName = strdup(deviceName);
775 device->ExtraData = pData;
777 return ALC_NO_ERROR;
780 static void DSoundCloseCapture(ALCdevice *device)
782 DSoundCaptureData *pData = device->ExtraData;
784 DestroyRingBuffer(pData->pRing);
785 pData->pRing = NULL;
787 if(pData->DSCbuffer != NULL)
789 IDirectSoundCaptureBuffer_Stop(pData->DSCbuffer);
790 IDirectSoundCaptureBuffer_Release(pData->DSCbuffer);
791 pData->DSCbuffer = NULL;
794 IDirectSoundCapture_Release(pData->lpDSC);
795 pData->lpDSC = NULL;
797 free(pData);
798 device->ExtraData = NULL;
801 static void DSoundStartCapture(ALCdevice *device)
803 DSoundCaptureData *pData = device->ExtraData;
804 HRESULT hr;
806 hr = IDirectSoundCaptureBuffer_Start(pData->DSCbuffer, DSCBSTART_LOOPING);
807 if(FAILED(hr))
809 ERR("start failed: 0x%08lx\n", hr);
810 aluHandleDisconnect(device);
814 static void DSoundStopCapture(ALCdevice *device)
816 DSoundCaptureData *pData = device->ExtraData;
817 HRESULT hr;
819 hr = IDirectSoundCaptureBuffer_Stop(pData->DSCbuffer);
820 if(FAILED(hr))
822 ERR("stop failed: 0x%08lx\n", hr);
823 aluHandleDisconnect(device);
827 static ALCenum DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
829 DSoundCaptureData *pData = pDevice->ExtraData;
830 ReadRingBuffer(pData->pRing, pBuffer, lSamples);
831 return ALC_NO_ERROR;
834 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
836 DSoundCaptureData *pData = pDevice->ExtraData;
837 DWORD dwRead, dwCursor, dwBufferBytes, dwNumBytes;
838 void *pvAudio1, *pvAudio2;
839 DWORD dwAudioBytes1, dwAudioBytes2;
840 DWORD FrameSize;
841 HRESULT hr;
843 if(!pDevice->Connected)
844 goto done;
846 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
847 dwBufferBytes = pData->dwBufferBytes;
848 dwCursor = pData->dwCursor;
850 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pData->DSCbuffer, NULL, &dwRead);
851 if(SUCCEEDED(hr))
853 dwNumBytes = (dwBufferBytes + dwRead - dwCursor) % dwBufferBytes;
854 if(dwNumBytes == 0)
855 goto done;
856 hr = IDirectSoundCaptureBuffer_Lock(pData->DSCbuffer,
857 dwCursor, dwNumBytes,
858 &pvAudio1, &dwAudioBytes1,
859 &pvAudio2, &dwAudioBytes2, 0);
861 if(SUCCEEDED(hr))
863 WriteRingBuffer(pData->pRing, pvAudio1, dwAudioBytes1/FrameSize);
864 if(pvAudio2 != NULL)
865 WriteRingBuffer(pData->pRing, pvAudio2, dwAudioBytes2/FrameSize);
866 hr = IDirectSoundCaptureBuffer_Unlock(pData->DSCbuffer,
867 pvAudio1, dwAudioBytes1,
868 pvAudio2, dwAudioBytes2);
869 pData->dwCursor = (dwCursor + dwAudioBytes1 + dwAudioBytes2) % dwBufferBytes;
872 if(FAILED(hr))
874 ERR("update failed: 0x%08lx\n", hr);
875 aluHandleDisconnect(pDevice);
878 done:
879 return RingBufferSize(pData->pRing);
882 static const BackendFuncs DSoundFuncs = {
883 DSoundOpenPlayback,
884 DSoundClosePlayback,
885 DSoundResetPlayback,
886 DSoundStopPlayback,
887 DSoundOpenCapture,
888 DSoundCloseCapture,
889 DSoundStartCapture,
890 DSoundStopCapture,
891 DSoundCaptureSamples,
892 DSoundAvailableSamples
896 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
898 if(!DSoundLoad())
899 return ALC_FALSE;
900 *FuncList = DSoundFuncs;
901 return ALC_TRUE;
904 void alcDSoundDeinit(void)
906 ALuint i;
908 for(i = 0;i < NumPlaybackDevices;++i)
909 free(PlaybackDeviceList[i].name);
910 free(PlaybackDeviceList);
911 PlaybackDeviceList = NULL;
912 NumPlaybackDevices = 0;
914 for(i = 0;i < NumCaptureDevices;++i)
915 free(CaptureDeviceList[i].name);
916 free(CaptureDeviceList);
917 CaptureDeviceList = NULL;
918 NumCaptureDevices = 0;
920 if(ds_handle)
921 CloseLib(ds_handle);
922 ds_handle = NULL;
925 void alcDSoundProbe(enum DevProbe type)
927 HRESULT hr, hrcom;
928 ALuint i;
930 switch(type)
932 case ALL_DEVICE_PROBE:
933 for(i = 0;i < NumPlaybackDevices;++i)
934 free(PlaybackDeviceList[i].name);
935 free(PlaybackDeviceList);
936 PlaybackDeviceList = NULL;
937 NumPlaybackDevices = 0;
939 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
940 if(FAILED(hr))
941 ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr);
942 else
944 for(i = 0;i < NumPlaybackDevices;i++)
945 AppendAllDeviceList(PlaybackDeviceList[i].name);
947 break;
949 case CAPTURE_DEVICE_PROBE:
950 for(i = 0;i < NumCaptureDevices;++i)
951 free(CaptureDeviceList[i].name);
952 free(CaptureDeviceList);
953 CaptureDeviceList = NULL;
954 NumCaptureDevices = 0;
956 /* Initialize COM to prevent name truncation */
957 hrcom = CoInitialize(NULL);
958 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
959 if(FAILED(hr))
960 ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr);
961 else
963 for(i = 0;i < NumCaptureDevices;i++)
964 AppendCaptureDeviceList(CaptureDeviceList[i].name);
966 if(SUCCEEDED(hrcom))
967 CoUninitialize();
968 break;