Move the device lock into the backend function table
[openal-soft.git] / Alc / backends / dsound.c
blobe38bf30f9ab01aa49dcb6f92d54acf9801225144
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 *DS;
65 IDirectSoundBuffer *DSpbuffer;
66 IDirectSoundBuffer *DSsbuffer;
67 IDirectSoundNotify *DSnotify;
68 HANDLE NotifyEvent;
70 volatile int killNow;
71 ALvoid *thread;
72 } DSoundPlaybackData;
74 typedef struct {
75 // DirectSound Capture Device
76 IDirectSoundCapture *DSC;
77 IDirectSoundCaptureBuffer *DSCbuffer;
78 DWORD BufferBytes;
79 DWORD Cursor;
80 RingBuffer *Ring;
81 } DSoundCaptureData;
84 typedef struct {
85 ALCchar *name;
86 GUID guid;
87 } DevMap;
89 static DevMap *PlaybackDeviceList;
90 static ALuint NumPlaybackDevices;
91 static DevMap *CaptureDeviceList;
92 static ALuint NumCaptureDevices;
94 #define MAX_UPDATES 128
96 static ALCboolean DSoundLoad(void)
98 if(!ds_handle)
100 ds_handle = LoadLib("dsound.dll");
101 if(ds_handle == NULL)
103 ERR("Failed to load dsound.dll\n");
104 return ALC_FALSE;
107 #define LOAD_FUNC(f) do { \
108 p##f = GetSymbol(ds_handle, #f); \
109 if(p##f == NULL) { \
110 CloseLib(ds_handle); \
111 ds_handle = NULL; \
112 return ALC_FALSE; \
114 } while(0)
115 LOAD_FUNC(DirectSoundCreate);
116 LOAD_FUNC(DirectSoundEnumerateA);
117 LOAD_FUNC(DirectSoundCaptureCreate);
118 LOAD_FUNC(DirectSoundCaptureEnumerateA);
119 #undef LOAD_FUNC
121 return ALC_TRUE;
125 static BOOL CALLBACK DSoundEnumPlaybackDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
127 LPOLESTR guidstr = NULL;
128 char str[1024];
129 HRESULT hr;
130 void *temp;
131 int count;
132 ALuint i;
134 (void)data;
135 (void)drvname;
137 if(!guid)
138 return TRUE;
140 count = 0;
141 do {
142 if(count == 0)
143 snprintf(str, sizeof(str), "%s", desc);
144 else
145 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
146 count++;
148 for(i = 0;i < NumPlaybackDevices;i++)
150 if(strcmp(str, PlaybackDeviceList[i].name) == 0)
151 break;
153 } while(i != NumPlaybackDevices);
155 hr = StringFromCLSID(guid, &guidstr);
156 if(SUCCEEDED(hr))
158 TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr);
159 CoTaskMemFree(guidstr);
162 temp = realloc(PlaybackDeviceList, sizeof(DevMap) * (NumPlaybackDevices+1));
163 if(temp)
165 PlaybackDeviceList = temp;
166 PlaybackDeviceList[NumPlaybackDevices].name = strdup(str);
167 PlaybackDeviceList[NumPlaybackDevices].guid = *guid;
168 NumPlaybackDevices++;
171 return TRUE;
175 static BOOL CALLBACK DSoundEnumCaptureDevices(LPGUID guid, LPCSTR desc, LPCSTR drvname, LPVOID data)
177 LPOLESTR guidstr = NULL;
178 char str[1024];
179 HRESULT hr;
180 void *temp;
181 int count;
182 ALuint i;
184 (void)data;
185 (void)drvname;
187 if(!guid)
188 return TRUE;
190 count = 0;
191 do {
192 if(count == 0)
193 snprintf(str, sizeof(str), "%s", desc);
194 else
195 snprintf(str, sizeof(str), "%s #%d", desc, count+1);
196 count++;
198 for(i = 0;i < NumCaptureDevices;i++)
200 if(strcmp(str, CaptureDeviceList[i].name) == 0)
201 break;
203 } while(i != NumCaptureDevices);
205 hr = StringFromCLSID(guid, &guidstr);
206 if(SUCCEEDED(hr))
208 TRACE("Got device \"%s\", GUID \"%ls\"\n", str, guidstr);
209 CoTaskMemFree(guidstr);
212 temp = realloc(CaptureDeviceList, sizeof(DevMap) * (NumCaptureDevices+1));
213 if(temp)
215 CaptureDeviceList = temp;
216 CaptureDeviceList[NumCaptureDevices].name = strdup(str);
217 CaptureDeviceList[NumCaptureDevices].guid = *guid;
218 NumCaptureDevices++;
221 return TRUE;
225 static ALuint DSoundPlaybackProc(ALvoid *ptr)
227 ALCdevice *Device = (ALCdevice*)ptr;
228 DSoundPlaybackData *data = (DSoundPlaybackData*)Device->ExtraData;
229 DSBCAPS DSBCaps;
230 DWORD LastCursor = 0;
231 DWORD PlayCursor;
232 VOID *WritePtr1, *WritePtr2;
233 DWORD WriteCnt1, WriteCnt2;
234 BOOL Playing = FALSE;
235 DWORD FrameSize;
236 DWORD FragSize;
237 DWORD avail;
238 HRESULT err;
240 SetRTPriority();
242 memset(&DSBCaps, 0, sizeof(DSBCaps));
243 DSBCaps.dwSize = sizeof(DSBCaps);
244 err = IDirectSoundBuffer_GetCaps(data->DSsbuffer, &DSBCaps);
245 if(FAILED(err))
247 ERR("Failed to get buffer caps: 0x%lx\n", err);
248 aluHandleDisconnect(Device);
249 return 1;
252 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
253 FragSize = Device->UpdateSize * FrameSize;
255 IDirectSoundBuffer_GetCurrentPosition(data->DSsbuffer, &LastCursor, NULL);
256 while(!data->killNow)
258 // Get current play cursor
259 IDirectSoundBuffer_GetCurrentPosition(data->DSsbuffer, &PlayCursor, NULL);
260 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
262 if(avail < FragSize)
264 if(!Playing)
266 err = IDirectSoundBuffer_Play(data->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
267 if(FAILED(err))
269 ERR("Failed to play buffer: 0x%lx\n", err);
270 aluHandleDisconnect(Device);
271 return 1;
273 Playing = TRUE;
276 avail = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
277 if(avail != WAIT_OBJECT_0)
278 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail);
279 continue;
281 avail -= avail%FragSize;
283 // Lock output buffer
284 WriteCnt1 = 0;
285 WriteCnt2 = 0;
286 err = IDirectSoundBuffer_Lock(data->DSsbuffer, LastCursor, avail, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
288 // If the buffer is lost, restore it and lock
289 if(err == DSERR_BUFFERLOST)
291 WARN("Buffer lost, restoring...\n");
292 err = IDirectSoundBuffer_Restore(data->DSsbuffer);
293 if(SUCCEEDED(err))
295 Playing = FALSE;
296 LastCursor = 0;
297 err = IDirectSoundBuffer_Lock(data->DSsbuffer, 0, DSBCaps.dwBufferBytes, &WritePtr1, &WriteCnt1, &WritePtr2, &WriteCnt2, 0);
301 // Successfully locked the output buffer
302 if(SUCCEEDED(err))
304 // If we have an active context, mix data directly into output buffer otherwise fill with silence
305 aluMixData(Device, WritePtr1, WriteCnt1/FrameSize);
306 aluMixData(Device, WritePtr2, WriteCnt2/FrameSize);
308 // Unlock output buffer only when successfully locked
309 IDirectSoundBuffer_Unlock(data->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
311 else
313 ERR("Buffer lock error: %#lx\n", err);
314 aluHandleDisconnect(Device);
315 return 1;
318 // Update old write cursor location
319 LastCursor += WriteCnt1+WriteCnt2;
320 LastCursor %= DSBCaps.dwBufferBytes;
323 return 0;
326 static ALCenum DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
328 DSoundPlaybackData *data = NULL;
329 LPGUID guid = NULL;
330 HRESULT hr;
332 if(!PlaybackDeviceList)
334 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
335 if(FAILED(hr))
336 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
339 if(!deviceName && NumPlaybackDevices > 0)
341 deviceName = PlaybackDeviceList[0].name;
342 guid = &PlaybackDeviceList[0].guid;
344 else
346 ALuint i;
348 for(i = 0;i < NumPlaybackDevices;i++)
350 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
352 guid = &PlaybackDeviceList[i].guid;
353 break;
356 if(i == NumPlaybackDevices)
357 return ALC_INVALID_VALUE;
360 //Initialise requested device
361 data = calloc(1, sizeof(DSoundPlaybackData));
362 if(!data)
363 return ALC_OUT_OF_MEMORY;
365 hr = DS_OK;
366 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
367 if(data->NotifyEvent == NULL)
368 hr = E_FAIL;
370 //DirectSound Init code
371 if(SUCCEEDED(hr))
372 hr = DirectSoundCreate(guid, &data->DS, NULL);
373 if(SUCCEEDED(hr))
374 hr = IDirectSound_SetCooperativeLevel(data->DS, GetForegroundWindow(), DSSCL_PRIORITY);
375 if(FAILED(hr))
377 if(data->DS)
378 IDirectSound_Release(data->DS);
379 if(data->NotifyEvent)
380 CloseHandle(data->NotifyEvent);
381 free(data);
382 ERR("Device init failed: 0x%08lx\n", hr);
383 return ALC_INVALID_VALUE;
386 device->DeviceName = strdup(deviceName);
387 device->ExtraData = data;
388 return ALC_NO_ERROR;
391 static void DSoundClosePlayback(ALCdevice *device)
393 DSoundPlaybackData *data = device->ExtraData;
395 if(data->DSnotify)
396 IDirectSoundNotify_Release(data->DSnotify);
397 data->DSnotify = NULL;
398 if(data->DSsbuffer)
399 IDirectSoundBuffer_Release(data->DSsbuffer);
400 data->DSsbuffer = NULL;
401 if(data->DSpbuffer != NULL)
402 IDirectSoundBuffer_Release(data->DSpbuffer);
403 data->DSpbuffer = NULL;
405 IDirectSound_Release(data->DS);
406 CloseHandle(data->NotifyEvent);
407 free(data);
408 device->ExtraData = NULL;
411 static ALCboolean DSoundResetPlayback(ALCdevice *device)
413 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
414 DSBUFFERDESC DSBDescription;
415 WAVEFORMATEXTENSIBLE OutputType;
416 DWORD speakers;
417 HRESULT hr;
419 memset(&OutputType, 0, sizeof(OutputType));
421 if(data->DSnotify)
422 IDirectSoundNotify_Release(data->DSnotify);
423 data->DSnotify = NULL;
424 if(data->DSsbuffer)
425 IDirectSoundBuffer_Release(data->DSsbuffer);
426 data->DSsbuffer = NULL;
427 if(data->DSpbuffer != NULL)
428 IDirectSoundBuffer_Release(data->DSpbuffer);
429 data->DSpbuffer = NULL;
431 switch(device->FmtType)
433 case DevFmtByte:
434 device->FmtType = DevFmtUByte;
435 break;
436 case DevFmtFloat:
437 if((device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
438 break;
439 /* fall-through */
440 case DevFmtUShort:
441 device->FmtType = DevFmtShort;
442 break;
443 case DevFmtUInt:
444 device->FmtType = DevFmtInt;
445 break;
446 case DevFmtUByte:
447 case DevFmtShort:
448 case DevFmtInt:
449 break;
452 hr = IDirectSound_GetSpeakerConfig(data->DS, &speakers);
453 if(SUCCEEDED(hr))
455 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
457 speakers = DSSPEAKER_CONFIG(speakers);
458 if(speakers == DSSPEAKER_MONO)
459 device->FmtChans = DevFmtMono;
460 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
461 device->FmtChans = DevFmtStereo;
462 else if(speakers == DSSPEAKER_QUAD)
463 device->FmtChans = DevFmtQuad;
464 else if(speakers == DSSPEAKER_5POINT1)
465 device->FmtChans = DevFmtX51;
466 else if(speakers == DSSPEAKER_7POINT1)
467 device->FmtChans = DevFmtX71;
468 else
469 ERR("Unknown system speaker config: 0x%lx\n", speakers);
472 switch(device->FmtChans)
474 case DevFmtMono:
475 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
476 break;
477 case DevFmtStereo:
478 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
479 SPEAKER_FRONT_RIGHT;
480 break;
481 case DevFmtQuad:
482 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
483 SPEAKER_FRONT_RIGHT |
484 SPEAKER_BACK_LEFT |
485 SPEAKER_BACK_RIGHT;
486 break;
487 case DevFmtX51:
488 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
489 SPEAKER_FRONT_RIGHT |
490 SPEAKER_FRONT_CENTER |
491 SPEAKER_LOW_FREQUENCY |
492 SPEAKER_BACK_LEFT |
493 SPEAKER_BACK_RIGHT;
494 break;
495 case DevFmtX51Side:
496 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
497 SPEAKER_FRONT_RIGHT |
498 SPEAKER_FRONT_CENTER |
499 SPEAKER_LOW_FREQUENCY |
500 SPEAKER_SIDE_LEFT |
501 SPEAKER_SIDE_RIGHT;
502 break;
503 case DevFmtX61:
504 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
505 SPEAKER_FRONT_RIGHT |
506 SPEAKER_FRONT_CENTER |
507 SPEAKER_LOW_FREQUENCY |
508 SPEAKER_BACK_CENTER |
509 SPEAKER_SIDE_LEFT |
510 SPEAKER_SIDE_RIGHT;
511 break;
512 case DevFmtX71:
513 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
514 SPEAKER_FRONT_RIGHT |
515 SPEAKER_FRONT_CENTER |
516 SPEAKER_LOW_FREQUENCY |
517 SPEAKER_BACK_LEFT |
518 SPEAKER_BACK_RIGHT |
519 SPEAKER_SIDE_LEFT |
520 SPEAKER_SIDE_RIGHT;
521 break;
524 retry_open:
525 hr = S_OK;
526 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
527 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
528 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
529 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
530 OutputType.Format.nSamplesPerSec = device->Frequency;
531 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
532 OutputType.Format.cbSize = 0;
535 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
537 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
538 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
539 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
540 if(device->FmtType == DevFmtFloat)
541 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
542 else
543 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
545 if(data->DSpbuffer)
546 IDirectSoundBuffer_Release(data->DSpbuffer);
547 data->DSpbuffer = NULL;
549 else
551 if(SUCCEEDED(hr))
553 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
554 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
555 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
556 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->DSpbuffer, NULL);
558 if(SUCCEEDED(hr))
559 hr = IDirectSoundBuffer_SetFormat(data->DSpbuffer,&OutputType.Format);
562 if(SUCCEEDED(hr))
564 if(device->NumUpdates > MAX_UPDATES)
566 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
567 MAX_UPDATES-1) / MAX_UPDATES;
568 device->NumUpdates = MAX_UPDATES;
571 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
572 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
573 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
574 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
575 OutputType.Format.nBlockAlign;
576 DSBDescription.lpwfxFormat=&OutputType.Format;
577 hr = IDirectSound_CreateSoundBuffer(data->DS, &DSBDescription, &data->DSsbuffer, NULL);
578 if(FAILED(hr) && device->FmtType == DevFmtFloat)
580 device->FmtType = DevFmtShort;
581 goto retry_open;
585 if(SUCCEEDED(hr))
587 hr = IDirectSoundBuffer_QueryInterface(data->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&data->DSnotify);
588 if(SUCCEEDED(hr))
590 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
591 ALuint i;
593 for(i = 0;i < device->NumUpdates;++i)
595 notifies[i].dwOffset = i * device->UpdateSize *
596 OutputType.Format.nBlockAlign;
597 notifies[i].hEventNotify = data->NotifyEvent;
599 if(IDirectSoundNotify_SetNotificationPositions(data->DSnotify, device->NumUpdates, notifies) != DS_OK)
600 hr = E_FAIL;
604 if(FAILED(hr))
606 if(data->DSnotify != NULL)
607 IDirectSoundNotify_Release(data->DSnotify);
608 data->DSnotify = NULL;
609 if(data->DSsbuffer != NULL)
610 IDirectSoundBuffer_Release(data->DSsbuffer);
611 data->DSsbuffer = NULL;
612 if(data->DSpbuffer != NULL)
613 IDirectSoundBuffer_Release(data->DSpbuffer);
614 data->DSpbuffer = NULL;
615 return ALC_FALSE;
618 ResetEvent(data->NotifyEvent);
619 SetDefaultWFXChannelOrder(device);
621 return ALC_TRUE;
624 static ALCboolean DSoundStartPlayback(ALCdevice *device)
626 DSoundPlaybackData *data = (DSoundPlaybackData*)device->ExtraData;
628 data->thread = StartThread(DSoundPlaybackProc, device);
629 if(data->thread == NULL)
630 return ALC_FALSE;
632 return ALC_TRUE;
635 static void DSoundStopPlayback(ALCdevice *device)
637 DSoundPlaybackData *data = device->ExtraData;
639 if(!data->thread)
640 return;
642 data->killNow = 1;
643 StopThread(data->thread);
644 data->thread = NULL;
646 data->killNow = 0;
647 IDirectSoundBuffer_Stop(data->DSsbuffer);
651 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
653 DSoundCaptureData *data = NULL;
654 WAVEFORMATEXTENSIBLE InputType;
655 DSCBUFFERDESC DSCBDescription;
656 LPGUID guid = NULL;
657 HRESULT hr, hrcom;
658 ALuint samples;
660 if(!CaptureDeviceList)
662 /* Initialize COM to prevent name truncation */
663 hrcom = CoInitialize(NULL);
664 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
665 if(FAILED(hr))
666 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
667 if(SUCCEEDED(hrcom))
668 CoUninitialize();
671 if(!deviceName && NumCaptureDevices > 0)
673 deviceName = CaptureDeviceList[0].name;
674 guid = &CaptureDeviceList[0].guid;
676 else
678 ALuint i;
680 for(i = 0;i < NumCaptureDevices;i++)
682 if(strcmp(deviceName, CaptureDeviceList[i].name) == 0)
684 guid = &CaptureDeviceList[i].guid;
685 break;
688 if(i == NumCaptureDevices)
689 return ALC_INVALID_VALUE;
692 switch(device->FmtType)
694 case DevFmtByte:
695 case DevFmtUShort:
696 case DevFmtUInt:
697 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
698 return ALC_INVALID_ENUM;
700 case DevFmtUByte:
701 case DevFmtShort:
702 case DevFmtInt:
703 case DevFmtFloat:
704 break;
707 //Initialise requested device
708 data = calloc(1, sizeof(DSoundCaptureData));
709 if(!data)
710 return ALC_OUT_OF_MEMORY;
712 hr = DS_OK;
714 //DirectSoundCapture Init code
715 if(SUCCEEDED(hr))
716 hr = DirectSoundCaptureCreate(guid, &data->DSC, NULL);
717 if(SUCCEEDED(hr))
719 memset(&InputType, 0, sizeof(InputType));
721 switch(device->FmtChans)
723 case DevFmtMono:
724 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
725 break;
726 case DevFmtStereo:
727 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
728 SPEAKER_FRONT_RIGHT;
729 break;
730 case DevFmtQuad:
731 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
732 SPEAKER_FRONT_RIGHT |
733 SPEAKER_BACK_LEFT |
734 SPEAKER_BACK_RIGHT;
735 break;
736 case DevFmtX51:
737 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
738 SPEAKER_FRONT_RIGHT |
739 SPEAKER_FRONT_CENTER |
740 SPEAKER_LOW_FREQUENCY |
741 SPEAKER_BACK_LEFT |
742 SPEAKER_BACK_RIGHT;
743 break;
744 case DevFmtX51Side:
745 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
746 SPEAKER_FRONT_RIGHT |
747 SPEAKER_FRONT_CENTER |
748 SPEAKER_LOW_FREQUENCY |
749 SPEAKER_SIDE_LEFT |
750 SPEAKER_SIDE_RIGHT;
751 break;
752 case DevFmtX61:
753 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
754 SPEAKER_FRONT_RIGHT |
755 SPEAKER_FRONT_CENTER |
756 SPEAKER_LOW_FREQUENCY |
757 SPEAKER_BACK_CENTER |
758 SPEAKER_SIDE_LEFT |
759 SPEAKER_SIDE_RIGHT;
760 break;
761 case DevFmtX71:
762 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
763 SPEAKER_FRONT_RIGHT |
764 SPEAKER_FRONT_CENTER |
765 SPEAKER_LOW_FREQUENCY |
766 SPEAKER_BACK_LEFT |
767 SPEAKER_BACK_RIGHT |
768 SPEAKER_SIDE_LEFT |
769 SPEAKER_SIDE_RIGHT;
770 break;
773 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
774 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
775 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
776 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
777 InputType.Format.nSamplesPerSec = device->Frequency;
778 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
779 InputType.Format.cbSize = 0;
781 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
783 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
784 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
785 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
786 if(device->FmtType == DevFmtFloat)
787 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
788 else
789 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
792 samples = device->UpdateSize * device->NumUpdates;
793 samples = maxu(samples, 100 * device->Frequency / 1000);
795 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
796 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
797 DSCBDescription.dwFlags = 0;
798 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
799 DSCBDescription.lpwfxFormat = &InputType.Format;
801 hr = IDirectSoundCapture_CreateCaptureBuffer(data->DSC, &DSCBDescription, &data->DSCbuffer, NULL);
803 if(SUCCEEDED(hr))
805 data->Ring = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
806 if(data->Ring == NULL)
807 hr = DSERR_OUTOFMEMORY;
810 if(FAILED(hr))
812 ERR("Device init failed: 0x%08lx\n", hr);
814 DestroyRingBuffer(data->Ring);
815 data->Ring = NULL;
816 if(data->DSCbuffer != NULL)
817 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
818 data->DSCbuffer = NULL;
819 if(data->DSC)
820 IDirectSoundCapture_Release(data->DSC);
821 data->DSC = NULL;
823 free(data);
824 return ALC_INVALID_VALUE;
827 data->BufferBytes = DSCBDescription.dwBufferBytes;
828 SetDefaultWFXChannelOrder(device);
830 device->DeviceName = strdup(deviceName);
831 device->ExtraData = data;
833 return ALC_NO_ERROR;
836 static void DSoundCloseCapture(ALCdevice *device)
838 DSoundCaptureData *data = device->ExtraData;
840 DestroyRingBuffer(data->Ring);
841 data->Ring = NULL;
843 if(data->DSCbuffer != NULL)
845 IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
846 IDirectSoundCaptureBuffer_Release(data->DSCbuffer);
847 data->DSCbuffer = NULL;
850 IDirectSoundCapture_Release(data->DSC);
851 data->DSC = NULL;
853 free(data);
854 device->ExtraData = NULL;
857 static void DSoundStartCapture(ALCdevice *device)
859 DSoundCaptureData *data = device->ExtraData;
860 HRESULT hr;
862 hr = IDirectSoundCaptureBuffer_Start(data->DSCbuffer, DSCBSTART_LOOPING);
863 if(FAILED(hr))
865 ERR("start failed: 0x%08lx\n", hr);
866 aluHandleDisconnect(device);
870 static void DSoundStopCapture(ALCdevice *device)
872 DSoundCaptureData *data = device->ExtraData;
873 HRESULT hr;
875 hr = IDirectSoundCaptureBuffer_Stop(data->DSCbuffer);
876 if(FAILED(hr))
878 ERR("stop failed: 0x%08lx\n", hr);
879 aluHandleDisconnect(device);
883 static ALCenum DSoundCaptureSamples(ALCdevice *Device, ALCvoid *pBuffer, ALCuint lSamples)
885 DSoundCaptureData *data = Device->ExtraData;
886 ReadRingBuffer(data->Ring, pBuffer, lSamples);
887 return ALC_NO_ERROR;
890 static ALCuint DSoundAvailableSamples(ALCdevice *Device)
892 DSoundCaptureData *data = Device->ExtraData;
893 DWORD ReadCursor, LastCursor, BufferBytes, NumBytes;
894 VOID *ReadPtr1, *ReadPtr2;
895 DWORD ReadCnt1, ReadCnt2;
896 DWORD FrameSize;
897 HRESULT hr;
899 if(!Device->Connected)
900 goto done;
902 FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
903 BufferBytes = data->BufferBytes;
904 LastCursor = data->Cursor;
906 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(data->DSCbuffer, NULL, &ReadCursor);
907 if(SUCCEEDED(hr))
909 NumBytes = (ReadCursor-LastCursor + BufferBytes) % BufferBytes;
910 if(NumBytes == 0)
911 goto done;
912 hr = IDirectSoundCaptureBuffer_Lock(data->DSCbuffer, LastCursor, NumBytes,
913 &ReadPtr1, &ReadCnt1,
914 &ReadPtr2, &ReadCnt2, 0);
916 if(SUCCEEDED(hr))
918 WriteRingBuffer(data->Ring, ReadPtr1, ReadCnt1/FrameSize);
919 if(ReadPtr2 != NULL)
920 WriteRingBuffer(data->Ring, ReadPtr2, ReadCnt2/FrameSize);
921 hr = IDirectSoundCaptureBuffer_Unlock(data->DSCbuffer,
922 ReadPtr1, ReadCnt1,
923 ReadPtr2, ReadCnt2);
924 data->Cursor = (LastCursor+ReadCnt1+ReadCnt2) % BufferBytes;
927 if(FAILED(hr))
929 ERR("update failed: 0x%08lx\n", hr);
930 aluHandleDisconnect(Device);
933 done:
934 return RingBufferSize(data->Ring);
938 static ALint64 DSoundGetLatency(ALCdevice *device)
940 (void)device;
941 return 0;
945 static const BackendFuncs DSoundFuncs = {
946 DSoundOpenPlayback,
947 DSoundClosePlayback,
948 DSoundResetPlayback,
949 DSoundStartPlayback,
950 DSoundStopPlayback,
951 DSoundOpenCapture,
952 DSoundCloseCapture,
953 DSoundStartCapture,
954 DSoundStopCapture,
955 DSoundCaptureSamples,
956 DSoundAvailableSamples,
957 ALCdevice_LockDefault,
958 ALCdevice_UnlockDefault,
959 DSoundGetLatency
963 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
965 if(!DSoundLoad())
966 return ALC_FALSE;
967 *FuncList = DSoundFuncs;
968 return ALC_TRUE;
971 void alcDSoundDeinit(void)
973 ALuint i;
975 for(i = 0;i < NumPlaybackDevices;++i)
976 free(PlaybackDeviceList[i].name);
977 free(PlaybackDeviceList);
978 PlaybackDeviceList = NULL;
979 NumPlaybackDevices = 0;
981 for(i = 0;i < NumCaptureDevices;++i)
982 free(CaptureDeviceList[i].name);
983 free(CaptureDeviceList);
984 CaptureDeviceList = NULL;
985 NumCaptureDevices = 0;
987 if(ds_handle)
988 CloseLib(ds_handle);
989 ds_handle = NULL;
992 void alcDSoundProbe(enum DevProbe type)
994 HRESULT hr, hrcom;
995 ALuint i;
997 switch(type)
999 case ALL_DEVICE_PROBE:
1000 for(i = 0;i < NumPlaybackDevices;++i)
1001 free(PlaybackDeviceList[i].name);
1002 free(PlaybackDeviceList);
1003 PlaybackDeviceList = NULL;
1004 NumPlaybackDevices = 0;
1006 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
1007 if(FAILED(hr))
1008 ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr);
1009 else
1011 for(i = 0;i < NumPlaybackDevices;i++)
1012 AppendAllDevicesList(PlaybackDeviceList[i].name);
1014 break;
1016 case CAPTURE_DEVICE_PROBE:
1017 for(i = 0;i < NumCaptureDevices;++i)
1018 free(CaptureDeviceList[i].name);
1019 free(CaptureDeviceList);
1020 CaptureDeviceList = NULL;
1021 NumCaptureDevices = 0;
1023 /* Initialize COM to prevent name truncation */
1024 hrcom = CoInitialize(NULL);
1025 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
1026 if(FAILED(hr))
1027 ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr);
1028 else
1030 for(i = 0;i < NumCaptureDevices;i++)
1031 AppendCaptureDeviceList(CaptureDeviceList[i].name);
1033 if(SUCCEEDED(hrcom))
1034 CoUninitialize();
1035 break;