652 lines
19 KiB
C++
Raw Normal View History

2007-11-13 18:02:18 -08:00
/**
* OpenAL cross platform audio library
* Copyright (C) 1999-2007 by authors.
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2007-11-13 18:02:18 -08:00
* Or go to http://www.gnu.org/copyleft/lgpl.html
*/
2008-01-16 14:09:04 -08:00
#include "config.h"
2018-11-15 23:41:09 -08:00
#include "backends/winmm.h"
2007-11-13 18:02:18 -08:00
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
2018-11-09 01:34:59 -08:00
#include <array>
#include <atomic>
#include <thread>
#include <vector>
#include <string>
#include <algorithm>
2018-12-29 03:11:06 -08:00
#include <functional>
2018-11-09 01:34:59 -08:00
2007-11-13 18:02:18 -08:00
#include "alMain.h"
2012-09-14 02:14:29 -07:00
#include "alu.h"
#include "ringbuffer.h"
2013-10-27 08:14:13 -07:00
#include "threads.h"
2018-11-09 01:34:59 -08:00
#include "compat.h"
2007-11-13 18:02:18 -08:00
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#endif
2018-11-09 01:34:59 -08:00
namespace {
2018-11-09 01:34:59 -08:00
#define DEVNAME_HEAD "OpenAL Soft on "
2007-11-13 18:02:18 -08:00
al::vector<std::string> PlaybackDevices;
al::vector<std::string> CaptureDevices;
bool checkName(const al::vector<std::string> &list, const std::string &name)
2018-11-09 01:34:59 -08:00
{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
2018-11-08 20:32:31 -08:00
void ProbePlaybackDevices(void)
{
2018-11-09 01:34:59 -08:00
PlaybackDevices.clear();
2018-11-09 01:34:59 -08:00
ALuint numdevs{waveOutGetNumDevs()};
PlaybackDevices.reserve(numdevs);
for(ALuint i{0};i < numdevs;i++)
{
2018-11-09 01:34:59 -08:00
std::string dname;
2018-11-09 01:34:59 -08:00
WAVEOUTCAPSW WaveCaps{};
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
2018-11-09 01:34:59 -08:00
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
int count{1};
std::string newname{basename};
while(checkName(PlaybackDevices, newname))
{
2018-11-09 01:34:59 -08:00
newname = basename;
newname += " #";
newname += std::to_string(++count);
}
2018-11-09 01:34:59 -08:00
dname = std::move(newname);
2018-11-09 01:34:59 -08:00
TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
}
2018-11-09 01:34:59 -08:00
PlaybackDevices.emplace_back(std::move(dname));
}
}
2018-11-08 20:32:31 -08:00
void ProbeCaptureDevices(void)
{
2018-11-09 01:34:59 -08:00
CaptureDevices.clear();
2018-11-09 01:34:59 -08:00
ALuint numdevs{waveInGetNumDevs()};
CaptureDevices.reserve(numdevs);
for(ALuint i{0};i < numdevs;i++)
{
2018-11-09 01:34:59 -08:00
std::string dname;
2018-11-09 01:34:59 -08:00
WAVEINCAPSW WaveCaps{};
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
{
2018-11-09 01:34:59 -08:00
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
int count{1};
std::string newname{basename};
while(checkName(CaptureDevices, newname))
{
2018-11-09 01:34:59 -08:00
newname = basename;
newname += " #";
newname += std::to_string(++count);
}
2018-11-09 01:34:59 -08:00
dname = std::move(newname);
2018-11-09 01:34:59 -08:00
TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i);
}
2018-11-09 01:34:59 -08:00
CaptureDevices.emplace_back(std::move(dname));
}
}
struct WinMMPlayback final : public BackendBase {
WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
~WinMMPlayback() override;
static void CALLBACK waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
int mixerProc();
ALCenum open(const ALCchar *name) override;
ALCboolean reset() override;
ALCboolean start() override;
void stop() override;
2018-12-27 17:48:02 -08:00
std::atomic<ALuint> mWritable{0u};
al::semaphore mSem;
int mIdx{0};
std::array<WAVEHDR,4> mWaveBuffer{};
2018-12-27 17:48:02 -08:00
HWAVEOUT mOutHdl{nullptr};
2018-12-27 17:48:02 -08:00
WAVEFORMATEX mFormat{};
std::atomic<bool> mKillNow{true};
2018-11-26 17:31:04 -08:00
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMPlayback::"; }
DEF_NEWDEL(WinMMPlayback)
2018-11-08 20:32:31 -08:00
};
WinMMPlayback::~WinMMPlayback()
{
if(mOutHdl)
waveOutClose(mOutHdl);
mOutHdl = nullptr;
2018-11-09 01:34:59 -08:00
al_free(mWaveBuffer[0].lpData);
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
}
2007-11-13 18:02:18 -08:00
void CALLBACK WinMMPlayback::waveOutProcC(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
{ reinterpret_cast<WinMMPlayback*>(instance)->waveOutProc(device, msg, param1, param2); }
/* WinMMPlayback::waveOutProc
*
* Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
* completed and returns to the application (for more data)
*/
void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT UNUSED(device), UINT msg,
DWORD_PTR UNUSED(param1), DWORD_PTR UNUSED(param2))
2007-11-13 18:02:18 -08:00
{
if(msg != WOM_DONE) return;
mWritable.fetch_add(1, std::memory_order_acq_rel);
mSem.post();
2007-11-13 18:02:18 -08:00
}
FORCE_ALIGN int WinMMPlayback::mixerProc()
2007-11-13 18:02:18 -08:00
{
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
2007-11-13 18:02:18 -08:00
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
ALsizei todo = mWritable.load(std::memory_order_acquire);
2018-11-09 01:34:59 -08:00
if(todo < 1)
{
unlock();
mSem.wait();
lock();
continue;
}
int widx{mIdx};
2018-11-09 01:34:59 -08:00
do {
WAVEHDR &waveHdr = mWaveBuffer[widx];
widx = (widx+1) % mWaveBuffer.size();
2007-11-13 18:02:18 -08:00
aluMixData(mDevice, waveHdr.lpData, mDevice->UpdateSize);
mWritable.fetch_sub(1, std::memory_order_acq_rel);
waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR));
2018-11-09 01:34:59 -08:00
} while(--todo);
mIdx = widx;
2007-11-13 18:02:18 -08:00
}
unlock();
2007-11-13 18:02:18 -08:00
return 0;
}
ALCenum WinMMPlayback::open(const ALCchar *name)
2007-11-13 18:02:18 -08:00
{
2018-11-09 01:34:59 -08:00
if(PlaybackDevices.empty())
ProbePlaybackDevices();
// Find the Device ID matching the deviceName if valid
auto iter = name ?
std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
2018-11-09 01:34:59 -08:00
PlaybackDevices.cbegin();
if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE;
2018-12-27 17:48:02 -08:00
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
retry_open:
mFormat = WAVEFORMATEX{};
if(mDevice->FmtType == DevFmtFloat)
{
mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
mFormat.wBitsPerSample = 32;
}
else
{
mFormat.wFormatTag = WAVE_FORMAT_PCM;
if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
mFormat.wBitsPerSample = 8;
else
mFormat.wBitsPerSample = 16;
}
mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
mFormat.nSamplesPerSec = mDevice->Frequency;
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
mFormat.cbSize = 0;
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMPlayback::waveOutProcC,
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
2018-11-09 01:34:59 -08:00
if(res != MMSYSERR_NOERROR)
{
if(mDevice->FmtType == DevFmtFloat)
{
mDevice->FmtType = DevFmtShort;
goto retry_open;
}
2011-07-13 01:43:00 -07:00
ERR("waveOutOpen failed: %u\n", res);
2018-11-09 01:34:59 -08:00
return ALC_INVALID_VALUE;
}
mDevice->DeviceName = PlaybackDevices[DeviceID];
return ALC_NO_ERROR;
2007-11-13 18:02:18 -08:00
}
ALCboolean WinMMPlayback::reset()
{
mDevice->UpdateSize = static_cast<ALuint>(
(ALuint64)mDevice->UpdateSize * mFormat.nSamplesPerSec / mDevice->Frequency);
mDevice->UpdateSize = (mDevice->UpdateSize*mDevice->NumUpdates + 3) / 4;
mDevice->NumUpdates = 4;
mDevice->Frequency = mFormat.nSamplesPerSec;
if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
if(mFormat.wBitsPerSample == 32)
mDevice->FmtType = DevFmtFloat;
else
{
ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample);
return ALC_FALSE;
}
}
else if(mFormat.wFormatTag == WAVE_FORMAT_PCM)
{
if(mFormat.wBitsPerSample == 16)
mDevice->FmtType = DevFmtShort;
else if(mFormat.wBitsPerSample == 8)
mDevice->FmtType = DevFmtUByte;
else
{
ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample);
return ALC_FALSE;
}
}
else
{
ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag);
return ALC_FALSE;
}
if(mFormat.nChannels == 2)
mDevice->FmtChans = DevFmtStereo;
else if(mFormat.nChannels == 1)
mDevice->FmtChans = DevFmtMono;
else
{
ERR("Unhandled channel count: %d\n", mFormat.nChannels);
return ALC_FALSE;
}
SetDefaultWFXChannelOrder(mDevice);
ALuint BufferSize{mDevice->UpdateSize * mDevice->frameSizeFromFmt()};
al_free(mWaveBuffer[0].lpData);
mWaveBuffer[0] = WAVEHDR{};
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < mWaveBuffer.size();i++)
{
mWaveBuffer[i] = WAVEHDR{};
mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
mWaveBuffer[i].dwBufferLength = BufferSize;
}
mIdx = 0;
return ALC_TRUE;
}
ALCboolean WinMMPlayback::start()
{
2018-11-09 01:34:59 -08:00
try {
std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
[this](WAVEHDR &waveHdr) -> void
{ waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); }
2018-11-09 01:34:59 -08:00
);
mWritable.store(static_cast<ALuint>(mWaveBuffer.size()), std::memory_order_release);
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this};
2018-11-09 01:34:59 -08:00
return ALC_TRUE;
}
2018-11-09 01:34:59 -08:00
catch(std::exception& e) {
ERR("Failed to start mixing thread: %s\n", e.what());
}
catch(...) {
}
return ALC_FALSE;
}
void WinMMPlayback::stop()
2018-11-09 01:34:59 -08:00
{
if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
2018-11-09 01:34:59 -08:00
return;
mThread.join();
2018-11-09 01:34:59 -08:00
while(mWritable.load(std::memory_order_acquire) < mWaveBuffer.size())
mSem.wait();
std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(),
[this](WAVEHDR &waveHdr) -> void
{ waveOutUnprepareHeader(mOutHdl, &waveHdr, sizeof(WAVEHDR)); }
2018-11-09 01:34:59 -08:00
);
mWritable.store(0, std::memory_order_release);
2018-11-09 01:34:59 -08:00
}
struct WinMMCapture final : public BackendBase {
WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
~WinMMCapture() override;
static void CALLBACK waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2);
int captureProc();
ALCenum open(const ALCchar *name) override;
ALCboolean start() override;
void stop() override;
ALCenum captureSamples(void *buffer, ALCuint samples) override;
ALCuint availableSamples() override;
2018-12-27 17:48:02 -08:00
std::atomic<ALuint> mReadable{0u};
al::semaphore mSem;
int mIdx{0};
std::array<WAVEHDR,4> mWaveBuffer{};
2018-12-27 17:48:02 -08:00
HWAVEIN mInHdl{nullptr};
2018-12-27 17:48:02 -08:00
RingBufferPtr mRing{nullptr};
2018-12-27 17:48:02 -08:00
WAVEFORMATEX mFormat{};
std::atomic<bool> mKillNow{true};
2018-11-26 17:31:04 -08:00
std::thread mThread;
static constexpr inline const char *CurrentPrefix() noexcept { return "WinMMCapture::"; }
DEF_NEWDEL(WinMMCapture)
2018-11-08 20:32:31 -08:00
};
WinMMCapture::~WinMMCapture()
2007-11-13 18:02:18 -08:00
{
2018-11-09 01:34:59 -08:00
// Close the Wave device
if(mInHdl)
waveInClose(mInHdl);
mInHdl = nullptr;
al_free(mWaveBuffer[0].lpData);
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
}
void CALLBACK WinMMCapture::waveInProcC(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
{ reinterpret_cast<WinMMCapture*>(instance)->waveInProc(device, msg, param1, param2); }
/* WinMMCapture::waveInProc
*
* Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
* completed and returns to the application (with more data).
*/
void CALLBACK WinMMCapture::waveInProc(HWAVEIN UNUSED(device), UINT msg,
DWORD_PTR UNUSED(param1), DWORD_PTR UNUSED(param2))
{
if(msg != WIM_DATA) return;
mReadable.fetch_add(1, std::memory_order_acq_rel);
mSem.post();
}
int WinMMCapture::captureProc()
{
althrd_setname(RECORD_THREAD_NAME);
lock();
while(!mKillNow.load(std::memory_order_acquire) &&
mDevice->Connected.load(std::memory_order_acquire))
{
ALuint todo{mReadable.load(std::memory_order_acquire)};
2018-11-09 01:34:59 -08:00
if(todo < 1)
{
unlock();
mSem.wait();
lock();
continue;
2018-11-09 01:34:59 -08:00
}
int widx{mIdx};
2018-11-09 01:34:59 -08:00
do {
WAVEHDR &waveHdr = mWaveBuffer[widx];
widx = (widx+1) % mWaveBuffer.size();
2018-11-09 01:34:59 -08:00
mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
mReadable.fetch_sub(1, std::memory_order_acq_rel);
waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
2018-11-09 01:34:59 -08:00
} while(--todo);
mIdx = widx;
}
unlock();
return 0;
}
ALCenum WinMMCapture::open(const ALCchar *name)
{
2018-11-09 01:34:59 -08:00
if(CaptureDevices.empty())
ProbeCaptureDevices();
2010-03-09 06:09:29 -08:00
2007-11-13 18:02:18 -08:00
// Find the Device ID matching the deviceName if valid
auto iter = name ?
std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
2018-11-09 01:34:59 -08:00
CaptureDevices.cbegin();
if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE;
2018-12-27 17:48:02 -08:00
auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
2007-11-13 18:02:18 -08:00
switch(mDevice->FmtChans)
{
case DevFmtMono:
case DevFmtStereo:
break;
case DevFmtQuad:
case DevFmtX51:
case DevFmtX51Rear:
case DevFmtX61:
case DevFmtX71:
case DevFmtAmbi3D:
return ALC_INVALID_ENUM;
}
switch(mDevice->FmtType)
{
case DevFmtUByte:
case DevFmtShort:
case DevFmtInt:
case DevFmtFloat:
break;
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
return ALC_INVALID_ENUM;
}
mFormat = WAVEFORMATEX{};
mFormat.wFormatTag = (mDevice->FmtType == DevFmtFloat) ?
WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
mFormat.nChannels = mDevice->channelsFromFmt();
mFormat.wBitsPerSample = mDevice->bytesFromFmt() * 8;
mFormat.nBlockAlign = mFormat.wBitsPerSample * mFormat.nChannels / 8;
mFormat.nSamplesPerSec = mDevice->Frequency;
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
mFormat.cbSize = 0;
MMRESULT res{waveInOpen(&mInHdl, DeviceID, &mFormat, (DWORD_PTR)&WinMMCapture::waveInProcC,
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
2018-11-09 01:34:59 -08:00
if(res != MMSYSERR_NOERROR)
{
2011-07-13 01:43:00 -07:00
ERR("waveInOpen failed: %u\n", res);
2018-11-09 01:34:59 -08:00
return ALC_INVALID_VALUE;
}
2007-11-13 18:02:18 -08:00
2018-11-09 01:34:59 -08:00
// Ensure each buffer is 50ms each
DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u};
BufferSize -= (BufferSize % mFormat.nBlockAlign);
2007-11-13 18:02:18 -08:00
2018-11-09 01:34:59 -08:00
// Allocate circular memory buffer for the captured audio
// Make sure circular buffer is at least 100ms in size
ALuint CapturedDataSize{mDevice->UpdateSize*mDevice->NumUpdates};
2018-12-27 17:48:02 -08:00
CapturedDataSize = static_cast<ALuint>(
std::max<size_t>(CapturedDataSize, BufferSize*mWaveBuffer.size()));
2018-12-27 17:48:02 -08:00
mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false);
if(!mRing) return ALC_INVALID_VALUE;
2018-12-27 17:48:02 -08:00
al_free(mWaveBuffer[0].lpData);
mWaveBuffer[0] = WAVEHDR{};
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize*4));
mWaveBuffer[0].dwBufferLength = BufferSize;
for(size_t i{1};i < mWaveBuffer.size();++i)
2007-11-13 18:02:18 -08:00
{
mWaveBuffer[i] = WAVEHDR{};
mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength;
mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength;
2007-11-13 18:02:18 -08:00
}
mDevice->DeviceName = CaptureDevices[DeviceID];
return ALC_NO_ERROR;
2007-11-13 18:02:18 -08:00
}
ALCboolean WinMMCapture::start()
2007-11-13 18:02:18 -08:00
{
2018-11-09 01:34:59 -08:00
try {
for(size_t i{0};i < mWaveBuffer.size();++i)
2018-11-09 01:34:59 -08:00
{
waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
2018-11-09 01:34:59 -08:00
}
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this};
2018-11-09 01:34:59 -08:00
waveInStart(mInHdl);
2018-11-09 01:34:59 -08:00
return ALC_TRUE;
}
catch(std::exception& e) {
ERR("Failed to start mixing thread: %s\n", e.what());
}
catch(...) {
}
return ALC_FALSE;
2007-11-13 18:02:18 -08:00
}
void WinMMCapture::stop()
2007-11-13 18:02:18 -08:00
{
waveInStop(mInHdl);
2018-11-09 01:34:59 -08:00
mKillNow.store(true, std::memory_order_release);
if(mThread.joinable())
2018-11-09 01:34:59 -08:00
{
mSem.post();
mThread.join();
2018-11-09 01:34:59 -08:00
}
waveInReset(mInHdl);
for(size_t i{0};i < mWaveBuffer.size();++i)
waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR));
2018-11-09 01:34:59 -08:00
mReadable.store(0, std::memory_order_release);
mIdx = 0;
2007-11-13 18:02:18 -08:00
}
ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples)
2007-11-13 18:02:18 -08:00
{
mRing->read(buffer, samples);
return ALC_NO_ERROR;
2007-11-13 18:02:18 -08:00
}
ALCuint WinMMCapture::availableSamples()
{ return (ALCuint)mRing->readSpace(); }
2007-11-13 18:02:18 -08:00
2018-11-15 23:41:09 -08:00
} // namespace
2018-11-08 20:32:31 -08:00
2018-11-15 23:41:09 -08:00
bool WinMMBackendFactory::init()
{ return true; }
2018-11-15 23:41:09 -08:00
void WinMMBackendFactory::deinit()
2007-11-13 18:02:18 -08:00
{
2018-11-09 01:34:59 -08:00
PlaybackDevices.clear();
CaptureDevices.clear();
}
2018-12-29 01:38:26 -08:00
bool WinMMBackendFactory::querySupport(BackendType type)
{ return type == BackendType::Playback || type == BackendType::Capture; }
void WinMMBackendFactory::probe(DevProbe type, std::string *outnames)
{
2018-11-09 01:34:59 -08:00
auto add_device = [outnames](const std::string &dname) -> void
{
/* +1 to also append the null char (to ensure a null-separated list and
* double-null terminated list).
*/
if(!dname.empty())
outnames->append(dname.c_str(), dname.length()+1);
2018-11-09 01:34:59 -08:00
};
2011-06-14 04:02:58 -07:00
switch(type)
{
2011-06-14 04:02:58 -07:00
case ALL_DEVICE_PROBE:
ProbePlaybackDevices();
2018-11-09 01:34:59 -08:00
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
2011-06-14 04:02:58 -07:00
break;
case CAPTURE_DEVICE_PROBE:
ProbeCaptureDevices();
2018-11-09 01:34:59 -08:00
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
2011-06-14 04:02:58 -07:00
break;
2007-11-13 18:02:18 -08:00
}
}
2018-12-29 02:16:16 -08:00
BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
{
2018-12-29 01:38:26 -08:00
if(type == BackendType::Playback)
2018-12-29 02:16:16 -08:00
return BackendPtr{new WinMMPlayback{device}};
2018-12-29 01:38:26 -08:00
if(type == BackendType::Capture)
2018-12-29 02:16:16 -08:00
return BackendPtr{new WinMMCapture{device}};
2018-11-09 01:34:59 -08:00
return nullptr;
}
2018-11-15 23:41:09 -08:00
BackendFactory &WinMMBackendFactory::getFactory()
{
2018-11-15 23:41:09 -08:00
static WinMMBackendFactory factory{};
return factory;
}