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_
50 #include "ringbuffer.h"
56 /* MinGW-w64 needs this for some unknown reason now. */
57 using LPCWAVEFORMATEX
= const WAVEFORMATEX
*;
61 #ifndef DSSPEAKER_5POINT1
62 # define DSSPEAKER_5POINT1 0x00000006
64 #ifndef DSSPEAKER_5POINT1_BACK
65 # define DSSPEAKER_5POINT1_BACK 0x00000006
67 #ifndef DSSPEAKER_7POINT1
68 # define DSSPEAKER_7POINT1 0x00000007
70 #ifndef DSSPEAKER_7POINT1_SURROUND
71 # define DSSPEAKER_7POINT1_SURROUND 0x00000008
73 #ifndef DSSPEAKER_5POINT1_SURROUND
74 # define DSSPEAKER_5POINT1_SURROUND 0x00000009
78 /* Some headers seem to define these as macros for __uuidof, which is annoying
79 * since some headers don't declare them at all. Hopefully the ifdef is enough
80 * to tell if they need to be declared.
82 #ifndef KSDATAFORMAT_SUBTYPE_PCM
83 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
85 #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
86 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
91 #define DEVNAME_HEAD "OpenAL Soft on "
96 HRESULT (WINAPI
*pDirectSoundCreate
)(const GUID
*pcGuidDevice
, IDirectSound
**ppDS
, IUnknown
*pUnkOuter
);
97 HRESULT (WINAPI
*pDirectSoundEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
98 HRESULT (WINAPI
*pDirectSoundCaptureCreate
)(const GUID
*pcGuidDevice
, IDirectSoundCapture
**ppDSC
, IUnknown
*pUnkOuter
);
99 HRESULT (WINAPI
*pDirectSoundCaptureEnumerateW
)(LPDSENUMCALLBACKW pDSEnumCallback
, void *pContext
);
101 #ifndef IN_IDE_PARSER
102 #define DirectSoundCreate pDirectSoundCreate
103 #define DirectSoundEnumerateW pDirectSoundEnumerateW
104 #define DirectSoundCaptureCreate pDirectSoundCaptureCreate
105 #define DirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW
110 #define MAX_UPDATES 128
116 template<typename T0
, typename T1
>
117 DevMap(T0
&& name_
, T1
&& guid_
)
118 : name
{std::forward
<T0
>(name_
)}, guid
{std::forward
<T1
>(guid_
)}
122 al::vector
<DevMap
> PlaybackDevices
;
123 al::vector
<DevMap
> CaptureDevices
;
125 bool checkName(const al::vector
<DevMap
> &list
, const std::string
&name
)
127 auto match_name
= [&name
](const DevMap
&entry
) -> bool
128 { return entry
.name
== name
; };
129 return std::find_if(list
.cbegin(), list
.cend(), match_name
) != list
.cend();
132 BOOL CALLBACK
DSoundEnumDevices(GUID
*guid
, const WCHAR
*desc
, const WCHAR
*, void *data
) noexcept
137 auto& devices
= *static_cast<al::vector
<DevMap
>*>(data
);
138 const std::string basename
{DEVNAME_HEAD
+ wstr_to_utf8(desc
)};
141 std::string newname
{basename
};
142 while(checkName(devices
, newname
))
146 newname
+= std::to_string(++count
);
148 devices
.emplace_back(std::move(newname
), *guid
);
149 const DevMap
&newentry
= devices
.back();
151 OLECHAR
*guidstr
{nullptr};
152 HRESULT hr
{StringFromCLSID(*guid
, &guidstr
)};
155 TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry
.name
.c_str(), guidstr
);
156 CoTaskMemFree(guidstr
);
163 struct DSoundPlayback final
: public BackendBase
{
164 DSoundPlayback(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
165 ~DSoundPlayback() override
;
169 void open(const ALCchar
*name
) override
;
170 bool reset() override
;
171 bool start() override
;
172 void stop() override
;
174 IDirectSound
*mDS
{nullptr};
175 IDirectSoundBuffer
*mPrimaryBuffer
{nullptr};
176 IDirectSoundBuffer
*mBuffer
{nullptr};
177 IDirectSoundNotify
*mNotifies
{nullptr};
178 HANDLE mNotifyEvent
{nullptr};
180 std::atomic
<bool> mKillNow
{true};
183 DEF_NEWDEL(DSoundPlayback
)
186 DSoundPlayback::~DSoundPlayback()
189 mNotifies
->Release();
195 mPrimaryBuffer
->Release();
196 mPrimaryBuffer
= nullptr;
202 CloseHandle(mNotifyEvent
);
203 mNotifyEvent
= nullptr;
207 FORCE_ALIGN
int DSoundPlayback::mixerProc()
210 althrd_setname(MIXER_THREAD_NAME
);
213 DSBCaps
.dwSize
= sizeof(DSBCaps
);
214 HRESULT err
{mBuffer
->GetCaps(&DSBCaps
)};
217 ERR("Failed to get buffer caps: 0x%lx\n", err
);
218 aluHandleDisconnect(mDevice
, "Failure retrieving playback buffer info: 0x%lx", err
);
222 ALuint FrameSize
{mDevice
->frameSizeFromFmt()};
223 DWORD FragSize
{mDevice
->UpdateSize
* FrameSize
};
226 DWORD LastCursor
{0u};
227 mBuffer
->GetCurrentPosition(&LastCursor
, nullptr);
228 while(!mKillNow
.load(std::memory_order_acquire
) &&
229 mDevice
->Connected
.load(std::memory_order_acquire
))
231 // Get current play cursor
233 mBuffer
->GetCurrentPosition(&PlayCursor
, nullptr);
234 DWORD avail
= (PlayCursor
-LastCursor
+DSBCaps
.dwBufferBytes
) % DSBCaps
.dwBufferBytes
;
240 err
= mBuffer
->Play(0, 0, DSBPLAY_LOOPING
);
243 ERR("Failed to play buffer: 0x%lx\n", err
);
244 aluHandleDisconnect(mDevice
, "Failure starting playback: 0x%lx", err
);
250 avail
= WaitForSingleObjectEx(mNotifyEvent
, 2000, FALSE
);
251 if(avail
!= WAIT_OBJECT_0
)
252 ERR("WaitForSingleObjectEx error: 0x%lx\n", avail
);
255 avail
-= avail
%FragSize
;
257 // Lock output buffer
258 void *WritePtr1
, *WritePtr2
;
259 DWORD WriteCnt1
{0u}, WriteCnt2
{0u};
260 err
= mBuffer
->Lock(LastCursor
, avail
, &WritePtr1
, &WriteCnt1
, &WritePtr2
, &WriteCnt2
, 0);
262 // If the buffer is lost, restore it and lock
263 if(err
== DSERR_BUFFERLOST
)
265 WARN("Buffer lost, restoring...\n");
266 err
= mBuffer
->Restore();
271 err
= mBuffer
->Lock(0, DSBCaps
.dwBufferBytes
, &WritePtr1
, &WriteCnt1
,
272 &WritePtr2
, &WriteCnt2
, 0);
278 std::unique_lock
<DSoundPlayback
> dlock
{*this};
279 aluMixData(mDevice
, WritePtr1
, WriteCnt1
/FrameSize
);
281 aluMixData(mDevice
, WritePtr2
, WriteCnt2
/FrameSize
);
284 mBuffer
->Unlock(WritePtr1
, WriteCnt1
, WritePtr2
, WriteCnt2
);
288 ERR("Buffer lock error: %#lx\n", err
);
289 std::lock_guard
<DSoundPlayback
> _
{*this};
290 aluHandleDisconnect(mDevice
, "Failed to lock output buffer: 0x%lx", err
);
294 // Update old write cursor location
295 LastCursor
+= WriteCnt1
+WriteCnt2
;
296 LastCursor
%= DSBCaps
.dwBufferBytes
;
302 void DSoundPlayback::open(const ALCchar
*name
)
305 if(PlaybackDevices
.empty())
307 /* Initialize COM to prevent name truncation */
308 HRESULT hrcom
{CoInitialize(nullptr)};
309 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
311 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
316 const GUID
*guid
{nullptr};
317 if(!name
&& !PlaybackDevices
.empty())
319 name
= PlaybackDevices
[0].name
.c_str();
320 guid
= &PlaybackDevices
[0].guid
;
324 auto iter
= std::find_if(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(),
325 [name
](const DevMap
&entry
) -> bool
326 { return entry
.name
== name
; }
328 if(iter
== PlaybackDevices
.cend())
329 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
334 mNotifyEvent
= CreateEventW(nullptr, FALSE
, FALSE
, nullptr);
335 if(!mNotifyEvent
) hr
= E_FAIL
;
337 //DirectSound Init code
339 hr
= DirectSoundCreate(guid
, &mDS
, nullptr);
341 hr
= mDS
->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY
);
343 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device init failed: 0x%08lx", hr
};
345 mDevice
->DeviceName
= name
;
348 bool DSoundPlayback::reset()
351 mNotifies
->Release();
357 mPrimaryBuffer
->Release();
358 mPrimaryBuffer
= nullptr;
360 switch(mDevice
->FmtType
)
363 mDevice
->FmtType
= DevFmtUByte
;
366 if(mDevice
->Flags
.get
<SampleTypeRequest
>())
370 mDevice
->FmtType
= DevFmtShort
;
373 mDevice
->FmtType
= DevFmtInt
;
381 WAVEFORMATEXTENSIBLE OutputType
{};
383 HRESULT hr
{mDS
->GetSpeakerConfig(&speakers
)};
386 speakers
= DSSPEAKER_CONFIG(speakers
);
387 if(!mDevice
->Flags
.get
<ChannelsRequest
>())
389 if(speakers
== DSSPEAKER_MONO
)
390 mDevice
->FmtChans
= DevFmtMono
;
391 else if(speakers
== DSSPEAKER_STEREO
|| speakers
== DSSPEAKER_HEADPHONE
)
392 mDevice
->FmtChans
= DevFmtStereo
;
393 else if(speakers
== DSSPEAKER_QUAD
)
394 mDevice
->FmtChans
= DevFmtQuad
;
395 else if(speakers
== DSSPEAKER_5POINT1_SURROUND
)
396 mDevice
->FmtChans
= DevFmtX51
;
397 else if(speakers
== DSSPEAKER_5POINT1_BACK
)
398 mDevice
->FmtChans
= DevFmtX51Rear
;
399 else if(speakers
== DSSPEAKER_7POINT1
|| speakers
== DSSPEAKER_7POINT1_SURROUND
)
400 mDevice
->FmtChans
= DevFmtX71
;
402 ERR("Unknown system speaker config: 0x%lx\n", speakers
);
404 mDevice
->IsHeadphones
= (mDevice
->FmtChans
== DevFmtStereo
&&
405 speakers
== DSSPEAKER_HEADPHONE
);
407 switch(mDevice
->FmtChans
)
410 OutputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
413 mDevice
->FmtChans
= DevFmtStereo
;
416 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
420 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
421 SPEAKER_FRONT_RIGHT
|
426 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
427 SPEAKER_FRONT_RIGHT
|
428 SPEAKER_FRONT_CENTER
|
429 SPEAKER_LOW_FREQUENCY
|
434 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
435 SPEAKER_FRONT_RIGHT
|
436 SPEAKER_FRONT_CENTER
|
437 SPEAKER_LOW_FREQUENCY
|
442 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
443 SPEAKER_FRONT_RIGHT
|
444 SPEAKER_FRONT_CENTER
|
445 SPEAKER_LOW_FREQUENCY
|
446 SPEAKER_BACK_CENTER
|
451 OutputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
|
452 SPEAKER_FRONT_RIGHT
|
453 SPEAKER_FRONT_CENTER
|
454 SPEAKER_LOW_FREQUENCY
|
464 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
465 OutputType
.Format
.nChannels
= static_cast<WORD
>(mDevice
->channelsFromFmt());
466 OutputType
.Format
.wBitsPerSample
= static_cast<WORD
>(mDevice
->bytesFromFmt() * 8);
467 OutputType
.Format
.nBlockAlign
= static_cast<WORD
>(OutputType
.Format
.nChannels
*
468 OutputType
.Format
.wBitsPerSample
/ 8);
469 OutputType
.Format
.nSamplesPerSec
= mDevice
->Frequency
;
470 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
471 OutputType
.Format
.nBlockAlign
;
472 OutputType
.Format
.cbSize
= 0;
475 if(OutputType
.Format
.nChannels
> 2 || mDevice
->FmtType
== DevFmtFloat
)
477 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
478 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
479 OutputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
480 if(mDevice
->FmtType
== DevFmtFloat
)
481 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
483 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
486 mPrimaryBuffer
->Release();
487 mPrimaryBuffer
= nullptr;
491 if(SUCCEEDED(hr
) && !mPrimaryBuffer
)
493 DSBUFFERDESC DSBDescription
{};
494 DSBDescription
.dwSize
= sizeof(DSBDescription
);
495 DSBDescription
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
496 hr
= mDS
->CreateSoundBuffer(&DSBDescription
, &mPrimaryBuffer
, nullptr);
499 hr
= mPrimaryBuffer
->SetFormat(&OutputType
.Format
);
504 ALuint num_updates
{mDevice
->BufferSize
/ mDevice
->UpdateSize
};
505 if(num_updates
> MAX_UPDATES
)
506 num_updates
= MAX_UPDATES
;
507 mDevice
->BufferSize
= mDevice
->UpdateSize
* num_updates
;
509 DSBUFFERDESC DSBDescription
{};
510 DSBDescription
.dwSize
= sizeof(DSBDescription
);
511 DSBDescription
.dwFlags
= DSBCAPS_CTRLPOSITIONNOTIFY
| DSBCAPS_GETCURRENTPOSITION2
|
513 DSBDescription
.dwBufferBytes
= mDevice
->BufferSize
* OutputType
.Format
.nBlockAlign
;
514 DSBDescription
.lpwfxFormat
= &OutputType
.Format
;
516 hr
= mDS
->CreateSoundBuffer(&DSBDescription
, &mBuffer
, nullptr);
517 if(FAILED(hr
) && mDevice
->FmtType
== DevFmtFloat
)
519 mDevice
->FmtType
= DevFmtShort
;
527 hr
= mBuffer
->QueryInterface(IID_IDirectSoundNotify
, &ptr
);
530 auto Notifies
= static_cast<IDirectSoundNotify
*>(ptr
);
531 mNotifies
= Notifies
;
533 ALuint num_updates
{mDevice
->BufferSize
/ mDevice
->UpdateSize
};
534 assert(num_updates
<= MAX_UPDATES
);
536 std::array
<DSBPOSITIONNOTIFY
,MAX_UPDATES
> nots
;
537 for(ALuint i
{0};i
< num_updates
;++i
)
539 nots
[i
].dwOffset
= i
* mDevice
->UpdateSize
* OutputType
.Format
.nBlockAlign
;
540 nots
[i
].hEventNotify
= mNotifyEvent
;
542 if(Notifies
->SetNotificationPositions(num_updates
, nots
.data()) != DS_OK
)
550 mNotifies
->Release();
556 mPrimaryBuffer
->Release();
557 mPrimaryBuffer
= nullptr;
561 ResetEvent(mNotifyEvent
);
562 SetDefaultWFXChannelOrder(mDevice
);
567 bool DSoundPlayback::start()
570 mKillNow
.store(false, std::memory_order_release
);
571 mThread
= std::thread
{std::mem_fn(&DSoundPlayback::mixerProc
), this};
574 catch(std::exception
& e
) {
575 ERR("Failed to start mixing thread: %s\n", e
.what());
582 void DSoundPlayback::stop()
584 if(mKillNow
.exchange(true, std::memory_order_acq_rel
) || !mThread
.joinable())
592 struct DSoundCapture final
: public BackendBase
{
593 DSoundCapture(ALCdevice
*device
) noexcept
: BackendBase
{device
} { }
594 ~DSoundCapture() override
;
596 void open(const ALCchar
*name
) override
;
597 bool start() override
;
598 void stop() override
;
599 ALCenum
captureSamples(al::byte
*buffer
, ALCuint samples
) override
;
600 ALCuint
availableSamples() override
;
602 IDirectSoundCapture
*mDSC
{nullptr};
603 IDirectSoundCaptureBuffer
*mDSCbuffer
{nullptr};
604 DWORD mBufferBytes
{0u};
609 DEF_NEWDEL(DSoundCapture
)
612 DSoundCapture::~DSoundCapture()
617 mDSCbuffer
->Release();
618 mDSCbuffer
= nullptr;
627 void DSoundCapture::open(const ALCchar
*name
)
630 if(CaptureDevices
.empty())
632 /* Initialize COM to prevent name truncation */
633 HRESULT hrcom
{CoInitialize(nullptr)};
634 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
636 ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr
);
641 const GUID
*guid
{nullptr};
642 if(!name
&& !CaptureDevices
.empty())
644 name
= CaptureDevices
[0].name
.c_str();
645 guid
= &CaptureDevices
[0].guid
;
649 auto iter
= std::find_if(CaptureDevices
.cbegin(), CaptureDevices
.cend(),
650 [name
](const DevMap
&entry
) -> bool
651 { return entry
.name
== name
; }
653 if(iter
== CaptureDevices
.cend())
654 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device name \"%s\" not found", name
};
658 switch(mDevice
->FmtType
)
663 WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice
->FmtType
));
664 throw al::backend_exception
{ALC_INVALID_VALUE
, "%s capture samples not supported",
665 DevFmtTypeString(mDevice
->FmtType
)};
674 WAVEFORMATEXTENSIBLE InputType
{};
675 switch(mDevice
->FmtChans
)
678 InputType
.dwChannelMask
= SPEAKER_FRONT_CENTER
;
681 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
;
684 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_BACK_LEFT
|
688 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_FRONT_CENTER
|
689 SPEAKER_LOW_FREQUENCY
| SPEAKER_SIDE_LEFT
| SPEAKER_SIDE_RIGHT
;
692 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_FRONT_CENTER
|
693 SPEAKER_LOW_FREQUENCY
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
;
696 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_FRONT_CENTER
|
697 SPEAKER_LOW_FREQUENCY
| SPEAKER_BACK_CENTER
| SPEAKER_SIDE_LEFT
| SPEAKER_SIDE_RIGHT
;
700 InputType
.dwChannelMask
= SPEAKER_FRONT_LEFT
| SPEAKER_FRONT_RIGHT
| SPEAKER_FRONT_CENTER
|
701 SPEAKER_LOW_FREQUENCY
| SPEAKER_BACK_LEFT
| SPEAKER_BACK_RIGHT
| SPEAKER_SIDE_LEFT
|
705 WARN("%s capture not supported\n", DevFmtChannelsString(mDevice
->FmtChans
));
706 throw al::backend_exception
{ALC_INVALID_VALUE
, "%s capture not supported",
707 DevFmtChannelsString(mDevice
->FmtChans
)};
710 InputType
.Format
.wFormatTag
= WAVE_FORMAT_PCM
;
711 InputType
.Format
.nChannels
= static_cast<WORD
>(mDevice
->channelsFromFmt());
712 InputType
.Format
.wBitsPerSample
= static_cast<WORD
>(mDevice
->bytesFromFmt() * 8);
713 InputType
.Format
.nBlockAlign
= static_cast<WORD
>(InputType
.Format
.nChannels
*
714 InputType
.Format
.wBitsPerSample
/ 8);
715 InputType
.Format
.nSamplesPerSec
= mDevice
->Frequency
;
716 InputType
.Format
.nAvgBytesPerSec
= InputType
.Format
.nSamplesPerSec
*
717 InputType
.Format
.nBlockAlign
;
718 InputType
.Format
.cbSize
= 0;
719 InputType
.Samples
.wValidBitsPerSample
= InputType
.Format
.wBitsPerSample
;
720 if(mDevice
->FmtType
== DevFmtFloat
)
721 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
723 InputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
725 if(InputType
.Format
.nChannels
> 2 || mDevice
->FmtType
== DevFmtFloat
)
727 InputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
728 InputType
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
731 ALuint samples
{mDevice
->BufferSize
};
732 samples
= maxu(samples
, 100 * mDevice
->Frequency
/ 1000);
734 DSCBUFFERDESC DSCBDescription
{};
735 DSCBDescription
.dwSize
= sizeof(DSCBDescription
);
736 DSCBDescription
.dwFlags
= 0;
737 DSCBDescription
.dwBufferBytes
= samples
* InputType
.Format
.nBlockAlign
;
738 DSCBDescription
.lpwfxFormat
= &InputType
.Format
;
740 //DirectSoundCapture Init code
741 hr
= DirectSoundCaptureCreate(guid
, &mDSC
, nullptr);
743 mDSC
->CreateCaptureBuffer(&DSCBDescription
, &mDSCbuffer
, nullptr);
745 mRing
= CreateRingBuffer(mDevice
->BufferSize
, InputType
.Format
.nBlockAlign
, false);
751 mDSCbuffer
->Release();
752 mDSCbuffer
= nullptr;
757 throw al::backend_exception
{ALC_INVALID_VALUE
, "Device init failed: 0x%08lx", hr
};
760 mBufferBytes
= DSCBDescription
.dwBufferBytes
;
761 SetDefaultWFXChannelOrder(mDevice
);
763 mDevice
->DeviceName
= name
;
766 bool DSoundCapture::start()
768 HRESULT hr
{mDSCbuffer
->Start(DSCBSTART_LOOPING
)};
771 ERR("start failed: 0x%08lx\n", hr
);
772 aluHandleDisconnect(mDevice
, "Failure starting capture: 0x%lx", hr
);
778 void DSoundCapture::stop()
780 HRESULT hr
{mDSCbuffer
->Stop()};
783 ERR("stop failed: 0x%08lx\n", hr
);
784 aluHandleDisconnect(mDevice
, "Failure stopping capture: 0x%lx", hr
);
788 ALCenum
DSoundCapture::captureSamples(al::byte
*buffer
, ALCuint samples
)
790 mRing
->read(buffer
, samples
);
794 ALCuint
DSoundCapture::availableSamples()
796 if(!mDevice
->Connected
.load(std::memory_order_acquire
))
797 return static_cast<ALCuint
>(mRing
->readSpace());
799 ALuint FrameSize
{mDevice
->frameSizeFromFmt()};
800 DWORD BufferBytes
{mBufferBytes
};
801 DWORD LastCursor
{mCursor
};
804 void *ReadPtr1
{}, *ReadPtr2
{};
805 DWORD ReadCnt1
{}, ReadCnt2
{};
806 HRESULT hr
{mDSCbuffer
->GetCurrentPosition(nullptr, &ReadCursor
)};
809 DWORD NumBytes
{(ReadCursor
-LastCursor
+ BufferBytes
) % BufferBytes
};
810 if(!NumBytes
) return static_cast<ALCubyte
>(mRing
->readSpace());
811 hr
= mDSCbuffer
->Lock(LastCursor
, NumBytes
, &ReadPtr1
, &ReadCnt1
, &ReadPtr2
, &ReadCnt2
, 0);
815 mRing
->write(ReadPtr1
, ReadCnt1
/FrameSize
);
816 if(ReadPtr2
!= nullptr && ReadCnt2
> 0)
817 mRing
->write(ReadPtr2
, ReadCnt2
/FrameSize
);
818 hr
= mDSCbuffer
->Unlock(ReadPtr1
, ReadCnt1
, ReadPtr2
, ReadCnt2
);
819 mCursor
= (LastCursor
+ReadCnt1
+ReadCnt2
) % BufferBytes
;
824 ERR("update failed: 0x%08lx\n", hr
);
825 aluHandleDisconnect(mDevice
, "Failure retrieving capture data: 0x%lx", hr
);
828 return static_cast<ALCuint
>(mRing
->readSpace());
834 BackendFactory
&DSoundBackendFactory::getFactory()
836 static DSoundBackendFactory factory
{};
840 bool DSoundBackendFactory::init()
845 ds_handle
= LoadLib("dsound.dll");
848 ERR("Failed to load dsound.dll\n");
852 #define LOAD_FUNC(f) do { \
853 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
856 CloseLib(ds_handle); \
857 ds_handle = nullptr; \
861 LOAD_FUNC(DirectSoundCreate
);
862 LOAD_FUNC(DirectSoundEnumerateW
);
863 LOAD_FUNC(DirectSoundCaptureCreate
);
864 LOAD_FUNC(DirectSoundCaptureEnumerateW
);
871 bool DSoundBackendFactory::querySupport(BackendType type
)
872 { return (type
== BackendType::Playback
|| type
== BackendType::Capture
); }
874 void DSoundBackendFactory::probe(DevProbe type
, std::string
*outnames
)
876 auto add_device
= [outnames
](const DevMap
&entry
) -> void
878 /* +1 to also append the null char (to ensure a null-separated list and
879 * double-null terminated list).
881 outnames
->append(entry
.name
.c_str(), entry
.name
.length()+1);
884 /* Initialize COM to prevent name truncation */
886 HRESULT hrcom
{CoInitialize(nullptr)};
889 case DevProbe::Playback
:
890 PlaybackDevices
.clear();
891 hr
= DirectSoundEnumerateW(DSoundEnumDevices
, &PlaybackDevices
);
893 ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr
);
894 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
897 case DevProbe::Capture
:
898 CaptureDevices
.clear();
899 hr
= DirectSoundCaptureEnumerateW(DSoundEnumDevices
, &CaptureDevices
);
901 ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr
);
902 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
909 BackendPtr
DSoundBackendFactory::createBackend(ALCdevice
*device
, BackendType type
)
911 if(type
== BackendType::Playback
)
912 return BackendPtr
{new DSoundPlayback
{device
}};
913 if(type
== BackendType::Capture
)
914 return BackendPtr
{new DSoundCapture
{device
}};