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/winmm.h"
41 #include "ringbuffer.h"
45 #ifndef WAVE_FORMAT_IEEE_FLOAT
46 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
51 #define DEVNAME_HEAD "OpenAL Soft on "
54 al::vector
<std::string
> PlaybackDevices
;
55 al::vector
<std::string
> CaptureDevices
;
57 bool checkName(const al::vector
<std::string
> &list
, const std::string
&name
)
58 { return std::find(list
.cbegin(), list
.cend(), name
) != list
.cend(); }
60 void ProbePlaybackDevices(void)
62 PlaybackDevices
.clear();
64 ALuint numdevs
{waveOutGetNumDevs()};
65 PlaybackDevices
.reserve(numdevs
);
66 for(ALuint i
{0};i
< numdevs
;i
++)
70 WAVEOUTCAPSW WaveCaps
{};
71 if(waveOutGetDevCapsW(i
, &WaveCaps
, sizeof(WaveCaps
)) == MMSYSERR_NOERROR
)
73 const std::string basename
{DEVNAME_HEAD
+ wstr_to_utf8(WaveCaps
.szPname
)};
76 std::string newname
{basename
};
77 while(checkName(PlaybackDevices
, newname
))
81 newname
+= std::to_string(++count
);
83 dname
= std::move(newname
);
85 TRACE("Got device \"%s\", ID %u\n", dname
.c_str(), i
);
87 PlaybackDevices
.emplace_back(std::move(dname
));
91 void ProbeCaptureDevices(void)
93 CaptureDevices
.clear();
95 ALuint numdevs
{waveInGetNumDevs()};
96 CaptureDevices
.reserve(numdevs
);
97 for(ALuint i
{0};i
< numdevs
;i
++)
101 WAVEINCAPSW WaveCaps
{};
102 if(waveInGetDevCapsW(i
, &WaveCaps
, sizeof(WaveCaps
)) == MMSYSERR_NOERROR
)
104 const std::string basename
{DEVNAME_HEAD
+ wstr_to_utf8(WaveCaps
.szPname
)};
107 std::string newname
{basename
};
108 while(checkName(CaptureDevices
, newname
))
112 newname
+= std::to_string(++count
);
114 dname
= std::move(newname
);
116 TRACE("Got device \"%s\", ID %u\n", dname
.c_str(), i
);
118 CaptureDevices
.emplace_back(std::move(dname
));
123 struct ALCwinmmPlayback final
: public ALCbackend
{
124 std::atomic
<ALuint
> Writable
{0u};
127 std::array
<WAVEHDR
,4> WaveBuffer
;
129 HWAVEOUT OutHdl
{nullptr};
131 WAVEFORMATEX Format
{};
133 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
137 void ALCwinmmPlayback_Construct(ALCwinmmPlayback
*self
, ALCdevice
*device
);
138 void ALCwinmmPlayback_Destruct(ALCwinmmPlayback
*self
);
140 void CALLBACK
ALCwinmmPlayback_waveOutProc(HWAVEOUT device
, UINT msg
, DWORD_PTR instance
, DWORD_PTR param1
, DWORD_PTR param2
);
141 int ALCwinmmPlayback_mixerProc(ALCwinmmPlayback
*self
);
143 ALCenum
ALCwinmmPlayback_open(ALCwinmmPlayback
*self
, const ALCchar
*name
);
144 ALCboolean
ALCwinmmPlayback_reset(ALCwinmmPlayback
*self
);
145 ALCboolean
ALCwinmmPlayback_start(ALCwinmmPlayback
*self
);
146 void ALCwinmmPlayback_stop(ALCwinmmPlayback
*self
);
147 DECLARE_FORWARD2(ALCwinmmPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
148 DECLARE_FORWARD(ALCwinmmPlayback
, ALCbackend
, ALCuint
, availableSamples
)
149 DECLARE_FORWARD(ALCwinmmPlayback
, ALCbackend
, ClockLatency
, getClockLatency
)
150 DECLARE_FORWARD(ALCwinmmPlayback
, ALCbackend
, void, lock
)
151 DECLARE_FORWARD(ALCwinmmPlayback
, ALCbackend
, void, unlock
)
152 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback
)
154 DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback
);
157 void ALCwinmmPlayback_Construct(ALCwinmmPlayback
*self
, ALCdevice
*device
)
159 new (self
) ALCwinmmPlayback
{};
160 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
161 SET_VTABLE2(ALCwinmmPlayback
, ALCbackend
, self
);
163 alsem_init(&self
->Sem
, 0);
164 std::fill(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(), WAVEHDR
{});
167 void ALCwinmmPlayback_Destruct(ALCwinmmPlayback
*self
)
170 waveOutClose(self
->OutHdl
);
171 self
->OutHdl
= nullptr;
173 al_free(self
->WaveBuffer
[0].lpData
);
174 std::fill(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(), WAVEHDR
{});
176 alsem_destroy(&self
->Sem
);
178 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
179 self
->~ALCwinmmPlayback();
183 /* ALCwinmmPlayback_waveOutProc
185 * Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
186 * is completed and returns to the application (for more data)
188 void CALLBACK
ALCwinmmPlayback_waveOutProc(HWAVEOUT
UNUSED(device
), UINT msg
,
189 DWORD_PTR instance
, DWORD_PTR
UNUSED(param1
),
190 DWORD_PTR
UNUSED(param2
))
195 auto self
= reinterpret_cast<ALCwinmmPlayback
*>(instance
);
196 self
->Writable
.fetch_add(1, std::memory_order_acq_rel
);
197 alsem_post(&self
->Sem
);
200 FORCE_ALIGN
int ALCwinmmPlayback_mixerProc(ALCwinmmPlayback
*self
)
202 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
205 althrd_setname(MIXER_THREAD_NAME
);
207 ALCwinmmPlayback_lock(self
);
208 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
209 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
211 ALsizei todo
= self
->Writable
.load(std::memory_order_acquire
);
214 ALCwinmmPlayback_unlock(self
);
215 alsem_wait(&self
->Sem
);
216 ALCwinmmPlayback_lock(self
);
222 WAVEHDR
&waveHdr
= self
->WaveBuffer
[widx
];
223 widx
= (widx
+1) % self
->WaveBuffer
.size();
225 aluMixData(device
, waveHdr
.lpData
, device
->UpdateSize
);
226 self
->Writable
.fetch_sub(1, std::memory_order_acq_rel
);
227 waveOutWrite(self
->OutHdl
, &waveHdr
, sizeof(WAVEHDR
));
231 ALCwinmmPlayback_unlock(self
);
237 ALCenum
ALCwinmmPlayback_open(ALCwinmmPlayback
*self
, const ALCchar
*deviceName
)
239 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
241 if(PlaybackDevices
.empty())
242 ProbePlaybackDevices();
244 // Find the Device ID matching the deviceName if valid
245 auto iter
= deviceName
?
246 std::find(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), deviceName
) :
247 PlaybackDevices
.cbegin();
248 if(iter
== PlaybackDevices
.cend()) return ALC_INVALID_VALUE
;
249 UINT DeviceID
{static_cast<UINT
>(std::distance(PlaybackDevices
.cbegin(), iter
))};
252 memset(&self
->Format
, 0, sizeof(WAVEFORMATEX
));
253 if(device
->FmtType
== DevFmtFloat
)
255 self
->Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
256 self
->Format
.wBitsPerSample
= 32;
260 self
->Format
.wFormatTag
= WAVE_FORMAT_PCM
;
261 if(device
->FmtType
== DevFmtUByte
|| device
->FmtType
== DevFmtByte
)
262 self
->Format
.wBitsPerSample
= 8;
264 self
->Format
.wBitsPerSample
= 16;
266 self
->Format
.nChannels
= ((device
->FmtChans
== DevFmtMono
) ? 1 : 2);
267 self
->Format
.nBlockAlign
= self
->Format
.wBitsPerSample
*
268 self
->Format
.nChannels
/ 8;
269 self
->Format
.nSamplesPerSec
= device
->Frequency
;
270 self
->Format
.nAvgBytesPerSec
= self
->Format
.nSamplesPerSec
*
271 self
->Format
.nBlockAlign
;
272 self
->Format
.cbSize
= 0;
274 MMRESULT res
{waveOutOpen(&self
->OutHdl
, DeviceID
, &self
->Format
,
275 (DWORD_PTR
)&ALCwinmmPlayback_waveOutProc
, (DWORD_PTR
)self
, CALLBACK_FUNCTION
277 if(res
!= MMSYSERR_NOERROR
)
279 if(device
->FmtType
== DevFmtFloat
)
281 device
->FmtType
= DevFmtShort
;
284 ERR("waveOutOpen failed: %u\n", res
);
285 return ALC_INVALID_VALUE
;
288 device
->DeviceName
= PlaybackDevices
[DeviceID
];
292 ALCboolean
ALCwinmmPlayback_reset(ALCwinmmPlayback
*self
)
294 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
296 device
->UpdateSize
= (ALuint
)((ALuint64
)device
->UpdateSize
*
297 self
->Format
.nSamplesPerSec
/
299 device
->UpdateSize
= (device
->UpdateSize
*device
->NumUpdates
+ 3) / 4;
300 device
->NumUpdates
= 4;
301 device
->Frequency
= self
->Format
.nSamplesPerSec
;
303 if(self
->Format
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
305 if(self
->Format
.wBitsPerSample
== 32)
306 device
->FmtType
= DevFmtFloat
;
309 ERR("Unhandled IEEE float sample depth: %d\n", self
->Format
.wBitsPerSample
);
313 else if(self
->Format
.wFormatTag
== WAVE_FORMAT_PCM
)
315 if(self
->Format
.wBitsPerSample
== 16)
316 device
->FmtType
= DevFmtShort
;
317 else if(self
->Format
.wBitsPerSample
== 8)
318 device
->FmtType
= DevFmtUByte
;
321 ERR("Unhandled PCM sample depth: %d\n", self
->Format
.wBitsPerSample
);
327 ERR("Unhandled format tag: 0x%04x\n", self
->Format
.wFormatTag
);
331 if(self
->Format
.nChannels
== 2)
332 device
->FmtChans
= DevFmtStereo
;
333 else if(self
->Format
.nChannels
== 1)
334 device
->FmtChans
= DevFmtMono
;
337 ERR("Unhandled channel count: %d\n", self
->Format
.nChannels
);
340 SetDefaultWFXChannelOrder(device
);
342 ALuint BufferSize
{device
->UpdateSize
*
343 FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
)};
345 al_free(self
->WaveBuffer
[0].lpData
);
346 self
->WaveBuffer
[0] = WAVEHDR
{};
347 self
->WaveBuffer
[0].lpData
= static_cast<char*>(al_calloc(16,
348 BufferSize
* self
->WaveBuffer
.size()));
349 self
->WaveBuffer
[0].dwBufferLength
= BufferSize
;
350 for(size_t i
{1};i
< self
->WaveBuffer
.size();i
++)
352 self
->WaveBuffer
[i
] = WAVEHDR
{};
353 self
->WaveBuffer
[i
].lpData
= self
->WaveBuffer
[i
-1].lpData
+
354 self
->WaveBuffer
[i
-1].dwBufferLength
;
355 self
->WaveBuffer
[i
].dwBufferLength
= BufferSize
;
362 ALCboolean
ALCwinmmPlayback_start(ALCwinmmPlayback
*self
)
365 std::for_each(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(),
366 [self
](WAVEHDR
&waveHdr
) -> void
367 { waveOutPrepareHeader(self
->OutHdl
, &waveHdr
, sizeof(WAVEHDR
)); }
369 self
->Writable
.store(self
->WaveBuffer
.size(), std::memory_order_release
);
371 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
372 self
->mThread
= std::thread(ALCwinmmPlayback_mixerProc
, self
);
375 catch(std::exception
& e
) {
376 ERR("Failed to start mixing thread: %s\n", e
.what());
383 void ALCwinmmPlayback_stop(ALCwinmmPlayback
*self
)
385 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
) || !self
->mThread
.joinable())
387 self
->mThread
.join();
389 while(self
->Writable
.load(std::memory_order_acquire
) < self
->WaveBuffer
.size())
390 alsem_wait(&self
->Sem
);
391 std::for_each(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(),
392 [self
](WAVEHDR
&waveHdr
) -> void
393 { waveOutUnprepareHeader(self
->OutHdl
, &waveHdr
, sizeof(WAVEHDR
)); }
395 self
->Writable
.store(0, std::memory_order_release
);
399 struct ALCwinmmCapture final
: public ALCbackend
{
400 std::atomic
<ALuint
> Readable
{0u};
403 std::array
<WAVEHDR
,4> WaveBuffer
;
405 HWAVEIN InHdl
{nullptr};
407 ll_ringbuffer_t
*Ring
{nullptr};
409 WAVEFORMATEX Format
{};
411 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
415 void ALCwinmmCapture_Construct(ALCwinmmCapture
*self
, ALCdevice
*device
);
416 void ALCwinmmCapture_Destruct(ALCwinmmCapture
*self
);
418 void CALLBACK
ALCwinmmCapture_waveInProc(HWAVEIN device
, UINT msg
, DWORD_PTR instance
, DWORD_PTR param1
, DWORD_PTR param2
);
419 int ALCwinmmCapture_captureProc(ALCwinmmCapture
*self
);
421 ALCenum
ALCwinmmCapture_open(ALCwinmmCapture
*self
, const ALCchar
*deviceName
);
422 DECLARE_FORWARD(ALCwinmmCapture
, ALCbackend
, ALCboolean
, reset
)
423 ALCboolean
ALCwinmmCapture_start(ALCwinmmCapture
*self
);
424 void ALCwinmmCapture_stop(ALCwinmmCapture
*self
);
425 ALCenum
ALCwinmmCapture_captureSamples(ALCwinmmCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
426 ALCuint
ALCwinmmCapture_availableSamples(ALCwinmmCapture
*self
);
427 DECLARE_FORWARD(ALCwinmmCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
428 DECLARE_FORWARD(ALCwinmmCapture
, ALCbackend
, void, lock
)
429 DECLARE_FORWARD(ALCwinmmCapture
, ALCbackend
, void, unlock
)
430 DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture
)
432 DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture
);
435 void ALCwinmmCapture_Construct(ALCwinmmCapture
*self
, ALCdevice
*device
)
437 new (self
) ALCwinmmCapture
{};
438 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
439 SET_VTABLE2(ALCwinmmCapture
, ALCbackend
, self
);
441 alsem_init(&self
->Sem
, 0);
442 std::fill(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(), WAVEHDR
{});
445 void ALCwinmmCapture_Destruct(ALCwinmmCapture
*self
)
447 // Close the Wave device
449 waveInClose(self
->InHdl
);
450 self
->InHdl
= nullptr;
452 al_free(self
->WaveBuffer
[0].lpData
);
453 std::fill(self
->WaveBuffer
.begin(), self
->WaveBuffer
.end(), WAVEHDR
{});
455 ll_ringbuffer_free(self
->Ring
);
456 self
->Ring
= nullptr;
458 alsem_destroy(&self
->Sem
);
460 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
461 self
->~ALCwinmmCapture();
465 /* ALCwinmmCapture_waveInProc
467 * Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
468 * is completed and returns to the application (with more data).
470 void CALLBACK
ALCwinmmCapture_waveInProc(HWAVEIN
UNUSED(device
), UINT msg
,
471 DWORD_PTR instance
, DWORD_PTR
UNUSED(param1
),
472 DWORD_PTR
UNUSED(param2
))
477 auto self
= reinterpret_cast<ALCwinmmCapture
*>(instance
);
478 self
->Readable
.fetch_add(1, std::memory_order_acq_rel
);
479 alsem_post(&self
->Sem
);
482 int ALCwinmmCapture_captureProc(ALCwinmmCapture
*self
)
484 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
486 althrd_setname(RECORD_THREAD_NAME
);
488 ALCwinmmCapture_lock(self
);
489 while(!self
->mKillNow
.load(std::memory_order_acquire
) &&
490 ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
))
492 ALsizei todo
= self
->Readable
.load(std::memory_order_acquire
);
495 ALCwinmmCapture_unlock(self
);
496 alsem_wait(&self
->Sem
);
497 ALCwinmmCapture_lock(self
);
503 WAVEHDR
&waveHdr
= self
->WaveBuffer
[widx
];
504 widx
= (widx
+1) % self
->WaveBuffer
.size();
506 ll_ringbuffer_write(self
->Ring
, waveHdr
.lpData
,
507 waveHdr
.dwBytesRecorded
/ self
->Format
.nBlockAlign
509 self
->Readable
.fetch_sub(1, std::memory_order_acq_rel
);
510 waveInAddBuffer(self
->InHdl
, &waveHdr
, sizeof(WAVEHDR
));
514 ALCwinmmCapture_unlock(self
);
520 ALCenum
ALCwinmmCapture_open(ALCwinmmCapture
*self
, const ALCchar
*deviceName
)
522 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
524 if(CaptureDevices
.empty())
525 ProbeCaptureDevices();
527 // Find the Device ID matching the deviceName if valid
528 auto iter
= deviceName
?
529 std::find(CaptureDevices
.cbegin(), CaptureDevices
.cend(), deviceName
) :
530 CaptureDevices
.cbegin();
531 if(iter
== CaptureDevices
.cend()) return ALC_INVALID_VALUE
;
532 UINT DeviceID
{static_cast<UINT
>(std::distance(CaptureDevices
.cbegin(), iter
))};
534 switch(device
->FmtChans
)
546 return ALC_INVALID_ENUM
;
549 switch(device
->FmtType
)
560 return ALC_INVALID_ENUM
;
563 memset(&self
->Format
, 0, sizeof(WAVEFORMATEX
));
564 self
->Format
.wFormatTag
= (device
->FmtType
== DevFmtFloat
) ?
565 WAVE_FORMAT_IEEE_FLOAT
: WAVE_FORMAT_PCM
;
566 self
->Format
.nChannels
= ChannelsFromDevFmt(device
->FmtChans
, device
->mAmbiOrder
);
567 self
->Format
.wBitsPerSample
= BytesFromDevFmt(device
->FmtType
) * 8;
568 self
->Format
.nBlockAlign
= self
->Format
.wBitsPerSample
*
569 self
->Format
.nChannels
/ 8;
570 self
->Format
.nSamplesPerSec
= device
->Frequency
;
571 self
->Format
.nAvgBytesPerSec
= self
->Format
.nSamplesPerSec
*
572 self
->Format
.nBlockAlign
;
573 self
->Format
.cbSize
= 0;
575 MMRESULT res
{waveInOpen(&self
->InHdl
, DeviceID
, &self
->Format
,
576 (DWORD_PTR
)&ALCwinmmCapture_waveInProc
, (DWORD_PTR
)self
, CALLBACK_FUNCTION
578 if(res
!= MMSYSERR_NOERROR
)
580 ERR("waveInOpen failed: %u\n", res
);
581 return ALC_INVALID_VALUE
;
584 // Ensure each buffer is 50ms each
585 DWORD BufferSize
{self
->Format
.nAvgBytesPerSec
/ 20};
586 BufferSize
-= (BufferSize
% self
->Format
.nBlockAlign
);
588 // Allocate circular memory buffer for the captured audio
589 // Make sure circular buffer is at least 100ms in size
590 DWORD CapturedDataSize
{std::max
<DWORD
>(device
->UpdateSize
*device
->NumUpdates
,
591 BufferSize
*self
->WaveBuffer
.size())};
593 self
->Ring
= ll_ringbuffer_create(CapturedDataSize
, self
->Format
.nBlockAlign
, false);
594 if(!self
->Ring
) return ALC_INVALID_VALUE
;
596 al_free(self
->WaveBuffer
[0].lpData
);
597 self
->WaveBuffer
[0] = WAVEHDR
{};
598 self
->WaveBuffer
[0].lpData
= static_cast<char*>(al_calloc(16, BufferSize
*4));
599 self
->WaveBuffer
[0].dwBufferLength
= BufferSize
;
600 for(size_t i
{1};i
< self
->WaveBuffer
.size();++i
)
602 self
->WaveBuffer
[i
] = WAVEHDR
{};
603 self
->WaveBuffer
[i
].lpData
= self
->WaveBuffer
[i
-1].lpData
+
604 self
->WaveBuffer
[i
-1].dwBufferLength
;
605 self
->WaveBuffer
[i
].dwBufferLength
= self
->WaveBuffer
[i
-1].dwBufferLength
;
608 device
->DeviceName
= CaptureDevices
[DeviceID
];
612 ALCboolean
ALCwinmmCapture_start(ALCwinmmCapture
*self
)
615 for(size_t i
{0};i
< self
->WaveBuffer
.size();++i
)
617 waveInPrepareHeader(self
->InHdl
, &self
->WaveBuffer
[i
], sizeof(WAVEHDR
));
618 waveInAddBuffer(self
->InHdl
, &self
->WaveBuffer
[i
], sizeof(WAVEHDR
));
621 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
622 self
->mThread
= std::thread(ALCwinmmCapture_captureProc
, self
);
624 waveInStart(self
->InHdl
);
627 catch(std::exception
& e
) {
628 ERR("Failed to start mixing thread: %s\n", e
.what());
635 void ALCwinmmCapture_stop(ALCwinmmCapture
*self
)
637 waveInStop(self
->InHdl
);
639 self
->mKillNow
.store(AL_TRUE
, std::memory_order_release
);
640 if(self
->mThread
.joinable())
642 alsem_post(&self
->Sem
);
643 self
->mThread
.join();
646 waveInReset(self
->InHdl
);
647 for(size_t i
{0};i
< self
->WaveBuffer
.size();++i
)
648 waveInUnprepareHeader(self
->InHdl
, &self
->WaveBuffer
[i
], sizeof(WAVEHDR
));
650 self
->Readable
.store(0, std::memory_order_release
);
654 ALCenum
ALCwinmmCapture_captureSamples(ALCwinmmCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
656 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
660 ALCuint
ALCwinmmCapture_availableSamples(ALCwinmmCapture
*self
)
662 return (ALCuint
)ll_ringbuffer_read_space(self
->Ring
);
668 bool WinMMBackendFactory::init()
671 void WinMMBackendFactory::deinit()
673 PlaybackDevices
.clear();
674 CaptureDevices
.clear();
677 bool WinMMBackendFactory::querySupport(ALCbackend_Type type
)
678 { return (type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
); }
680 void WinMMBackendFactory::probe(enum DevProbe type
, std::string
*outnames
)
682 auto add_device
= [outnames
](const std::string
&dname
) -> void
684 /* +1 to also append the null char (to ensure a null-separated list and
685 * double-null terminated list).
688 outnames
->append(dname
.c_str(), dname
.length()+1);
692 case ALL_DEVICE_PROBE
:
693 ProbePlaybackDevices();
694 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
697 case CAPTURE_DEVICE_PROBE
:
698 ProbeCaptureDevices();
699 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
704 ALCbackend
*WinMMBackendFactory::createBackend(ALCdevice
*device
, ALCbackend_Type type
)
706 if(type
== ALCbackend_Playback
)
708 ALCwinmmPlayback
*backend
;
709 NEW_OBJ(backend
, ALCwinmmPlayback
)(device
);
710 if(!backend
) return nullptr;
711 return STATIC_CAST(ALCbackend
, backend
);
713 if(type
== ALCbackend_Capture
)
715 ALCwinmmCapture
*backend
;
716 NEW_OBJ(backend
, ALCwinmmCapture
)(device
);
717 if(!backend
) return nullptr;
718 return STATIC_CAST(ALCbackend
, backend
);
724 BackendFactory
&WinMMBackendFactory::getFactory()
726 static WinMMBackendFactory factory
{};