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_
37 #include "ringbuffer.h"
42 #include "backends/base.h"
44 #ifndef DSSPEAKER_5POINT1
45 # define DSSPEAKER_5POINT1 0x00000006
47 #ifndef DSSPEAKER_5POINT1_BACK
48 # define DSSPEAKER_5POINT1_BACK 0x00000006
50 #ifndef DSSPEAKER_7POINT1
51 # define DSSPEAKER_7POINT1 0x00000007
53 #ifndef DSSPEAKER_7POINT1_SURROUND
54 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
56 #ifndef DSSPEAKER_5POINT1_SURROUND
57 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
61 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
62 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
64 #define DEVNAME_HEAD "OpenAL Soft on "
68 static void *ds_handle
;
69 static HRESULT (WINAPI
*pDirectSoundCreate
)(const GUID
*pcGuidDevice
, IDirectSound
**ppDS
, IUnknown
*pUnkOuter
);
70 static HRESULT (WINAPI
*pDirectSoundEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
71 static HRESULT (WINAPI
*pDirectSoundCaptureCreate
)(const GUID
*pcGuidDevice
, IDirectSoundCapture
**ppDSC
, IUnknown
*pUnkOuter
);
72 static HRESULT (WINAPI
*pDirectSoundCaptureEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
74 #define DirectSoundCreate pDirectSoundCreate
75 #define DirectSoundEnumerateW pDirectSoundEnumerateW
76 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
77 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
81 static ALCboolean
DSoundLoad(void)
86 ds_handle
= LoadLib("dsound.dll");
89 ERR("Failed to load dsound.dll\n");
93 #define LOAD_FUNC(f) do { \
94 p##f = GetSymbol(ds_handle, #f); \
96 CloseLib(ds_handle); \
101 LOAD_FUNC(DirectSoundCreate
);
102 LOAD_FUNC(DirectSoundEnumerateW
);
103 LOAD_FUNC(DirectSoundCaptureCreate
);
104 LOAD_FUNC(DirectSoundCaptureEnumerateW
);
112 #define MAX_UPDATES 128
118 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
120 static vector_DevMap PlaybackDevices
;
121 static vector_DevMap CaptureDevices
;
123 static void clear_devlist(vector_DevMap
*list
)
125 #define DEINIT_STR(i) AL_STRING_DEINIT((i)->name)
126 VECTOR_FOR_EACH(DevMap
, *list
, DEINIT_STR
);
127 VECTOR_RESIZE(*list
, 0, 0);
131 static BOOL CALLBACK
DSoundEnumDevices(GUID
*guid
, const WCHAR
*desc
, const WCHAR
* UNUSED(drvname
), void *data
)
133 vector_DevMap
*devices
= data
;
134 OLECHAR
*guidstr
= NULL
;
142 AL_STRING_INIT(entry
.name
);
149 alstr_copy_cstr(&entry
.name
, DEVNAME_HEAD
);
150 alstr_append_wcstr(&entry
.name
, desc
);
154 snprintf(str
, sizeof(str
), " #%d", count
+1);
155 alstr_append_cstr(&entry
.name
, str
);
158 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
159 VECTOR_FIND_IF(iter
, const DevMap
, *devices
, MATCH_ENTRY
);
160 if(iter
== VECTOR_END(*devices
)) break;
166 hr
= StringFromCLSID(guid
, &guidstr
);
169 TRACE("Got device \"%s\", GUID \"%ls\"\n", alstr_get_cstr(entry
.name
), guidstr
);
170 CoTaskMemFree(guidstr
);
173 VECTOR_PUSH_BACK(*devices
, entry
);
179 typedef struct ALCdsoundPlayback
{
180 DERIVE_FROM_TYPE(ALCbackend
);
183 IDirectSoundBuffer
*PrimaryBuffer
;
184 IDirectSoundBuffer
*Buffer
;
185 IDirectSoundNotify
*Notifies
;
188 ATOMIC(ALenum
) killNow
;
192 static int ALCdsoundPlayback_mixerProc(void *ptr
);
194 static void ALCdsoundPlayback_Construct(ALCdsoundPlayback
*self
, ALCdevice
*device
);
195 static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback
*self
);
196 static ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*name
);
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
);
216 self
->PrimaryBuffer
= NULL
;
218 self
->Notifies
= NULL
;
219 self
->NotifyEvent
= NULL
;
220 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
223 static void ALCdsoundPlayback_Destruct(ALCdsoundPlayback
*self
)
226 IDirectSoundNotify_Release(self
->Notifies
);
227 self
->Notifies
= NULL
;
229 IDirectSoundBuffer_Release(self
->Buffer
);
231 if(self
->PrimaryBuffer
!= NULL
)
232 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
233 self
->PrimaryBuffer
= NULL
;
236 IDirectSound_Release(self
->DS
);
238 if(self
->NotifyEvent
)
239 CloseHandle(self
->NotifyEvent
);
240 self
->NotifyEvent
= NULL
;
242 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
246 FORCE_ALIGN
static int ALCdsoundPlayback_mixerProc(void *ptr
)
248 ALCdsoundPlayback
*self
= ptr
;
249 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
251 DWORD LastCursor
= 0;
253 void *WritePtr1
, *WritePtr2
;
254 DWORD WriteCnt1
, WriteCnt2
;
255 BOOL Playing
= FALSE
;
262 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
264 memset(&DSBCaps
, 0, sizeof(DSBCaps
));
265 DSBCaps
.dwSize
= sizeof(DSBCaps
);
266 err
= IDirectSoundBuffer_GetCaps(self
->Buffer
, &DSBCaps
);
269 ERR("Failed to get buffer caps: 0x%lx\n", err
);
270 ALCdevice_Lock(device
);
271 aluHandleDisconnect(device
, "Failure retrieving playback buffer info: 0x%lx", err
);
272 ALCdevice_Unlock(device
);
276 FrameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
277 FragSize
= device
->UpdateSize
* FrameSize
;
279 IDirectSoundBuffer_GetCurrentPosition(self
->Buffer
, &LastCursor
, NULL
);
280 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
) &&
281 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
283 // Get current play cursor
284 IDirectSoundBuffer_GetCurrentPosition(self
->Buffer
, &PlayCursor
, NULL
);
285 avail
= (PlayCursor
-LastCursor
+DSBCaps
.dwBufferBytes
) % DSBCaps
.dwBufferBytes
;
291 err
= IDirectSoundBuffer_Play(self
->Buffer
, 0, 0, DSBPLAY_LOOPING
);
294 ERR("Failed to play buffer: 0x%lx\n", err
);
295 ALCdevice_Lock(device
);
296 aluHandleDisconnect(device
, "Failure starting playback: 0x%lx", err
);
297 ALCdevice_Unlock(device
);
303 avail
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
304 if(avail
!= WAIT_OBJECT_0
)
305 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
308 avail
-= avail
%FragSize
;
310 // Lock output buffer
313 err
= IDirectSoundBuffer_Lock(self
->Buffer
, LastCursor
, avail
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
315 // If the buffer is lost, restore it and lock
316 if(err
== DSERR_BUFFERLOST
)
318 WARN("Buffer lost, restoring...\n");
319 err
= IDirectSoundBuffer_Restore(self
->Buffer
);
324 err
= IDirectSoundBuffer_Lock(self
->Buffer
, 0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
328 // Successfully locked the output buffer
331 // If we have an active context, mix data directly into output buffer otherwise fill with silence
332 ALCdevice_Lock(device
);
333 aluMixData(device
, WritePtr1
, WriteCnt1
/FrameSize
);
334 aluMixData(device
, WritePtr2
, WriteCnt2
/FrameSize
);
335 ALCdevice_Unlock(device
);
337 // Unlock output buffer only when successfully locked
338 IDirectSoundBuffer_Unlock(self
->Buffer
, WritePtr1
, WriteCnt1
, WritePtr2
, WriteCnt2
);
342 ERR("Buffer lock error: %#lx\n", err
);
343 ALCdevice_Lock(device
);
344 aluHandleDisconnect(device
, "Failed to lock output buffer: 0x%lx", err
);
345 ALCdevice_Unlock(device
);
349 // Update old write cursor location
350 LastCursor
+= WriteCnt1
+WriteCnt2
;
351 LastCursor
%= DSBCaps
.dwBufferBytes
;
357 static ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*deviceName
)
359 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
360 const GUID
*guid
= NULL
;
363 if(VECTOR_SIZE(PlaybackDevices
) == 0)
365 /* Initialize COM to prevent name truncation */
366 hrcom
= CoInitialize(NULL
);
367 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
369 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
374 if(!deviceName
&& VECTOR_SIZE(PlaybackDevices
) > 0)
376 deviceName
= alstr_get_cstr(VECTOR_FRONT(PlaybackDevices
).name
);
377 guid
= &VECTOR_FRONT(PlaybackDevices
).guid
;
383 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
384 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
386 if(iter
== VECTOR_END(PlaybackDevices
))
387 return ALC_INVALID_VALUE
;
392 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
393 if(self
->NotifyEvent
== NULL
)
396 //DirectSound Init code
398 hr
= DirectSoundCreate(guid
, &self
->DS
, NULL
);
400 hr
= IDirectSound_SetCooperativeLevel(self
->DS
, GetForegroundWindow(), DSSCL_PRIORITY
);
404 IDirectSound_Release(self
->DS
);
406 if(self
->NotifyEvent
)
407 CloseHandle(self
->NotifyEvent
);
408 self
->NotifyEvent
= NULL
;
410 ERR("Device init failed: 0x%08lx\n", hr
);
411 return ALC_INVALID_VALUE
;
414 alstr_copy_cstr(&device
->DeviceName
, deviceName
);
419 static ALCboolean
ALCdsoundPlayback_reset(ALCdsoundPlayback
*self
)
421 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
422 DSBUFFERDESC DSBDescription
;
423 WAVEFORMATEXTENSIBLE OutputType
;
427 memset(&OutputType
, 0, sizeof(OutputType
));
430 IDirectSoundNotify_Release(self
->Notifies
);
431 self
->Notifies
= NULL
;
433 IDirectSoundBuffer_Release(self
->Buffer
);
435 if(self
->PrimaryBuffer
!= NULL
)
436 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
437 self
->PrimaryBuffer
= NULL
;
439 switch(device
->FmtType
)
442 device
->FmtType
= DevFmtUByte
;
445 if((device
->Flags
&DEVICE_SAMPLE_TYPE_REQUEST
))
449 device
->FmtType
= DevFmtShort
;
452 device
->FmtType
= DevFmtInt
;
460 hr
= IDirectSound_GetSpeakerConfig(self
->DS
, &speakers
);
463 speakers
= DSSPEAKER_CONFIG(speakers
);
464 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
466 if(speakers
== DSSPEAKER_MONO
)
467 device
->FmtChans
= DevFmtMono
;
468 else if(speakers
== DSSPEAKER_STEREO
|| speakers
== DSSPEAKER_HEADPHONE
)
469 device
->FmtChans
= DevFmtStereo
;
470 else if(speakers
== DSSPEAKER_QUAD
)
471 device
->FmtChans
= DevFmtQuad
;
472 else if(speakers
== DSSPEAKER_5POINT1_SURROUND
)
473 device
->FmtChans
= DevFmtX51
;
474 else if(speakers
== DSSPEAKER_5POINT1_BACK
)
475 device
->FmtChans
= DevFmtX51Rear
;
476 else if(speakers
== DSSPEAKER_7POINT1
|| speakers
== DSSPEAKER_7POINT1_SURROUND
)
477 device
->FmtChans
= DevFmtX71
;
479 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
481 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
482 speakers
== DSSPEAKER_HEADPHONE
);
484 switch(device
->FmtChans
)
487 OutputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
490 device
->FmtChans
= DevFmtStereo
;
493 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
497 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
498 SPEAKER_FRONT_RIGHT
|
503 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
504 SPEAKER_FRONT_RIGHT
|
505 SPEAKER_FRONT_CENTER
|
506 SPEAKER_LOW_FREQUENCY
|
511 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
512 SPEAKER_FRONT_RIGHT
|
513 SPEAKER_FRONT_CENTER
|
514 SPEAKER_LOW_FREQUENCY
|
519 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
520 SPEAKER_FRONT_RIGHT
|
521 SPEAKER_FRONT_CENTER
|
522 SPEAKER_LOW_FREQUENCY
|
523 SPEAKER_BACK_CENTER
|
528 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
529 SPEAKER_FRONT_RIGHT
|
530 SPEAKER_FRONT_CENTER
|
531 SPEAKER_LOW_FREQUENCY
|
541 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
542 OutputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
543 OutputType
.Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
544 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*OutputType
.Format
.wBitsPerSample
/8;
545 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
546 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*OutputType
.Format
.nBlockAlign
;
547 OutputType
.Format
.cbSize
= 0;
550 if(OutputType
.Format
.nChannels
> 2 || device
->FmtType
== DevFmtFloat
)
552 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
553 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
554 OutputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
555 if(device
->FmtType
== DevFmtFloat
)
556 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
558 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
560 if(self
->PrimaryBuffer
)
561 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
562 self
->PrimaryBuffer
= NULL
;
566 if(SUCCEEDED(hr
) && !self
->PrimaryBuffer
)
568 memset(&DSBDescription
,0,sizeof(DSBUFFERDESC
));
569 DSBDescription
.dwSize
=sizeof(DSBUFFERDESC
);
570 DSBDescription
.dwFlags
=DSBCAPS_PRIMARYBUFFER
;
571 hr
= IDirectSound_CreateSoundBuffer(self
->DS
, &DSBDescription
, &self
->PrimaryBuffer
, NULL
);
574 hr
= IDirectSoundBuffer_SetFormat(self
->PrimaryBuffer
,&OutputType
.Format
);
579 if(device
->NumUpdates
> MAX_UPDATES
)
581 device
->UpdateSize
= (device
->UpdateSize
*device
->NumUpdates
+
582 MAX_UPDATES
-1) / MAX_UPDATES
;
583 device
->NumUpdates
= MAX_UPDATES
;
586 memset(&DSBDescription
,0,sizeof(DSBUFFERDESC
));
587 DSBDescription
.dwSize
=sizeof(DSBUFFERDESC
);
588 DSBDescription
.dwFlags
=DSBCAPS_CTRLPOSITIONNOTIFY
|DSBCAPS_GETCURRENTPOSITION2
|DSBCAPS_GLOBALFOCUS
;
589 DSBDescription
.dwBufferBytes
=device
->UpdateSize
* device
->NumUpdates
*
590 OutputType
.Format
.nBlockAlign
;
591 DSBDescription
.lpwfxFormat
=&OutputType
.Format
;
592 hr
= IDirectSound_CreateSoundBuffer(self
->DS
, &DSBDescription
, &self
->Buffer
, NULL
);
593 if(FAILED(hr
) && device
->FmtType
== DevFmtFloat
)
595 device
->FmtType
= DevFmtShort
;
602 hr
= IDirectSoundBuffer_QueryInterface(self
->Buffer
, &IID_IDirectSoundNotify
, (void**)&self
->Notifies
);
605 DSBPOSITIONNOTIFY notifies
[MAX_UPDATES
];
608 for(i
= 0;i
< device
->NumUpdates
;++i
)
610 notifies
[i
].dwOffset
= i
* device
->UpdateSize
*
611 OutputType
.Format
.nBlockAlign
;
612 notifies
[i
].hEventNotify
= self
->NotifyEvent
;
614 if(IDirectSoundNotify_SetNotificationPositions(self
->Notifies
, device
->NumUpdates
, notifies
) != DS_OK
)
621 if(self
->Notifies
!= NULL
)
622 IDirectSoundNotify_Release(self
->Notifies
);
623 self
->Notifies
= NULL
;
624 if(self
->Buffer
!= NULL
)
625 IDirectSoundBuffer_Release(self
->Buffer
);
627 if(self
->PrimaryBuffer
!= NULL
)
628 IDirectSoundBuffer_Release(self
->PrimaryBuffer
);
629 self
->PrimaryBuffer
= NULL
;
633 ResetEvent(self
->NotifyEvent
);
634 SetDefaultWFXChannelOrder(device
);
639 static ALCboolean
ALCdsoundPlayback_start(ALCdsoundPlayback
*self
)
641 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
642 if(althrd_create(&self
->thread
, ALCdsoundPlayback_mixerProc
, self
) != althrd_success
)
648 static void ALCdsoundPlayback_stop(ALCdsoundPlayback
*self
)
652 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
654 althrd_join(self
->thread
, &res
);
656 IDirectSoundBuffer_Stop(self
->Buffer
);
661 typedef struct ALCdsoundCapture
{
662 DERIVE_FROM_TYPE(ALCbackend
);
664 IDirectSoundCapture
*DSC
;
665 IDirectSoundCaptureBuffer
*DSCbuffer
;
669 ll_ringbuffer_t
*Ring
;
672 static void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
);
673 static void ALCdsoundCapture_Destruct(ALCdsoundCapture
*self
);
674 static ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*name
);
675 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ALCboolean
, reset
)
676 static ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
);
677 static void ALCdsoundCapture_stop(ALCdsoundCapture
*self
);
678 static ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
679 static ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
);
680 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
681 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, lock
)
682 static DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, unlock
)
683 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture
)
685 DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture
);
687 static void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
)
689 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
690 SET_VTABLE2(ALCdsoundCapture
, ALCbackend
, self
);
693 self
->DSCbuffer
= NULL
;
697 static void ALCdsoundCapture_Destruct(ALCdsoundCapture
*self
)
699 ll_ringbuffer_free(self
->Ring
);
702 if(self
->DSCbuffer
!= NULL
)
704 IDirectSoundCaptureBuffer_Stop(self
->DSCbuffer
);
705 IDirectSoundCaptureBuffer_Release(self
->DSCbuffer
);
706 self
->DSCbuffer
= NULL
;
710 IDirectSoundCapture_Release(self
->DSC
);
713 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
717 static ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*deviceName
)
719 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
720 WAVEFORMATEXTENSIBLE InputType
;
721 DSCBUFFERDESC DSCBDescription
;
722 const GUID
*guid
= NULL
;
726 if(VECTOR_SIZE(CaptureDevices
) == 0)
728 /* Initialize COM to prevent name truncation */
729 hrcom
= CoInitialize(NULL
);
730 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
732 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
737 if(!deviceName
&& VECTOR_SIZE(CaptureDevices
) > 0)
739 deviceName
= alstr_get_cstr(VECTOR_FRONT(CaptureDevices
).name
);
740 guid
= &VECTOR_FRONT(CaptureDevices
).guid
;
746 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0)
747 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
749 if(iter
== VECTOR_END(CaptureDevices
))
750 return ALC_INVALID_VALUE
;
754 switch(device
->FmtType
)
759 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
760 return ALC_INVALID_ENUM
;
769 memset(&InputType
, 0, sizeof(InputType
));
770 switch(device
->FmtChans
)
773 InputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
776 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
780 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
781 SPEAKER_FRONT_RIGHT
|
786 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
787 SPEAKER_FRONT_RIGHT
|
788 SPEAKER_FRONT_CENTER
|
789 SPEAKER_LOW_FREQUENCY
|
794 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
795 SPEAKER_FRONT_RIGHT
|
796 SPEAKER_FRONT_CENTER
|
797 SPEAKER_LOW_FREQUENCY
|
802 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
803 SPEAKER_FRONT_RIGHT
|
804 SPEAKER_FRONT_CENTER
|
805 SPEAKER_LOW_FREQUENCY
|
806 SPEAKER_BACK_CENTER
|
811 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
812 SPEAKER_FRONT_RIGHT
|
813 SPEAKER_FRONT_CENTER
|
814 SPEAKER_LOW_FREQUENCY
|
821 WARN("%s capture not supported\n", DevFmtChannelsString(device
->FmtChans
));
822 return ALC_INVALID_ENUM
;
825 InputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
826 InputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
);
827 InputType
.Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
828 InputType
.Format
.nBlockAlign
= InputType
.Format
.nChannels
*InputType
.Format
.wBitsPerSample
/8;
829 InputType
.Format
.nSamplesPerSec
= device
->Frequency
;
830 InputType
.Format
.nAvgBytesPerSec
= InputType
.Format
.nSamplesPerSec
*InputType
.Format
.nBlockAlign
;
831 InputType
.Format
.cbSize
= 0;
832 InputType
.Samples
.wValidBitsPerSample
= InputType
.Format
.wBitsPerSample
;
833 if(device
->FmtType
== DevFmtFloat
)
834 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
836 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
838 if(InputType
.Format
.nChannels
> 2 || device
->FmtType
== DevFmtFloat
)
840 InputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
841 InputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
844 samples
= device
->UpdateSize
* device
->NumUpdates
;
845 samples
= maxu(samples
, 100 * device
->Frequency
/ 1000);
847 memset(&DSCBDescription
, 0, sizeof(DSCBUFFERDESC
));
848 DSCBDescription
.dwSize
= sizeof(DSCBUFFERDESC
);
849 DSCBDescription
.dwFlags
= 0;
850 DSCBDescription
.dwBufferBytes
= samples
* InputType
.Format
.nBlockAlign
;
851 DSCBDescription
.lpwfxFormat
= &InputType
.Format
;
853 //DirectSoundCapture Init code
854 hr
= DirectSoundCaptureCreate(guid
, &self
->DSC
, NULL
);
856 hr
= IDirectSoundCapture_CreateCaptureBuffer(self
->DSC
, &DSCBDescription
, &self
->DSCbuffer
, NULL
);
859 self
->Ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
,
860 InputType
.Format
.nBlockAlign
, false);
861 if(self
->Ring
== NULL
)
862 hr
= DSERR_OUTOFMEMORY
;
867 ERR("Device init failed: 0x%08lx\n", hr
);
869 ll_ringbuffer_free(self
->Ring
);
871 if(self
->DSCbuffer
!= NULL
)
872 IDirectSoundCaptureBuffer_Release(self
->DSCbuffer
);
873 self
->DSCbuffer
= NULL
;
875 IDirectSoundCapture_Release(self
->DSC
);
878 return ALC_INVALID_VALUE
;
881 self
->BufferBytes
= DSCBDescription
.dwBufferBytes
;
882 SetDefaultWFXChannelOrder(device
);
884 alstr_copy_cstr(&device
->DeviceName
, deviceName
);
889 static ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
)
893 hr
= IDirectSoundCaptureBuffer_Start(self
->DSCbuffer
, DSCBSTART_LOOPING
);
896 ERR("start failed: 0x%08lx\n", hr
);
897 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
,
898 "Failure starting capture: 0x%lx", hr
);
905 static void ALCdsoundCapture_stop(ALCdsoundCapture
*self
)
909 hr
= IDirectSoundCaptureBuffer_Stop(self
->DSCbuffer
);
912 ERR("stop failed: 0x%08lx\n", hr
);
913 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
,
914 "Failure stopping capture: 0x%lx", hr
);
918 static ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
920 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
924 static ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
)
926 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
927 DWORD ReadCursor
, LastCursor
, BufferBytes
, NumBytes
;
928 void *ReadPtr1
, *ReadPtr2
;
929 DWORD ReadCnt1
, ReadCnt2
;
933 if(!ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
936 FrameSize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
);
937 BufferBytes
= self
->BufferBytes
;
938 LastCursor
= self
->Cursor
;
940 hr
= IDirectSoundCaptureBuffer_GetCurrentPosition(self
->DSCbuffer
, NULL
, &ReadCursor
);
943 NumBytes
= (ReadCursor
-LastCursor
+ BufferBytes
) % BufferBytes
;
946 hr
= IDirectSoundCaptureBuffer_Lock(self
->DSCbuffer
, LastCursor
, NumBytes
,
947 &ReadPtr1
, &ReadCnt1
,
948 &ReadPtr2
, &ReadCnt2
, 0);
952 ll_ringbuffer_write(self
->Ring
, ReadPtr1
, ReadCnt1
/FrameSize
);
954 ll_ringbuffer_write(self
->Ring
, ReadPtr2
, ReadCnt2
/FrameSize
);
955 hr
= IDirectSoundCaptureBuffer_Unlock(self
->DSCbuffer
,
958 self
->Cursor
= (LastCursor
+ReadCnt1
+ReadCnt2
) % BufferBytes
;
963 ERR("update failed: 0x%08lx\n", hr
);
964 aluHandleDisconnect(device
, "Failure retrieving capture data: 0x%lx", hr
);
968 return (ALCuint
)ll_ringbuffer_read_space(self
->Ring
);
972 static inline void AppendAllDevicesList2(const DevMap
*entry
)
973 { AppendAllDevicesList(alstr_get_cstr(entry
->name
)); }
974 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
975 { AppendCaptureDeviceList(alstr_get_cstr(entry
->name
)); }
977 typedef struct ALCdsoundBackendFactory
{
978 DERIVE_FROM_TYPE(ALCbackendFactory
);
979 } ALCdsoundBackendFactory
;
980 #define ALCDSOUNDBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCdsoundBackendFactory, ALCbackendFactory) } }
982 ALCbackendFactory
*ALCdsoundBackendFactory_getFactory(void);
984 static ALCboolean
ALCdsoundBackendFactory_init(ALCdsoundBackendFactory
*self
);
985 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory
*self
);
986 static ALCboolean
ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory
*self
, ALCbackend_Type type
);
987 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory
*self
, enum DevProbe type
);
988 static ALCbackend
* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
989 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCdsoundBackendFactory
);
992 ALCbackendFactory
*ALCdsoundBackendFactory_getFactory(void)
994 static ALCdsoundBackendFactory factory
= ALCDSOUNDBACKENDFACTORY_INITIALIZER
;
995 return STATIC_CAST(ALCbackendFactory
, &factory
);
999 static ALCboolean
ALCdsoundBackendFactory_init(ALCdsoundBackendFactory
* UNUSED(self
))
1001 VECTOR_INIT(PlaybackDevices
);
1002 VECTOR_INIT(CaptureDevices
);
1009 static void ALCdsoundBackendFactory_deinit(ALCdsoundBackendFactory
* UNUSED(self
))
1011 clear_devlist(&PlaybackDevices
);
1012 VECTOR_DEINIT(PlaybackDevices
);
1014 clear_devlist(&CaptureDevices
);
1015 VECTOR_DEINIT(CaptureDevices
);
1019 CloseLib(ds_handle
);
1024 static ALCboolean
ALCdsoundBackendFactory_querySupport(ALCdsoundBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1026 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1031 static void ALCdsoundBackendFactory_probe(ALCdsoundBackendFactory
* UNUSED(self
), enum DevProbe type
)
1035 /* Initialize COM to prevent name truncation */
1036 hrcom
= CoInitialize(NULL
);
1039 case ALL_DEVICE_PROBE
:
1040 clear_devlist(&PlaybackDevices
);
1041 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
1043 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr
);
1044 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1047 case CAPTURE_DEVICE_PROBE
:
1048 clear_devlist(&CaptureDevices
);
1049 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
1051 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr
);
1052 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1055 if(SUCCEEDED(hrcom
))
1059 static ALCbackend
* ALCdsoundBackendFactory_createBackend(ALCdsoundBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1061 if(type
== ALCbackend_Playback
)
1063 ALCdsoundPlayback
*backend
;
1064 NEW_OBJ(backend
, ALCdsoundPlayback
)(device
);
1065 if(!backend
) return NULL
;
1066 return STATIC_CAST(ALCbackend
, backend
);
1069 if(type
== ALCbackend_Capture
)
1071 ALCdsoundCapture
*backend
;
1072 NEW_OBJ(backend
, ALCdsoundCapture
)(device
);
1073 if(!backend
) return NULL
;
1074 return STATIC_CAST(ALCbackend
, backend
);