Lock the device before calling aluHandleDisconnect
[openal-soft.git] / Alc / backends / dsound.c
blobc6f666e1c3df386a1987861e5a42ba0fac702c6e
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 *PrimaryBuffer;
65 IDirectSoundBuffer *Buffer;
66 IDirectSoundNotify *Notifies;
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->Buffer, &DSBCaps);
244 if(FAILED(err))
246 ERR("Failed to get buffer caps: 0x%lx\n", err);
247 ALCdevice_Lock(Device);
248 aluHandleDisconnect(Device);
249 ALCdevice_Unlock(Device);
250 return 1;
253 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
254 FragSize = Device->UpdateSize * FrameSize;
256 IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &LastCursor, NULL);
257 while(!data->killNow)
259 // Get current play cursor
260 IDirectSoundBuffer_GetCurrentPosition(data->Buffer, &PlayCursor, NULL);
261 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
263 if(avail < FragSize)
265 if(!Playing)
267 err = IDirectSoundBuffer_Play(data->Buffer, 0, 0, DSBPLAY_LOOPING);
268 if(FAILED(err))
270 ERR("Failed to play buffer: 0x%lx\n", err);
271 ALCdevice_Lock(Device);
272 aluHandleDisconnect(Device);
273 ALCdevice_Unlock(Device);
274 return 1;
276 Playing = TRUE;
279 avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
280 if(avail != WAIT_OBJECT_0)
281 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
282 continue;
284 avail -= avail%FragSize;
286 // Lock output buffer
287 WriteCnt1 = 0;
288 WriteCnt2 = 0;
289 err = IDirectSoundBuffer_Lock(data->Buffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
291 // If the buffer is lost, restore it and lock
292 if(err == DSERR_BUFFERLOST)
294 WARN("Buffer lost, restoring...\n");
295 err = IDirectSoundBuffer_Restore(data->Buffer);
296 if(SUCCEEDED(err))
298 Playing = FALSE;
299 LastCursor = 0;
300 err = IDirectSoundBuffer_Lock(data->Buffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
304 // Successfully locked the output buffer
305 if(SUCCEEDED(err))
307 // If we have an active context, mix data directly into output buffer otherwise fill with silence
308 aluMixData(Device, WritePtr1, WriteCnt1/FrameSize);
309 aluMixData(Device, WritePtr2, WriteCnt2/FrameSize);
311 // Unlock output buffer only when successfully locked
312 IDirectSoundBuffer_Unlock(data->Buffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
314 else
316 ERR("Buffer lock error: %#lx\n", err);
317 ALCdevice_Lock(Device);
318 aluHandleDisconnect(Device);
319 ALCdevice_Unlock(Device);
320 return 1;
323 // Update old write cursor location
324 LastCursor += WriteCnt1+WriteCnt2;
325 LastCursor %= DSBCaps.dwBufferBytes;
328 return 0;
331 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
333 DSoundPlaybackData *data = NULL;
334 LPGUID guid = NULL;
335 HRESULT hr;
337 if(!PlaybackDeviceList)
339 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
340 if(FAILED(hr))
341 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
344 if(!deviceName && NumPlaybackDevices > 0)
346 deviceName = PlaybackDeviceList[0].name;
347 guid = &PlaybackDeviceList[0].guid;
349 else
351 ALuint i;
353 for(i = 0;i < NumPlaybackDevices;i++)
355 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
357 guid = &PlaybackDeviceList[i].guid;
358 break;
361 if(i == NumPlaybackDevices)
362 return ALC_INVALID_VALUE;
365 //Initialise requested device
366 data = calloc(1, sizeof(DSoundPlaybackData));
367 if(!data)
368 return ALC_OUT_OF_MEMORY;
370 hr = DS_OK;
371 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
372 if(data->NotifyEvent == NULL)
373 hr = E_FAIL;
375 //DirectSound Init code
376 if(SUCCEEDED(hr))
377 hr = DirectSoundCreate(guid, &data->DS, NULL);
378 if(SUCCEEDED(hr))
379 hr = IDirectSound_SetCooperativeLevel(data->DS, GetForegroundWindow(), DSSCL_PRIORITY);
380 if(FAILED(hr))
382 if(data->DS)
383 IDirectSound_Release(data->DS);
384 if(data->NotifyEvent)
385 CloseHandle(data->NotifyEvent);
386 free(data);
387 ERR("Device init failed: 0x%08lx\n", hr);
388 return ALC_INVALID_VALUE;
391 device->DeviceName = strdup(deviceName);
392 device->ExtraData = data;
393 return ALC_NO_ERROR;
396 static void DSoundClosePlayback(ALCdevice *device)
398 DSoundPlaybackData *data = device->ExtraData;
400 if(data->Notifies)
401 IDirectSoundNotify_Release(data->Notifies);
402 data->Notifies = NULL;
403 if(data->Buffer)
404 IDirectSoundBuffer_Release(data->Buffer);
405 data->Buffer = NULL;
406 if(data->PrimaryBuffer != NULL)
407 IDirectSoundBuffer_Release(data->PrimaryBuffer);
408 data->PrimaryBuffer = NULL;
410 IDirectSound_Release(data->DS);
411 CloseHandle(data->NotifyEvent);
412 free(data);
413 device->ExtraData = NULL;
416 static ALCboolean DSoundResetPlayback(ALCdevice *device)
418 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
419 DSBUFFERDESC DSBDescription;
420 WAVEFORMATEXTENSIBLE OutputType;
421 DWORD speakers;
422 HRESULT hr;
424 memset(&OutputType, 0, sizeof(OutputType));
426 if(data->Notifies)
427 IDirectSoundNotify_Release(data->Notifies);
428 data->Notifies = NULL;
429 if(data->Buffer)
430 IDirectSoundBuffer_Release(data->Buffer);
431 data->Buffer = NULL;
432 if(data->PrimaryBuffer != NULL)
433 IDirectSoundBuffer_Release(data->PrimaryBuffer);
434 data->PrimaryBuffer = NULL;
436 switch(device->FmtType)
438 case DevFmtByte:
439 device->FmtType = DevFmtUByte;
440 break;
441 case DevFmtFloat:
442 if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
443 break;
444 /* fall-through */
445 case DevFmtUShort:
446 device->FmtType = DevFmtShort;
447 break;
448 case DevFmtUInt:
449 device->FmtType = DevFmtInt;
450 break;
451 case DevFmtUByte:
452 case DevFmtShort:
453 case DevFmtInt:
454 break;
457 hr = IDirectSound_GetSpeakerConfig(data->DS, &speakers);
458 if(SUCCEEDED(hr))
460 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
462 speakers = DSSPEAKER_CONFIG(speakers);
463 if(speakers == DSSPEAKER_MONO)
464 device->FmtChans = DevFmtMono;
465 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
466 device->FmtChans = DevFmtStereo;
467 else if(speakers == DSSPEAKER_QUAD)
468 device->FmtChans = DevFmtQuad;
469 else if(speakers == DSSPEAKER_5POINT1)
470 device->FmtChans = DevFmtX51;
471 else if(speakers == DSSPEAKER_7POINT1)
472 device->FmtChans = DevFmtX71;
473 else
474 ERR("Unknown system speaker config: 0x%lx\n", speakers);
477 switch(device->FmtChans)
479 case DevFmtMono:
480 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
481 break;
482 case DevFmtStereo:
483 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
484 SPEAKER_FRONT_RIGHT;
485 break;
486 case DevFmtQuad:
487 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
488 SPEAKER_FRONT_RIGHT |
489 SPEAKER_BACK_LEFT |
490 SPEAKER_BACK_RIGHT;
491 break;
492 case DevFmtX51:
493 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
494 SPEAKER_FRONT_RIGHT |
495 SPEAKER_FRONT_CENTER |
496 SPEAKER_LOW_FREQUENCY |
497 SPEAKER_BACK_LEFT |
498 SPEAKER_BACK_RIGHT;
499 break;
500 case DevFmtX51Side:
501 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
502 SPEAKER_FRONT_RIGHT |
503 SPEAKER_FRONT_CENTER |
504 SPEAKER_LOW_FREQUENCY |
505 SPEAKER_SIDE_LEFT |
506 SPEAKER_SIDE_RIGHT;
507 break;
508 case DevFmtX61:
509 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
510 SPEAKER_FRONT_RIGHT |
511 SPEAKER_FRONT_CENTER |
512 SPEAKER_LOW_FREQUENCY |
513 SPEAKER_BACK_CENTER |
514 SPEAKER_SIDE_LEFT |
515 SPEAKER_SIDE_RIGHT;
516 break;
517 case DevFmtX71:
518 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
519 SPEAKER_FRONT_RIGHT |
520 SPEAKER_FRONT_CENTER |
521 SPEAKER_LOW_FREQUENCY |
522 SPEAKER_BACK_LEFT |
523 SPEAKER_BACK_RIGHT |
524 SPEAKER_SIDE_LEFT |
525 SPEAKER_SIDE_RIGHT;
526 break;
529 retry_open:
530 hr = S_OK;
531 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
532 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
533 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
534 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
535 OutputType.Format.nSamplesPerSec = device->Frequency;
536 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
537 OutputType.Format.cbSize = 0;
540 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
542 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
543 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
544 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
545 if(device->FmtType == DevFmtFloat)
546 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
547 else
548 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
550 if(data->PrimaryBuffer)
551 IDirectSoundBuffer_Release(data->PrimaryBuffer);
552 data->PrimaryBuffer = NULL;
554 else
556 if(SUCCEEDED(hr) && !data->PrimaryBuffer)
558 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
559 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
560 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
561 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->PrimaryBuffer, NULL);
563 if(SUCCEEDED(hr))
564 hr = IDirectSoundBuffer_SetFormat(data->PrimaryBuffer,&OutputType.Format);
567 if(SUCCEEDED(hr))
569 if(device->NumUpdates > MAX_UPDATES)
571 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
572 MAX_UPDATES-1) / MAX_UPDATES;
573 device->NumUpdates = MAX_UPDATES;
576 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
577 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
578 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
579 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
580 OutputType.Format.nBlockAlign;
581 DSBDescription.lpwfxFormat=&OutputType.Format;
582 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->Buffer, NULL);
583 if(FAILED(hr) && device->FmtType == DevFmtFloat)
585 device->FmtType = DevFmtShort;
586 goto retry_open;
590 if(SUCCEEDED(hr))
592 hr = IDirectSoundBuffer_QueryInterface(data->Buffer, &IID_IDirectSoundNotify, (LPVOID *)&data->Notifies);
593 if(SUCCEEDED(hr))
595 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
596 ALuint i;
598 for(i = 0;i < device->NumUpdates;++i)
600 notifies[i].dwOffset = i * device->UpdateSize *
601 OutputType.Format.nBlockAlign;
602 notifies[i].hEventNotify = data->NotifyEvent;
604 if(IDirectSoundNotify_SetNotificationPositions(data->Notifies, device->NumUpdates, notifies) != DS_OK)
605 hr = E_FAIL;
609 if(FAILED(hr))
611 if(data->Notifies != NULL)
612 IDirectSoundNotify_Release(data->Notifies);
613 data->Notifies = NULL;
614 if(data->Buffer != NULL)
615 IDirectSoundBuffer_Release(data->Buffer);
616 data->Buffer = NULL;
617 if(data->PrimaryBuffer != NULL)
618 IDirectSoundBuffer_Release(data->PrimaryBuffer);
619 data->PrimaryBuffer = NULL;
620 return ALC_FALSE;
623 ResetEvent(data->NotifyEvent);
624 SetDefaultWFXChannelOrder(device);
626 return ALC_TRUE;
629 static ALCboolean DSoundStartPlayback(ALCdevice *device)
631 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
633 data->thread = StartThread(DSoundPlaybackProc, device);
634 if(data->thread == NULL)
635 return ALC_FALSE;
637 return ALC_TRUE;
640 static void DSoundStopPlayback(ALCdevice *device)
642 DSoundPlaybackData *data = device->ExtraData;
644 if(!data->thread)
645 return;
647 data->killNow = 1;
648 StopThread(data->thread);
649 data->thread = NULL;
651 data->killNow = 0;
652 IDirectSoundBuffer_Stop(data->Buffer);
656 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
658 DSoundCaptureData *data = NULL;
659 WAVEFORMATEXTENSIBLE InputType;
660 DSCBUFFERDESC DSCBDescription;
661 LPGUID guid = NULL;
662 HRESULT hr, hrcom;
663 ALuint samples;
665 if(!CaptureDeviceList)
667 /* Initialize COM to prevent name truncation */
668 hrcom = CoInitialize(NULL);
669 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
670 if(FAILED(hr))
671 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
672 if(SUCCEEDED(hrcom))
673 CoUninitialize();
676 if(!deviceName && NumCaptureDevices > 0)
678 deviceName = CaptureDeviceList[0].name;
679 guid = &CaptureDeviceList[0].guid;
681 else
683 ALuint i;
685 for(i = 0;i < NumCaptureDevices;i++)
687 if(strcmp(deviceName, CaptureDeviceList[i].name) == 0)
689 guid = &CaptureDeviceList[i].guid;
690 break;
693 if(i == NumCaptureDevices)
694 return ALC_INVALID_VALUE;
697 switch(device->FmtType)
699 case DevFmtByte:
700 case DevFmtUShort:
701 case DevFmtUInt:
702 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
703 return ALC_INVALID_ENUM;
705 case DevFmtUByte:
706 case DevFmtShort:
707 case DevFmtInt:
708 case DevFmtFloat:
709 break;
712 //Initialise requested device
713 data = calloc(1, sizeof(DSoundCaptureData));
714 if(!data)
715 return ALC_OUT_OF_MEMORY;
717 hr = DS_OK;
719 //DirectSoundCapture Init code
720 if(SUCCEEDED(hr))
721 hr = DirectSoundCaptureCreate(guid, &data->DSC, NULL);
722 if(SUCCEEDED(hr))
724 memset(&InputType, 0, sizeof(InputType));
726 switch(device->FmtChans)
728 case DevFmtMono:
729 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
730 break;
731 case DevFmtStereo:
732 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
733 SPEAKER_FRONT_RIGHT;
734 break;
735 case DevFmtQuad:
736 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
737 SPEAKER_FRONT_RIGHT |
738 SPEAKER_BACK_LEFT |
739 SPEAKER_BACK_RIGHT;
740 break;
741 case DevFmtX51:
742 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
743 SPEAKER_FRONT_RIGHT |
744 SPEAKER_FRONT_CENTER |
745 SPEAKER_LOW_FREQUENCY |
746 SPEAKER_BACK_LEFT |
747 SPEAKER_BACK_RIGHT;
748 break;
749 case DevFmtX51Side:
750 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
751 SPEAKER_FRONT_RIGHT |
752 SPEAKER_FRONT_CENTER |
753 SPEAKER_LOW_FREQUENCY |
754 SPEAKER_SIDE_LEFT |
755 SPEAKER_SIDE_RIGHT;
756 break;
757 case DevFmtX61:
758 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
759 SPEAKER_FRONT_RIGHT |
760 SPEAKER_FRONT_CENTER |
761 SPEAKER_LOW_FREQUENCY |
762 SPEAKER_BACK_CENTER |
763 SPEAKER_SIDE_LEFT |
764 SPEAKER_SIDE_RIGHT;
765 break;
766 case DevFmtX71:
767 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
768 SPEAKER_FRONT_RIGHT |
769 SPEAKER_FRONT_CENTER |
770 SPEAKER_LOW_FREQUENCY |
771 SPEAKER_BACK_LEFT |
772 SPEAKER_BACK_RIGHT |
773 SPEAKER_SIDE_LEFT |
774 SPEAKER_SIDE_RIGHT;
775 break;
778 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
779 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
780 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
781 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
782 InputType.Format.nSamplesPerSec = device->Frequency;
783 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
784 InputType.Format.cbSize = 0;
786 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
788 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
789 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
790 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
791 if(device->FmtType == DevFmtFloat)
792 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
793 else
794 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
797 samples = device->UpdateSize * device->NumUpdates;
798 samples = maxu(samples, 100 * device->Frequency / 1000);
800 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
801 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
802 DSCBDescription.dwFlags = 0;
803 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
804 DSCBDescription.lpwfxFormat = &InputType.Format;
806 hr = IDirectSoundCapture_CreateCaptureBuffer(data->DSC, &DSCBDescription, &data->DSCbuffer, NULL);
808 if(SUCCEEDED(hr))
810 data->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
811 if(data->Ring == NULL)
812 hr = DSERR_OUTOFMEMORY;
815 if(FAILED(hr))
817 ERR("Device init failed: 0x%08lx\n", hr);
819 DestroyRingBuffer(data->Ring);
820 data->Ring = NULL;
821 if(data->DSCbuffer != NULL)
822 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
823 data->DSCbuffer = NULL;
824 if(data->DSC)
825 IDirectSoundCapture_Release(data->DSC);
826 data->DSC = NULL;
828 free(data);
829 return ALC_INVALID_VALUE;
832 data->BufferBytes = DSCBDescription.dwBufferBytes;
833 SetDefaultWFXChannelOrder(device);
835 device->DeviceName = strdup(deviceName);
836 device->ExtraData = data;
838 return ALC_NO_ERROR;
841 static void DSoundCloseCapture(ALCdevice *device)
843 DSoundCaptureData *data = device->ExtraData;
845 DestroyRingBuffer(data->Ring);
846 data->Ring = NULL;
848 if(data->DSCbuffer != NULL)
850 IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
851 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
852 data->DSCbuffer = NULL;
855 IDirectSoundCapture_Release(data->DSC);
856 data->DSC = NULL;
858 free(data);
859 device->ExtraData = NULL;
862 static void DSoundStartCapture(ALCdevice *device)
864 DSoundCaptureData *data = device->ExtraData;
865 HRESULT hr;
867 hr = IDirectSoundCaptureBuffer_Start(data->DSCbuffer, DSCBSTART_LOOPING);
868 if(FAILED(hr))
870 ERR("start failed: 0x%08lx\n", hr);
871 aluHandleDisconnect(device);
875 static void DSoundStopCapture(ALCdevice *device)
877 DSoundCaptureData *data = device->ExtraData;
878 HRESULT hr;
880 hr = IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
881 if(FAILED(hr))
883 ERR("stop failed: 0x%08lx\n", hr);
884 aluHandleDisconnect(device);
888 static ALCenum DSoundCaptureSamples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
890 DSoundCaptureData *data = Device->ExtraData;
891 ReadRingBuffer(data->Ring, pBuffer, lSamples);
892 return ALC_NO_ERROR;
895 static ALCuint DSoundAvailableSamples(ALCdevice *Device)
897 DSoundCaptureData *data = Device->ExtraData;
898 DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
899 VOID *ReadPtr1, *ReadPtr2;
900 DWORD ReadCnt1, ReadCnt2;
901 DWORD FrameSize;
902 HRESULT hr;
904 if(!Device->Connected)
905 goto done;
907 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
908 BufferBytes = data->BufferBytes;
909 LastCursor = data->Cursor;
911 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(data->DSCbuffer, NULL, &ReadCursor);
912 if(SUCCEEDED(hr))
914 NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
915 if(NumBytes == 0)
916 goto done;
917 hr = IDirectSoundCaptureBuffer_Lock(data->DSCbuffer, LastCursor, NumBytes,
918 &ReadPtr1, &ReadCnt1,
919 &ReadPtr2, &ReadCnt2, 0);
921 if(SUCCEEDED(hr))
923 WriteRingBuffer(data->Ring, ReadPtr1, ReadCnt1/FrameSize);
924 if(ReadPtr2 != NULL)
925 WriteRingBuffer(data->Ring, ReadPtr2, ReadCnt2/FrameSize);
926 hr = IDirectSoundCaptureBuffer_Unlock(data->DSCbuffer,
927 ReadPtr1, ReadCnt1,
928 ReadPtr2, ReadCnt2);
929 data->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
932 if(FAILED(hr))
934 ERR("update failed: 0x%08lx\n", hr);
935 aluHandleDisconnect(Device);
938 done:
939 return RingBufferSize(data->Ring);
943 static const BackendFuncs DSoundFuncs = {
944 DSoundOpenPlayback,
945 DSoundClosePlayback,
946 DSoundResetPlayback,
947 DSoundStartPlayback,
948 DSoundStopPlayback,
949 DSoundOpenCapture,
950 DSoundCloseCapture,
951 DSoundStartCapture,
952 DSoundStopCapture,
953 DSoundCaptureSamples,
954 DSoundAvailableSamples,
955 ALCdevice_LockDefault,
956 ALCdevice_UnlockDefault,
957 ALCdevice_GetLatencyDefault
961 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
963 if(!DSoundLoad())
964 return ALC_FALSE;
965 *FuncList = DSoundFuncs;
966 return ALC_TRUE;
969 void alcDSoundDeinit(void)
971 ALuint i;
973 for(i = 0;i < NumPlaybackDevices;++i)
974 free(PlaybackDeviceList[i].name);
975 free(PlaybackDeviceList);
976 PlaybackDeviceList = NULL;
977 NumPlaybackDevices = 0;
979 for(i = 0;i < NumCaptureDevices;++i)
980 free(CaptureDeviceList[i].name);
981 free(CaptureDeviceList);
982 CaptureDeviceList = NULL;
983 NumCaptureDevices = 0;
985 if(ds_handle)
986 CloseLib(ds_handle);
987 ds_handle = NULL;
990 void alcDSoundProbe(enum DevProbe type)
992 HRESULT hr, hrcom;
993 ALuint i;
995 switch(type)
997 case ALL_DEVICE_PROBE:
998 for(i = 0;i < NumPlaybackDevices;++i)
999 free(PlaybackDeviceList[i].name);
1000 free(PlaybackDeviceList);
1001 PlaybackDeviceList = NULL;
1002 NumPlaybackDevices = 0;
1004 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
1005 if(FAILED(hr))
1006 ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr);
1007 else
1009 for(i = 0;i < NumPlaybackDevices;i++)
1010 AppendAllDevicesList(PlaybackDeviceList[i].name);
1012 break;
1014 case CAPTURE_DEVICE_PROBE:
1015 for(i = 0;i < NumCaptureDevices;++i)
1016 free(CaptureDeviceList[i].name);
1017 free(CaptureDeviceList);
1018 CaptureDeviceList = NULL;
1019 NumCaptureDevices = 0;
1021 /* Initialize COM to prevent name truncation */
1022 hrcom = CoInitialize(NULL);
1023 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
1024 if(FAILED(hr))
1025 ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr);
1026 else
1028 for(i = 0;i < NumCaptureDevices;i++)
1029 AppendCaptureDeviceList(CaptureDeviceList[i].name);
1031 if(SUCCEEDED(hrcom))
1032 CoUninitialize();
1033 break;