Rename some struct members
[openal-soft.git] / Alc / backends / winmm.cpp
blobb4651b4ce105c624d5b352ec9895e63fa21e9e38
1 /**
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
21 #include "config.h"
23 #include "backends/winmm.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
29 #include <windows.h>
30 #include <mmsystem.h>
32 #include <array>
33 #include <atomic>
34 #include <thread>
35 #include <vector>
36 #include <string>
37 #include <algorithm>
39 #include "alMain.h"
40 #include "alu.h"
41 #include "ringbuffer.h"
42 #include "threads.h"
43 #include "compat.h"
45 #ifndef WAVE_FORMAT_IEEE_FLOAT
46 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
47 #endif
49 namespace {
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++)
68 std::string dname;
70 WAVEOUTCAPSW WaveCaps{};
71 if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
73 const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
75 int count{1};
76 std::string newname{basename};
77 while(checkName(PlaybackDevices, newname))
79 newname = basename;
80 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++)
99 std::string dname;
101 WAVEINCAPSW WaveCaps{};
102 if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
104 const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
106 int count{1};
107 std::string newname{basename};
108 while(checkName(CaptureDevices, newname))
110 newname = basename;
111 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};
125 alsem_t Sem;
126 int Idx{0};
127 std::array<WAVEHDR,4> WaveBuffer;
129 HWAVEOUT OutHdl{nullptr};
131 WAVEFORMATEX Format{};
133 std::atomic<ALenum> mKillNow{AL_TRUE};
134 std::thread mThread;
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)
169 if(self->OutHdl)
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))
192 if(msg != WOM_DONE)
193 return;
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;
204 SetRTPriority();
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);
212 if(todo < 1)
214 ALCwinmmPlayback_unlock(self);
215 alsem_wait(&self->Sem);
216 ALCwinmmPlayback_lock(self);
217 continue;
220 int widx{self->Idx};
221 do {
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));
228 } while(--todo);
229 self->Idx = widx;
231 ALCwinmmPlayback_unlock(self);
233 return 0;
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))};
251 retry_open:
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;
258 else
260 self->Format.wFormatTag = WAVE_FORMAT_PCM;
261 if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
262 self->Format.wBitsPerSample = 8;
263 else
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;
282 goto retry_open;
284 ERR("waveOutOpen failed: %u\n", res);
285 return ALC_INVALID_VALUE;
288 device->DeviceName = PlaybackDevices[DeviceID];
289 return ALC_NO_ERROR;
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 /
298 device->Frequency);
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;
307 else
309 ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
310 return ALC_FALSE;
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;
319 else
321 ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
322 return ALC_FALSE;
325 else
327 ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
328 return ALC_FALSE;
331 if(self->Format.nChannels == 2)
332 device->FmtChans = DevFmtStereo;
333 else if(self->Format.nChannels == 1)
334 device->FmtChans = DevFmtMono;
335 else
337 ERR("Unhandled channel count: %d\n", self->Format.nChannels);
338 return ALC_FALSE;
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;
357 self->Idx = 0;
359 return ALC_TRUE;
362 ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
364 try {
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);
373 return ALC_TRUE;
375 catch(std::exception& e) {
376 ERR("Failed to start mixing thread: %s\n", e.what());
378 catch(...) {
380 return ALC_FALSE;
383 void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
385 if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
386 return;
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};
401 alsem_t Sem;
402 int Idx{0};
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};
412 std::thread mThread;
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
448 if(self->InHdl)
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))
474 if(msg != WIM_DATA)
475 return;
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);
493 if(todo < 1)
495 ALCwinmmCapture_unlock(self);
496 alsem_wait(&self->Sem);
497 ALCwinmmCapture_lock(self);
498 continue;
501 int widx{self->Idx};
502 do {
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));
511 } while(--todo);
512 self->Idx = widx;
514 ALCwinmmCapture_unlock(self);
516 return 0;
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)
536 case DevFmtMono:
537 case DevFmtStereo:
538 break;
540 case DevFmtQuad:
541 case DevFmtX51:
542 case DevFmtX51Rear:
543 case DevFmtX61:
544 case DevFmtX71:
545 case DevFmtAmbi3D:
546 return ALC_INVALID_ENUM;
549 switch(device->FmtType)
551 case DevFmtUByte:
552 case DevFmtShort:
553 case DevFmtInt:
554 case DevFmtFloat:
555 break;
557 case DevFmtByte:
558 case DevFmtUShort:
559 case DevFmtUInt:
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];
609 return ALC_NO_ERROR;
612 ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
614 try {
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);
625 return ALC_TRUE;
627 catch(std::exception& e) {
628 ERR("Failed to start mixing thread: %s\n", e.what());
630 catch(...) {
632 return ALC_FALSE;
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);
651 self->Idx = 0;
654 ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
656 ll_ringbuffer_read(self->Ring, buffer, samples);
657 return ALC_NO_ERROR;
660 ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
662 return (ALCuint)ll_ringbuffer_read_space(self->Ring);
665 } // namespace
668 bool WinMMBackendFactory::init()
669 { return true; }
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).
687 if(!dname.empty())
688 outnames->append(dname.c_str(), dname.length()+1);
690 switch(type)
692 case ALL_DEVICE_PROBE:
693 ProbePlaybackDevices();
694 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
695 break;
697 case CAPTURE_DEVICE_PROBE:
698 ProbeCaptureDevices();
699 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
700 break;
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);
721 return nullptr;
724 BackendFactory &WinMMBackendFactory::getFactory()
726 static WinMMBackendFactory factory{};
727 return factory;