From 93b9b51652e45abf406388c127dfcc5bf8595298 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 22 Mar 2016 16:00:45 -1000 Subject: [PATCH] Dropping XAudio2 for WASAPI for the benefit of Win7 builders/users --- CMakeLists.txt | 2 +- lib/audiodev/WASAPI.cpp | 274 +++++++++++++++++++++++++++++++++++++++ lib/audiodev/XAudio2.cpp | 241 ---------------------------------- 3 files changed, 275 insertions(+), 242 deletions(-) create mode 100644 lib/audiodev/WASAPI.cpp delete mode 100644 lib/audiodev/XAudio2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3468b32..4d46b3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ if(WIN32) lib/inputdev/HIDDeviceWinUSB.cpp lib/graphicsdev/D3D11.cpp lib/graphicsdev/D3D12.cpp - lib/audiodev/XAudio2.cpp) + lib/audiodev/WASAPI.cpp) list(APPEND PLAT_HDRS include/boo/graphicsdev/D3D.hpp) diff --git a/lib/audiodev/WASAPI.cpp b/lib/audiodev/WASAPI.cpp new file mode 100644 index 0000000..3d8fc9d --- /dev/null +++ b/lib/audiodev/WASAPI.cpp @@ -0,0 +1,274 @@ +#include "../win/Win32Common.hpp" +#include "boo/audiodev/IAudioVoiceAllocator.hpp" +#include "logvisor/logvisor.hpp" + +#include +#include + +const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); +const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); +const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); + +namespace boo +{ +static logvisor::Module Log("boo::WASAPI"); + +struct WASAPIAudioVoice : IAudioVoice +{ + struct WASAPIAudioVoiceAllocator& m_parent; + std::list::iterator m_parentIt; + + ChannelMap m_map; + IAudioVoiceCallback* m_cb; + ComPtr m_audClient; + ComPtr m_renderClient; + UINT32 m_bufferFrames = 1024; + size_t m_frameSize; + + const ChannelMap& channelMap() const {return m_map;} + + WASAPIAudioVoice(WASAPIAudioVoiceAllocator& parent, IMMDevice* dev, AudioChannelSet set, + unsigned sampleRate, IAudioVoiceCallback* cb) + : m_parent(parent), m_cb(cb) + { + unsigned chCount = ChannelCount(set); + + WAVEFORMATEX desc = {}; + desc.wFormatTag = WAVE_FORMAT_PCM; + desc.nChannels = chCount; + desc.nSamplesPerSec = sampleRate; + desc.wBitsPerSample = 16; + desc.nBlockAlign = desc.nChannels * desc.wBitsPerSample / 8; + desc.nAvgBytesPerSec = desc.nSamplesPerSec * desc.nBlockAlign; + + if (FAILED(dev->Activate(IID_IAudioClient, CLSCTX_ALL, + nullptr, &m_audClient))) + { + Log.report(logvisor::Fatal, "unable to create audio client"); + return; + } + + WAVEFORMATEX* works; + m_audClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &desc, &works); + + HRESULT hr = m_audClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, + 1000000, 0, &desc, nullptr); + + if (FAILED(hr)) + { + Log.report(logvisor::Fatal, "unable to initialize audio client"); + return; + } + + if (FAILED(m_audClient->GetBufferSize(&m_bufferFrames))) + { + Log.report(logvisor::Fatal, "unable to obtain audio buffer size"); + return; + } + + if (FAILED(m_audClient->GetService(IID_IAudioRenderClient, &m_renderClient))) + { + Log.report(logvisor::Fatal, "unable to create audio render client"); + return; + } + + switch (chCount) + { + case 2: + m_map.m_channelCount = 2; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + break; + case 4: + m_map.m_channelCount = 4; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + m_map.m_channels[2] = AudioChannel::RearLeft; + m_map.m_channels[3] = AudioChannel::RearRight; + break; + case 5: + m_map.m_channelCount = 5; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + m_map.m_channels[2] = AudioChannel::FrontCenter; + m_map.m_channels[3] = AudioChannel::RearLeft; + m_map.m_channels[4] = AudioChannel::RearRight; + break; + case 6: + m_map.m_channelCount = 6; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + m_map.m_channels[2] = AudioChannel::FrontCenter; + m_map.m_channels[3] = AudioChannel::LFE; + m_map.m_channels[4] = AudioChannel::RearLeft; + m_map.m_channels[5] = AudioChannel::RearRight; + break; + case 8: + m_map.m_channelCount = 8; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + m_map.m_channels[2] = AudioChannel::FrontCenter; + m_map.m_channels[3] = AudioChannel::LFE; + m_map.m_channels[4] = AudioChannel::RearLeft; + m_map.m_channels[5] = AudioChannel::RearRight; + m_map.m_channels[6] = AudioChannel::SideLeft; + m_map.m_channels[7] = AudioChannel::SideRight; + break; + default: + Log.report(logvisor::Error, "unknown channel layout %u; using stereo", chCount); + m_map.m_channelCount = 2; + m_map.m_channels[0] = AudioChannel::FrontLeft; + m_map.m_channels[1] = AudioChannel::FrontRight; + break; + } + + while (m_map.m_channelCount < chCount) + m_map.m_channels[m_map.m_channelCount++] = AudioChannel::Unknown; + + m_frameSize = chCount * 2; + + for (unsigned i=0 ; i<3 ; ++i) + m_cb->needsNextBuffer(*this, m_bufferFrames); + } + + void bufferSampleData(const int16_t* data, size_t frames) + { + BYTE* dataOut; + if (FAILED(m_renderClient->GetBuffer(frames, &dataOut))) + { + Log.report(logvisor::Fatal, L"unable to obtain audio buffer"); + return; + } + + memcpy(dataOut, data, frames * m_frameSize); + + if (FAILED(m_renderClient->ReleaseBuffer(frames, 0))) + { + Log.report(logvisor::Fatal, L"unable to release audio buffer"); + return; + } + } + + void start() + { + m_audClient->Start(); + } + + void stop() + { + m_audClient->Stop(); + } + + void pump() + { + UINT32 padding; + if (FAILED(m_audClient->GetCurrentPadding(&padding))) + { + Log.report(logvisor::Fatal, L"unable to obtain audio buffer padding"); + return; + } + INT32 available = m_bufferFrames - padding; + m_cb->needsNextBuffer(*this, available); + } + + ~WASAPIAudioVoice(); +}; + +struct WASAPIAudioVoiceAllocator : IAudioVoiceAllocator +{ + ComPtr m_device; + AudioChannelSet m_maxSet = AudioChannelSet::Unknown; + std::list m_allocatedVoices; + + WASAPIAudioVoiceAllocator() + { + ComPtr pEnumerator; + + if (FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, + CLSCTX_ALL, IID_IMMDeviceEnumerator, + &pEnumerator))) + { + Log.report(logvisor::Fatal, L"unable to create MMDeviceEnumerator instance"); + return; + } + + if (FAILED(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &m_device))) + { + Log.report(logvisor::Fatal, L"unable to obtain default audio device"); + return; + } + + ComPtr pAudioClient; + if (FAILED(m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, &pAudioClient))) + { + Log.report(logvisor::Fatal, L"unable to create audio client from device"); + return; + } + + WAVEFORMATEXTENSIBLE* pwfx; + if (FAILED(pAudioClient->GetMixFormat((WAVEFORMATEX**)&pwfx))) + { + Log.report(logvisor::Fatal, L"unable to obtain audio mix format from device"); + return; + } + + if ((pwfx->dwChannelMask & (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)) == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)) + { + m_maxSet = AudioChannelSet::Stereo; + if ((pwfx->dwChannelMask & (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)) == (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)) + { + m_maxSet = AudioChannelSet::Quad; + if ((pwfx->dwChannelMask & (SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY)) == (SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY)) + { + m_maxSet = AudioChannelSet::Surround51; + if ((pwfx->dwChannelMask & (SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)) == (SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)) + { + m_maxSet = AudioChannelSet::Surround71; + } + } + } + } + + CoTaskMemFree(pwfx); + } + + ~WASAPIAudioVoiceAllocator() + { + } + + AudioChannelSet getAvailableSet() + { + return m_maxSet; + } + + std::unique_ptr allocateNewVoice(AudioChannelSet layoutOut, + unsigned sampleRate, + IAudioVoiceCallback* cb) + { + WASAPIAudioVoice* newVoice = new WASAPIAudioVoice(*this, m_device.Get(), layoutOut, sampleRate, cb); + newVoice->m_parentIt = m_allocatedVoices.insert(m_allocatedVoices.end(), newVoice); + std::unique_ptr ret(newVoice); + if (!newVoice->m_audClient) + return {}; + return ret; + } + + void pumpVoices() + { + for (WASAPIAudioVoice* vox : m_allocatedVoices) + vox->pump(); + } +}; + +WASAPIAudioVoice::~WASAPIAudioVoice() +{ + m_parent.m_allocatedVoices.erase(m_parentIt); +} + +std::unique_ptr NewAudioVoiceAllocator() +{ + return std::make_unique(); +} + +} diff --git a/lib/audiodev/XAudio2.cpp b/lib/audiodev/XAudio2.cpp deleted file mode 100644 index 89d0908..0000000 --- a/lib/audiodev/XAudio2.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "../win/Win32Common.hpp" -#include "boo/audiodev/IAudioVoiceAllocator.hpp" -#include "logvisor/logvisor.hpp" - -#include - -namespace boo -{ -static logvisor::Module Log("boo::XAudio2"); - -struct XA2AudioVoice : IAudioVoice -{ - ChannelMap m_map; - IAudioVoiceCallback* m_cb; - IXAudio2SourceVoice* m_voiceQueue; - XAUDIO2_BUFFER m_buffers[3] = {}; - size_t m_bufferFrames = 1024; - size_t m_frameSize; - - const ChannelMap& channelMap() const {return m_map;} - - unsigned m_fillBuf = 0; - struct Callback : IXAudio2VoiceCallback - { - XA2AudioVoice& m_voice; - Callback(XA2AudioVoice& voice) : m_voice(voice) {} - - STDMETHOD_(void, OnBufferEnd)(void* pBufferContext) - { - m_voice.m_cb->needsNextBuffer(m_voice, m_voice.m_bufferFrames); - } - STDMETHOD_(void, OnBufferStart)(void* pBufferContext) {} - STDMETHOD_(void, OnLoopEnd)(void* pBufferContext) {} - STDMETHOD_(void, OnStreamEnd)() {} - STDMETHOD_(void, OnVoiceError)(void* pBufferContext, HRESULT error) {} - STDMETHOD_(void, OnVoiceProcessingPassEnd)() {} - STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 bytes_required) {} - } m_xaCb; - - XA2AudioVoice(IXAudio2& xa2, AudioChannelSet set, unsigned sampleRate, IAudioVoiceCallback* cb) - : m_cb(cb), m_xaCb(*this) - { - unsigned chCount = ChannelCount(set); - - WAVEFORMATEX desc = {}; - desc.wFormatTag = WAVE_FORMAT_PCM; - desc.nChannels = chCount; - desc.nSamplesPerSec = sampleRate; - desc.wBitsPerSample = 16; - desc.nBlockAlign = desc.nChannels * desc.wBitsPerSample / 8; - desc.nAvgBytesPerSec = desc.nSamplesPerSec * desc.nBlockAlign; - - HRESULT err = xa2.CreateSourceVoice(&m_voiceQueue, &desc, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &m_xaCb); - if (FAILED(err)) - { - Log.report(logvisor::Fatal, "unable to create source voice"); - return; - } - - XAUDIO2_VOICE_DETAILS voxDetails; - m_voiceQueue->GetVoiceDetails(&voxDetails); - switch (voxDetails.InputChannels) - { - case 2: - m_map.m_channelCount = 2; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - break; - case 4: - m_map.m_channelCount = 4; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - m_map.m_channels[2] = AudioChannel::RearLeft; - m_map.m_channels[3] = AudioChannel::RearRight; - break; - case 5: - m_map.m_channelCount = 5; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - m_map.m_channels[2] = AudioChannel::FrontCenter; - m_map.m_channels[3] = AudioChannel::RearLeft; - m_map.m_channels[4] = AudioChannel::RearRight; - break; - case 6: - m_map.m_channelCount = 6; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - m_map.m_channels[2] = AudioChannel::FrontCenter; - m_map.m_channels[3] = AudioChannel::LFE; - m_map.m_channels[4] = AudioChannel::RearLeft; - m_map.m_channels[5] = AudioChannel::RearRight; - break; - case 8: - m_map.m_channelCount = 8; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - m_map.m_channels[2] = AudioChannel::FrontCenter; - m_map.m_channels[3] = AudioChannel::LFE; - m_map.m_channels[4] = AudioChannel::RearLeft; - m_map.m_channels[5] = AudioChannel::RearRight; - m_map.m_channels[6] = AudioChannel::SideLeft; - m_map.m_channels[7] = AudioChannel::SideRight; - break; - default: - Log.report(logvisor::Error, "unknown channel layout %u; using stereo", voxDetails.InputChannels); - m_map.m_channelCount = 2; - m_map.m_channels[0] = AudioChannel::FrontLeft; - m_map.m_channels[1] = AudioChannel::FrontRight; - break; - } - - while (m_map.m_channelCount < chCount) - m_map.m_channels[m_map.m_channelCount++] = AudioChannel::Unknown; - - m_frameSize = chCount * 2; - - for (unsigned i=0 ; i<3 ; ++i) - m_cb->needsNextBuffer(*this, m_bufferFrames); - } - - void bufferSampleData(const int16_t* data, size_t frames) - { - XAUDIO2_BUFFER* buf = &m_buffers[m_fillBuf++]; - if (m_fillBuf == 3) - m_fillBuf = 0; - buf->Flags = 0; - buf->AudioBytes = frames * m_frameSize; - buf->pAudioData = reinterpret_cast(data); - buf->PlayBegin = 0; - buf->PlayLength = 0; - buf->LoopBegin = 0; - buf->LoopLength = 0; - buf->LoopCount = 0; - buf->pContext = nullptr; - m_voiceQueue->SubmitSourceBuffer(buf); - } - - ~XA2AudioVoice() - { - m_voiceQueue->DestroyVoice(); - } - - void start() - { - m_voiceQueue->Start(); - } - - void stop() - { - m_voiceQueue->Stop(); - } -}; - -typedef HRESULT (__stdcall *PFN_XAudio2Create)(_Outptr_ IXAudio2** ppXAudio2, UINT32 Flags, - XAUDIO2_PROCESSOR XAudio2Processor); - -static PFN_XAudio2Create MyXAudio2Create = nullptr; - -struct XA2AudioVoiceAllocator : IAudioVoiceAllocator -{ - ComPtr m_xa2; - IXAudio2MasteringVoice* m_masterVoice; - AudioChannelSet m_maxSet = AudioChannelSet::Unknown; - - XA2AudioVoiceAllocator() - { - if (!MyXAudio2Create) - { - HMODULE mod = LoadLibraryW(XAUDIO2_DLL_W); - if (!mod) - Log.report(logvisor::Fatal, L"unable to load " XAUDIO2_DLL_W); - MyXAudio2Create = PFN_XAudio2Create(GetProcAddress(mod, "XAudio2Create")); - if (!MyXAudio2Create) - Log.report(logvisor::Fatal, L"unable to find XAudio2Create in " XAUDIO2_DLL_W); - } - - if (FAILED(MyXAudio2Create(&m_xa2, 0, XAUDIO2_DEFAULT_PROCESSOR))) - { - Log.report(logvisor::Fatal, "Unable to initialize XAudio2"); - return; - } - if (FAILED(m_xa2->CreateMasteringVoice(&m_masterVoice))) - { - Log.report(logvisor::Fatal, "Unable to initialize XAudio2 mastering voice"); - return; - } - DWORD channelMask; - if (FAILED(m_masterVoice->GetChannelMask(&channelMask))) - { - Log.report(logvisor::Fatal, "Unable to get mastering voice's channel mask"); - return; - } - if ((channelMask & (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)) == (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)) - { - m_maxSet = AudioChannelSet::Stereo; - if ((channelMask & (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)) == (SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)) - { - m_maxSet = AudioChannelSet::Quad; - if ((channelMask & (SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY)) == (SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY)) - { - m_maxSet = AudioChannelSet::Surround51; - if ((channelMask & (SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)) == (SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)) - { - m_maxSet = AudioChannelSet::Surround71; - } - } - } - } - } - - ~XA2AudioVoiceAllocator() - { - m_masterVoice->DestroyVoice(); - } - - AudioChannelSet getAvailableSet() - { - return m_maxSet; - } - - std::unique_ptr allocateNewVoice(AudioChannelSet layoutOut, - unsigned sampleRate, - IAudioVoiceCallback* cb) - { - AudioChannelSet acset = std::min(layoutOut, m_maxSet); - std::unique_ptr newVoice = std::make_unique - (*m_xa2.Get(), acset, sampleRate, cb); - if (!static_cast(newVoice.get())->m_voiceQueue) - return {}; - return newVoice; - } - - void pumpVoices() {} -}; - -std::unique_ptr NewAudioVoiceAllocator() -{ - return std::make_unique(); -} - -}