From 95528019685cc8041af5a3d2f5c33ccd669e3242 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 13 Jul 2016 18:59:41 -1000 Subject: [PATCH] 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);