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"
25 #define WIN32_LEAN_AND_MEAN
34 #ifndef _WAVEFORMATEXTENSIBLE_
53 #include "ringbuffer.h"
57 /* MinGW-w64 needs this for some unknown reason now. */
58 using LPCWAVEFORMATEX
= const WAVEFORMATEX
*;
62 #ifndef DSSPEAKER_5POINT1
63 # define DSSPEAKER_5POINT1 0x00000006
65 #ifndef DSSPEAKER_5POINT1_BACK
66 # define DSSPEAKER_5POINT1_BACK 0x00000006
68 #ifndef DSSPEAKER_7POINT1
69 # define DSSPEAKER_7POINT1 0x00000007
71 #ifndef DSSPEAKER_7POINT1_SURROUND
72 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
74 #ifndef DSSPEAKER_5POINT1_SURROUND
75 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
79 /* Some headers seem to define these as macros for __uuidof, which is annoying
80 * since some headers don't declare them at all. Hopefully the ifdef is enough
81 * to tell if they need to be declared.
83 #ifndef KSDATAFORMAT_SUBTYPE_PCM
84 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
86 #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
87 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
92 #define DEVNAME_HEAD "OpenAL Soft on "
97 HRESULT (WINAPI
*pDirectSoundCreate
)(const GUID
*pcGuidDevice
, IDirectSound
**ppDS
, IUnknown
*pUnkOuter
);
98 HRESULT (WINAPI
*pDirectSoundEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
99 HRESULT (WINAPI
*pDirectSoundCaptureCreate
)(const GUID
*pcGuidDevice
, IDirectSoundCapture
**ppDSC
, IUnknown
*pUnkOuter
);
100 HRESULT (WINAPI
*pDirectSoundCaptureEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
102 #ifndef IN_IDE_PARSER
103 #define DirectSoundCreate pDirectSoundCreate
104 #define DirectSoundEnumerateW pDirectSoundEnumerateW
105 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
106 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
111 #define MONO SPEAKER_FRONT_CENTER
112 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
113 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
114 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
115 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
116 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
117 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
119 #define MAX_UPDATES 128
125 template<typename T0
, typename T1
>
126 DevMap(T0
&& name_
, T1
&& guid_
)
127 : name
{std::forward
<T0
>(name_
)}, guid
{std::forward
<T1
>(guid_
)}
131 al::vector
<DevMap
> PlaybackDevices
;
132 al::vector
<DevMap
> CaptureDevices
;
134 bool checkName(const al::vector
<DevMap
> &list
, const std::string
&name
)
136 auto match_name
= [&name
](const DevMap
&entry
) -> bool
137 { return entry
.name
== name
; };
138 return std::find_if(list
.cbegin(), list
.cend(), match_name
) != list
.cend();
141 BOOL CALLBACK
DSoundEnumDevices(GUID
*guid
, const WCHAR
*desc
, const WCHAR
*, void *data
) noexcept
146 auto& devices
= *static_cast<al::vector
<DevMap
>*>(data
);
147 const std::string basename
{DEVNAME_HEAD
+ wstr_to_utf8(desc
)};
150 std::string newname
{basename
};
151 while(checkName(devices
, newname
))
155 newname
+= std::to_string(++count
);
157 devices
.emplace_back(std::move(newname
), *guid
);
158 const DevMap
&newentry
= devices
.back();
160 OLECHAR
*guidstr
{nullptr};
161 HRESULT hr
{StringFromCLSID(*guid
, &guidstr
)};
164 TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry
.name
.c_str(), guidstr
);
165 CoTaskMemFree(guidstr
);
172 struct DSoundPlayback final
: public BackendBase
{
173 DSoundPlayback(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
174 ~DSoundPlayback() override
;
178 void open(const ALCchar
*name
) override
;
179 bool reset() override
;
180 void start() override
;
181 void stop() override
;
183 IDirectSound
*mDS
{nullptr};
184 IDirectSoundBuffer
*mPrimaryBuffer
{nullptr};
185 IDirectSoundBuffer
*mBuffer
{nullptr};
186 IDirectSoundNotify
*mNotifies
{nullptr};
187 HANDLE mNotifyEvent
{nullptr};
189 std::atomic
<bool> mKillNow
{true};
192 DEF_NEWDEL(DSoundPlayback
)
195 DSoundPlayback::~DSoundPlayback()
198 mNotifies
->Release();
204 mPrimaryBuffer
->Release();
205 mPrimaryBuffer
= nullptr;
211 CloseHandle(mNotifyEvent
);
212 mNotifyEvent
= nullptr;
216 FORCE_ALIGN
int DSoundPlayback::mixerProc()
219 althrd_setname(MIXER_THREAD_NAME
);
222 DSBCaps
.dwSize
= sizeof(DSBCaps
);
223 HRESULT err
{mBuffer
->GetCaps(&DSBCaps
)};
226 ERR("Failed to get buffer caps: 0x%lx\n", err
);
227 mDevice
->handleDisconnect("Failure retrieving playback buffer info: 0x%lx", err
);
231 const size_t FrameStep
{mDevice
->channelsFromFmt()};
232 ALuint FrameSize
{mDevice
->frameSizeFromFmt()};
233 DWORD FragSize
{mDevice
->UpdateSize
* FrameSize
};
236 DWORD LastCursor
{0u};
237 mBuffer
->GetCurrentPosition(&LastCursor
, nullptr);
238 while(!mKillNow
.load(std::memory_order_acquire
) &&
239 mDevice
->Connected
.load(std::memory_order_acquire
))
241 // Get current play cursor
243 mBuffer
->GetCurrentPosition(&PlayCursor
, nullptr);
244 DWORD avail
= (PlayCursor
-LastCursor
+DSBCaps
.dwBufferBytes
) % DSBCaps
.dwBufferBytes
;
250 err
= mBuffer
->Play(0, 0, DSBPLAY_LOOPING
);
253 ERR("Failed to play buffer: 0x%lx\n", err
);
254 mDevice
->handleDisconnect("Failure starting playback: 0x%lx", err
);
260 avail
= WaitForSingleObjectEx(mNotifyEvent
, 2000, FALSE
);
261 if(avail
!= WAIT_OBJECT_0
)
262 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
265 avail
-= avail
%FragSize
;
267 // Lock output buffer
268 void *WritePtr1
, *WritePtr2
;
269 DWORD WriteCnt1
{0u}, WriteCnt2
{0u};
270 err
= mBuffer
->Lock(LastCursor
, avail
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
272 // If the buffer is lost, restore it and lock
273 if(err
== DSERR_BUFFERLOST
)
275 WARN("Buffer lost, restoring...\n");
276 err
= mBuffer
->Restore();
281 err
= mBuffer
->Lock(0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
,
282 &WritePtr2
, &WriteCnt2
, 0);
288 mDevice
->renderSamples(WritePtr1
, WriteCnt1
/FrameSize
, FrameStep
);
290 mDevice
->renderSamples(WritePtr2
, WriteCnt2
/FrameSize
, FrameStep
);
292 mBuffer
->Unlock(WritePtr1
, WriteCnt1
, WritePtr2
, WriteCnt2
);
296 ERR("Buffer lock error: %#lx\n", err
);
297 mDevice
->handleDisconnect("Failed to lock output buffer: 0x%lx", err
);
301 // Update old write cursor location
302 LastCursor
+= WriteCnt1
+WriteCnt2
;
303 LastCursor
%= DSBCaps
.dwBufferBytes
;
309 void DSoundPlayback::open(const ALCchar
*name
)
312 if(PlaybackDevices
.empty())
314 /* Initialize COM to prevent name truncation */
315 HRESULT hrcom
{CoInitialize(nullptr)};
316 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
318 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
323 const GUID
*guid
{nullptr};
324 if(!name
&& !PlaybackDevices
.empty())
326 name
= PlaybackDevices
[0].name
.c_str();
327 guid
= &PlaybackDevices
[0].guid
;
331 auto iter
= std::find_if(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(),
332 [name
](const DevMap
&entry
) -> bool
333 { return entry
.name
== name
; }
335 if(iter
== PlaybackDevices
.cend())
336 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
341 mNotifyEvent
= CreateEventW(nullptr, FALSE
, FALSE
, nullptr);
342 if(!mNotifyEvent
) hr
= E_FAIL
;
344 //DirectSound Init code
346 hr
= DirectSoundCreate(guid
, &mDS
, nullptr);
348 hr
= mDS
->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY
);
350 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device init failed: 0x%08lx", hr
};
352 mDevice
->DeviceName
= name
;
355 bool DSoundPlayback::reset()
358 mNotifies
->Release();
364 mPrimaryBuffer
->Release();
365 mPrimaryBuffer
= nullptr;
367 switch(mDevice
->FmtType
)
370 mDevice
->FmtType
= DevFmtUByte
;
373 if(mDevice
->Flags
.get
<SampleTypeRequest
>())
377 mDevice
->FmtType
= DevFmtShort
;
380 mDevice
->FmtType
= DevFmtInt
;
388 WAVEFORMATEXTENSIBLE OutputType
{};
390 HRESULT hr
{mDS
->GetSpeakerConfig(&speakers
)};
393 speakers
= DSSPEAKER_CONFIG(speakers
);
394 if(!mDevice
->Flags
.get
<ChannelsRequest
>())
396 if(speakers
== DSSPEAKER_MONO
)
397 mDevice
->FmtChans
= DevFmtMono
;
398 else if(speakers
== DSSPEAKER_STEREO
|| speakers
== DSSPEAKER_HEADPHONE
)
399 mDevice
->FmtChans
= DevFmtStereo
;
400 else if(speakers
== DSSPEAKER_QUAD
)
401 mDevice
->FmtChans
= DevFmtQuad
;
402 else if(speakers
== DSSPEAKER_5POINT1_SURROUND
)
403 mDevice
->FmtChans
= DevFmtX51
;
404 else if(speakers
== DSSPEAKER_5POINT1_BACK
)
405 mDevice
->FmtChans
= DevFmtX51Rear
;
406 else if(speakers
== DSSPEAKER_7POINT1
|| speakers
== DSSPEAKER_7POINT1_SURROUND
)
407 mDevice
->FmtChans
= DevFmtX71
;
409 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
411 mDevice
->IsHeadphones
= mDevice
->FmtChans
== DevFmtStereo
412 && speakers
== DSSPEAKER_HEADPHONE
;
414 switch(mDevice
->FmtChans
)
416 case DevFmtMono
: OutputType
.dwChannelMask
= MONO
; break;
417 case DevFmtAmbi3D
: mDevice
->FmtChans
= DevFmtStereo
;
419 case DevFmtStereo
: OutputType
.dwChannelMask
= STEREO
; break;
420 case DevFmtQuad
: OutputType
.dwChannelMask
= QUAD
; break;
421 case DevFmtX51
: OutputType
.dwChannelMask
= X5DOT1
; break;
422 case DevFmtX51Rear
: OutputType
.dwChannelMask
= X5DOT1REAR
; break;
423 case DevFmtX61
: OutputType
.dwChannelMask
= X6DOT1
; break;
424 case DevFmtX71
: OutputType
.dwChannelMask
= X7DOT1
; break;
429 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
430 OutputType
.Format
.nChannels
= static_cast<WORD
>(mDevice
->channelsFromFmt());
431 OutputType
.Format
.wBitsPerSample
= static_cast<WORD
>(mDevice
->bytesFromFmt() * 8);
432 OutputType
.Format
.nBlockAlign
= static_cast<WORD
>(OutputType
.Format
.nChannels
*
433 OutputType
.Format
.wBitsPerSample
/ 8);
434 OutputType
.Format
.nSamplesPerSec
= mDevice
->Frequency
;
435 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
436 OutputType
.Format
.nBlockAlign
;
437 OutputType
.Format
.cbSize
= 0;
440 if(OutputType
.Format
.nChannels
> 2 || mDevice
->FmtType
== DevFmtFloat
)
442 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
443 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
444 OutputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
445 if(mDevice
->FmtType
== DevFmtFloat
)
446 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
448 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
451 mPrimaryBuffer
->Release();
452 mPrimaryBuffer
= nullptr;
456 if(SUCCEEDED(hr
) && !mPrimaryBuffer
)
458 DSBUFFERDESC DSBDescription
{};
459 DSBDescription
.dwSize
= sizeof(DSBDescription
);
460 DSBDescription
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
461 hr
= mDS
->CreateSoundBuffer(&DSBDescription
, &mPrimaryBuffer
, nullptr);
464 hr
= mPrimaryBuffer
->SetFormat(&OutputType
.Format
);
469 ALuint num_updates
{mDevice
->BufferSize
/ mDevice
->UpdateSize
};
470 if(num_updates
> MAX_UPDATES
)
471 num_updates
= MAX_UPDATES
;
472 mDevice
->BufferSize
= mDevice
->UpdateSize
* num_updates
;
474 DSBUFFERDESC DSBDescription
{};
475 DSBDescription
.dwSize
= sizeof(DSBDescription
);
476 DSBDescription
.dwFlags
= DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_GETCURRENTPOSITION2
477 | DSBCAPS_GLOBALFOCUS
;
478 DSBDescription
.dwBufferBytes
= mDevice
->BufferSize
* OutputType
.Format
.nBlockAlign
;
479 DSBDescription
.lpwfxFormat
= &OutputType
.Format
;
481 hr
= mDS
->CreateSoundBuffer(&DSBDescription
, &mBuffer
, nullptr);
482 if(FAILED(hr
) && mDevice
->FmtType
== DevFmtFloat
)
484 mDevice
->FmtType
= DevFmtShort
;
492 hr
= mBuffer
->QueryInterface(IID_IDirectSoundNotify
, &ptr
);
495 mNotifies
= static_cast<IDirectSoundNotify
*>(ptr
);
497 ALuint num_updates
{mDevice
->BufferSize
/ mDevice
->UpdateSize
};
498 assert(num_updates
<= MAX_UPDATES
);
500 std::array
<DSBPOSITIONNOTIFY
,MAX_UPDATES
> nots
;
501 for(ALuint i
{0};i
< num_updates
;++i
)
503 nots
[i
].dwOffset
= i
* mDevice
->UpdateSize
* OutputType
.Format
.nBlockAlign
;
504 nots
[i
].hEventNotify
= mNotifyEvent
;
506 if(mNotifies
->SetNotificationPositions(num_updates
, nots
.data()) != DS_OK
)
514 mNotifies
->Release();
520 mPrimaryBuffer
->Release();
521 mPrimaryBuffer
= nullptr;
525 ResetEvent(mNotifyEvent
);
526 setChannelOrderFromWFXMask(OutputType
.dwChannelMask
);
531 void DSoundPlayback::start()
534 mKillNow
.store(false, std::memory_order_release
);
535 mThread
= std::thread
{std::mem_fn(&DSoundPlayback::mixerProc
), this};
537 catch(std::exception
& e
) {
538 throw al::backend_exception
{ALC_INVALID_DEVICE
, "Failed to start mixing thread: %s",
543 void DSoundPlayback::stop()
545 if(mKillNow
.exchange(true, std::memory_order_acq_rel
) || !mThread
.joinable())
553 struct DSoundCapture final
: public BackendBase
{
554 DSoundCapture(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
555 ~DSoundCapture() override
;
557 void open(const ALCchar
*name
) override
;
558 void start() override
;
559 void stop() override
;
560 ALCenum
captureSamples(al::byte
*buffer
, ALCuint samples
) override
;
561 ALCuint
availableSamples() override
;
563 IDirectSoundCapture
*mDSC
{nullptr};
564 IDirectSoundCaptureBuffer
*mDSCbuffer
{nullptr};
565 DWORD mBufferBytes
{0u};
570 DEF_NEWDEL(DSoundCapture
)
573 DSoundCapture::~DSoundCapture()
578 mDSCbuffer
->Release();
579 mDSCbuffer
= nullptr;
588 void DSoundCapture::open(const ALCchar
*name
)
591 if(CaptureDevices
.empty())
593 /* Initialize COM to prevent name truncation */
594 HRESULT hrcom
{CoInitialize(nullptr)};
595 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
597 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
602 const GUID
*guid
{nullptr};
603 if(!name
&& !CaptureDevices
.empty())
605 name
= CaptureDevices
[0].name
.c_str();
606 guid
= &CaptureDevices
[0].guid
;
610 auto iter
= std::find_if(CaptureDevices
.cbegin(), CaptureDevices
.cend(),
611 [name
](const DevMap
&entry
) -> bool
612 { return entry
.name
== name
; }
614 if(iter
== CaptureDevices
.cend())
615 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
619 switch(mDevice
->FmtType
)
624 WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice
->FmtType
));
625 throw al::backend_exception
{ALC_INVALID_VALUE
, "%s capture samples not supported",
626 DevFmtTypeString(mDevice
->FmtType
)};
635 WAVEFORMATEXTENSIBLE InputType
{};
636 switch(mDevice
->FmtChans
)
638 case DevFmtMono
: InputType
.dwChannelMask
= MONO
; break;
639 case DevFmtStereo
: InputType
.dwChannelMask
= STEREO
; break;
640 case DevFmtQuad
: InputType
.dwChannelMask
= QUAD
; break;
641 case DevFmtX51
: InputType
.dwChannelMask
= X5DOT1
; break;
642 case DevFmtX51Rear
: InputType
.dwChannelMask
= X5DOT1REAR
; break;
643 case DevFmtX61
: InputType
.dwChannelMask
= X6DOT1
; break;
644 case DevFmtX71
: InputType
.dwChannelMask
= X7DOT1
; break;
646 WARN("%s capture not supported\n", DevFmtChannelsString(mDevice
->FmtChans
));
647 throw al::backend_exception
{ALC_INVALID_VALUE
, "%s capture not supported",
648 DevFmtChannelsString(mDevice
->FmtChans
)};
651 InputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
652 InputType
.Format
.nChannels
= static_cast<WORD
>(mDevice
->channelsFromFmt());
653 InputType
.Format
.wBitsPerSample
= static_cast<WORD
>(mDevice
->bytesFromFmt() * 8);
654 InputType
.Format
.nBlockAlign
= static_cast<WORD
>(InputType
.Format
.nChannels
*
655 InputType
.Format
.wBitsPerSample
/ 8);
656 InputType
.Format
.nSamplesPerSec
= mDevice
->Frequency
;
657 InputType
.Format
.nAvgBytesPerSec
= InputType
.Format
.nSamplesPerSec
*
658 InputType
.Format
.nBlockAlign
;
659 InputType
.Format
.cbSize
= 0;
660 InputType
.Samples
.wValidBitsPerSample
= InputType
.Format
.wBitsPerSample
;
661 if(mDevice
->FmtType
== DevFmtFloat
)
662 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
664 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
666 if(InputType
.Format
.nChannels
> 2 || mDevice
->FmtType
== DevFmtFloat
)
668 InputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
669 InputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
672 ALuint samples
{mDevice
->BufferSize
};
673 samples
= maxu(samples
, 100 * mDevice
->Frequency
/ 1000);
675 DSCBUFFERDESC DSCBDescription
{};
676 DSCBDescription
.dwSize
= sizeof(DSCBDescription
);
677 DSCBDescription
.dwFlags
= 0;
678 DSCBDescription
.dwBufferBytes
= samples
* InputType
.Format
.nBlockAlign
;
679 DSCBDescription
.lpwfxFormat
= &InputType
.Format
;
681 //DirectSoundCapture Init code
682 hr
= DirectSoundCaptureCreate(guid
, &mDSC
, nullptr);
684 mDSC
->CreateCaptureBuffer(&DSCBDescription
, &mDSCbuffer
, nullptr);
686 mRing
= RingBuffer::Create(mDevice
->BufferSize
, InputType
.Format
.nBlockAlign
, false);
692 mDSCbuffer
->Release();
693 mDSCbuffer
= nullptr;
698 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device init failed: 0x%08lx", hr
};
701 mBufferBytes
= DSCBDescription
.dwBufferBytes
;
702 setChannelOrderFromWFXMask(InputType
.dwChannelMask
);
704 mDevice
->DeviceName
= name
;
707 void DSoundCapture::start()
709 const HRESULT hr
{mDSCbuffer
->Start(DSCBSTART_LOOPING
)};
711 throw al::backend_exception
{ALC_INVALID_DEVICE
, "Failure starting capture: 0x%lx", hr
};
714 void DSoundCapture::stop()
716 HRESULT hr
{mDSCbuffer
->Stop()};
719 ERR("stop failed: 0x%08lx\n", hr
);
720 mDevice
->handleDisconnect("Failure stopping capture: 0x%lx", hr
);
724 ALCenum
DSoundCapture::captureSamples(al::byte
*buffer
, ALCuint samples
)
726 mRing
->read(buffer
, samples
);
730 ALCuint
DSoundCapture::availableSamples()
732 if(!mDevice
->Connected
.load(std::memory_order_acquire
))
733 return static_cast<ALCuint
>(mRing
->readSpace());
735 ALuint FrameSize
{mDevice
->frameSizeFromFmt()};
736 DWORD BufferBytes
{mBufferBytes
};
737 DWORD LastCursor
{mCursor
};
740 void *ReadPtr1
{}, *ReadPtr2
{};
741 DWORD ReadCnt1
{}, ReadCnt2
{};
742 HRESULT hr
{mDSCbuffer
->GetCurrentPosition(nullptr, &ReadCursor
)};
745 DWORD NumBytes
{(ReadCursor
-LastCursor
+ BufferBytes
) % BufferBytes
};
746 if(!NumBytes
) return static_cast<ALCubyte
>(mRing
->readSpace());
747 hr
= mDSCbuffer
->Lock(LastCursor
, NumBytes
, &ReadPtr1
, &ReadCnt1
, &ReadPtr2
, &ReadCnt2
, 0);
751 mRing
->write(ReadPtr1
, ReadCnt1
/FrameSize
);
752 if(ReadPtr2
!= nullptr && ReadCnt2
> 0)
753 mRing
->write(ReadPtr2
, ReadCnt2
/FrameSize
);
754 hr
= mDSCbuffer
->Unlock(ReadPtr1
, ReadCnt1
, ReadPtr2
, ReadCnt2
);
755 mCursor
= (LastCursor
+ReadCnt1
+ReadCnt2
) % BufferBytes
;
760 ERR("update failed: 0x%08lx\n", hr
);
761 mDevice
->handleDisconnect("Failure retrieving capture data: 0x%lx", hr
);
764 return static_cast<ALCuint
>(mRing
->readSpace());
770 BackendFactory
&DSoundBackendFactory::getFactory()
772 static DSoundBackendFactory factory
{};
776 bool DSoundBackendFactory::init()
781 ds_handle
= LoadLib("dsound.dll");
784 ERR("Failed to load dsound.dll\n");
788 #define LOAD_FUNC(f) do { \
789 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
792 CloseLib(ds_handle); \
793 ds_handle = nullptr; \
797 LOAD_FUNC(DirectSoundCreate
);
798 LOAD_FUNC(DirectSoundEnumerateW
);
799 LOAD_FUNC(DirectSoundCaptureCreate
);
800 LOAD_FUNC(DirectSoundCaptureEnumerateW
);
807 bool DSoundBackendFactory::querySupport(BackendType type
)
808 { return (type
== BackendType::Playback
|| type
== BackendType::Capture
); }
810 std::string
DSoundBackendFactory::probe(BackendType type
)
812 std::string outnames
;
813 auto add_device
= [&outnames
](const DevMap
&entry
) -> void
815 /* +1 to also append the null char (to ensure a null-separated list and
816 * double-null terminated list).
818 outnames
.append(entry
.name
.c_str(), entry
.name
.length()+1);
821 /* Initialize COM to prevent name truncation */
823 HRESULT hrcom
{CoInitialize(nullptr)};
826 case BackendType::Playback
:
827 PlaybackDevices
.clear();
828 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
830 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr
);
831 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
834 case BackendType::Capture
:
835 CaptureDevices
.clear();
836 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
838 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr
);
839 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
848 BackendPtr
DSoundBackendFactory::createBackend(ALCdevice
*device
, BackendType type
)
850 if(type
== BackendType::Playback
)
851 return BackendPtr
{new DSoundPlayback
{device
}};
852 if(type
== BackendType::Capture
)
853 return BackendPtr
{new DSoundCapture
{device
}};