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
23 #include "backends/dsound.h"
31 #ifndef _WAVEFORMATEXTENSIBLE_
44 #include "ringbuffer.h"
47 /* MinGW-w64 needs this for some unknown reason now. */
48 typedef const WAVEFORMATEX
*LPCWAVEFORMATEX
;
52 #ifndef DSSPEAKER_5POINT1
53 # define DSSPEAKER_5POINT1 0x00000006
55 #ifndef DSSPEAKER_5POINT1_BACK
56 # define DSSPEAKER_5POINT1_BACK 0x00000006
58 #ifndef DSSPEAKER_7POINT1
59 # define DSSPEAKER_7POINT1 0x00000007
61 #ifndef DSSPEAKER_7POINT1_SURROUND
62 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
64 #ifndef DSSPEAKER_5POINT1_SURROUND
65 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
69 /* Some headers seem to define these as macros for __uuidof, which is annoying
70 * since some headers don't declare them at all. Hopefully the ifdef is enough
71 * to tell if they need to be declared.
73 #ifndef KSDATAFORMAT_SUBTYPE_PCM
74 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
76 #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
77 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
82 #define DEVNAME_HEAD "OpenAL Soft on "
87 HRESULT (WINAPI
*pDirectSoundCreate
)(const GUID
*pcGuidDevice
, IDirectSound
**ppDS
, IUnknown
*pUnkOuter
);
88 HRESULT (WINAPI
*pDirectSoundEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
89 HRESULT (WINAPI
*pDirectSoundCaptureCreate
)(const GUID
*pcGuidDevice
, IDirectSoundCapture
**ppDSC
, IUnknown
*pUnkOuter
);
90 HRESULT (WINAPI
*pDirectSoundCaptureEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
93 #define DirectSoundCreate pDirectSoundCreate
94 #define DirectSoundEnumerateW pDirectSoundEnumerateW
95 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
96 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
101 bool DSoundLoad(void)
106 ds_handle
= LoadLib("dsound.dll");
109 ERR("Failed to load dsound.dll\n");
113 #define LOAD_FUNC(f) do { \
114 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
117 CloseLib(ds_handle); \
118 ds_handle = nullptr; \
122 LOAD_FUNC(DirectSoundCreate
);
123 LOAD_FUNC(DirectSoundEnumerateW
);
124 LOAD_FUNC(DirectSoundCaptureCreate
);
125 LOAD_FUNC(DirectSoundCaptureEnumerateW
);
133 #define MAX_UPDATES 128
139 template<typename T0
, typename T1
>
140 DevMap(T0
&& name_
, T1
&& guid_
)
141 : name
{std::forward
<T0
>(name_
)}, guid
{std::forward
<T1
>(guid_
)}
145 al::vector
<DevMap
> PlaybackDevices
;
146 al::vector
<DevMap
> CaptureDevices
;
148 bool checkName(const al::vector
<DevMap
> &list
, const std::string
&name
)
150 return std::find_if(list
.cbegin(), list
.cend(),
151 [&name
](const DevMap
&entry
) -> bool
152 { return entry
.name
== name
; }
156 BOOL CALLBACK
DSoundEnumDevices(GUID
*guid
, const WCHAR
*desc
, const WCHAR
* UNUSED(drvname
), void *data
)
161 auto& devices
= *reinterpret_cast<al::vector
<DevMap
>*>(data
);
162 const std::string basename
{DEVNAME_HEAD
+ wstr_to_utf8(desc
)};
165 std::string newname
{basename
};
166 while(checkName(devices
, newname
))
170 newname
+= std::to_string(++count
);
172 devices
.emplace_back(std::move(newname
), *guid
);
173 const DevMap
&newentry
= devices
.back();
175 OLECHAR
*guidstr
{nullptr};
176 HRESULT hr
{StringFromCLSID(*guid
, &guidstr
)};
179 TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry
.name
.c_str(), guidstr
);
180 CoTaskMemFree(guidstr
);
187 struct ALCdsoundPlayback final
: public ALCbackend
{
188 IDirectSound
*DS
{nullptr};
189 IDirectSoundBuffer
*PrimaryBuffer
{nullptr};
190 IDirectSoundBuffer
*Buffer
{nullptr};
191 IDirectSoundNotify
*Notifies
{nullptr};
192 HANDLE NotifyEvent
{nullptr};
194 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
198 int ALCdsoundPlayback_mixerProc(ALCdsoundPlayback
*self
);
200 void ALCdsoundPlayback_Construct(ALCdsoundPlayback
*self
, ALCdevice
*device
);
201 void ALCdsoundPlayback_Destruct(ALCdsoundPlayback
*self
);
202 ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*name
);
203 ALCboolean
ALCdsoundPlayback_reset(ALCdsoundPlayback
*self
);
204 ALCboolean
ALCdsoundPlayback_start(ALCdsoundPlayback
*self
);
205 void ALCdsoundPlayback_stop(ALCdsoundPlayback
*self
);
206 DECLARE_FORWARD2(ALCdsoundPlayback
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
207 DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, ALCuint
, availableSamples
)
208 DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
209 DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, void, lock
)
210 DECLARE_FORWARD(ALCdsoundPlayback
, ALCbackend
, void, unlock
)
211 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundPlayback
)
213 DEFINE_ALCBACKEND_VTABLE(ALCdsoundPlayback
);
216 void ALCdsoundPlayback_Construct(ALCdsoundPlayback
*self
, ALCdevice
*device
)
218 new (self
) ALCdsoundPlayback
{};
219 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
220 SET_VTABLE2(ALCdsoundPlayback
, ALCbackend
, self
);
223 void ALCdsoundPlayback_Destruct(ALCdsoundPlayback
*self
)
226 self
->Notifies
->Release();
227 self
->Notifies
= nullptr;
229 self
->Buffer
->Release();
230 self
->Buffer
= nullptr;
231 if(self
->PrimaryBuffer
)
232 self
->PrimaryBuffer
->Release();
233 self
->PrimaryBuffer
= nullptr;
238 if(self
->NotifyEvent
)
239 CloseHandle(self
->NotifyEvent
);
240 self
->NotifyEvent
= nullptr;
242 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
243 self
->~ALCdsoundPlayback();
247 FORCE_ALIGN
int ALCdsoundPlayback_mixerProc(ALCdsoundPlayback
*self
)
249 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
252 althrd_setname(MIXER_THREAD_NAME
);
254 IDirectSoundBuffer
*const Buffer
{self
->Buffer
};
257 DSBCaps
.dwSize
= sizeof(DSBCaps
);
258 HRESULT err
{Buffer
->GetCaps(&DSBCaps
)};
261 ERR("Failed to get buffer caps: 0x%lx\n", err
);
262 ALCdsoundPlayback_lock(self
);
263 aluHandleDisconnect(device
, "Failure retrieving playback buffer info: 0x%lx", err
);
264 ALCdsoundPlayback_unlock(self
);
268 ALsizei FrameSize
{FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
)};
269 DWORD FragSize
{device
->UpdateSize
* FrameSize
};
272 DWORD LastCursor
{0u};
273 Buffer
->GetCurrentPosition(&LastCursor
, nullptr);
274 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
275 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
277 // Get current play cursor
279 Buffer
->GetCurrentPosition(&PlayCursor
, nullptr);
280 DWORD avail
= (PlayCursor
-LastCursor
+DSBCaps
.dwBufferBytes
) % DSBCaps
.dwBufferBytes
;
286 err
= Buffer
->Play(0, 0, DSBPLAY_LOOPING
);
289 ERR("Failed to play buffer: 0x%lx\n", err
);
290 ALCdsoundPlayback_lock(self
);
291 aluHandleDisconnect(device
, "Failure starting playback: 0x%lx", err
);
292 ALCdsoundPlayback_unlock(self
);
298 avail
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
299 if(avail
!= WAIT_OBJECT_0
)
300 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
303 avail
-= avail
%FragSize
;
305 // Lock output buffer
306 void *WritePtr1
, *WritePtr2
;
307 DWORD WriteCnt1
{0u}, WriteCnt2
{0u};
308 err
= Buffer
->Lock(LastCursor
, avail
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
310 // If the buffer is lost, restore it and lock
311 if(err
== DSERR_BUFFERLOST
)
313 WARN("Buffer lost, restoring...\n");
314 err
= Buffer
->Restore();
319 err
= Buffer
->Lock(0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
,
320 &WritePtr2
, &WriteCnt2
, 0);
324 // Successfully locked the output buffer
327 // If we have an active context, mix data directly into output buffer otherwise fill with silence
328 ALCdsoundPlayback_lock(self
);
329 aluMixData(device
, WritePtr1
, WriteCnt1
/FrameSize
);
330 aluMixData(device
, WritePtr2
, WriteCnt2
/FrameSize
);
331 ALCdsoundPlayback_unlock(self
);
333 // Unlock output buffer only when successfully locked
334 Buffer
->Unlock(WritePtr1
, WriteCnt1
, WritePtr2
, WriteCnt2
);
338 ERR("Buffer lock error: %#lx\n", err
);
339 ALCdsoundPlayback_lock(self
);
340 aluHandleDisconnect(device
, "Failed to lock output buffer: 0x%lx", err
);
341 ALCdsoundPlayback_unlock(self
);
345 // Update old write cursor location
346 LastCursor
+= WriteCnt1
+WriteCnt2
;
347 LastCursor
%= DSBCaps
.dwBufferBytes
;
353 ALCenum
ALCdsoundPlayback_open(ALCdsoundPlayback
*self
, const ALCchar
*deviceName
)
355 ALCdevice
*device
{STATIC_CAST(ALCbackend
, self
)->mDevice
};
358 if(PlaybackDevices
.empty())
360 /* Initialize COM to prevent name truncation */
361 HRESULT hrcom
{CoInitialize(nullptr)};
362 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
364 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
369 const GUID
*guid
{nullptr};
370 if(!deviceName
&& !PlaybackDevices
.empty())
372 deviceName
= PlaybackDevices
[0].name
.c_str();
373 guid
= &PlaybackDevices
[0].guid
;
377 auto iter
= std::find_if(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(),
378 [deviceName
](const DevMap
&entry
) -> bool
379 { return entry
.name
== deviceName
; }
381 if(iter
== PlaybackDevices
.cend())
382 return ALC_INVALID_VALUE
;
387 self
->NotifyEvent
= CreateEventW(nullptr, FALSE
, FALSE
, nullptr);
388 if(!self
->NotifyEvent
) hr
= E_FAIL
;
390 //DirectSound Init code
392 hr
= DirectSoundCreate(guid
, &self
->DS
, nullptr);
394 hr
= self
->DS
->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY
);
397 ERR("Device init failed: 0x%08lx\n", hr
);
398 return ALC_INVALID_VALUE
;
401 device
->DeviceName
= deviceName
;
405 ALCboolean
ALCdsoundPlayback_reset(ALCdsoundPlayback
*self
)
407 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
410 self
->Notifies
->Release();
411 self
->Notifies
= nullptr;
413 self
->Buffer
->Release();
414 self
->Buffer
= nullptr;
415 if(self
->PrimaryBuffer
)
416 self
->PrimaryBuffer
->Release();
417 self
->PrimaryBuffer
= nullptr;
419 switch(device
->FmtType
)
422 device
->FmtType
= DevFmtUByte
;
425 if((device
->Flags
&DEVICE_SAMPLE_TYPE_REQUEST
))
429 device
->FmtType
= DevFmtShort
;
432 device
->FmtType
= DevFmtInt
;
440 WAVEFORMATEXTENSIBLE OutputType
{};
442 HRESULT hr
{self
->DS
->GetSpeakerConfig(&speakers
)};
445 speakers
= DSSPEAKER_CONFIG(speakers
);
446 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
448 if(speakers
== DSSPEAKER_MONO
)
449 device
->FmtChans
= DevFmtMono
;
450 else if(speakers
== DSSPEAKER_STEREO
|| speakers
== DSSPEAKER_HEADPHONE
)
451 device
->FmtChans
= DevFmtStereo
;
452 else if(speakers
== DSSPEAKER_QUAD
)
453 device
->FmtChans
= DevFmtQuad
;
454 else if(speakers
== DSSPEAKER_5POINT1_SURROUND
)
455 device
->FmtChans
= DevFmtX51
;
456 else if(speakers
== DSSPEAKER_5POINT1_BACK
)
457 device
->FmtChans
= DevFmtX51Rear
;
458 else if(speakers
== DSSPEAKER_7POINT1
|| speakers
== DSSPEAKER_7POINT1_SURROUND
)
459 device
->FmtChans
= DevFmtX71
;
461 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
463 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
464 speakers
== DSSPEAKER_HEADPHONE
);
466 switch(device
->FmtChans
)
469 OutputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
472 device
->FmtChans
= DevFmtStereo
;
475 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
479 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
480 SPEAKER_FRONT_RIGHT
|
485 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
486 SPEAKER_FRONT_RIGHT
|
487 SPEAKER_FRONT_CENTER
|
488 SPEAKER_LOW_FREQUENCY
|
493 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
494 SPEAKER_FRONT_RIGHT
|
495 SPEAKER_FRONT_CENTER
|
496 SPEAKER_LOW_FREQUENCY
|
501 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
502 SPEAKER_FRONT_RIGHT
|
503 SPEAKER_FRONT_CENTER
|
504 SPEAKER_LOW_FREQUENCY
|
505 SPEAKER_BACK_CENTER
|
510 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
511 SPEAKER_FRONT_RIGHT
|
512 SPEAKER_FRONT_CENTER
|
513 SPEAKER_LOW_FREQUENCY
|
523 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
524 OutputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
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
;
540 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
542 if(self
->PrimaryBuffer
)
543 self
->PrimaryBuffer
->Release();
544 self
->PrimaryBuffer
= nullptr;
548 if(SUCCEEDED(hr
) && !self
->PrimaryBuffer
)
550 DSBUFFERDESC DSBDescription
{};
551 DSBDescription
.dwSize
= sizeof(DSBDescription
);
552 DSBDescription
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
553 hr
= self
->DS
->CreateSoundBuffer(&DSBDescription
, &self
->PrimaryBuffer
, nullptr);
556 hr
= self
->PrimaryBuffer
->SetFormat(&OutputType
.Format
);
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 DSBUFFERDESC DSBDescription
{};
569 DSBDescription
.dwSize
= sizeof(DSBDescription
);
570 DSBDescription
.dwFlags
= DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_GETCURRENTPOSITION2
|
572 DSBDescription
.dwBufferBytes
= device
->UpdateSize
* device
->NumUpdates
*
573 OutputType
.Format
.nBlockAlign
;
574 DSBDescription
.lpwfxFormat
= &OutputType
.Format
;
576 hr
= self
->DS
->CreateSoundBuffer(&DSBDescription
, &self
->Buffer
, nullptr);
577 if(FAILED(hr
) && device
->FmtType
== DevFmtFloat
)
579 device
->FmtType
= DevFmtShort
;
587 hr
= self
->Buffer
->QueryInterface(IID_IDirectSoundNotify
, &ptr
);
590 auto Notifies
= reinterpret_cast<IDirectSoundNotify
*>(ptr
);
591 self
->Notifies
= Notifies
;
593 device
->NumUpdates
= minu(device
->NumUpdates
, MAX_UPDATES
);
595 std::array
<DSBPOSITIONNOTIFY
,MAX_UPDATES
> nots
;
596 for(ALuint i
{0};i
< device
->NumUpdates
;++i
)
598 nots
[i
].dwOffset
= i
* device
->UpdateSize
* OutputType
.Format
.nBlockAlign
;
599 nots
[i
].hEventNotify
= self
->NotifyEvent
;
601 if(Notifies
->SetNotificationPositions(device
->NumUpdates
, nots
.data()) != DS_OK
)
609 self
->Notifies
->Release();
610 self
->Notifies
= nullptr;
612 self
->Buffer
->Release();
613 self
->Buffer
= nullptr;
614 if(self
->PrimaryBuffer
)
615 self
->PrimaryBuffer
->Release();
616 self
->PrimaryBuffer
= nullptr;
620 ResetEvent(self
->NotifyEvent
);
621 SetDefaultWFXChannelOrder(device
);
626 ALCboolean
ALCdsoundPlayback_start(ALCdsoundPlayback
*self
)
629 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
630 self
->mThread
= std::thread(ALCdsoundPlayback_mixerProc
, self
);
633 catch(std::exception
& e
) {
634 ERR("Failed to start mixing thread: %s\n", e
.what());
641 void ALCdsoundPlayback_stop(ALCdsoundPlayback
*self
)
643 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
) || !self
->mThread
.joinable())
646 self
->mThread
.join();
648 self
->Buffer
->Stop();
652 struct ALCdsoundCapture final
: public ALCbackend
{
653 IDirectSoundCapture
*DSC
{nullptr};
654 IDirectSoundCaptureBuffer
*DSCbuffer
{nullptr};
655 DWORD BufferBytes
{0u};
658 ll_ringbuffer_t
*Ring
{nullptr};
661 void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
);
662 void ALCdsoundCapture_Destruct(ALCdsoundCapture
*self
);
663 ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*name
);
664 DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ALCboolean
, reset
)
665 ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
);
666 void ALCdsoundCapture_stop(ALCdsoundCapture
*self
);
667 ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
668 ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
);
669 DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
670 DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, lock
)
671 DECLARE_FORWARD(ALCdsoundCapture
, ALCbackend
, void, unlock
)
672 DECLARE_DEFAULT_ALLOCATORS(ALCdsoundCapture
)
673 DEFINE_ALCBACKEND_VTABLE(ALCdsoundCapture
);
675 void ALCdsoundCapture_Construct(ALCdsoundCapture
*self
, ALCdevice
*device
)
677 new (self
) ALCdsoundCapture
{};
678 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
679 SET_VTABLE2(ALCdsoundCapture
, ALCbackend
, self
);
682 void ALCdsoundCapture_Destruct(ALCdsoundCapture
*self
)
684 ll_ringbuffer_free(self
->Ring
);
685 self
->Ring
= nullptr;
689 self
->DSCbuffer
->Stop();
690 self
->DSCbuffer
->Release();
691 self
->DSCbuffer
= nullptr;
695 self
->DSC
->Release();
698 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
699 self
->~ALCdsoundCapture();
703 ALCenum
ALCdsoundCapture_open(ALCdsoundCapture
*self
, const ALCchar
*deviceName
)
705 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
708 if(CaptureDevices
.empty())
710 /* Initialize COM to prevent name truncation */
711 HRESULT hrcom
{CoInitialize(nullptr)};
712 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
714 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
719 const GUID
*guid
{nullptr};
720 if(!deviceName
&& !CaptureDevices
.empty())
722 deviceName
= CaptureDevices
[0].name
.c_str();
723 guid
= &CaptureDevices
[0].guid
;
727 auto iter
= std::find_if(CaptureDevices
.cbegin(), CaptureDevices
.cend(),
728 [deviceName
](const DevMap
&entry
) -> bool
729 { return entry
.name
== deviceName
; }
731 if(iter
== CaptureDevices
.cend())
732 return ALC_INVALID_VALUE
;
736 switch(device
->FmtType
)
741 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
742 return ALC_INVALID_ENUM
;
751 WAVEFORMATEXTENSIBLE InputType
{};
752 switch(device
->FmtChans
)
755 InputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
758 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
762 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
763 SPEAKER_FRONT_RIGHT
|
768 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
769 SPEAKER_FRONT_RIGHT
|
770 SPEAKER_FRONT_CENTER
|
771 SPEAKER_LOW_FREQUENCY
|
776 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
777 SPEAKER_FRONT_RIGHT
|
778 SPEAKER_FRONT_CENTER
|
779 SPEAKER_LOW_FREQUENCY
|
784 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
785 SPEAKER_FRONT_RIGHT
|
786 SPEAKER_FRONT_CENTER
|
787 SPEAKER_LOW_FREQUENCY
|
788 SPEAKER_BACK_CENTER
|
793 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
794 SPEAKER_FRONT_RIGHT
|
795 SPEAKER_FRONT_CENTER
|
796 SPEAKER_LOW_FREQUENCY
|
803 WARN("%s capture not supported\n", DevFmtChannelsString(device
->FmtChans
));
804 return ALC_INVALID_ENUM
;
807 InputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
808 InputType
.Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
809 InputType
.Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
810 InputType
.Format
.nBlockAlign
= InputType
.Format
.nChannels
*InputType
.Format
.wBitsPerSample
/8;
811 InputType
.Format
.nSamplesPerSec
= device
->Frequency
;
812 InputType
.Format
.nAvgBytesPerSec
= InputType
.Format
.nSamplesPerSec
*InputType
.Format
.nBlockAlign
;
813 InputType
.Format
.cbSize
= 0;
814 InputType
.Samples
.wValidBitsPerSample
= InputType
.Format
.wBitsPerSample
;
815 if(device
->FmtType
== DevFmtFloat
)
816 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
818 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
820 if(InputType
.Format
.nChannels
> 2 || device
->FmtType
== DevFmtFloat
)
822 InputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
823 InputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
826 ALuint samples
{device
->UpdateSize
* device
->NumUpdates
};
827 samples
= maxu(samples
, 100 * device
->Frequency
/ 1000);
829 DSCBUFFERDESC DSCBDescription
{};
830 DSCBDescription
.dwSize
= sizeof(DSCBDescription
);
831 DSCBDescription
.dwFlags
= 0;
832 DSCBDescription
.dwBufferBytes
= samples
* InputType
.Format
.nBlockAlign
;
833 DSCBDescription
.lpwfxFormat
= &InputType
.Format
;
835 //DirectSoundCapture Init code
836 hr
= DirectSoundCaptureCreate(guid
, &self
->DSC
, nullptr);
838 self
->DSC
->CreateCaptureBuffer(&DSCBDescription
, &self
->DSCbuffer
, nullptr);
841 self
->Ring
= ll_ringbuffer_create(device
->UpdateSize
*device
->NumUpdates
,
842 InputType
.Format
.nBlockAlign
, false);
843 if(!self
->Ring
) hr
= DSERR_OUTOFMEMORY
;
848 ERR("Device init failed: 0x%08lx\n", hr
);
850 ll_ringbuffer_free(self
->Ring
);
851 self
->Ring
= nullptr;
853 self
->DSCbuffer
->Release();
854 self
->DSCbuffer
= nullptr;
856 self
->DSC
->Release();
859 return ALC_INVALID_VALUE
;
862 self
->BufferBytes
= DSCBDescription
.dwBufferBytes
;
863 SetDefaultWFXChannelOrder(device
);
865 device
->DeviceName
= deviceName
;
869 ALCboolean
ALCdsoundCapture_start(ALCdsoundCapture
*self
)
871 HRESULT hr
{self
->DSCbuffer
->Start(DSCBSTART_LOOPING
)};
874 ERR("start failed: 0x%08lx\n", hr
);
875 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
,
876 "Failure starting capture: 0x%lx", hr
);
883 void ALCdsoundCapture_stop(ALCdsoundCapture
*self
)
885 HRESULT hr
{self
->DSCbuffer
->Stop()};
888 ERR("stop failed: 0x%08lx\n", hr
);
889 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
,
890 "Failure stopping capture: 0x%lx", hr
);
894 ALCenum
ALCdsoundCapture_captureSamples(ALCdsoundCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
896 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
900 ALCuint
ALCdsoundCapture_availableSamples(ALCdsoundCapture
*self
)
902 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
904 if(!ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
905 return ll_ringbuffer_read_space(self
->Ring
);
907 ALsizei FrameSize
{FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
)};
908 DWORD BufferBytes
{self
->BufferBytes
};
909 DWORD LastCursor
{self
->Cursor
};
912 void *ReadPtr1
, *ReadPtr2
;
913 DWORD ReadCnt1
, ReadCnt2
;
914 HRESULT hr
{self
->DSCbuffer
->GetCurrentPosition(nullptr, &ReadCursor
)};
917 DWORD NumBytes
{(ReadCursor
-LastCursor
+ BufferBytes
) % BufferBytes
};
918 if(!NumBytes
) return ll_ringbuffer_read_space(self
->Ring
);
919 hr
= self
->DSCbuffer
->Lock(LastCursor
, NumBytes
, &ReadPtr1
, &ReadCnt1
,
920 &ReadPtr2
, &ReadCnt2
, 0);
924 ll_ringbuffer_write(self
->Ring
, ReadPtr1
, ReadCnt1
/FrameSize
);
925 if(ReadPtr2
!= nullptr)
926 ll_ringbuffer_write(self
->Ring
, ReadPtr2
, ReadCnt2
/FrameSize
);
927 hr
= self
->DSCbuffer
->Unlock(ReadPtr1
, ReadCnt1
, ReadPtr2
, ReadCnt2
);
928 self
->Cursor
= (LastCursor
+ReadCnt1
+ReadCnt2
) % BufferBytes
;
933 ERR("update failed: 0x%08lx\n", hr
);
934 aluHandleDisconnect(device
, "Failure retrieving capture data: 0x%lx", hr
);
937 return ll_ringbuffer_read_space(self
->Ring
);
943 BackendFactory
&DSoundBackendFactory::getFactory()
945 static DSoundBackendFactory factory
{};
949 bool DSoundBackendFactory::init()
950 { return DSoundLoad(); }
952 void DSoundBackendFactory::deinit()
954 PlaybackDevices
.clear();
955 CaptureDevices
.clear();
964 bool DSoundBackendFactory::querySupport(ALCbackend_Type type
)
965 { return (type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
); }
967 void DSoundBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
969 auto add_device
= [outnames
](const DevMap
&entry
) -> void
971 /* +1 to also append the null char (to ensure a null-separated list and
972 * double-null terminated list).
974 outnames
->append(entry
.name
.c_str(), entry
.name
.length()+1);
977 /* Initialize COM to prevent name truncation */
979 HRESULT hrcom
{CoInitialize(nullptr)};
982 case ALL_DEVICE_PROBE
:
983 PlaybackDevices
.clear();
984 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
986 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr
);
987 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
990 case CAPTURE_DEVICE_PROBE
:
991 CaptureDevices
.clear();
992 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
994 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr
);
995 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
1002 ALCbackend
*DSoundBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
1004 if(type
== ALCbackend_Playback
)
1006 ALCdsoundPlayback
*backend
;
1007 NEW_OBJ(backend
, ALCdsoundPlayback
)(device
);
1008 if(!backend
) return nullptr;
1009 return STATIC_CAST(ALCbackend
, backend
);
1012 if(type
== ALCbackend_Capture
)
1014 ALCdsoundCapture
*backend
;
1015 NEW_OBJ(backend
, ALCdsoundCapture
)(device
);
1016 if(!backend
) return nullptr;
1017 return STATIC_CAST(ALCbackend
, backend
);