diff options
Diffstat (limited to 'Alc/backends/winmm.cpp')
-rw-r--r-- | Alc/backends/winmm.cpp | 640 |
1 files changed, 0 insertions, 640 deletions
diff --git a/Alc/backends/winmm.cpp b/Alc/backends/winmm.cpp deleted file mode 100644 index cd32e95b..00000000 --- a/Alc/backends/winmm.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/** - * 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. - * Or go to http://www.gnu.org/copyleft/lgpl.html - */ - -#include "config.h" - -#include "backends/winmm.h" - -#include <stdlib.h> -#include <stdio.h> -#include <memory.h> - -#include <windows.h> -#include <mmsystem.h> - -#include <array> -#include <atomic> -#include <thread> -#include <vector> -#include <string> -#include <algorithm> -#include <functional> - -#include "alcmain.h" -#include "alu.h" -#include "ringbuffer.h" -#include "threads.h" -#include "compat.h" - -#ifndef WAVE_FORMAT_IEEE_FLOAT -#define WAVE_FORMAT_IEEE_FLOAT 0x0003 -#endif - -namespace { - -#define DEVNAME_HEAD "OpenAL Soft on " - - -al::vector<std::string> PlaybackDevices; -al::vector<std::string> CaptureDevices; - -bool checkName(const al::vector<std::string> &list, const std::string &name) -{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); } - -void ProbePlaybackDevices(void) -{ - PlaybackDevices.clear(); - - ALuint numdevs{waveOutGetNumDevs()}; - PlaybackDevices.reserve(numdevs); - for(ALuint i{0};i < numdevs;i++) - { - std::string dname; - - WAVEOUTCAPSW WaveCaps{}; - if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) - { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; - - int count{1}; - std::string newname{basename}; - while(checkName(PlaybackDevices, newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } - dname = std::move(newname); - - TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i); - } - PlaybackDevices.emplace_back(std::move(dname)); - } -} - -void ProbeCaptureDevices(void) -{ - CaptureDevices.clear(); - - ALuint numdevs{waveInGetNumDevs()}; - CaptureDevices.reserve(numdevs); - for(ALuint i{0};i < numdevs;i++) - { - std::string dname; - - WAVEINCAPSW WaveCaps{}; - if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) - { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; - - int count{1}; - std::string newname{basename}; - while(checkName(CaptureDevices, newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } - dname = std::move(newname); - - TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i); - } - 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; - - std::atomic<ALuint> mWritable{0u}; - al::semaphore mSem; - int mIdx{0}; - std::array<WAVEHDR,4> mWaveBuffer{}; - - HWAVEOUT mOutHdl{nullptr}; - - WAVEFORMATEX mFormat{}; - - std::atomic<bool> mKillNow{true}; - std::thread mThread; - - DEF_NEWDEL(WinMMPlayback) -}; - -WinMMPlayback::~WinMMPlayback() -{ - if(mOutHdl) - waveOutClose(mOutHdl); - mOutHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); -} - - -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, UINT msg, DWORD_PTR, DWORD_PTR) -{ - if(msg != WOM_DONE) return; - mWritable.fetch_add(1, std::memory_order_acq_rel); - mSem.post(); -} - -FORCE_ALIGN int WinMMPlayback::mixerProc() -{ - SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); - - lock(); - while(!mKillNow.load(std::memory_order_acquire) && - mDevice->Connected.load(std::memory_order_acquire)) - { - ALsizei todo = mWritable.load(std::memory_order_acquire); - if(todo < 1) - { - unlock(); - mSem.wait(); - lock(); - continue; - } - - int widx{mIdx}; - do { - WAVEHDR &waveHdr = mWaveBuffer[widx]; - widx = (widx+1) % mWaveBuffer.size(); - - aluMixData(mDevice, waveHdr.lpData, mDevice->UpdateSize); - mWritable.fetch_sub(1, std::memory_order_acq_rel); - waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR)); - } while(--todo); - mIdx = widx; - } - unlock(); - - return 0; -} - - -ALCenum WinMMPlayback::open(const ALCchar *name) -{ - if(PlaybackDevices.empty()) - ProbePlaybackDevices(); - - // Find the Device ID matching the deviceName if valid - auto iter = name ? - std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) : - PlaybackDevices.cbegin(); - if(iter == PlaybackDevices.cend()) return ALC_INVALID_VALUE; - 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)}; - if(res != MMSYSERR_NOERROR) - { - if(mDevice->FmtType == DevFmtFloat) - { - mDevice->FmtType = DevFmtShort; - goto retry_open; - } - ERR("waveOutOpen failed: %u\n", res); - return ALC_INVALID_VALUE; - } - - mDevice->DeviceName = PlaybackDevices[DeviceID]; - return ALC_NO_ERROR; -} - -ALCboolean WinMMPlayback::reset() -{ - mDevice->BufferSize = static_cast<ALuint>(uint64_t{mDevice->BufferSize} * - mFormat.nSamplesPerSec / mDevice->Frequency); - mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3; - mDevice->UpdateSize = mDevice->BufferSize / 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() -{ - try { - std::for_each(mWaveBuffer.begin(), mWaveBuffer.end(), - [this](WAVEHDR &waveHdr) -> void - { waveOutPrepareHeader(mOutHdl, &waveHdr, static_cast<UINT>(sizeof(WAVEHDR))); } - ); - 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}; - return ALC_TRUE; - } - catch(std::exception& e) { - ERR("Failed to start mixing thread: %s\n", e.what()); - } - catch(...) { - } - return ALC_FALSE; -} - -void WinMMPlayback::stop() -{ - if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable()) - return; - mThread.join(); - - 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)); } - ); - mWritable.store(0, std::memory_order_release); -} - - -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; - - std::atomic<ALuint> mReadable{0u}; - al::semaphore mSem; - int mIdx{0}; - std::array<WAVEHDR,4> mWaveBuffer{}; - - HWAVEIN mInHdl{nullptr}; - - RingBufferPtr mRing{nullptr}; - - WAVEFORMATEX mFormat{}; - - std::atomic<bool> mKillNow{true}; - std::thread mThread; - - DEF_NEWDEL(WinMMCapture) -}; - -WinMMCapture::~WinMMCapture() -{ - // 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, UINT msg, DWORD_PTR, DWORD_PTR) -{ - 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)}; - if(todo < 1) - { - unlock(); - mSem.wait(); - lock(); - continue; - } - - int widx{mIdx}; - do { - WAVEHDR &waveHdr = mWaveBuffer[widx]; - widx = (widx+1) % mWaveBuffer.size(); - - mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign); - mReadable.fetch_sub(1, std::memory_order_acq_rel); - waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR)); - } while(--todo); - mIdx = widx; - } - unlock(); - - return 0; -} - - -ALCenum WinMMCapture::open(const ALCchar *name) -{ - if(CaptureDevices.empty()) - ProbeCaptureDevices(); - - // Find the Device ID matching the deviceName if valid - auto iter = name ? - std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) : - CaptureDevices.cbegin(); - if(iter == CaptureDevices.cend()) return ALC_INVALID_VALUE; - auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter)); - - 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)}; - if(res != MMSYSERR_NOERROR) - { - ERR("waveInOpen failed: %u\n", res); - return ALC_INVALID_VALUE; - } - - // Ensure each buffer is 50ms each - DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u}; - BufferSize -= (BufferSize % mFormat.nBlockAlign); - - // Allocate circular memory buffer for the captured audio - // Make sure circular buffer is at least 100ms in size - ALuint CapturedDataSize{mDevice->BufferSize}; - CapturedDataSize = static_cast<ALuint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size())); - - mRing = CreateRingBuffer(CapturedDataSize, mFormat.nBlockAlign, false); - if(!mRing) return ALC_INVALID_VALUE; - - 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) - { - mWaveBuffer[i] = WAVEHDR{}; - mWaveBuffer[i].lpData = mWaveBuffer[i-1].lpData + mWaveBuffer[i-1].dwBufferLength; - mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength; - } - - mDevice->DeviceName = CaptureDevices[DeviceID]; - return ALC_NO_ERROR; -} - -ALCboolean WinMMCapture::start() -{ - try { - for(size_t i{0};i < mWaveBuffer.size();++i) - { - waveInPrepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR)); - waveInAddBuffer(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR)); - } - - mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this}; - - waveInStart(mInHdl); - return ALC_TRUE; - } - catch(std::exception& e) { - ERR("Failed to start mixing thread: %s\n", e.what()); - } - catch(...) { - } - return ALC_FALSE; -} - -void WinMMCapture::stop() -{ - waveInStop(mInHdl); - - mKillNow.store(true, std::memory_order_release); - if(mThread.joinable()) - { - mSem.post(); - mThread.join(); - } - - waveInReset(mInHdl); - for(size_t i{0};i < mWaveBuffer.size();++i) - waveInUnprepareHeader(mInHdl, &mWaveBuffer[i], sizeof(WAVEHDR)); - - mReadable.store(0, std::memory_order_release); - mIdx = 0; -} - -ALCenum WinMMCapture::captureSamples(void *buffer, ALCuint samples) -{ - mRing->read(buffer, samples); - return ALC_NO_ERROR; -} - -ALCuint WinMMCapture::availableSamples() -{ return (ALCuint)mRing->readSpace(); } - -} // namespace - - -bool WinMMBackendFactory::init() -{ return true; } - -bool WinMMBackendFactory::querySupport(BackendType type) -{ return type == BackendType::Playback || type == BackendType::Capture; } - -void WinMMBackendFactory::probe(DevProbe type, std::string *outnames) -{ - 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); - }; - switch(type) - { - case DevProbe::Playback: - ProbePlaybackDevices(); - std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); - break; - - case DevProbe::Capture: - ProbeCaptureDevices(); - std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); - break; - } -} - -BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type) -{ - if(type == BackendType::Playback) - return BackendPtr{new WinMMPlayback{device}}; - if(type == BackendType::Capture) - return BackendPtr{new WinMMCapture{device}}; - return nullptr; -} - -BackendFactory &WinMMBackendFactory::getFactory() -{ - static WinMMBackendFactory factory{}; - return factory; -} |