Use a separate backend callback to start playback of the device
[openal-soft.git] / Alc / backends / dsound.c
blob0eab62a418a9b9b0734fa665c5e924195afa8279
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 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 *pDevice = (ALCdevice*)ptr;
228 DSoundPlaybackData *pData = (DSoundPlaybackData*)pDevice->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(pData->DSsbuffer, &DSBCaps);
245 if(FAILED(err))
247 ERR("Failed to get buffer caps: 0x%lx\n", err);
248 aluHandleDisconnect(pDevice);
249 return 1;
252 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
253 FragSize = pDevice->UpdateSize * FrameSize;
255 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &LastCursor, NULL);
256 while(!pData->killNow)
258 // Get current play cursor
259 IDirectSoundBuffer_GetCurrentPosition(pData->DSsbuffer, &PlayCursor, NULL);
260 avail = (PlayCursor-LastCursor+DSBCaps.dwBufferBytes) % DSBCaps.dwBufferBytes;
262 if(avail < FragSize)
264 if(!Playing)
266 err = IDirectSoundBuffer_Play(pData->DSsbuffer, 0, 0, DSBPLAY_LOOPING);
267 if(FAILED(err))
269 ERR("Failed to play buffer: 0x%lx\n", err);
270 aluHandleDisconnect(pDevice);
271 return 1;
273 Playing = TRUE;
276 avail = WaitForSingleObjectEx(pData->hNotifyEvent, 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(pData->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(pData->DSsbuffer);
293 if(SUCCEEDED(err))
295 Playing = FALSE;
296 LastCursor = 0;
297 err = IDirectSoundBuffer_Lock(pData->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(pDevice, WritePtr1, WriteCnt1/FrameSize);
306 aluMixData(pDevice, WritePtr2, WriteCnt2/FrameSize);
308 // Unlock output buffer only when successfully locked
309 IDirectSoundBuffer_Unlock(pData->DSsbuffer, WritePtr1, WriteCnt1, WritePtr2, WriteCnt2);
311 else
313 ERR("Buffer lock error: %#lx\n", err);
314 aluHandleDisconnect(pDevice);
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 *pData = 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 pData = calloc(1, sizeof(DSoundPlaybackData));
362 if(!pData)
363 return ALC_OUT_OF_MEMORY;
365 hr = DS_OK;
366 pData->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
367 if(pData->hNotifyEvent == NULL)
368 hr = E_FAIL;
370 //DirectSound Init code
371 if(SUCCEEDED(hr))
372 hr = DirectSoundCreate(guid, &pData->lpDS, NULL);
373 if(SUCCEEDED(hr))
374 hr = IDirectSound_SetCooperativeLevel(pData->lpDS, GetForegroundWindow(), DSSCL_PRIORITY);
375 if(FAILED(hr))
377 if(pData->lpDS)
378 IDirectSound_Release(pData->lpDS);
379 if(pData->hNotifyEvent)
380 CloseHandle(pData->hNotifyEvent);
381 free(pData);
382 ERR("Device init failed: 0x%08lx\n", hr);
383 return ALC_INVALID_VALUE;
386 device->szDeviceName = strdup(deviceName);
387 device->ExtraData = pData;
388 return ALC_NO_ERROR;
391 static void DSoundClosePlayback(ALCdevice *device)
393 DSoundPlaybackData *pData = device->ExtraData;
395 if(pData->DSnotify)
396 IDirectSoundNotify_Release(pData->DSnotify);
397 pData->DSnotify = NULL;
398 if(pData->DSsbuffer)
399 IDirectSoundBuffer_Release(pData->DSsbuffer);
400 pData->DSsbuffer = NULL;
401 if(pData->DSpbuffer != NULL)
402 IDirectSoundBuffer_Release(pData->DSpbuffer);
403 pData->DSpbuffer = NULL;
405 IDirectSound_Release(pData->lpDS);
406 CloseHandle(pData->hNotifyEvent);
407 free(pData);
408 device->ExtraData = NULL;
411 static ALCboolean DSoundResetPlayback(ALCdevice *device)
413 DSoundPlaybackData *pData = (DSoundPlaybackData*)device->ExtraData;
414 DSBUFFERDESC DSBDescription;
415 WAVEFORMATEXTENSIBLE OutputType;
416 DWORD speakers;
417 HRESULT hr;
419 memset(&OutputType, 0, sizeof(OutputType));
421 if(pData->DSnotify)
422 IDirectSoundNotify_Release(pData->DSnotify);
423 pData->DSnotify = NULL;
424 if(pData->DSsbuffer)
425 IDirectSoundBuffer_Release(pData->DSsbuffer);
426 pData->DSsbuffer = NULL;
427 if(pData->DSpbuffer != NULL)
428 IDirectSoundBuffer_Release(pData->DSpbuffer);
429 pData->DSpbuffer = NULL;
431 switch(device->FmtType)
433 case DevFmtByte:
434 device->FmtType = DevFmtUByte;
435 break;
436 case DevFmtUShort:
437 device->FmtType = DevFmtShort;
438 break;
439 case DevFmtUInt:
440 device->FmtType = DevFmtInt;
441 break;
442 case DevFmtUByte:
443 case DevFmtShort:
444 case DevFmtInt:
445 case DevFmtFloat:
446 break;
449 hr = IDirectSound_GetSpeakerConfig(pData->lpDS, &speakers);
450 if(SUCCEEDED(hr))
452 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
454 speakers = DSSPEAKER_CONFIG(speakers);
455 if(speakers == DSSPEAKER_MONO)
456 device->FmtChans = DevFmtMono;
457 else if(speakers == DSSPEAKER_STEREO || speakers == DSSPEAKER_HEADPHONE)
458 device->FmtChans = DevFmtStereo;
459 else if(speakers == DSSPEAKER_QUAD)
460 device->FmtChans = DevFmtQuad;
461 else if(speakers == DSSPEAKER_5POINT1)
462 device->FmtChans = DevFmtX51;
463 else if(speakers == DSSPEAKER_7POINT1)
464 device->FmtChans = DevFmtX71;
465 else
466 ERR("Unknown system speaker config: 0x%lx\n", speakers);
469 switch(device->FmtChans)
471 case DevFmtMono:
472 OutputType.dwChannelMask = SPEAKER_FRONT_CENTER;
473 break;
474 case DevFmtStereo:
475 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
476 SPEAKER_FRONT_RIGHT;
477 break;
478 case DevFmtQuad:
479 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
480 SPEAKER_FRONT_RIGHT |
481 SPEAKER_BACK_LEFT |
482 SPEAKER_BACK_RIGHT;
483 break;
484 case DevFmtX51:
485 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
486 SPEAKER_FRONT_RIGHT |
487 SPEAKER_FRONT_CENTER |
488 SPEAKER_LOW_FREQUENCY |
489 SPEAKER_BACK_LEFT |
490 SPEAKER_BACK_RIGHT;
491 break;
492 case DevFmtX51Side:
493 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
494 SPEAKER_FRONT_RIGHT |
495 SPEAKER_FRONT_CENTER |
496 SPEAKER_LOW_FREQUENCY |
497 SPEAKER_SIDE_LEFT |
498 SPEAKER_SIDE_RIGHT;
499 break;
500 case DevFmtX61:
501 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
502 SPEAKER_FRONT_RIGHT |
503 SPEAKER_FRONT_CENTER |
504 SPEAKER_LOW_FREQUENCY |
505 SPEAKER_BACK_CENTER |
506 SPEAKER_SIDE_LEFT |
507 SPEAKER_SIDE_RIGHT;
508 break;
509 case DevFmtX71:
510 OutputType.dwChannelMask = SPEAKER_FRONT_LEFT |
511 SPEAKER_FRONT_RIGHT |
512 SPEAKER_FRONT_CENTER |
513 SPEAKER_LOW_FREQUENCY |
514 SPEAKER_BACK_LEFT |
515 SPEAKER_BACK_RIGHT |
516 SPEAKER_SIDE_LEFT |
517 SPEAKER_SIDE_RIGHT;
518 break;
521 retry_open:
522 hr = S_OK;
523 OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
524 OutputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
525 OutputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
526 OutputType.Format.nBlockAlign = OutputType.Format.nChannels*OutputType.Format.wBitsPerSample/8;
527 OutputType.Format.nSamplesPerSec = device->Frequency;
528 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec*OutputType.Format.nBlockAlign;
529 OutputType.Format.cbSize = 0;
532 if(OutputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
534 OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
535 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
536 OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
537 if(device->FmtType == DevFmtFloat)
538 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
539 else
540 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
542 if(pData->DSpbuffer)
543 IDirectSoundBuffer_Release(pData->DSpbuffer);
544 pData->DSpbuffer = NULL;
546 else
548 if(SUCCEEDED(hr))
550 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
551 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
552 DSBDescription.dwFlags=DSBCAPS_PRIMARYBUFFER;
553 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSpbuffer, NULL);
555 if(SUCCEEDED(hr))
556 hr = IDirectSoundBuffer_SetFormat(pData->DSpbuffer,&OutputType.Format);
559 if(SUCCEEDED(hr))
561 if(device->NumUpdates > MAX_UPDATES)
563 device->UpdateSize = (device->UpdateSize*device->NumUpdates +
564 MAX_UPDATES-1) / MAX_UPDATES;
565 device->NumUpdates = MAX_UPDATES;
568 memset(&DSBDescription,0,sizeof(DSBUFFERDESC));
569 DSBDescription.dwSize=sizeof(DSBUFFERDESC);
570 DSBDescription.dwFlags=DSBCAPS_CTRLPOSITIONNOTIFY|DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_GLOBALFOCUS;
571 DSBDescription.dwBufferBytes=device->UpdateSize * device->NumUpdates *
572 OutputType.Format.nBlockAlign;
573 DSBDescription.lpwfxFormat=&OutputType.Format;
574 hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL);
575 if(FAILED(hr) && device->FmtType == DevFmtFloat)
577 device->FmtType = DevFmtShort;
578 goto retry_open;
582 if(SUCCEEDED(hr))
584 hr = IDirectSoundBuffer_QueryInterface(pData->DSsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&pData->DSnotify);
585 if(SUCCEEDED(hr))
587 DSBPOSITIONNOTIFY notifies[MAX_UPDATES];
588 ALuint i;
590 for(i = 0;i < device->NumUpdates;++i)
592 notifies[i].dwOffset = i * device->UpdateSize *
593 OutputType.Format.nBlockAlign;
594 notifies[i].hEventNotify = pData->hNotifyEvent;
596 if(IDirectSoundNotify_SetNotificationPositions(pData->DSnotify, device->NumUpdates, notifies) != DS_OK)
597 hr = E_FAIL;
601 if(FAILED(hr))
603 if(pData->DSnotify != NULL)
604 IDirectSoundNotify_Release(pData->DSnotify);
605 pData->DSnotify = NULL;
606 if(pData->DSsbuffer != NULL)
607 IDirectSoundBuffer_Release(pData->DSsbuffer);
608 pData->DSsbuffer = NULL;
609 if(pData->DSpbuffer != NULL)
610 IDirectSoundBuffer_Release(pData->DSpbuffer);
611 pData->DSpbuffer = NULL;
612 return ALC_FALSE;
615 ResetEvent(pData->hNotifyEvent);
616 SetDefaultWFXChannelOrder(device);
618 return ALC_TRUE;
621 static ALCboolean DSoundStartPlayback(ALCdevice *device)
623 DSoundPlaybackData *pData = (DSoundPlaybackData*)device->ExtraData;
625 pData->thread = StartThread(DSoundPlaybackProc, device);
626 if(pData->thread == NULL)
627 return ALC_FALSE;
629 return ALC_TRUE;
632 static void DSoundStopPlayback(ALCdevice *device)
634 DSoundPlaybackData *pData = device->ExtraData;
636 if(!pData->thread)
637 return;
639 pData->killNow = 1;
640 StopThread(pData->thread);
641 pData->thread = NULL;
643 pData->killNow = 0;
644 IDirectSoundBuffer_Stop(pData->DSsbuffer);
648 static ALCenum DSoundOpenCapture(ALCdevice *device, const ALCchar *deviceName)
650 DSoundCaptureData *pData = NULL;
651 WAVEFORMATEXTENSIBLE InputType;
652 DSCBUFFERDESC DSCBDescription;
653 LPGUID guid = NULL;
654 HRESULT hr, hrcom;
655 ALuint samples;
657 if(!CaptureDeviceList)
659 /* Initialize COM to prevent name truncation */
660 hrcom = CoInitialize(NULL);
661 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
662 if(FAILED(hr))
663 ERR("Error enumerating DirectSound devices (%#x)!\n", (unsigned int)hr);
664 if(SUCCEEDED(hrcom))
665 CoUninitialize();
668 if(!deviceName && NumCaptureDevices > 0)
670 deviceName = CaptureDeviceList[0].name;
671 guid = &CaptureDeviceList[0].guid;
673 else
675 ALuint i;
677 for(i = 0;i < NumCaptureDevices;i++)
679 if(strcmp(deviceName, CaptureDeviceList[i].name) == 0)
681 guid = &CaptureDeviceList[i].guid;
682 break;
685 if(i == NumCaptureDevices)
686 return ALC_INVALID_VALUE;
689 switch(device->FmtType)
691 case DevFmtByte:
692 case DevFmtUShort:
693 case DevFmtUInt:
694 WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
695 return ALC_INVALID_ENUM;
697 case DevFmtUByte:
698 case DevFmtShort:
699 case DevFmtInt:
700 case DevFmtFloat:
701 break;
704 //Initialise requested device
705 pData = calloc(1, sizeof(DSoundCaptureData));
706 if(!pData)
707 return ALC_OUT_OF_MEMORY;
709 hr = DS_OK;
711 //DirectSoundCapture Init code
712 if(SUCCEEDED(hr))
713 hr = DirectSoundCaptureCreate(guid, &pData->lpDSC, NULL);
714 if(SUCCEEDED(hr))
716 memset(&InputType, 0, sizeof(InputType));
718 switch(device->FmtChans)
720 case DevFmtMono:
721 InputType.dwChannelMask = SPEAKER_FRONT_CENTER;
722 break;
723 case DevFmtStereo:
724 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
725 SPEAKER_FRONT_RIGHT;
726 break;
727 case DevFmtQuad:
728 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
729 SPEAKER_FRONT_RIGHT |
730 SPEAKER_BACK_LEFT |
731 SPEAKER_BACK_RIGHT;
732 break;
733 case DevFmtX51:
734 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
735 SPEAKER_FRONT_RIGHT |
736 SPEAKER_FRONT_CENTER |
737 SPEAKER_LOW_FREQUENCY |
738 SPEAKER_BACK_LEFT |
739 SPEAKER_BACK_RIGHT;
740 break;
741 case DevFmtX51Side:
742 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
743 SPEAKER_FRONT_RIGHT |
744 SPEAKER_FRONT_CENTER |
745 SPEAKER_LOW_FREQUENCY |
746 SPEAKER_SIDE_LEFT |
747 SPEAKER_SIDE_RIGHT;
748 break;
749 case DevFmtX61:
750 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
751 SPEAKER_FRONT_RIGHT |
752 SPEAKER_FRONT_CENTER |
753 SPEAKER_LOW_FREQUENCY |
754 SPEAKER_BACK_CENTER |
755 SPEAKER_SIDE_LEFT |
756 SPEAKER_SIDE_RIGHT;
757 break;
758 case DevFmtX71:
759 InputType.dwChannelMask = SPEAKER_FRONT_LEFT |
760 SPEAKER_FRONT_RIGHT |
761 SPEAKER_FRONT_CENTER |
762 SPEAKER_LOW_FREQUENCY |
763 SPEAKER_BACK_LEFT |
764 SPEAKER_BACK_RIGHT |
765 SPEAKER_SIDE_LEFT |
766 SPEAKER_SIDE_RIGHT;
767 break;
770 InputType.Format.wFormatTag = WAVE_FORMAT_PCM;
771 InputType.Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
772 InputType.Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
773 InputType.Format.nBlockAlign = InputType.Format.nChannels*InputType.Format.wBitsPerSample/8;
774 InputType.Format.nSamplesPerSec = device->Frequency;
775 InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec*InputType.Format.nBlockAlign;
776 InputType.Format.cbSize = 0;
778 if(InputType.Format.nChannels > 2 || device->FmtType == DevFmtFloat)
780 InputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
781 InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
782 InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
783 if(device->FmtType == DevFmtFloat)
784 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
785 else
786 InputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
789 samples = device->UpdateSize * device->NumUpdates;
790 samples = maxu(samples, 100 * device->Frequency / 1000);
792 memset(&DSCBDescription, 0, sizeof(DSCBUFFERDESC));
793 DSCBDescription.dwSize = sizeof(DSCBUFFERDESC);
794 DSCBDescription.dwFlags = 0;
795 DSCBDescription.dwBufferBytes = samples * InputType.Format.nBlockAlign;
796 DSCBDescription.lpwfxFormat = &InputType.Format;
798 hr = IDirectSoundCapture_CreateCaptureBuffer(pData->lpDSC, &DSCBDescription, &pData->DSCbuffer, NULL);
800 if(SUCCEEDED(hr))
802 pData->pRing = CreateRingBuffer(InputType.Format.nBlockAlign, device->UpdateSize * device->NumUpdates);
803 if(pData->pRing == NULL)
804 hr = DSERR_OUTOFMEMORY;
807 if(FAILED(hr))
809 ERR("Device init failed: 0x%08lx\n", hr);
811 DestroyRingBuffer(pData->pRing);
812 pData->pRing = NULL;
813 if(pData->DSCbuffer != NULL)
814 IDirectSoundCaptureBuffer_Release(pData->DSCbuffer);
815 pData->DSCbuffer = NULL;
816 if(pData->lpDSC)
817 IDirectSoundCapture_Release(pData->lpDSC);
818 pData->lpDSC = NULL;
820 free(pData);
821 return ALC_INVALID_VALUE;
824 pData->dwBufferBytes = DSCBDescription.dwBufferBytes;
825 SetDefaultWFXChannelOrder(device);
827 device->szDeviceName = strdup(deviceName);
828 device->ExtraData = pData;
830 return ALC_NO_ERROR;
833 static void DSoundCloseCapture(ALCdevice *device)
835 DSoundCaptureData *pData = device->ExtraData;
837 DestroyRingBuffer(pData->pRing);
838 pData->pRing = NULL;
840 if(pData->DSCbuffer != NULL)
842 IDirectSoundCaptureBuffer_Stop(pData->DSCbuffer);
843 IDirectSoundCaptureBuffer_Release(pData->DSCbuffer);
844 pData->DSCbuffer = NULL;
847 IDirectSoundCapture_Release(pData->lpDSC);
848 pData->lpDSC = NULL;
850 free(pData);
851 device->ExtraData = NULL;
854 static void DSoundStartCapture(ALCdevice *device)
856 DSoundCaptureData *pData = device->ExtraData;
857 HRESULT hr;
859 hr = IDirectSoundCaptureBuffer_Start(pData->DSCbuffer, DSCBSTART_LOOPING);
860 if(FAILED(hr))
862 ERR("start failed: 0x%08lx\n", hr);
863 aluHandleDisconnect(device);
867 static void DSoundStopCapture(ALCdevice *device)
869 DSoundCaptureData *pData = device->ExtraData;
870 HRESULT hr;
872 hr = IDirectSoundCaptureBuffer_Stop(pData->DSCbuffer);
873 if(FAILED(hr))
875 ERR("stop failed: 0x%08lx\n", hr);
876 aluHandleDisconnect(device);
880 static ALCenum DSoundCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
882 DSoundCaptureData *pData = pDevice->ExtraData;
883 ReadRingBuffer(pData->pRing, pBuffer, lSamples);
884 return ALC_NO_ERROR;
887 static ALCuint DSoundAvailableSamples(ALCdevice *pDevice)
889 DSoundCaptureData *pData = pDevice->ExtraData;
890 DWORD dwRead, dwCursor, dwBufferBytes, dwNumBytes;
891 void *pvAudio1, *pvAudio2;
892 DWORD dwAudioBytes1, dwAudioBytes2;
893 DWORD FrameSize;
894 HRESULT hr;
896 if(!pDevice->Connected)
897 goto done;
899 FrameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
900 dwBufferBytes = pData->dwBufferBytes;
901 dwCursor = pData->dwCursor;
903 hr = IDirectSoundCaptureBuffer_GetCurrentPosition(pData->DSCbuffer, NULL, &dwRead);
904 if(SUCCEEDED(hr))
906 dwNumBytes = (dwBufferBytes + dwRead - dwCursor) % dwBufferBytes;
907 if(dwNumBytes == 0)
908 goto done;
909 hr = IDirectSoundCaptureBuffer_Lock(pData->DSCbuffer,
910 dwCursor, dwNumBytes,
911 &pvAudio1, &dwAudioBytes1,
912 &pvAudio2, &dwAudioBytes2, 0);
914 if(SUCCEEDED(hr))
916 WriteRingBuffer(pData->pRing, pvAudio1, dwAudioBytes1/FrameSize);
917 if(pvAudio2 != NULL)
918 WriteRingBuffer(pData->pRing, pvAudio2, dwAudioBytes2/FrameSize);
919 hr = IDirectSoundCaptureBuffer_Unlock(pData->DSCbuffer,
920 pvAudio1, dwAudioBytes1,
921 pvAudio2, dwAudioBytes2);
922 pData->dwCursor = (dwCursor + dwAudioBytes1 + dwAudioBytes2) % dwBufferBytes;
925 if(FAILED(hr))
927 ERR("update failed: 0x%08lx\n", hr);
928 aluHandleDisconnect(pDevice);
931 done:
932 return RingBufferSize(pData->pRing);
935 static const BackendFuncs DSoundFuncs = {
936 DSoundOpenPlayback,
937 DSoundClosePlayback,
938 DSoundResetPlayback,
939 DSoundStartPlayback,
940 DSoundStopPlayback,
941 DSoundOpenCapture,
942 DSoundCloseCapture,
943 DSoundStartCapture,
944 DSoundStopCapture,
945 DSoundCaptureSamples,
946 DSoundAvailableSamples
950 ALCboolean alcDSoundInit(BackendFuncs *FuncList)
952 if(!DSoundLoad())
953 return ALC_FALSE;
954 *FuncList = DSoundFuncs;
955 return ALC_TRUE;
958 void alcDSoundDeinit(void)
960 ALuint i;
962 for(i = 0;i < NumPlaybackDevices;++i)
963 free(PlaybackDeviceList[i].name);
964 free(PlaybackDeviceList);
965 PlaybackDeviceList = NULL;
966 NumPlaybackDevices = 0;
968 for(i = 0;i < NumCaptureDevices;++i)
969 free(CaptureDeviceList[i].name);
970 free(CaptureDeviceList);
971 CaptureDeviceList = NULL;
972 NumCaptureDevices = 0;
974 if(ds_handle)
975 CloseLib(ds_handle);
976 ds_handle = NULL;
979 void alcDSoundProbe(enum DevProbe type)
981 HRESULT hr, hrcom;
982 ALuint i;
984 switch(type)
986 case ALL_DEVICE_PROBE:
987 for(i = 0;i < NumPlaybackDevices;++i)
988 free(PlaybackDeviceList[i].name);
989 free(PlaybackDeviceList);
990 PlaybackDeviceList = NULL;
991 NumPlaybackDevices = 0;
993 hr = DirectSoundEnumerateA(DSoundEnumPlaybackDevices, NULL);
994 if(FAILED(hr))
995 ERR("Error enumerating DirectSound playback devices (%#x)!\n", (unsigned int)hr);
996 else
998 for(i = 0;i < NumPlaybackDevices;i++)
999 AppendAllDeviceList(PlaybackDeviceList[i].name);
1001 break;
1003 case CAPTURE_DEVICE_PROBE:
1004 for(i = 0;i < NumCaptureDevices;++i)
1005 free(CaptureDeviceList[i].name);
1006 free(CaptureDeviceList);
1007 CaptureDeviceList = NULL;
1008 NumCaptureDevices = 0;
1010 /* Initialize COM to prevent name truncation */
1011 hrcom = CoInitialize(NULL);
1012 hr = DirectSoundCaptureEnumerateA(DSoundEnumCaptureDevices, NULL);
1013 if(FAILED(hr))
1014 ERR("Error enumerating DirectSound capture devices (%#x)!\n", (unsigned int)hr);
1015 else
1017 for(i = 0;i < NumCaptureDevices;i++)
1018 AppendCaptureDeviceList(CaptureDeviceList[i].name);
1020 if(SUCCEEDED(hrcom))
1021 CoUninitialize();
1022 break;