Add an example program showing how to apply reverb to a source
[openal-soft.git] / Alc / backends / dsound.c
blobba144ae307c1566d7bc2091fb87dcfd80abe6d7e
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 "alu.h"
38 #ifndef DSSPEAKER_5POINT1
39 #define DSSPEAKER_5POINT1 6
40 #endif
41 #ifndef DSSPEAKER_7POINT1
42 #define DSSPEAKER_7POINT1 7
43 #endif
45 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
49 static void *ds_handle;
50 static HRESULT (WINAPI *pDirectSoundCreate)(LPCGUID pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter);
51 static HRESULT (WINAPI *pDirectSoundEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
52 static HRESULT (WINAPI *pDirectSoundCaptureCreate)(LPCGUID pcGuidDevice, IDirectSoundCapture **ppDSC, IUnknown *pUnkOuter);
53 static HRESULT (WINAPI *pDirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA pDSEnumCallback, void *pContext);
55 #define DirectSoundCreate pDirectSoundCreate
56 #define DirectSoundEnumerateA pDirectSoundEnumerateA
57 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
58 #define DirectSoundCaptureEnumerateA pDirectSoundCaptureEnumerateA
61 typedef struct {
62 // DirectSound Playback Device
63 IDirectSound *DS;
64 IDirectSoundBuffer *DSpbuffer;
65 IDirectSoundBuffer *DSsbuffer;
66 IDirectSoundNotify *DSnotify;
67 HANDLE NotifyEvent;
69 volatile int killNow;
70 ALvoid *thread;
71 } DSoundPlaybackData;
73 typedef struct {
74 // DirectSound Capture Device
75 IDirectSoundCapture *DSC;
76 IDirectSoundCaptureBuffer *DSCbuffer;
77 DWORD BufferBytes;
78 DWORD Cursor;
79 RingBuffer *Ring;
80 } DSoundCaptureData;
83 typedef struct {
84 ALCchar *name;
85 GUID guid;
86 } DevMap;
88 static DevMap *PlaybackDeviceList;
89 static ALuint NumPlaybackDevices;
90 static DevMap *CaptureDeviceList;
91 static ALuint NumCaptureDevices;
93 #define MAX_UPDATES 128
95 static ALCboolean DSoundLoad(void)
97 if(!ds_handle)
99 ds_handle = LoadLib("dsound.dll");
100 if(ds_handle == NULL)
102 ERR("Failed to load dsound.dll\n");
103 return ALC_FALSE;
106 #define LOAD_FUNC(f) do { \
107 p##f = GetSymbol(ds_handle, #f); \
108 if(p##f == NULL) { \
109 CloseLib(ds_handle); \
110 ds_handle = NULL; \
111 return ALC_FALSE; \
113 } while(0)
114 LOAD_FUNC(DirectSoundCreate);
115 LOAD_FUNC(DirectSoundEnumerateA);
116 LOAD_FUNC(DirectSoundCaptureCreate);
117 LOAD_FUNC(DirectSoundCaptureEnumerateA);
118 #undef LOAD_FUNC
120 return ALC_TRUE;
124 static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
126 LPOLESTR guidstr = NULL;
127 char str[1024];
128 HRESULT hr;
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 hr = StringFromCLSID(guid, &guidstr);
155 if(SUCCEEDED(hr))
157 TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr);
158 CoTaskMemFree(guidstr);
161 temp = realloc(PlaybackDeviceList, sizeof(DevMap) * (NumPlaybackDevices+1));
162 if(temp)
164 PlaybackDeviceList = temp;
165 PlaybackDeviceList[NumPlaybackDevices].name = strdup(str);
166 PlaybackDeviceList[NumPlaybackDevices].guid = *guid;
167 NumPlaybackDevices++;
170 return TRUE;
174 static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
176 LPOLESTR guidstr = NULL;
177 char str[1024];
178 HRESULT hr;
179 void *temp;
180 int count;
181 ALuint i;
183 (void)data;
184 (void)drvname;
186 if(!guid)
187 return TRUE;
189 count = 0;
190 do {
191 if(count == 0)
192 snprintf(str, sizeof(str), "%s", desc);
193 else
194 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
195 count++;
197 for(i = 0;i < NumCaptureDevices;i++)
199 if(strcmp(str, CaptureDeviceList[i].name) == 0)
200 break;
202 } while(i != NumCaptureDevices);
204 hr = StringFromCLSID(guid, &guidstr);
205 if(SUCCEEDED(hr))
207 TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr);
208 CoTaskMemFree(guidstr);
211 temp = realloc(CaptureDeviceList, sizeof(DevMap) * (NumCaptureDevices+1));
212 if(temp)
214 CaptureDeviceList = temp;
215 CaptureDeviceList[NumCaptureDevices].name = strdup(str);
216 CaptureDeviceList[NumCaptureDevices].guid = *guid;
217 NumCaptureDevices++;
220 return TRUE;
224 static ALuint DSoundPlaybackProc(ALvoid *ptr)
226 ALCdevice *Device = (ALCdevice*)ptr;
227 DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData;
228 DSBCAPS DSBCaps;
229 DWORD LastCursor = 0;
230 DWORD PlayCursor;
231 VOID *WritePtr1, *WritePtr2;
232 DWORD WriteCnt1, WriteCnt2;
233 BOOL Playing = FALSE;
234 DWORD FrameSize;
235 DWORD FragSize;
236 DWORD avail;
237 HRESULT err;
239 SetRTPriority();
241 memset(&DSBCaps, 0, sizeof(DSBCaps));
242 DSBCaps.dwSize = sizeof(DSBCaps);
243 err = IDirectSoundBuffer_GetCaps(data->DSsbuffer, &DSBCaps);
244 if(FAILED(err))
246 ERR("Failed to get buffer caps: 0x%lx\n", err);
247 aluHandleDisconnect(Device);
248 return 1;
251 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
252 FragSize = Device->UpdateSize * FrameSize;
254 IDirectSoundBuffer_GetCurrentPosition(data->DSsbuffer, &LastCursor, NULL);
255 while(!data->killNow)
257 // Get current play cursor
258 IDirectSoundBuffer_GetCurrentPosition(data->DSsbuffer, &PlayCursor, NULL);
259 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
261 if(avail < FragSize)
263 if(!Playing)
265 err = IDirectSoundBuffer_Play(data->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
266 if(FAILED(err))
268 ERR("Failed to play buffer: 0x%lx\n", err);
269 aluHandleDisconnect(Device);
270 return 1;
272 Playing = TRUE;
275 avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
276 if(avail != WAIT_OBJECT_0)
277 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
278 continue;
280 avail -= avail%FragSize;
282 // Lock output buffer
283 WriteCnt1 = 0;
284 WriteCnt2 = 0;
285 err = IDirectSoundBuffer_Lock(data->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
287 // If the buffer is lost, restore it and lock
288 if(err == DSERR_BUFFERLOST)
290 WARN("Buffer lost, restoring...\n");
291 err = IDirectSoundBuffer_Restore(data->DSsbuffer);
292 if(SUCCEEDED(err))
294 Playing = FALSE;
295 LastCursor = 0;
296 err = IDirectSoundBuffer_Lock(data->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
300 // Successfully locked the output buffer
301 if(SUCCEEDED(err))
303 // If we have an active context, mix data directly into output buffer otherwise fill with silence
304 aluMixData(Device, WritePtr1, WriteCnt1/FrameSize);
305 aluMixData(Device, WritePtr2, WriteCnt2/FrameSize);
307 // Unlock output buffer only when successfully locked
308 IDirectSoundBuffer_Unlock(data->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
310 else
312 ERR("Buffer lock error: %#lx\n", err);
313 aluHandleDisconnect(Device);
314 return 1;
317 // Update old write cursor location
318 LastCursor += WriteCnt1+WriteCnt2;
319 LastCursor %= DSBCaps.dwBufferBytes;
322 return 0;
325 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
327 DSoundPlaybackData *data = NULL;
328 LPGUID guid = NULL;
329 HRESULT hr;
331 if(!PlaybackDeviceList)
333 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
334 if(FAILED(hr))
335 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
338 if(!deviceName && NumPlaybackDevices > 0)
340 deviceName = PlaybackDeviceList[0].name;
341 guid = &PlaybackDeviceList[0].guid;
343 else
345 ALuint i;
347 for(i = 0;i < NumPlaybackDevices;i++)
349 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
351 guid = &PlaybackDeviceList[i].guid;
352 break;
355 if(i == NumPlaybackDevices)
356 return ALC_INVALID_VALUE;
359 //Initialise requested device
360 data = calloc(1, sizeof(DSoundPlaybackData));
361 if(!data)
362 return ALC_OUT_OF_MEMORY;
364 hr = DS_OK;
365 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
366 if(data->NotifyEvent == NULL)
367 hr = E_FAIL;
369 //DirectSound Init code
370 if(SUCCEEDED(hr))
371 hr = DirectSoundCreate(guid, &data->DS, NULL);
372 if(SUCCEEDED(hr))
373 hr = IDirectSound_SetCooperativeLevel(data->DS, GetForegroundWindow(), DSSCL_PRIORITY);
374 if(FAILED(hr))
376 if(data->DS)
377 IDirectSound_Release(data->DS);
378 if(data->NotifyEvent)
379 CloseHandle(data->NotifyEvent);
380 free(data);
381 ERR("Device init failed: 0x%08lx\n", hr);
382 return ALC_INVALID_VALUE;
385 device->DeviceName = strdup(deviceName);
386 device->ExtraData = data;
387 return ALC_NO_ERROR;
390 static void DSoundClosePlayback(ALCdevice *device)
392 DSoundPlaybackData *data = device->ExtraData;
394 if(data->DSnotify)
395 IDirectSoundNotify_Release(data->DSnotify);
396 data->DSnotify = NULL;
397 if(data->DSsbuffer)
398 IDirectSoundBuffer_Release(data->DSsbuffer);
399 data->DSsbuffer = NULL;
400 if(data->DSpbuffer != NULL)
401 IDirectSoundBuffer_Release(data->DSpbuffer);
402 data->DSpbuffer = NULL;
404 IDirectSound_Release(data->DS);
405 CloseHandle(data->NotifyEvent);
406 free(data);
407 device->ExtraData = NULL;
410 static ALCboolean DSoundResetPlayback(ALCdevice *device)
412 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
413 DSBUFFERDESC DSBDescription;
414 WAVEFORMATEXTENSIBLE OutputType;
415 DWORD speakers;
416 HRESULT hr;
418 memset(&OutputType, 0, sizeof(OutputType));
420 if(data->DSnotify)
421 IDirectSoundNotify_Release(data->DSnotify);
422 data->DSnotify = NULL;
423 if(data->DSsbuffer)
424 IDirectSoundBuffer_Release(data->DSsbuffer);
425 data->DSsbuffer = NULL;
426 if(data->DSpbuffer != NULL)
427 IDirectSoundBuffer_Release(data->DSpbuffer);
428 data->DSpbuffer = NULL;
430 switch(device->FmtType)
432 case DevFmtByte:
433 device->FmtType = DevFmtUByte;
434 break;
435 case DevFmtFloat:
436 if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
437 break;
438 /* fall-through */
439 case DevFmtUShort:
440 device->FmtType = DevFmtShort;
441 break;
442 case DevFmtUInt:
443 device->FmtType = DevFmtInt;
444 break;
445 case DevFmtUByte:
446 case DevFmtShort:
447 case DevFmtInt:
448 break;
451 hr = IDirectSound_GetSpeakerConfig(data->DS, &speakers);
452 if(SUCCEEDED(hr))
454 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
456 speakers = DSSPEAKER_CONFIG(speakers);
457 if(speakers == DSSPEAKER_MONO)
458 device->FmtChans = DevFmtMono;
459 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
460 device->FmtChans = DevFmtStereo;
461 else if(speakers == DSSPEAKER_QUAD)
462 device->FmtChans = DevFmtQuad;
463 else if(speakers == DSSPEAKER_5POINT1)
464 device->FmtChans = DevFmtX51;
465 else if(speakers == DSSPEAKER_7POINT1)
466 device->FmtChans = DevFmtX71;
467 else
468 ERR("Unknown system speaker config: 0x%lx\n", speakers);
471 switch(device->FmtChans)
473 case DevFmtMono:
474 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
475 break;
476 case DevFmtStereo:
477 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
478 SPEAKER_FRONT_RIGHT;
479 break;
480 case DevFmtQuad:
481 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
482 SPEAKER_FRONT_RIGHT |
483 SPEAKER_BACK_LEFT |
484 SPEAKER_BACK_RIGHT;
485 break;
486 case DevFmtX51:
487 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
488 SPEAKER_FRONT_RIGHT |
489 SPEAKER_FRONT_CENTER |
490 SPEAKER_LOW_FREQUENCY |
491 SPEAKER_BACK_LEFT |
492 SPEAKER_BACK_RIGHT;
493 break;
494 case DevFmtX51Side:
495 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
496 SPEAKER_FRONT_RIGHT |
497 SPEAKER_FRONT_CENTER |
498 SPEAKER_LOW_FREQUENCY |
499 SPEAKER_SIDE_LEFT |
500 SPEAKER_SIDE_RIGHT;
501 break;
502 case DevFmtX61:
503 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
504 SPEAKER_FRONT_RIGHT |
505 SPEAKER_FRONT_CENTER |
506 SPEAKER_LOW_FREQUENCY |
507 SPEAKER_BACK_CENTER |
508 SPEAKER_SIDE_LEFT |
509 SPEAKER_SIDE_RIGHT;
510 break;
511 case DevFmtX71:
512 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
513 SPEAKER_FRONT_RIGHT |
514 SPEAKER_FRONT_CENTER |
515 SPEAKER_LOW_FREQUENCY |
516 SPEAKER_BACK_LEFT |
517 SPEAKER_BACK_RIGHT |
518 SPEAKER_SIDE_LEFT |
519 SPEAKER_SIDE_RIGHT;
520 break;
523 retry_open:
524 hr = S_OK;
525 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
526 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
527 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
528 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
529 OutputType.Format.nSamplesPerSec = device->Frequency;
530 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
531 OutputType.Format.cbSize = 0;
534 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
536 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
537 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
538 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
539 if(device->FmtType == DevFmtFloat)
540 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
541 else
542 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
544 if(data->DSpbuffer)
545 IDirectSoundBuffer_Release(data->DSpbuffer);
546 data->DSpbuffer = NULL;
548 else
550 if(SUCCEEDED(hr))
552 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
553 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
554 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
555 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->DSpbuffer, NULL);
557 if(SUCCEEDED(hr))
558 hr = IDirectSoundBuffer_SetFormat(data->DSpbuffer,&OutputType.Format);
561 if(SUCCEEDED(hr))
563 if(device->NumUpdates > MAX_UPDATES)
565 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
566 MAX_UPDATES-1) / MAX_UPDATES;
567 device->NumUpdates = MAX_UPDATES;
570 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
571 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
572 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
573 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
574 OutputType.Format.nBlockAlign;
575 DSBDescription.lpwfxFormat=&OutputType.Format;
576 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->DSsbuffer, NULL);
577 if(FAILED(hr) && device->FmtType == DevFmtFloat)
579 device->FmtType = DevFmtShort;
580 goto retry_open;
584 if(SUCCEEDED(hr))
586 hr = IDirectSoundBuffer_QueryInterface(data->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&data->DSnotify);
587 if(SUCCEEDED(hr))
589 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
590 ALuint i;
592 for(i = 0;i < device->NumUpdates;++i)
594 notifies[i].dwOffset = i * device->UpdateSize *
595 OutputType.Format.nBlockAlign;
596 notifies[i].hEventNotify = data->NotifyEvent;
598 if(IDirectSoundNotify_SetNotificationPositions(data->DSnotify, device->NumUpdates, notifies) != DS_OK)
599 hr = E_FAIL;
603 if(FAILED(hr))
605 if(data->DSnotify != NULL)
606 IDirectSoundNotify_Release(data->DSnotify);
607 data->DSnotify = NULL;
608 if(data->DSsbuffer != NULL)
609 IDirectSoundBuffer_Release(data->DSsbuffer);
610 data->DSsbuffer = NULL;
611 if(data->DSpbuffer != NULL)
612 IDirectSoundBuffer_Release(data->DSpbuffer);
613 data->DSpbuffer = NULL;
614 return ALC_FALSE;
617 ResetEvent(data->NotifyEvent);
618 SetDefaultWFXChannelOrder(device);
620 return ALC_TRUE;
623 static ALCboolean DSoundStartPlayback(ALCdevice *device)
625 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
627 data->thread = StartThread(DSoundPlaybackProc, device);
628 if(data->thread == NULL)
629 return ALC_FALSE;
631 return ALC_TRUE;
634 static void DSoundStopPlayback(ALCdevice *device)
636 DSoundPlaybackData *data = device->ExtraData;
638 if(!data->thread)
639 return;
641 data->killNow = 1;
642 StopThread(data->thread);
643 data->thread = NULL;
645 data->killNow = 0;
646 IDirectSoundBuffer_Stop(data->DSsbuffer);
650 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
652 DSoundCaptureData *data = NULL;
653 WAVEFORMATEXTENSIBLE InputType;
654 DSCBUFFERDESC DSCBDescription;
655 LPGUID guid = NULL;
656 HRESULT hr, hrcom;
657 ALuint samples;
659 if(!CaptureDeviceList)
661 /* Initialize COM to prevent name truncation */
662 hrcom = CoInitialize(NULL);
663 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
664 if(FAILED(hr))
665 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
666 if(SUCCEEDED(hrcom))
667 CoUninitialize();
670 if(!deviceName && NumCaptureDevices > 0)
672 deviceName = CaptureDeviceList[0].name;
673 guid = &CaptureDeviceList[0].guid;
675 else
677 ALuint i;
679 for(i = 0;i < NumCaptureDevices;i++)
681 if(strcmp(deviceName, CaptureDeviceList[i].name) == 0)
683 guid = &CaptureDeviceList[i].guid;
684 break;
687 if(i == NumCaptureDevices)
688 return ALC_INVALID_VALUE;
691 switch(device->FmtType)
693 case DevFmtByte:
694 case DevFmtUShort:
695 case DevFmtUInt:
696 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
697 return ALC_INVALID_ENUM;
699 case DevFmtUByte:
700 case DevFmtShort:
701 case DevFmtInt:
702 case DevFmtFloat:
703 break;
706 //Initialise requested device
707 data = calloc(1, sizeof(DSoundCaptureData));
708 if(!data)
709 return ALC_OUT_OF_MEMORY;
711 hr = DS_OK;
713 //DirectSoundCapture Init code
714 if(SUCCEEDED(hr))
715 hr = DirectSoundCaptureCreate(guid, &data->DSC, NULL);
716 if(SUCCEEDED(hr))
718 memset(&InputType, 0, sizeof(InputType));
720 switch(device->FmtChans)
722 case DevFmtMono:
723 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
724 break;
725 case DevFmtStereo:
726 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
727 SPEAKER_FRONT_RIGHT;
728 break;
729 case DevFmtQuad:
730 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
731 SPEAKER_FRONT_RIGHT |
732 SPEAKER_BACK_LEFT |
733 SPEAKER_BACK_RIGHT;
734 break;
735 case DevFmtX51:
736 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
737 SPEAKER_FRONT_RIGHT |
738 SPEAKER_FRONT_CENTER |
739 SPEAKER_LOW_FREQUENCY |
740 SPEAKER_BACK_LEFT |
741 SPEAKER_BACK_RIGHT;
742 break;
743 case DevFmtX51Side:
744 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
745 SPEAKER_FRONT_RIGHT |
746 SPEAKER_FRONT_CENTER |
747 SPEAKER_LOW_FREQUENCY |
748 SPEAKER_SIDE_LEFT |
749 SPEAKER_SIDE_RIGHT;
750 break;
751 case DevFmtX61:
752 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
753 SPEAKER_FRONT_RIGHT |
754 SPEAKER_FRONT_CENTER |
755 SPEAKER_LOW_FREQUENCY |
756 SPEAKER_BACK_CENTER |
757 SPEAKER_SIDE_LEFT |
758 SPEAKER_SIDE_RIGHT;
759 break;
760 case DevFmtX71:
761 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
762 SPEAKER_FRONT_RIGHT |
763 SPEAKER_FRONT_CENTER |
764 SPEAKER_LOW_FREQUENCY |
765 SPEAKER_BACK_LEFT |
766 SPEAKER_BACK_RIGHT |
767 SPEAKER_SIDE_LEFT |
768 SPEAKER_SIDE_RIGHT;
769 break;
772 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
773 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
774 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
775 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
776 InputType.Format.nSamplesPerSec = device->Frequency;
777 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
778 InputType.Format.cbSize = 0;
780 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
782 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
783 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
784 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
785 if(device->FmtType == DevFmtFloat)
786 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
787 else
788 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
791 samples = device->UpdateSize * device->NumUpdates;
792 samples = maxu(samples, 100 * device->Frequency / 1000);
794 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
795 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
796 DSCBDescription.dwFlags = 0;
797 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
798 DSCBDescription.lpwfxFormat = &InputType.Format;
800 hr = IDirectSoundCapture_CreateCaptureBuffer(data->DSC, &DSCBDescription, &data->DSCbuffer, NULL);
802 if(SUCCEEDED(hr))
804 data->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
805 if(data->Ring == NULL)
806 hr = DSERR_OUTOFMEMORY;
809 if(FAILED(hr))
811 ERR("Device init failed: 0x%08lx\n", hr);
813 DestroyRingBuffer(data->Ring);
814 data->Ring = NULL;
815 if(data->DSCbuffer != NULL)
816 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
817 data->DSCbuffer = NULL;
818 if(data->DSC)
819 IDirectSoundCapture_Release(data->DSC);
820 data->DSC = NULL;
822 free(data);
823 return ALC_INVALID_VALUE;
826 data->BufferBytes = DSCBDescription.dwBufferBytes;
827 SetDefaultWFXChannelOrder(device);
829 device->DeviceName = strdup(deviceName);
830 device->ExtraData = data;
832 return ALC_NO_ERROR;
835 static void DSoundCloseCapture(ALCdevice *device)
837 DSoundCaptureData *data = device->ExtraData;
839 DestroyRingBuffer(data->Ring);
840 data->Ring = NULL;
842 if(data->DSCbuffer != NULL)
844 IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
845 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
846 data->DSCbuffer = NULL;
849 IDirectSoundCapture_Release(data->DSC);
850 data->DSC = NULL;
852 free(data);
853 device->ExtraData = NULL;
856 static void DSoundStartCapture(ALCdevice *device)
858 DSoundCaptureData *data = device->ExtraData;
859 HRESULT hr;
861 hr = IDirectSoundCaptureBuffer_Start(data->DSCbuffer, DSCBSTART_LOOPING);
862 if(FAILED(hr))
864 ERR("start failed: 0x%08lx\n", hr);
865 aluHandleDisconnect(device);
869 static void DSoundStopCapture(ALCdevice *device)
871 DSoundCaptureData *data = device->ExtraData;
872 HRESULT hr;
874 hr = IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
875 if(FAILED(hr))
877 ERR("stop failed: 0x%08lx\n", hr);
878 aluHandleDisconnect(device);
882 static ALCenum DSoundCaptureSamples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
884 DSoundCaptureData *data = Device->ExtraData;
885 ReadRingBuffer(data->Ring, pBuffer, lSamples);
886 return ALC_NO_ERROR;
889 static ALCuint DSoundAvailableSamples(ALCdevice *Device)
891 DSoundCaptureData *data = Device->ExtraData;
892 DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
893 VOID *ReadPtr1, *ReadPtr2;
894 DWORD ReadCnt1, ReadCnt2;
895 DWORD FrameSize;
896 HRESULT hr;
898 if(!Device->Connected)
899 goto done;
901 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
902 BufferBytes = data->BufferBytes;
903 LastCursor = data->Cursor;
905 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(data->DSCbuffer, NULL, &ReadCursor);
906 if(SUCCEEDED(hr))
908 NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
909 if(NumBytes == 0)
910 goto done;
911 hr = IDirectSoundCaptureBuffer_Lock(data->DSCbuffer, LastCursor, NumBytes,
912 &ReadPtr1, &ReadCnt1,
913 &ReadPtr2, &ReadCnt2, 0);
915 if(SUCCEEDED(hr))
917 WriteRingBuffer(data->Ring, ReadPtr1, ReadCnt1/FrameSize);
918 if(ReadPtr2 != NULL)
919 WriteRingBuffer(data->Ring, ReadPtr2, ReadCnt2/FrameSize);
920 hr = IDirectSoundCaptureBuffer_Unlock(data->DSCbuffer,
921 ReadPtr1, ReadCnt1,
922 ReadPtr2, ReadCnt2);
923 data->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
926 if(FAILED(hr))
928 ERR("update failed: 0x%08lx\n", hr);
929 aluHandleDisconnect(Device);
932 done:
933 return RingBufferSize(data->Ring);
937 static const BackendFuncs DSoundFuncs = {
938 DSoundOpenPlayback,
939 DSoundClosePlayback,
940 DSoundResetPlayback,
941 DSoundStartPlayback,
942 DSoundStopPlayback,
943 DSoundOpenCapture,
944 DSoundCloseCapture,
945 DSoundStartCapture,
946 DSoundStopCapture,
947 DSoundCaptureSamples,
948 DSoundAvailableSamples,
949 ALCdevice_LockDefault,
950 ALCdevice_UnlockDefault,
951 ALCdevice_GetLatencyDefault
955 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
957 if(!DSoundLoad())
958 return ALC_FALSE;
959 *FuncList = DSoundFuncs;
960 return ALC_TRUE;
963 void alcDSoundDeinit(void)
965 ALuint i;
967 for(i = 0;i < NumPlaybackDevices;++i)
968 free(PlaybackDeviceList[i].name);
969 free(PlaybackDeviceList);
970 PlaybackDeviceList = NULL;
971 NumPlaybackDevices = 0;
973 for(i = 0;i < NumCaptureDevices;++i)
974 free(CaptureDeviceList[i].name);
975 free(CaptureDeviceList);
976 CaptureDeviceList = NULL;
977 NumCaptureDevices = 0;
979 if(ds_handle)
980 CloseLib(ds_handle);
981 ds_handle = NULL;
984 void alcDSoundProbe(enum DevProbe type)
986 HRESULT hr, hrcom;
987 ALuint i;
989 switch(type)
991 case ALL_DEVICE_PROBE:
992 for(i = 0;i < NumPlaybackDevices;++i)
993 free(PlaybackDeviceList[i].name);
994 free(PlaybackDeviceList);
995 PlaybackDeviceList = NULL;
996 NumPlaybackDevices = 0;
998 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
999 if(FAILED(hr))
1000 ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr);
1001 else
1003 for(i = 0;i < NumPlaybackDevices;i++)
1004 AppendAllDevicesList(PlaybackDeviceList[i].name);
1006 break;
1008 case CAPTURE_DEVICE_PROBE:
1009 for(i = 0;i < NumCaptureDevices;++i)
1010 free(CaptureDeviceList[i].name);
1011 free(CaptureDeviceList);
1012 CaptureDeviceList = NULL;
1013 NumCaptureDevices = 0;
1015 /* Initialize COM to prevent name truncation */
1016 hrcom = CoInitialize(NULL);
1017 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
1018 if(FAILED(hr))
1019 ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr);
1020 else
1022 for(i = 0;i < NumCaptureDevices;i++)
1023 AppendCaptureDeviceList(CaptureDeviceList[i].name);
1025 if(SUCCEEDED(hrcom))
1026 CoUninitialize();
1027 break;