From 86003c4ac43e72ad54dabc5c2747d761175e3ec9 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 12 Jul 2016 17:03:52 -1000 Subject: [PATCH 1/2] Initial multiple-referencing submix refactor --- CMakeLists.txt | 1 - include/boo/audiodev/IAudioSubmix.hpp | 18 +- include/boo/audiodev/IAudioVoice.hpp | 19 +- include/boo/audiodev/IAudioVoiceEngine.hpp | 2 +- lib/audiodev/AudioSubmix.cpp | 380 ++++++++++++++------- lib/audiodev/AudioSubmix.hpp | 67 ++-- lib/audiodev/AudioVoice.cpp | 178 +++++----- lib/audiodev/AudioVoice.hpp | 51 ++- lib/audiodev/AudioVoiceEngine.cpp | 67 +++- lib/audiodev/AudioVoiceEngine.hpp | 10 +- lib/audiodev/IAudioMix.hpp | 25 -- 11 files changed, 477 insertions(+), 341 deletions(-) delete mode 100644 lib/audiodev/IAudioMix.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index adc0881..3600daf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,6 @@ add_library(boo lib/audiodev/AudioVoice.cpp lib/audiodev/AudioSubmix.hpp lib/audiodev/AudioSubmix.cpp - lib/audiodev/IAudioMix.hpp lib/audiodev/MIDIEncoder.cpp lib/audiodev/MIDIDecoder.cpp lib/audiodev/MIDICommon.hpp diff --git a/include/boo/audiodev/IAudioSubmix.hpp b/include/boo/audiodev/IAudioSubmix.hpp index e66d247..268a5eb 100644 --- a/include/boo/audiodev/IAudioSubmix.hpp +++ b/include/boo/audiodev/IAudioSubmix.hpp @@ -23,21 +23,11 @@ struct IAudioSubmix { virtual ~IAudioSubmix() = default; - /** Same as the IAudioVoice allocator, but produces audio within the submix */ - virtual std::unique_ptr allocateNewMonoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch=false)=0; + /** Reset channel-levels to silence; unbind all submixes */ + virtual void resetSendLevels()=0; - /** Same as allocateNewMonoVoice, but source audio is stereo-interleaved */ - virtual std::unique_ptr allocateNewStereoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch=false)=0; - - /** Same as the IAudioVoice allocator, but produces audio recursively within the submix */ - virtual std::unique_ptr allocateNewSubmix(IAudioSubmixCallback* cb=nullptr)=0; - - /** Sets gain factors for each channel once accumulated by the submix */ - virtual void setChannelGains(const float gains[8])=0; + /** Set channel-levels for target submix (AudioChannel enum for array index) */ + virtual void setSendLevel(IAudioSubmix* submix, float level, bool slew)=0; /** Gets fixed sample rate of submix this way */ virtual double getSampleRate() const=0; diff --git a/include/boo/audiodev/IAudioVoice.hpp b/include/boo/audiodev/IAudioVoice.hpp index 31f5d4d..96bf13d 100644 --- a/include/boo/audiodev/IAudioVoice.hpp +++ b/include/boo/audiodev/IAudioVoice.hpp @@ -6,6 +6,7 @@ namespace boo { +class IAudioSubmix; enum class AudioChannelSet { @@ -59,20 +60,14 @@ struct IAudioVoice /** Set sample rate into voice (may result in audio discontinuities) */ virtual void resetSampleRate(double sampleRate)=0; - /** Reset channel-gains to voice defaults */ - virtual void setDefaultMatrixCoefficients()=0; + /** Reset channel-levels to silence; unbind all submixes */ + virtual void resetChannelLevels()=0; - /** Set channel-gains for mono audio source (AudioChannel enum for array index) */ - virtual void setMonoMatrixCoefficients(const float coefs[8], bool slew)=0; + /** Set channel-levels for mono audio source (AudioChannel enum for array index) */ + virtual void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew)=0; - /** Set channel-gains for stereo audio source (AudioChannel enum for array index) */ - virtual void setStereoMatrixCoefficients(const float coefs[8][2], bool slew)=0; - - /** Set submix-channel-gains for mono audio source (AudioChannel enum for array index) */ - virtual void setMonoSubmixMatrixCoefficients(const float coefs[8], bool slew)=0; - - /** Set submix-channel-gains for stereo audio source (AudioChannel enum for array index) */ - virtual void setStereoSubmixMatrixCoefficients(const float coefs[8][2], bool slew)=0; + /** Set channel-levels for stereo audio source (AudioChannel enum for array index) */ + virtual void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew)=0; /** Called by client to dynamically adjust the pitch of voices with dynamic pitch enabled */ virtual void setPitchRatio(double ratio, bool slew)=0; diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 4cc75d4..2b257b7 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -33,7 +33,7 @@ struct IAudioVoiceEngine bool dynamicPitch=false)=0; /** Client calls this to allocate a Submix for gathering audio together for effects processing */ - virtual std::unique_ptr allocateNewSubmix(IAudioSubmixCallback* cb=nullptr)=0; + virtual std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb=nullptr)=0; /** Client may optionally register a 200-virtual-updates each second callback for the stream */ virtual void register5MsCallback(std::function&& callback)=0; diff --git a/lib/audiodev/AudioSubmix.cpp b/lib/audiodev/AudioSubmix.cpp index 4e5ba46..94d68e3 100644 --- a/lib/audiodev/AudioSubmix.cpp +++ b/lib/audiodev/AudioSubmix.cpp @@ -7,189 +7,321 @@ namespace boo { -AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioSubmixCallback* cb) -: m_root(root), m_parent(parent), m_cb(cb) +typedef union { - std::fill(std::begin(m_gains), std::end(m_gains), 1.f); + float v[4]; +#if __SSE__ + __m128 q; + __m64 d[2]; +#endif +} TVectorUnion; + +AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, bool mainOut) +: m_root(root), m_cb(cb), m_mainOut(mainOut) +{ + if (mainOut) + setSendLevel(&m_root.m_mainSubmix, 1.f, false); } AudioSubmix::~AudioSubmix() { - while (m_activeVoices.size()) - m_activeVoices.front()->unbindVoice(); - while (m_activeSubmixes.size()) - m_activeSubmixes.front()->unbindSubmix(); unbindSubmix(); } -void AudioSubmix::_pumpAndMixVoices(size_t frames, int16_t* dataOut, int16_t* mainOut) +bool AudioSubmix::_isDirectDependencyOf(AudioSubmix* send) { - const AudioVoiceEngineMixInfo& info = mixInfo(); - size_t sampleCount = frames * info.m_channelMap.m_channelCount; + return m_sendGains.find(send) != m_sendGains.cend(); +} + +bool AudioSubmix::_mergeC3(std::list& output, + std::vector>& lists) +{ + for (auto outerIt = lists.begin() ; outerIt != lists.cend() ; ++outerIt) + { + AudioSubmix* smx = outerIt->front(); + bool found = false; + for (auto innerIt = lists.begin() ; innerIt != lists.cend() ; ++innerIt) + { + if (outerIt == innerIt) + continue; + if (smx == innerIt->front()) + { + innerIt->pop_front(); + found = true; + } + } + if (found) + { + outerIt->pop_front(); + output.push_back(smx); + return true; + } + } + return false; +} + +std::list AudioSubmix::_linearizeC3() +{ + std::vector> lists = {{}}; + for (AudioSubmix* smx : m_root.m_activeSubmixes) + { + if (smx == this) + continue; + if (smx->_isDirectDependencyOf(this)) + lists[0].push_back(smx); + } + lists.reserve(lists[0].size() + 1); + for (AudioSubmix* smx : lists[0]) + lists.push_back(smx->_linearizeC3()); + + std::list ret = {this}; + while (_mergeC3(ret, lists)) {} + return ret; +} + +void AudioSubmix::_zeroFill16() +{ + if (m_scratch16.size()) + std::fill(m_scratch16.begin(), m_scratch16.end(), 0); +} + +void AudioSubmix::_zeroFill32() +{ + if (m_scratch32.size()) + std::fill(m_scratch32.begin(), m_scratch32.end(), 0); +} + +void AudioSubmix::_zeroFillFlt() +{ + if (m_scratchFlt.size()) + std::fill(m_scratchFlt.begin(), m_scratchFlt.end(), 0); +} + +int16_t* AudioSubmix::_getMergeBuf16(size_t frames) +{ + if (m_redirect16) + return m_redirect16; + + size_t sampleCount = frames * m_root.m_mixInfo.m_channelMap.m_channelCount; if (m_scratch16.size() < sampleCount) m_scratch16.resize(sampleCount); - /* Clear target buffer */ - memset(m_scratch16.data(), 0, sizeof(int16_t) * sampleCount); - - /* Pump child voices */ - for (AudioVoice* vox : m_activeVoices) - if (vox->m_running) - vox->pumpAndMix(m_parent.mixInfo(), frames, mainOut, m_scratch16.data()); - - /* Pump child submixes */ - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, m_scratch16.data(), mainOut); - - /* Apply submix effect (if available) */ - if (m_cb && m_cb->canApplyEffect()) - m_cb->applyEffect(m_scratch16.data(), frames, info.m_channelMap, info.m_sampleRate); - - /* Merge into output mix */ - auto it = m_scratch16.begin(); - for (size_t f=0 ; fm_running) - vox->pumpAndMix(m_parent.mixInfo(), frames, mainOut, m_scratch32.data()); - - /* Pump child submixes */ - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, m_scratch32.data(), mainOut); - - /* Apply submix effect (if available) */ - if (m_cb && m_cb->canApplyEffect()) - m_cb->applyEffect(m_scratch32.data(), frames, info.m_channelMap, info.m_sampleRate); - - /* Merge into output mix */ - auto it = m_scratch32.begin(); - for (size_t f=0 ; fm_running) - vox->pumpAndMix(m_parent.mixInfo(), frames, mainOut, m_scratchFlt.data()); +size_t AudioSubmix::_pumpAndMix16(size_t frames) +{ + size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; - /* Pump child submixes */ - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, m_scratchFlt.data(), mainOut); - - /* Apply submix effect (if available) */ - if (m_cb && m_cb->canApplyEffect()) - m_cb->applyEffect(m_scratchFlt.data(), frames, info.m_channelMap, info.m_sampleRate); - - /* Merge into output mix */ - auto it = m_scratchFlt.begin(); - for (size_t f=0 ; f(smx.first); + auto it = m_scratch16.begin(); + int16_t* dataOut = sm._getMergeBuf16(frames); + + for (size_t f=0 ; f::iterator it) +size_t AudioSubmix::_pumpAndMix32(size_t frames) { - m_activeVoices.erase(it); + size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; + + if (m_redirect32) + m_redirect32 += chanCount * frames; + else + { + size_t curSlewFrame = m_slewFrames; + for (auto& smx : m_sendGains) + { + curSlewFrame = m_curSlewFrame; + AudioSubmix& sm = *reinterpret_cast(smx.first); + auto it = m_scratch32.begin(); + int32_t* dataOut = sm._getMergeBuf32(frames); + + for (size_t f=0 ; f::iterator it) +size_t AudioSubmix::_pumpAndMixFlt(size_t frames) { - m_activeSubmixes.erase(it); + size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; + + if (m_redirectFlt) + m_redirectFlt += chanCount * frames; + else + { + size_t curSlewFrame = m_slewFrames; + for (auto& smx : m_sendGains) + { + curSlewFrame = m_curSlewFrame; + AudioSubmix& sm = *reinterpret_cast(smx.first); + auto it = m_scratchFlt.begin(); + float* dataOut = sm._getMergeBufFlt(frames); + + for (size_t f=0 ; f_resetSampleRate(vox->m_sampleRateIn); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_resetOutputSampleRate(); if (m_cb) - m_cb->resetOutputSampleRate(m_parent.mixInfo().m_sampleRate); + m_cb->resetOutputSampleRate(m_root.mixInfo().m_sampleRate); } -std::unique_ptr AudioSubmix::allocateNewMonoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch) +void AudioSubmix::resetSendLevels() { - std::unique_ptr ret = - std::make_unique(m_root, *this, cb, sampleRate, dynamicPitch); - AudioVoiceMono* retMono = static_cast(ret.get()); - retMono->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retMono)); - return ret; + if (m_sendGains.empty()) + return; + m_sendGains.clear(); + m_root.m_submixesDirty = true; } -std::unique_ptr AudioSubmix::allocateNewStereoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch) +void AudioSubmix::setSendLevel(IAudioSubmix* submix, float level, bool slew) { - std::unique_ptr ret = - std::make_unique(m_root, *this, cb, sampleRate, dynamicPitch); - AudioVoiceStereo* retStereo = static_cast(ret.get()); - retStereo->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retStereo)); - return ret; -} + auto search = m_sendGains.find(submix); + if (search == m_sendGains.cend()) + { + search = m_sendGains.emplace(submix, std::array{1.f, 1.f}).first; + m_root.m_submixesDirty = true; + } -std::unique_ptr AudioSubmix::allocateNewSubmix(IAudioSubmixCallback* cb) -{ - std::unique_ptr ret = - std::make_unique(m_root, *this, cb); - AudioSubmix* retIntern = static_cast(ret.get()); - retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern)); - return ret; -} + m_slewFrames = slew ? m_root.m_5msFrames : 0; + m_curSlewFrame = 0; -void AudioSubmix::setChannelGains(const float gains[8]) -{ - for (int i=0 ; i<8 ; ++i) - m_gains[i] = gains[i]; + search->second[0] = search->second[1]; + search->second[1] = level; } void AudioSubmix::unbindSubmix() { if (m_bound) { - m_parent._unbindFrom(m_parentIt); + m_root._unbindFrom(m_parentIt); m_bound = false; } } const AudioVoiceEngineMixInfo& AudioSubmix::mixInfo() const { - return m_parent.mixInfo(); + return m_root.mixInfo(); } double AudioSubmix::getSampleRate() const diff --git a/lib/audiodev/AudioSubmix.hpp b/lib/audiodev/AudioSubmix.hpp index e263e50..30929e0 100644 --- a/lib/audiodev/AudioSubmix.hpp +++ b/lib/audiodev/AudioSubmix.hpp @@ -2,9 +2,13 @@ #define BOO_AUDIOSUBMIX_HPP #include "boo/audiodev/IAudioSubmix.hpp" -#include "IAudioMix.hpp" #include #include +#include + +#if __SSE__ +#include +#endif struct AudioUnitVoiceEngine; struct VSTVoiceEngine; @@ -14,10 +18,14 @@ namespace boo { class BaseAudioVoiceEngine; class AudioVoice; +struct AudioVoiceEngineMixInfo; +/* Output gains for each mix-send/channel */ -class AudioSubmix : public IAudioSubmix, public IAudioMix +class AudioSubmix : public IAudioSubmix { friend class BaseAudioVoiceEngine; + friend class AudioVoiceMono; + friend class AudioVoiceStereo; friend struct WASAPIAudioVoiceEngine; friend struct ::AudioUnitVoiceEngine; friend struct ::VSTVoiceEngine; @@ -25,8 +33,8 @@ class AudioSubmix : public IAudioSubmix, public IAudioMix /* Mixer-engine relationships */ BaseAudioVoiceEngine& m_root; - IAudioMix& m_parent; std::list::iterator m_parentIt; + bool m_mainOut; bool m_bound = false; void bindSubmix(std::list::iterator pIt) { @@ -37,41 +45,52 @@ class AudioSubmix : public IAudioSubmix, public IAudioMix /* Callback (effect source, optional) */ IAudioSubmixCallback* m_cb; - /* Audio sources */ - std::list m_activeVoices; - std::list m_activeSubmixes; + /* Slew state for output gains */ + size_t m_slewFrames = 0; + size_t m_curSlewFrame = 0; - /* Output gains for each channel */ - float m_gains[8]; + /* Output gains for each mix-send/channel */ + std::unordered_map> m_sendGains; /* Temporary scratch buffers for accumulating submix audio */ std::vector m_scratch16; std::vector m_scratch32; std::vector m_scratchFlt; - void _pumpAndMixVoices(size_t frames, int16_t* dataOut, int16_t* mainOut); - void _pumpAndMixVoices(size_t frames, int32_t* dataOut, int32_t* mainOut); - void _pumpAndMixVoices(size_t frames, float* dataOut, float* mainOut); + /* Override scratch buffers with alternate destination */ + int16_t* m_redirect16 = nullptr; + int32_t* m_redirect32 = nullptr; + float* m_redirectFlt = nullptr; - void _unbindFrom(std::list::iterator it); - void _unbindFrom(std::list::iterator it); + /* C3-linearization support (to mitigate a potential diamond problem on 'clever' submix routes) */ + bool _isDirectDependencyOf(AudioSubmix* send); + std::list _linearizeC3(); + static bool _mergeC3(std::list& output, + std::vector>& lists); + + /* Fill scratch buffers with silence for new mix cycle */ + void _zeroFill16(); + void _zeroFill32(); + void _zeroFillFlt(); + + /* Receive audio from a single voice / submix */ + int16_t* _getMergeBuf16(size_t frames); + int32_t* _getMergeBuf32(size_t frames); + float* _getMergeBufFlt(size_t frames); + + /* Mix scratch buffers into sends */ + size_t _pumpAndMix16(size_t frames); + size_t _pumpAndMix32(size_t frames); + size_t _pumpAndMixFlt(size_t frames); void _resetOutputSampleRate(); public: ~AudioSubmix(); - AudioSubmix(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioSubmixCallback* cb); + AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, bool mainOut); - std::unique_ptr allocateNewMonoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch=false); - - std::unique_ptr allocateNewStereoVoice(double sampleRate, - IAudioVoiceCallback* cb, - bool dynamicPitch=false); - - std::unique_ptr allocateNewSubmix(IAudioSubmixCallback* cb=nullptr); - void setChannelGains(const float gains[8]); + void resetSendLevels(); + void setSendLevel(IAudioSubmix* submix, float level, bool slew); void unbindSubmix(); const AudioVoiceEngineMixInfo& mixInfo() const; double getSampleRate() const; diff --git a/lib/audiodev/AudioVoice.cpp b/lib/audiodev/AudioVoice.cpp index 3c9852e..bf76cc8 100644 --- a/lib/audiodev/AudioVoice.cpp +++ b/lib/audiodev/AudioVoice.cpp @@ -6,9 +6,9 @@ namespace boo { static logvisor::Module Log("boo::AudioVoice"); -AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, IAudioMix& parent, +AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate) -: m_root(root), m_parent(parent), m_cb(cb), m_dynamicRate(dynamicRate) {} +: m_root(root), m_cb(cb), m_dynamicRate(dynamicRate) {} AudioVoice::~AudioVoice() { @@ -66,14 +66,14 @@ void AudioVoice::unbindVoice() { if (m_bound) { - m_parent._unbindFrom(m_parentIt); + m_root._unbindFrom(m_parentIt); m_bound = false; } } -AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, +AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate) -: AudioVoice(root, parent, cb, dynamicRate) +: AudioVoice(root, cb, dynamicRate) { _resetSampleRate(sampleRate); } @@ -82,8 +82,8 @@ void AudioVoiceMono::_resetSampleRate(double sampleRate) { soxr_delete(m_src); - double rateOut = m_parent.mixInfo().m_sampleRate; - soxr_datatype_t formatOut = m_parent.mixInfo().m_sampleFormat; + double rateOut = m_root.mixInfo().m_sampleRate; + soxr_datatype_t formatOut = m_root.mixInfo().m_sampleFormat; soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut); soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0); @@ -120,8 +120,7 @@ size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t f return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data()); } -size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, int16_t* buf, int16_t* rbuf) +size_t AudioVoiceMono::pumpAndMix16(size_t frames) { std::vector& scratch16 = m_root.m_scratch16; if (scratch16.size() < frames) @@ -133,16 +132,17 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixMonoSampleData(mixInfo, scratch16.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch16.data(), smx._getMergeBuf16(oDone), oDone); + } } return oDone; } -size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, int32_t* buf, int32_t* rbuf) +size_t AudioVoiceMono::pumpAndMix32(size_t frames) { std::vector& scratch32 = m_root.m_scratch32; if (scratch32.size() < frames) @@ -154,16 +154,17 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixMonoSampleData(mixInfo, scratch32.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch32.data(), smx._getMergeBuf32(oDone), oDone); + } } return oDone; } -size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, float* buf, float* rbuf) +size_t AudioVoiceMono::pumpAndMixFlt(size_t frames) { std::vector& scratchFlt = m_root.m_scratchFlt; if (scratchFlt.size() < frames) @@ -175,27 +176,34 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixMonoSampleData(mixInfo, scratchFlt.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratchFlt.data(), smx._getMergeBufFlt(oDone), oDone); + } } return oDone; } -void AudioVoiceMono::setDefaultMatrixCoefficients() +void AudioVoiceMono::resetChannelLevels() { - m_matrix.setDefaultMatrixCoefficients(m_parent.mixInfo().m_channels); - float zero[8] = {}; - m_subMatrix.setMatrixCoefficients(zero); + m_root.m_submixesDirty = true; + m_sendMatrices.clear(); } -void AudioVoiceMono::setMonoMatrixCoefficients(const float coefs[8], bool slew) +void AudioVoiceMono::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) { - m_matrix.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); + auto search = m_sendMatrices.find(submix); + if (search == m_sendMatrices.cend()) + { + search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first; + m_root.m_submixesDirty = true; + } + search->second.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); } -void AudioVoiceMono::setStereoMatrixCoefficients(const float coefs[8][2], bool slew) +void AudioVoiceMono::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) { float newCoefs[8] = { @@ -208,33 +216,19 @@ void AudioVoiceMono::setStereoMatrixCoefficients(const float coefs[8][2], bool s coefs[6][0], coefs[7][0] }; - m_matrix.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); -} -void AudioVoiceMono::setMonoSubmixMatrixCoefficients(const float coefs[8], bool slew) -{ - m_subMatrix.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); -} - -void AudioVoiceMono::setStereoSubmixMatrixCoefficients(const float coefs[8][2], bool slew) -{ - float newCoefs[8] = + auto search = m_sendMatrices.find(submix); + if (search == m_sendMatrices.cend()) { - coefs[0][0], - coefs[1][0], - coefs[2][0], - coefs[3][0], - coefs[4][0], - coefs[5][0], - coefs[6][0], - coefs[7][0] - }; - m_subMatrix.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); + search = m_sendMatrices.emplace(submix, AudioMatrixMono{}).first; + m_root.m_submixesDirty = true; + } + search->second.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); } -AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, +AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate) -: AudioVoice(root, parent, cb, dynamicRate) +: AudioVoice(root, cb, dynamicRate) { _resetSampleRate(sampleRate); } @@ -243,8 +237,8 @@ void AudioVoiceStereo::_resetSampleRate(double sampleRate) { soxr_delete(m_src); - double rateOut = m_parent.mixInfo().m_sampleRate; - soxr_datatype_t formatOut = m_parent.mixInfo().m_sampleFormat; + double rateOut = m_root.mixInfo().m_sampleRate; + soxr_datatype_t formatOut = m_root.mixInfo().m_sampleFormat; soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, formatOut); soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0); @@ -282,8 +276,7 @@ size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data()); } -size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, int16_t* buf, int16_t* rbuf) +size_t AudioVoiceStereo::pumpAndMix16(size_t frames) { std::vector& scratch16 = m_root.m_scratch16; size_t samples = frames * 2; @@ -296,16 +289,17 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixStereoSampleData(mixInfo, scratch16.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch16.data(), smx._getMergeBuf16(oDone), oDone); + } } return oDone; } -size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, int32_t* buf, int32_t* rbuf) +size_t AudioVoiceStereo::pumpAndMix32(size_t frames) { std::vector& scratch32 = m_root.m_scratch32; size_t samples = frames * 2; @@ -318,16 +312,17 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixStereoSampleData(mixInfo, scratch32.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch32.data(), smx._getMergeBuf32(oDone), oDone); + } } return oDone; } -size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, - size_t frames, float* buf, float* rbuf) +size_t AudioVoiceStereo::pumpAndMixFlt(size_t frames) { std::vector& scratchFlt = m_root.m_scratchFlt; size_t samples = frames * 2; @@ -340,22 +335,23 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (oDone) { - m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone); - if (rbuf) - m_subMatrix.mixStereoSampleData(mixInfo, scratchFlt.data(), rbuf, oDone); + for (auto& mtx : m_sendMatrices) + { + AudioSubmix& smx = *reinterpret_cast(mtx.first); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratchFlt.data(), smx._getMergeBufFlt(oDone), oDone); + } } return oDone; } -void AudioVoiceStereo::setDefaultMatrixCoefficients() +void AudioVoiceStereo::resetChannelLevels() { - m_matrix.setDefaultMatrixCoefficients(m_parent.mixInfo().m_channels); - float zero[8][2] = {{}}; - m_subMatrix.setMatrixCoefficients(zero); + m_root.m_submixesDirty = true; + m_sendMatrices.clear(); } -void AudioVoiceStereo::setMonoMatrixCoefficients(const float coefs[8], bool slew) +void AudioVoiceStereo::setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew) { float newCoefs[8][2] = { @@ -368,33 +364,25 @@ void AudioVoiceStereo::setMonoMatrixCoefficients(const float coefs[8], bool slew {coefs[6], coefs[6]}, {coefs[7], coefs[7]} }; - m_matrix.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); -} -void AudioVoiceStereo::setStereoMatrixCoefficients(const float coefs[8][2], bool slew) -{ - m_matrix.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); -} - -void AudioVoiceStereo::setMonoSubmixMatrixCoefficients(const float coefs[8], bool slew) -{ - float newCoefs[8][2] = + auto search = m_sendMatrices.find(submix); + if (search == m_sendMatrices.cend()) { - {coefs[0], coefs[0]}, - {coefs[1], coefs[1]}, - {coefs[2], coefs[2]}, - {coefs[3], coefs[3]}, - {coefs[4], coefs[4]}, - {coefs[5], coefs[5]}, - {coefs[6], coefs[6]}, - {coefs[7], coefs[7]} - }; - m_subMatrix.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); + search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first; + m_root.m_submixesDirty = true; + } + search->second.setMatrixCoefficients(newCoefs, slew ? m_root.m_5msFrames : 0); } -void AudioVoiceStereo::setStereoSubmixMatrixCoefficients(const float coefs[8][2], bool slew) +void AudioVoiceStereo::setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew) { - m_subMatrix.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); + auto search = m_sendMatrices.find(submix); + if (search == m_sendMatrices.cend()) + { + search = m_sendMatrices.emplace(submix, AudioMatrixStereo{}).first; + m_root.m_submixesDirty = true; + } + search->second.setMatrixCoefficients(coefs, slew ? m_root.m_5msFrames : 0); } } diff --git a/lib/audiodev/AudioVoice.hpp b/lib/audiodev/AudioVoice.hpp index ce10197..c0b7172 100644 --- a/lib/audiodev/AudioVoice.hpp +++ b/lib/audiodev/AudioVoice.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "boo/audiodev/IAudioVoice.hpp" #include "AudioMatrix.hpp" @@ -14,7 +15,7 @@ namespace boo { class BaseAudioVoiceEngine; struct AudioVoiceEngineMixInfo; -class IAudioMix; +class IAudioSubmix; class AudioVoice : public IAudioVoice { @@ -28,7 +29,6 @@ class AudioVoice : public IAudioVoice protected: /* Mixer-engine relationships */ BaseAudioVoiceEngine& m_root; - IAudioMix& m_parent; std::list::iterator m_parentIt; bool m_bound = false; void bindVoice(std::list::iterator pIt) @@ -63,10 +63,11 @@ protected: /* Mid-pump update */ void _midUpdate(); - virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int16_t* buf, int16_t* rbuf)=0; - virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int32_t* buf, int32_t* rbuf)=0; - virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf, float* rbuf)=0; - AudioVoice(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, bool dynamicRate); + virtual size_t pumpAndMix16(size_t frames)=0; + virtual size_t pumpAndMix32(size_t frames)=0; + virtual size_t pumpAndMixFlt(size_t frames)=0; + + AudioVoice(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, bool dynamicRate); public: ~AudioVoice(); @@ -81,50 +82,44 @@ public: class AudioVoiceMono : public AudioVoice { - AudioMatrixMono m_matrix; - AudioMatrixMono m_subMatrix; + std::unordered_map m_sendMatrices; bool m_silentOut = false; void _resetSampleRate(double sampleRate); static size_t SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t requestedLen); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int16_t* buf, int16_t* rbuf); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int32_t* buf, int32_t* rbuf); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf, float* rbuf); + size_t pumpAndMix16(size_t frames); + size_t pumpAndMix32(size_t frames); + size_t pumpAndMixFlt(size_t frames); public: - AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, + AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate); - void setDefaultMatrixCoefficients(); - void setMonoMatrixCoefficients(const float coefs[8], bool slew); - void setStereoMatrixCoefficients(const float coefs[8][2], bool slew); - void setMonoSubmixMatrixCoefficients(const float coefs[8], bool slew); - void setStereoSubmixMatrixCoefficients(const float coefs[8][2], bool slew); + void resetChannelLevels(); + void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew); + void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew); }; class AudioVoiceStereo : public AudioVoice { - AudioMatrixStereo m_matrix; - AudioMatrixStereo m_subMatrix; + std::unordered_map m_sendMatrices; bool m_silentOut = false; void _resetSampleRate(double sampleRate); static size_t SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t requestedLen); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int16_t* buf, int16_t* rbuf); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int32_t* buf, int32_t* rbuf); - size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf, float* rbuf); + size_t pumpAndMix16(size_t frames); + size_t pumpAndMix32(size_t frames); + size_t pumpAndMixFlt(size_t frames); public: - AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, + AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioVoiceCallback* cb, double sampleRate, bool dynamicRate); - void setDefaultMatrixCoefficients(); - void setMonoMatrixCoefficients(const float coefs[8], bool slew); - void setStereoMatrixCoefficients(const float coefs[8][2], bool slew); - void setMonoSubmixMatrixCoefficients(const float coefs[8], bool slew); - void setStereoSubmixMatrixCoefficients(const float coefs[8][2], bool slew); + void resetChannelLevels(); + void setMonoChannelLevels(IAudioSubmix* submix, const float coefs[8], bool slew); + void setStereoChannelLevels(IAudioSubmix* submix, const float coefs[8][2], bool slew); }; } diff --git a/lib/audiodev/AudioVoiceEngine.cpp b/lib/audiodev/AudioVoiceEngine.cpp index d7c2daa..91f669b 100644 --- a/lib/audiodev/AudioVoiceEngine.cpp +++ b/lib/audiodev/AudioVoiceEngine.cpp @@ -15,6 +15,13 @@ BaseAudioVoiceEngine::~BaseAudioVoiceEngine() void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut) { memset(dataOut, 0, sizeof(int16_t) * frames * m_mixInfo.m_channelMap.m_channelCount); + m_mainSubmix.m_redirect16 = dataOut; + + if (m_submixesDirty) + { + m_linearizedSubmixes = m_mainSubmix._linearizeC3(); + m_submixesDirty = false; + } size_t remFrames = frames; while (remFrames) @@ -33,11 +40,16 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut) m_5msCallback(5.0 / 1000.0); } + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_zeroFill16(); + for (AudioVoice* vox : m_activeVoices) if (vox->m_running) - vox->pumpAndMix(m_mixInfo, thisFrames, dataOut, nullptr); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(thisFrames, dataOut, dataOut); + vox->pumpAndMix16(thisFrames); + + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_pumpAndMix16(thisFrames); + remFrames -= thisFrames; dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; } @@ -46,6 +58,13 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut) void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut) { memset(dataOut, 0, sizeof(int32_t) * frames * m_mixInfo.m_channelMap.m_channelCount); + m_mainSubmix.m_redirect32 = dataOut; + + if (m_submixesDirty) + { + m_linearizedSubmixes = m_mainSubmix._linearizeC3(); + m_submixesDirty = false; + } size_t remFrames = frames; while (remFrames) @@ -64,11 +83,16 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut) m_5msCallback(5.0 / 1000.0); } + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_zeroFill32(); + for (AudioVoice* vox : m_activeVoices) if (vox->m_running) - vox->pumpAndMix(m_mixInfo, thisFrames, dataOut, nullptr); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(thisFrames, dataOut, dataOut); + vox->pumpAndMix32(thisFrames); + + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_pumpAndMix32(thisFrames); + remFrames -= thisFrames; dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; } @@ -77,6 +101,13 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut) void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut) { memset(dataOut, 0, sizeof(float) * frames * m_mixInfo.m_channelMap.m_channelCount); + m_mainSubmix.m_redirectFlt = dataOut; + + if (m_submixesDirty) + { + m_linearizedSubmixes = m_mainSubmix._linearizeC3(); + m_submixesDirty = false; + } size_t remFrames = frames; while (remFrames) @@ -95,11 +126,16 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut) m_5msCallback(5.0 / 1000.0); } + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_zeroFillFlt(); + for (AudioVoice* vox : m_activeVoices) if (vox->m_running) - vox->pumpAndMix(m_mixInfo, thisFrames, dataOut, nullptr); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(thisFrames, dataOut, dataOut); + vox->pumpAndMixFlt(thisFrames); + + for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it) + (*it)->_pumpAndMixFlt(thisFrames); + remFrames -= thisFrames; dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; } @@ -112,6 +148,10 @@ void BaseAudioVoiceEngine::_unbindFrom(std::list::iterator it) void BaseAudioVoiceEngine::_unbindFrom(std::list::iterator it) { + for (AudioVoice* vox : m_activeVoices) + { + + } m_activeSubmixes.erase(it); } @@ -121,7 +161,7 @@ BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate, bool dynamicPitch) { std::unique_ptr ret = - std::make_unique(*this, *this, cb, sampleRate, dynamicPitch); + std::make_unique(*this, cb, sampleRate, dynamicPitch); AudioVoiceMono* retMono = static_cast(ret.get()); retMono->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retMono)); return ret; @@ -133,17 +173,16 @@ BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate, bool dynamicPitch) { std::unique_ptr ret = - std::make_unique(*this, *this, cb, sampleRate, dynamicPitch); + std::make_unique(*this, cb, sampleRate, dynamicPitch); AudioVoiceStereo* retStereo = static_cast(ret.get()); retStereo->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retStereo)); return ret; } std::unique_ptr -BaseAudioVoiceEngine::allocateNewSubmix(IAudioSubmixCallback* cb) +BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb) { - std::unique_ptr ret = - std::make_unique(*this, *this, cb); + std::unique_ptr ret = std::make_unique(*this, cb, mainOut); AudioSubmix* retIntern = static_cast(ret.get()); retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern)); return ret; diff --git a/lib/audiodev/AudioVoiceEngine.hpp b/lib/audiodev/AudioVoiceEngine.hpp index af7687f..1f0f9a2 100644 --- a/lib/audiodev/AudioVoiceEngine.hpp +++ b/lib/audiodev/AudioVoiceEngine.hpp @@ -4,7 +4,6 @@ #include "boo/audiodev/IAudioVoiceEngine.hpp" #include "AudioVoice.hpp" #include "AudioSubmix.hpp" -#include "IAudioMix.hpp" #include namespace boo @@ -22,7 +21,7 @@ struct AudioVoiceEngineMixInfo }; /** Base class for managing mixing and sample-rate-conversion amongst active voices */ -class BaseAudioVoiceEngine : public IAudioVoiceEngine, public IAudioMix +class BaseAudioVoiceEngine : public IAudioVoiceEngine { protected: friend class AudioVoice; @@ -41,6 +40,10 @@ protected: std::vector m_scratch32; std::vector m_scratchFlt; + AudioSubmix m_mainSubmix; + std::list m_linearizedSubmixes; + bool m_submixesDirty = true; + void _pumpAndMixVoices(size_t frames, int16_t* dataOut); void _pumpAndMixVoices(size_t frames, int32_t* dataOut); void _pumpAndMixVoices(size_t frames, float* dataOut); @@ -49,6 +52,7 @@ protected: void _unbindFrom(std::list::iterator it); public: + BaseAudioVoiceEngine() : m_mainSubmix(*this, nullptr, false) {} ~BaseAudioVoiceEngine(); std::unique_ptr allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb, @@ -58,7 +62,7 @@ public: IAudioVoiceCallback* cb, bool dynamicPitch=false); - std::unique_ptr allocateNewSubmix(IAudioSubmixCallback* cb); + std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb); void register5MsCallback(std::function&& callback); diff --git a/lib/audiodev/IAudioMix.hpp b/lib/audiodev/IAudioMix.hpp deleted file mode 100644 index 0c6645c..0000000 --- a/lib/audiodev/IAudioMix.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef BOO_IAUDIOMIX_HPP -#define BOO_IAUDIOMIX_HPP - -#include - -namespace boo -{ -struct AudioVoiceEngineMixInfo; -class AudioVoice; -class AudioSubmix; - -/** Entity that mixes audio from several child sources (engine root or submix) */ -class IAudioMix -{ - friend class AudioVoice; - friend class AudioSubmix; - virtual void _unbindFrom(std::list::iterator it)=0; - virtual void _unbindFrom(std::list::iterator it)=0; -public: - virtual const AudioVoiceEngineMixInfo& mixInfo() const=0; -}; - -} - -#endif // BOO_IAUDIOMIX_HPP From 95528019685cc8041af5a3d2f5c33ccd669e3242 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 13 Jul 2016 18:59:41 -1000 Subject: [PATCH 2/2] Working linearized Submixes --- include/boo/audiodev/IAudioVoice.hpp | 18 ++++ include/boo/audiodev/IAudioVoiceEngine.hpp | 2 +- lib/audiodev/AudioSubmix.cpp | 56 ++++++++--- lib/audiodev/AudioSubmix.hpp | 3 +- lib/audiodev/AudioVoice.cpp | 111 ++++++++++++++------- lib/audiodev/AudioVoiceEngine.cpp | 4 +- lib/audiodev/AudioVoiceEngine.hpp | 13 ++- 7 files changed, 147 insertions(+), 60 deletions(-) diff --git a/include/boo/audiodev/IAudioVoice.hpp b/include/boo/audiodev/IAudioVoice.hpp index 96bf13d..dc0af0e 100644 --- a/include/boo/audiodev/IAudioVoice.hpp +++ b/include/boo/audiodev/IAudioVoice.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace boo { @@ -91,6 +92,23 @@ struct IAudioVoiceCallback /** boo calls this on behalf of the audio platform to request more audio * frames from the client */ virtual size_t supplyAudio(IAudioVoice& voice, size_t frames, int16_t* data)=0; + + /** after resampling, boo calls this for each submix that this voice targets; + * client performs volume processing and bus-routing this way */ + virtual void routeAudio(size_t frames, double dt, int busId, int16_t* in, int16_t* out) + { + memmove(out, in, frames * 2); + } + + virtual void routeAudio(size_t frames, double dt, int busId, int32_t* in, int32_t* out) + { + memmove(out, in, frames * 4); + } + + virtual void routeAudio(size_t frames, double dt, int busId, float* in, float* out) + { + memmove(out, in, frames * 4); + } }; } diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 2b257b7..c98d9f0 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -33,7 +33,7 @@ struct IAudioVoiceEngine bool dynamicPitch=false)=0; /** Client calls this to allocate a Submix for gathering audio together for effects processing */ - virtual std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb=nullptr)=0; + virtual std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId)=0; /** Client may optionally register a 200-virtual-updates each second callback for the stream */ virtual void register5MsCallback(std::function&& callback)=0; diff --git a/lib/audiodev/AudioSubmix.cpp b/lib/audiodev/AudioSubmix.cpp index 94d68e3..6d90ab4 100644 --- a/lib/audiodev/AudioSubmix.cpp +++ b/lib/audiodev/AudioSubmix.cpp @@ -7,17 +7,8 @@ namespace boo { -typedef union -{ - float v[4]; -#if __SSE__ - __m128 q; - __m64 d[2]; -#endif -} TVectorUnion; - -AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, bool mainOut) -: m_root(root), m_cb(cb), m_mainOut(mainOut) +AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut) +: m_root(root), m_busId(busId), m_cb(cb), m_mainOut(mainOut) { if (mainOut) setSendLevel(&m_root.m_mainSubmix, 1.f, false); @@ -38,11 +29,13 @@ bool AudioSubmix::_mergeC3(std::list& output, { for (auto outerIt = lists.begin() ; outerIt != lists.cend() ; ++outerIt) { + if (outerIt->empty()) + continue; AudioSubmix* smx = outerIt->front(); bool found = false; for (auto innerIt = lists.begin() ; innerIt != lists.cend() ; ++innerIt) { - if (outerIt == innerIt) + if (innerIt->empty() || outerIt == innerIt) continue; if (smx == innerIt->front()) { @@ -135,12 +128,23 @@ float* AudioSubmix::_getMergeBufFlt(size_t frames) size_t AudioSubmix::_pumpAndMix16(size_t frames) { - size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; + ChannelMap& chMap = m_root.m_mixInfo.m_channelMap; + size_t chanCount = chMap.m_channelCount; if (m_redirect16) + { + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_redirect16, frames, chMap, m_root.m_mixInfo.m_sampleRate); m_redirect16 += chanCount * frames; + } else { + size_t sampleCount = frames * chanCount; + if (m_scratch16.size() < sampleCount) + m_scratch16.resize(sampleCount); + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_scratch16.data(), frames, chMap, m_root.m_mixInfo.m_sampleRate); + size_t curSlewFrame = m_slewFrames; for (auto& smx : m_sendGains) { @@ -184,12 +188,23 @@ size_t AudioSubmix::_pumpAndMix16(size_t frames) size_t AudioSubmix::_pumpAndMix32(size_t frames) { - size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; + ChannelMap& chMap = m_root.m_mixInfo.m_channelMap; + size_t chanCount = chMap.m_channelCount; if (m_redirect32) + { + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_redirect32, frames, chMap, m_root.m_mixInfo.m_sampleRate); m_redirect32 += chanCount * frames; + } else { + size_t sampleCount = frames * chanCount; + if (m_scratch32.size() < sampleCount) + m_scratch32.resize(sampleCount); + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_scratch32.data(), frames, chMap, m_root.m_mixInfo.m_sampleRate); + size_t curSlewFrame = m_slewFrames; for (auto& smx : m_sendGains) { @@ -233,12 +248,23 @@ size_t AudioSubmix::_pumpAndMix32(size_t frames) size_t AudioSubmix::_pumpAndMixFlt(size_t frames) { - size_t chanCount = m_root.m_mixInfo.m_channelMap.m_channelCount; + ChannelMap& chMap = m_root.m_mixInfo.m_channelMap; + size_t chanCount = chMap.m_channelCount; if (m_redirectFlt) + { + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_redirectFlt, frames, chMap, m_root.m_mixInfo.m_sampleRate); m_redirectFlt += chanCount * frames; + } else { + size_t sampleCount = frames * chanCount; + if (m_scratchFlt.size() < sampleCount) + m_scratchFlt.resize(sampleCount); + if (m_cb && m_cb->canApplyEffect()) + m_cb->applyEffect(m_scratchFlt.data(), frames, chMap, m_root.m_mixInfo.m_sampleRate); + size_t curSlewFrame = m_slewFrames; for (auto& smx : m_sendGains) { diff --git a/lib/audiodev/AudioSubmix.hpp b/lib/audiodev/AudioSubmix.hpp index 30929e0..d4ea29e 100644 --- a/lib/audiodev/AudioSubmix.hpp +++ b/lib/audiodev/AudioSubmix.hpp @@ -33,6 +33,7 @@ class AudioSubmix : public IAudioSubmix /* Mixer-engine relationships */ BaseAudioVoiceEngine& m_root; + int m_busId; std::list::iterator m_parentIt; bool m_mainOut; bool m_bound = false; @@ -87,7 +88,7 @@ class AudioSubmix : public IAudioSubmix public: ~AudioSubmix(); - AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, bool mainOut); + AudioSubmix(BaseAudioVoiceEngine& root, IAudioSubmixCallback* cb, int busId, bool mainOut); void resetSendLevels(); void setSendLevel(IAudioSubmix* submix, float level, bool slew); diff --git a/lib/audiodev/AudioVoice.cpp b/lib/audiodev/AudioVoice.cpp index bf76cc8..c754b02 100644 --- a/lib/audiodev/AudioVoice.cpp +++ b/lib/audiodev/AudioVoice.cpp @@ -122,20 +122,26 @@ size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t f size_t AudioVoiceMono::pumpAndMix16(size_t frames) { - std::vector& scratch16 = m_root.m_scratch16; - if (scratch16.size() < frames) - scratch16.resize(frames); + std::vector& scratch16Pre = m_root.m_scratch16Pre; + if (scratch16Pre.size() < frames) + scratch16Pre.resize(frames); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratch16Post = m_root.m_scratch16Post; + if (scratch16Post.size() < frames) + scratch16Post.resize(frames); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratch16.data(), frames); + size_t oDone = soxr_output(m_src, scratch16Pre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch16.data(), smx._getMergeBuf16(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratch16Pre.data(), scratch16Post.data()); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch16Post.data(), smx._getMergeBuf16(oDone), oDone); } } @@ -144,20 +150,26 @@ size_t AudioVoiceMono::pumpAndMix16(size_t frames) size_t AudioVoiceMono::pumpAndMix32(size_t frames) { - std::vector& scratch32 = m_root.m_scratch32; - if (scratch32.size() < frames) - scratch32.resize(frames); + std::vector& scratch32Pre = m_root.m_scratch32Pre; + if (scratch32Pre.size() < frames) + scratch32Pre.resize(frames); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratch32Post = m_root.m_scratch32Post; + if (scratch32Post.size() < frames) + scratch32Post.resize(frames); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratch32.data(), frames); + size_t oDone = soxr_output(m_src, scratch32Pre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch32.data(), smx._getMergeBuf32(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratch32Pre.data(), scratch32Post.data()); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratch32Post.data(), smx._getMergeBuf32(oDone), oDone); } } @@ -166,20 +178,26 @@ size_t AudioVoiceMono::pumpAndMix32(size_t frames) size_t AudioVoiceMono::pumpAndMixFlt(size_t frames) { - std::vector& scratchFlt = m_root.m_scratchFlt; - if (scratchFlt.size() < frames) - scratchFlt.resize(frames + 2); + std::vector& scratchFltPre = m_root.m_scratchFltPre; + if (scratchFltPre.size() < frames) + scratchFltPre.resize(frames + 2); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratchFltPost = m_root.m_scratchFltPost; + if (scratchFltPost.size() < frames) + scratchFltPost.resize(frames + 2); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratchFlt.data(), frames); + size_t oDone = soxr_output(m_src, scratchFltPre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratchFlt.data(), smx._getMergeBufFlt(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratchFltPre.data(), scratchFltPost.data()); + mtx.second.mixMonoSampleData(m_root.m_mixInfo, scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone); } } @@ -278,21 +296,28 @@ size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size size_t AudioVoiceStereo::pumpAndMix16(size_t frames) { - std::vector& scratch16 = m_root.m_scratch16; size_t samples = frames * 2; - if (scratch16.size() < samples) - scratch16.resize(samples); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratch16Pre = m_root.m_scratch16Pre; + if (scratch16Pre.size() < samples) + scratch16Pre.resize(samples); + + std::vector& scratch16Post = m_root.m_scratch16Post; + if (scratch16Post.size() < samples) + scratch16Post.resize(samples); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratch16.data(), frames); + size_t oDone = soxr_output(m_src, scratch16Pre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch16.data(), smx._getMergeBuf16(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratch16Pre.data(), scratch16Post.data()); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch16Post.data(), smx._getMergeBuf16(oDone), oDone); } } @@ -301,21 +326,28 @@ size_t AudioVoiceStereo::pumpAndMix16(size_t frames) size_t AudioVoiceStereo::pumpAndMix32(size_t frames) { - std::vector& scratch32 = m_root.m_scratch32; size_t samples = frames * 2; - if (scratch32.size() < samples) - scratch32.resize(samples); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratch32Pre = m_root.m_scratch32Pre; + if (scratch32Pre.size() < samples) + scratch32Pre.resize(samples); + + std::vector& scratch32Post = m_root.m_scratch32Post; + if (scratch32Post.size() < samples) + scratch32Post.resize(samples); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratch32.data(), frames); + size_t oDone = soxr_output(m_src, scratch32Pre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch32.data(), smx._getMergeBuf32(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratch32Pre.data(), scratch32Post.data()); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratch32Post.data(), smx._getMergeBuf32(oDone), oDone); } } @@ -324,21 +356,28 @@ size_t AudioVoiceStereo::pumpAndMix32(size_t frames) size_t AudioVoiceStereo::pumpAndMixFlt(size_t frames) { - std::vector& scratchFlt = m_root.m_scratchFlt; size_t samples = frames * 2; - if (scratchFlt.size() < samples) - scratchFlt.resize(samples + 4); - m_cb->preSupplyAudio(*this, frames / m_sampleRateOut); + std::vector& scratchFltPre = m_root.m_scratchFltPre; + if (scratchFltPre.size() < samples) + scratchFltPre.resize(samples + 4); + + std::vector& scratchFltPost = m_root.m_scratchFltPost; + if (scratchFltPost.size() < samples) + scratchFltPost.resize(samples + 4); + + double dt = frames / m_sampleRateOut; + m_cb->preSupplyAudio(*this, dt); _midUpdate(); - size_t oDone = soxr_output(m_src, scratchFlt.data(), frames); + size_t oDone = soxr_output(m_src, scratchFltPre.data(), frames); if (oDone) { for (auto& mtx : m_sendMatrices) { AudioSubmix& smx = *reinterpret_cast(mtx.first); - mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratchFlt.data(), smx._getMergeBufFlt(oDone), oDone); + m_cb->routeAudio(oDone, dt, smx.m_busId, scratchFltPre.data(), scratchFltPost.data()); + mtx.second.mixStereoSampleData(m_root.m_mixInfo, scratchFltPost.data(), smx._getMergeBufFlt(oDone), oDone); } } diff --git a/lib/audiodev/AudioVoiceEngine.cpp b/lib/audiodev/AudioVoiceEngine.cpp index 91f669b..bcb7d33 100644 --- a/lib/audiodev/AudioVoiceEngine.cpp +++ b/lib/audiodev/AudioVoiceEngine.cpp @@ -180,9 +180,9 @@ BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate, } std::unique_ptr -BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb) +BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId) { - std::unique_ptr ret = std::make_unique(*this, cb, mainOut); + std::unique_ptr ret = std::make_unique(*this, cb, busId, mainOut); AudioSubmix* retIntern = static_cast(ret.get()); retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern)); return ret; diff --git a/lib/audiodev/AudioVoiceEngine.hpp b/lib/audiodev/AudioVoiceEngine.hpp index 1f0f9a2..3b736ca 100644 --- a/lib/audiodev/AudioVoiceEngine.hpp +++ b/lib/audiodev/AudioVoiceEngine.hpp @@ -36,9 +36,12 @@ protected: /* Shared scratch buffers for accumulating audio data for resampling */ std::vector m_scratchIn; - std::vector m_scratch16; - std::vector m_scratch32; - std::vector m_scratchFlt; + std::vector m_scratch16Pre; + std::vector m_scratch32Pre; + std::vector m_scratchFltPre; + std::vector m_scratch16Post; + std::vector m_scratch32Post; + std::vector m_scratchFltPost; AudioSubmix m_mainSubmix; std::list m_linearizedSubmixes; @@ -52,7 +55,7 @@ protected: void _unbindFrom(std::list::iterator it); public: - BaseAudioVoiceEngine() : m_mainSubmix(*this, nullptr, false) {} + BaseAudioVoiceEngine() : m_mainSubmix(*this, nullptr, -1, false) {} ~BaseAudioVoiceEngine(); std::unique_ptr allocateNewMonoVoice(double sampleRate, IAudioVoiceCallback* cb, @@ -62,7 +65,7 @@ public: IAudioVoiceCallback* cb, bool dynamicPitch=false); - std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb); + std::unique_ptr allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId); void register5MsCallback(std::function&& callback);