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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
30 #ifndef _WAVEFORMATEXTENSIBLE_
41 #include "backends/base.h"
43 #ifndef DSSPEAKER_5POINT1
44 # define DSSPEAKER_5POINT1 0x00000006
46 #ifndef DSSPEAKER_5POINT1_BACK
47 # define DSSPEAKER_5POINT1_BACK 0x00000006
49 #ifndef DSSPEAKER_7POINT1
50 # define DSSPEAKER_7POINT1 0x00000007
52 #ifndef DSSPEAKER_7POINT1_SURROUND
53 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
55 #ifndef DSSPEAKER_5POINT1_SURROUND
56 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
60 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
61 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
63 #define DEVNAME_HEAD "OpenAL Soft on "
67 static void *ds_handle
;
68 static HRESULT (WINAPI
*pDirectSoundCreate
)(const GUID
*pcGuidDevice
, IDirectSound
**ppDS
, IUnknown
*pUnkOuter
);
69 static HRESULT (WINAPI
*pDirectSoundEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
70 static HRESULT (WINAPI
*pDirectSoundCaptureCreate
)(const GUID
*pcGuidDevice
, IDirectSoundCapture
**ppDSC
, IUnknown
*pUnkOuter
);
71 static HRESULT (WINAPI
*pDirectSoundCaptureEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
73 #define DirectSoundCreate pDirectSoundCreate
74 #define DirectSoundEnumerateW pDirectSoundEnumerateW
75 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
76 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
80 static ALCboolean
DSoundLoad(void)
85 ds_handle
= LoadLib("dsound.dll");
88 ERR("Failed to load dsound.dll\n");
92 #define LOAD_FUNC(f) do { \
93 p##f = GetSymbol(ds_handle, #f); \
95 CloseLib(ds_handle); \
100 LOAD_FUNC(DirectSoundCreate
);
101 LOAD_FUNC(DirectSoundEnumerateW
);
102 LOAD_FUNC(DirectSoundCaptureCreate
);
103 LOAD_FUNC(DirectSoundCaptureEnumerateW
);
111 #define MAX_UPDATES 128
117 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
119 static vector_DevMap PlaybackDevices
;
120 static vector_DevMap CaptureDevices
;
122 static void clear_devlist(vector_DevMap
*list
)
124 #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
125 VECTOR_FOR_EACH(DevMap
, *list
, DEINIT_STR
);
126 VECTOR_RESIZE(*list
, 0, 0);
130 static BOOL CALLBACK
DSoundEnumDevices(GUID
*guid
, const WCHAR
*desc
, const WCHAR
* UNUSED(drvname
), void *data
)
132 vector_DevMap
*devices
= data
;
133 OLECHAR
*guidstr
= NULL
;
141 AL_STRING_INIT(entry
.name
);
148 alstr_copy_cstr(&entry
.name
, DEVNAME_HEAD
);
149 alstr_append_wcstr(&entry
.name
, desc
);
153 snprintf(str
, sizeof(str
), " #%d", count
+1);
154 alstr_append_cstr(&entry
.name
, str
);
157 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
158 VECTOR_FIND_IF(iter
, const DevMap
, *devices
, MATCH_ENTRY
);
159 if(iter
== VECTOR_END(*devices
)) break;
165 hr
= StringFromCLSID(guid
, &guidstr
);
168 TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry
.name
), guidstr
);
169 CoTaskMemFree(guidstr
);
172 VECTOR_PUSH_BACK(*devices
, entry
);
178 typedef struct ALCdsoundPlayback
{
179 DERIVE_FROM_TYPE(ALCbackend
);
182 IDirectSoundBuffer
*PrimaryBuffer
;
183 IDirectSoundBuffer
*Buffer
;
184 IDirectSoundNotify
*Notifies
;
187 volatile int killNow
;
191 static int ALCdsoundPlayback_mixerProc(void *ptr
);
193 static void ALCdsoundPlayback_Construct(ALCdsoundPlayback
*self
, ALCdevice
*device
);
194 static DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, void, Destruct
)
195 static ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*name
);
196 static void ALCdsoundPlayback_close(ALCdsoundPlayback
*self
);
197 static ALCboolean
ALCdsoundPlayback_reset(ALCdsoundPlayback
*self
);
198 static ALCboolean
ALCdsoundPlayback_start(ALCdsoundPlayback
*self
);
199 static void ALCdsoundPlayback_stop(ALCdsoundPlayback
*self
);
200 static DECLARE_FORWARD2(ALCdsoundPlayback
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
201 static DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, ALCuint
, availableSamples
)
202 static DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
203 static DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, void, lock
)
204 static DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, void, unlock
)
205 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback
)
207 DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback
);
210 static void ALCdsoundPlayback_Construct(ALCdsoundPlayback
*self
, ALCdevice
*device
)
212 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
213 SET_VTABLE2(ALCdsoundPlayback
, ALCbackend
, self
);
217 FORCE_ALIGN
static int ALCdsoundPlayback_mixerProc(void *ptr
)
219 ALCdsoundPlayback
*self
= ptr
;
220 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
222 DWORD LastCursor
= 0;
224 void *WritePtr1
, *WritePtr2
;
225 DWORD WriteCnt1
, WriteCnt2
;
226 BOOL Playing
= FALSE
;
233 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
235 memset(&DSBCaps
, 0, sizeof(DSBCaps
));
236 DSBCaps
.dwSize
= sizeof(DSBCaps
);
237 err
= IDirectSoundBuffer_GetCaps(self
->Buffer
, &DSBCaps
);
240 ERR("Failed to get buffer caps: 0x%lx\n", err
);
241 ALCdevice_Lock(device
);
242 aluHandleDisconnect(device
);
243 ALCdevice_Unlock(device
);
247 FrameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
248 FragSize
= device
->UpdateSize
* FrameSize
;
250 IDirectSoundBuffer_GetCurrentPosition(self
->Buffer
, &LastCursor
, NULL
);
251 while(!self
->killNow
)
253 // Get current play cursor
254 IDirectSoundBuffer_GetCurrentPosition(self
->Buffer
, &PlayCursor
, NULL
);
255 avail
= (PlayCursor
-LastCursor
+DSBCaps
.dwBufferBytes
) % DSBCaps
.dwBufferBytes
;
261 err
= IDirectSoundBuffer_Play(self
->Buffer
, 0, 0, DSBPLAY_LOOPING
);
264 ERR("Failed to play buffer: 0x%lx\n", err
);
265 ALCdevice_Lock(device
);
266 aluHandleDisconnect(device
);
267 ALCdevice_Unlock(device
);
273 avail
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
274 if(avail
!= WAIT_OBJECT_0
)
275 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
278 avail
-= avail
%FragSize
;
280 // Lock output buffer
283 err
= IDirectSoundBuffer_Lock(self
->Buffer
, LastCursor
, avail
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
285 // If the buffer is lost, restore it and lock
286 if(err
== DSERR_BUFFERLOST
)
288 WARN("Buffer lost, restoring...\n");
289 err
= IDirectSoundBuffer_Restore(self
->Buffer
);
294 err
= IDirectSoundBuffer_Lock(self
->Buffer
, 0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
298 // Successfully locked the output buffer
301 // If we have an active context, mix data directly into output buffer otherwise fill with silence
302 ALCdevice_Lock(device
);
303 aluMixData(device
, WritePtr1
, WriteCnt1
/FrameSize
);
304 aluMixData(device
, WritePtr2
, WriteCnt2
/FrameSize
);
305 ALCdevice_Unlock(device
);
307 // Unlock output buffer only when successfully locked
308 IDirectSoundBuffer_Unlock(self
->Buffer
, WritePtr1
, WriteCnt1
, WritePtr2
, WriteCnt2
);
312 ERR("Buffer lock error: %#lx\n", err
);
313 ALCdevice_Lock(device
);
314 aluHandleDisconnect(device
);
315 ALCdevice_Unlock(device
);
319 // Update old write cursor location
320 LastCursor
+= WriteCnt1
+WriteCnt2
;
321 LastCursor
%= DSBCaps
.dwBufferBytes
;
327 static ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*deviceName
)
329 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
330 const GUID
*guid
= NULL
;
333 if(VECTOR_SIZE(PlaybackDevices
) == 0)
335 /* Initialize COM to prevent name truncation */
336 hrcom
= CoInitialize(NULL
);
337 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
339 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
344 if(!deviceName
&& VECTOR_SIZE(PlaybackDevices
) > 0)
346 deviceName
= alstr_get_cstr(VECTOR_FRONT(PlaybackDevices
).name
);
347 guid
= &VECTOR_FRONT(PlaybackDevices
).guid
;
353 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
354 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
356 if(iter
== VECTOR_END(PlaybackDevices
))
357 return ALC_INVALID_VALUE
;
362 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
363 if(self
->NotifyEvent
== NULL
)
366 //DirectSound Init code
368 hr
= DirectSoundCreate(guid
, &self
->DS
, NULL
);
370 hr
= IDirectSound_SetCooperativeLevel(self
->DS
, GetForegroundWindow(), DSSCL_PRIORITY
);
374 IDirectSound_Release(self
->DS
);
376 if(self
->NotifyEvent
)
377 CloseHandle(self
->NotifyEvent
);
378 self
->NotifyEvent
= NULL
;
380 ERR("Device init failed: 0x%08lx\n", hr
);
381 return ALC_INVALID_VALUE
;
384 alstr_copy_cstr(&device
->DeviceName
, deviceName
);
389 static void ALCdsoundPlayback_close(ALCdsoundPlayback
*self
)
392 IDirectSoundNotify_Release(self
->Notifies
);
393 self
->Notifies
= NULL
;
395 IDirectSoundBuffer_Release(self
->Buffer
);
397 if(self
->PrimaryBuffer
!= NULL
)
398 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
399 self
->PrimaryBuffer
= NULL
;
401 IDirectSound_Release(self
->DS
);
403 CloseHandle(self
->NotifyEvent
);
404 self
->NotifyEvent
= NULL
;
407 static ALCboolean
ALCdsoundPlayback_reset(ALCdsoundPlayback
*self
)
409 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
410 DSBUFFERDESC DSBDescription
;
411 WAVEFORMATEXTENSIBLE OutputType
;
415 memset(&OutputType
, 0, sizeof(OutputType
));
418 IDirectSoundNotify_Release(self
->Notifies
);
419 self
->Notifies
= NULL
;
421 IDirectSoundBuffer_Release(self
->Buffer
);
423 if(self
->PrimaryBuffer
!= NULL
)
424 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
425 self
->PrimaryBuffer
= NULL
;
427 switch(device
->FmtType
)
430 device
->FmtType
= DevFmtUByte
;
433 if((device
->Flags
&DEVICE_SAMPLE_TYPE_REQUEST
))
437 device
->FmtType
= DevFmtShort
;
440 device
->FmtType
= DevFmtInt
;
448 hr
= IDirectSound_GetSpeakerConfig(self
->DS
, &speakers
);
451 speakers
= DSSPEAKER_CONFIG(speakers
);
452 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
454 if(speakers
== DSSPEAKER_MONO
)
455 device
->FmtChans
= DevFmtMono
;
456 else if(speakers
== DSSPEAKER_STEREO
|| speakers
== DSSPEAKER_HEADPHONE
)
457 device
->FmtChans
= DevFmtStereo
;
458 else if(speakers
== DSSPEAKER_QUAD
)
459 device
->FmtChans
= DevFmtQuad
;
460 else if(speakers
== DSSPEAKER_5POINT1_SURROUND
)
461 device
->FmtChans
= DevFmtX51
;
462 else if(speakers
== DSSPEAKER_5POINT1_BACK
)
463 device
->FmtChans
= DevFmtX51Rear
;
464 else if(speakers
== DSSPEAKER_7POINT1
|| speakers
== DSSPEAKER_7POINT1_SURROUND
)
465 device
->FmtChans
= DevFmtX71
;
467 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
469 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
470 speakers
== DSSPEAKER_HEADPHONE
);
472 switch(device
->FmtChans
)
475 OutputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
478 device
->FmtChans
= DevFmtStereo
;
481 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
485 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
486 SPEAKER_FRONT_RIGHT
|
491 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
492 SPEAKER_FRONT_RIGHT
|
493 SPEAKER_FRONT_CENTER
|
494 SPEAKER_LOW_FREQUENCY
|
499 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
500 SPEAKER_FRONT_RIGHT
|
501 SPEAKER_FRONT_CENTER
|
502 SPEAKER_LOW_FREQUENCY
|
507 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
508 SPEAKER_FRONT_RIGHT
|
509 SPEAKER_FRONT_CENTER
|
510 SPEAKER_LOW_FREQUENCY
|
511 SPEAKER_BACK_CENTER
|
516 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
517 SPEAKER_FRONT_RIGHT
|
518 SPEAKER_FRONT_CENTER
|
519 SPEAKER_LOW_FREQUENCY
|
529 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
530 OutputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
531 OutputType
.Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
532 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*OutputType
.Format
.wBitsPerSample
/8;
533 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
534 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*OutputType
.Format
.nBlockAlign
;
535 OutputType
.Format
.cbSize
= 0;
538 if(OutputType
.Format
.nChannels
> 2 || device
->FmtType
== DevFmtFloat
)
540 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
541 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
542 OutputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
543 if(device
->FmtType
== DevFmtFloat
)
544 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
546 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
548 if(self
->PrimaryBuffer
)
549 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
550 self
->PrimaryBuffer
= NULL
;
554 if(SUCCEEDED(hr
) && !self
->PrimaryBuffer
)
556 memset(&DSBDescription
,0,sizeof(DSBUFFERDESC
));
557 DSBDescription
.dwSize
=sizeof(DSBUFFERDESC
);
558 DSBDescription
.dwFlags
=DSBCAPS_PRIMARYBUFFER
;
559 hr
= IDirectSound_CreateSoundBuffer(self
->DS
, &DSBDescription
, &self
->PrimaryBuffer
, NULL
);
562 hr
= IDirectSoundBuffer_SetFormat(self
->PrimaryBuffer
,&OutputType
.Format
);
567 if(device
->NumUpdates
> MAX_UPDATES
)
569 device
->UpdateSize
= (device
->UpdateSize
*device
->NumUpdates
+
570 MAX_UPDATES
-1) / MAX_UPDATES
;
571 device
->NumUpdates
= MAX_UPDATES
;
574 memset(&DSBDescription
,0,sizeof(DSBUFFERDESC
));
575 DSBDescription
.dwSize
=sizeof(DSBUFFERDESC
);
576 DSBDescription
.dwFlags
=DSBCAPS_CTRLPOSITIONNOTIFY
|DSBCAPS_GETCURRENTPOSITION2
|DSBCAPS_GLOBALFOCUS
;
577 DSBDescription
.dwBufferBytes
=device
->UpdateSize
* device
->NumUpdates
*
578 OutputType
.Format
.nBlockAlign
;
579 DSBDescription
.lpwfxFormat
=&OutputType
.Format
;
580 hr
= IDirectSound_CreateSoundBuffer(self
->DS
, &DSBDescription
, &self
->Buffer
, NULL
);
581 if(FAILED(hr
) && device
->FmtType
== DevFmtFloat
)
583 device
->FmtType
= DevFmtShort
;
590 hr
= IDirectSoundBuffer_QueryInterface(self
->Buffer
, &IID_IDirectSoundNotify
, (void**)&self
->Notifies
);
593 DSBPOSITIONNOTIFY notifies
[MAX_UPDATES
];
596 for(i
= 0;i
< device
->NumUpdates
;++i
)
598 notifies
[i
].dwOffset
= i
* device
->UpdateSize
*
599 OutputType
.Format
.nBlockAlign
;
600 notifies
[i
].hEventNotify
= self
->NotifyEvent
;
602 if(IDirectSoundNotify_SetNotificationPositions(self
->Notifies
, device
->NumUpdates
, notifies
) != DS_OK
)
609 if(self
->Notifies
!= NULL
)
610 IDirectSoundNotify_Release(self
->Notifies
);
611 self
->Notifies
= NULL
;
612 if(self
->Buffer
!= NULL
)
613 IDirectSoundBuffer_Release(self
->Buffer
);
615 if(self
->PrimaryBuffer
!= NULL
)
616 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
617 self
->PrimaryBuffer
= NULL
;
621 ResetEvent(self
->NotifyEvent
);
622 SetDefaultWFXChannelOrder(device
);
627 static ALCboolean
ALCdsoundPlayback_start(ALCdsoundPlayback
*self
)
630 if(althrd_create(&self
->thread
, ALCdsoundPlayback_mixerProc
, self
) != althrd_success
)
636 static void ALCdsoundPlayback_stop(ALCdsoundPlayback
*self
)
644 althrd_join(self
->thread
, &res
);
646 IDirectSoundBuffer_Stop(self
->Buffer
);
651 typedef struct ALCdsoundCapture
{
652 DERIVE_FROM_TYPE(ALCbackend
);
654 IDirectSoundCapture
*DSC
;
655 IDirectSoundCaptureBuffer
*DSCbuffer
;
659 ll_ringbuffer_t
*Ring
;
662 static void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
);
663 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, Destruct
)
664 static ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*name
);
665 static void ALCdsoundCapture_close(ALCdsoundCapture
*self
);
666 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ALCboolean
, reset
)
667 static ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
);
668 static void ALCdsoundCapture_stop(ALCdsoundCapture
*self
);
669 static ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
670 static ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
);
671 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
672 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, lock
)
673 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, unlock
)
674 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture
)
676 DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture
);
678 static void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
)
680 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
681 SET_VTABLE2(ALCdsoundCapture
, ALCbackend
, self
);
685 static ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*deviceName
)
687 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
688 WAVEFORMATEXTENSIBLE InputType
;
689 DSCBUFFERDESC DSCBDescription
;
690 const GUID
*guid
= NULL
;
694 if(VECTOR_SIZE(CaptureDevices
) == 0)
696 /* Initialize COM to prevent name truncation */
697 hrcom
= CoInitialize(NULL
);
698 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
700 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
705 if(!deviceName
&& VECTOR_SIZE(CaptureDevices
) > 0)
707 deviceName
= alstr_get_cstr(VECTOR_FRONT(CaptureDevices
).name
);
708 guid
= &VECTOR_FRONT(CaptureDevices
).guid
;
714 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
715 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
717 if(iter
== VECTOR_END(CaptureDevices
))
718 return ALC_INVALID_VALUE
;
722 switch(device
->FmtType
)
727 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
728 return ALC_INVALID_ENUM
;
737 memset(&InputType
, 0, sizeof(InputType
));
738 switch(device
->FmtChans
)
741 InputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
744 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
748 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
749 SPEAKER_FRONT_RIGHT
|
754 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
755 SPEAKER_FRONT_RIGHT
|
756 SPEAKER_FRONT_CENTER
|
757 SPEAKER_LOW_FREQUENCY
|
762 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
763 SPEAKER_FRONT_RIGHT
|
764 SPEAKER_FRONT_CENTER
|
765 SPEAKER_LOW_FREQUENCY
|
770 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
771 SPEAKER_FRONT_RIGHT
|
772 SPEAKER_FRONT_CENTER
|
773 SPEAKER_LOW_FREQUENCY
|
774 SPEAKER_BACK_CENTER
|
779 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
780 SPEAKER_FRONT_RIGHT
|
781 SPEAKER_FRONT_CENTER
|
782 SPEAKER_LOW_FREQUENCY
|
789 WARN("%s capture not supported\n", DevFmtChannelsString(device
->FmtChans
));
790 return ALC_INVALID_ENUM
;
793 InputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
794 InputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
795 InputType
.Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
796 InputType
.Format
.nBlockAlign
= InputType
.Format
.nChannels
*InputType
.Format
.wBitsPerSample
/8;
797 InputType
.Format
.nSamplesPerSec
= device
->Frequency
;
798 InputType
.Format
.nAvgBytesPerSec
= InputType
.Format
.nSamplesPerSec
*InputType
.Format
.nBlockAlign
;
799 InputType
.Format
.cbSize
= 0;
800 InputType
.Samples
.wValidBitsPerSample
= InputType
.Format
.wBitsPerSample
;
801 if(device
->FmtType
== DevFmtFloat
)
802 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
804 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
806 if(InputType
.Format
.nChannels
> 2 || device
->FmtType
== DevFmtFloat
)
808 InputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
809 InputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
812 samples
= device
->UpdateSize
* device
->NumUpdates
;
813 samples
= maxu(samples
, 100 * device
->Frequency
/ 1000);
815 memset(&DSCBDescription
, 0, sizeof(DSCBUFFERDESC
));
816 DSCBDescription
.dwSize
= sizeof(DSCBUFFERDESC
);
817 DSCBDescription
.dwFlags
= 0;
818 DSCBDescription
.dwBufferBytes
= samples
* InputType
.Format
.nBlockAlign
;
819 DSCBDescription
.lpwfxFormat
= &InputType
.Format
;
821 //DirectSoundCapture Init code
822 hr
= DirectSoundCaptureCreate(guid
, &self
->DSC
, NULL
);
824 hr
= IDirectSoundCapture_CreateCaptureBuffer(self
->DSC
, &DSCBDescription
, &self
->DSCbuffer
, NULL
);
827 self
->Ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
+ 1,
828 InputType
.Format
.nBlockAlign
);
829 if(self
->Ring
== NULL
)
830 hr
= DSERR_OUTOFMEMORY
;
835 ERR("Device init failed: 0x%08lx\n", hr
);
837 ll_ringbuffer_free(self
->Ring
);
839 if(self
->DSCbuffer
!= NULL
)
840 IDirectSoundCaptureBuffer_Release(self
->DSCbuffer
);
841 self
->DSCbuffer
= NULL
;
843 IDirectSoundCapture_Release(self
->DSC
);
846 return ALC_INVALID_VALUE
;
849 self
->BufferBytes
= DSCBDescription
.dwBufferBytes
;
850 SetDefaultWFXChannelOrder(device
);
852 alstr_copy_cstr(&device
->DeviceName
, deviceName
);
857 static void ALCdsoundCapture_close(ALCdsoundCapture
*self
)
859 ll_ringbuffer_free(self
->Ring
);
862 if(self
->DSCbuffer
!= NULL
)
864 IDirectSoundCaptureBuffer_Stop(self
->DSCbuffer
);
865 IDirectSoundCaptureBuffer_Release(self
->DSCbuffer
);
866 self
->DSCbuffer
= NULL
;
869 IDirectSoundCapture_Release(self
->DSC
);
873 static ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
)
877 hr
= IDirectSoundCaptureBuffer_Start(self
->DSCbuffer
, DSCBSTART_LOOPING
);
880 ERR("start failed: 0x%08lx\n", hr
);
881 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
);
888 static void ALCdsoundCapture_stop(ALCdsoundCapture
*self
)
892 hr
= IDirectSoundCaptureBuffer_Stop(self
->DSCbuffer
);
895 ERR("stop failed: 0x%08lx\n", hr
);
896 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
);
900 static ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
902 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
906 static ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
)
908 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
909 DWORD ReadCursor
, LastCursor
, BufferBytes
, NumBytes
;
910 void *ReadPtr1
, *ReadPtr2
;
911 DWORD ReadCnt1
, ReadCnt2
;
915 if(!device
->Connected
)
918 FrameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
919 BufferBytes
= self
->BufferBytes
;
920 LastCursor
= self
->Cursor
;
922 hr
= IDirectSoundCaptureBuffer_GetCurrentPosition(self
->DSCbuffer
, NULL
, &ReadCursor
);
925 NumBytes
= (ReadCursor
-LastCursor
+ BufferBytes
) % BufferBytes
;
928 hr
= IDirectSoundCaptureBuffer_Lock(self
->DSCbuffer
, LastCursor
, NumBytes
,
929 &ReadPtr1
, &ReadCnt1
,
930 &ReadPtr2
, &ReadCnt2
, 0);
934 ll_ringbuffer_write(self
->Ring
, ReadPtr1
, ReadCnt1
/FrameSize
);
936 ll_ringbuffer_write(self
->Ring
, ReadPtr2
, ReadCnt2
/FrameSize
);
937 hr
= IDirectSoundCaptureBuffer_Unlock(self
->DSCbuffer
,
940 self
->Cursor
= (LastCursor
+ReadCnt1
+ReadCnt2
) % BufferBytes
;
945 ERR("update failed: 0x%08lx\n", hr
);
946 aluHandleDisconnect(device
);
950 return ll_ringbuffer_read_space(self
->Ring
);
954 static inline void AppendAllDevicesList2(const DevMap
*entry
)
955 { AppendAllDevicesList(alstr_get_cstr(entry
->name
)); }
956 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
957 { AppendCaptureDeviceList(alstr_get_cstr(entry
->name
)); }
959 typedef struct ALCdsoundBackendFactory
{
960 DERIVE_FROM_TYPE(ALCbackendFactory
);
961 } ALCdsoundBackendFactory
;
962 #define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } }
964 ALCbackendFactory
*ALCdsoundBackendFactory_getFactory(void);
966 static ALCboolean
ALCdsoundBackendFactory_init(ALCdsoundBackendFactory
*self
);
967 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory
*self
);
968 static ALCboolean
ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory
*self
, ALCbackend_Type type
);
969 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory
*self
, enum DevProbe type
);
970 static ALCbackend
* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
971 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory
);
974 ALCbackendFactory
*ALCdsoundBackendFactory_getFactory(void)
976 static ALCdsoundBackendFactory factory
= ALCDSOUNDBACKENDFACTORY_INITIALIZER
;
977 return STATIC_CAST(ALCbackendFactory
, &factory
);
981 static ALCboolean
ALCdsoundBackendFactory_init(ALCdsoundBackendFactory
* UNUSED(self
))
983 VECTOR_INIT(PlaybackDevices
);
984 VECTOR_INIT(CaptureDevices
);
991 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory
* UNUSED(self
))
993 clear_devlist(&PlaybackDevices
);
994 VECTOR_DEINIT(PlaybackDevices
);
996 clear_devlist(&CaptureDevices
);
997 VECTOR_DEINIT(CaptureDevices
);
1001 CloseLib(ds_handle
);
1006 static ALCboolean
ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1008 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1013 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory
* UNUSED(self
), enum DevProbe type
)
1017 /* Initialize COM to prevent name truncation */
1018 hrcom
= CoInitialize(NULL
);
1021 case ALL_DEVICE_PROBE
:
1022 clear_devlist(&PlaybackDevices
);
1023 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
1025 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr
);
1026 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1029 case CAPTURE_DEVICE_PROBE
:
1030 clear_devlist(&CaptureDevices
);
1031 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
1033 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr
);
1034 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1037 if(SUCCEEDED(hrcom
))
1041 static ALCbackend
* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1043 if(type
== ALCbackend_Playback
)
1045 ALCdsoundPlayback
*backend
;
1046 NEW_OBJ(backend
, ALCdsoundPlayback
)(device
);
1047 if(!backend
) return NULL
;
1048 return STATIC_CAST(ALCbackend
, backend
);
1051 if(type
== ALCbackend_Capture
)
1053 ALCdsoundCapture
*backend
;
1054 NEW_OBJ(backend
, ALCdsoundCapture
)(device
);
1055 if(!backend
) return NULL
;
1056 return STATIC_CAST(ALCbackend
, backend
);