From 77507459cc38adafb5bc5286f9b81804f9e82bcc Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 19 May 2016 00:14:21 -1000 Subject: [PATCH] Add 5ms callback registration for precise audio updates --- include/boo/audiodev/IAudioVoiceEngine.hpp | 3 + lib/audiodev/ALSA.cpp | 2 + lib/audiodev/AudioVoice.cpp | 123 +++++++-------------- lib/audiodev/AudioVoice.hpp | 1 - lib/audiodev/AudioVoiceEngine.cpp | 98 +++++++++++++--- lib/audiodev/AudioVoiceEngine.hpp | 5 + 6 files changed, 130 insertions(+), 102 deletions(-) diff --git a/include/boo/audiodev/IAudioVoiceEngine.hpp b/include/boo/audiodev/IAudioVoiceEngine.hpp index 2367b17..1cd9ab7 100644 --- a/include/boo/audiodev/IAudioVoiceEngine.hpp +++ b/include/boo/audiodev/IAudioVoiceEngine.hpp @@ -35,6 +35,9 @@ struct IAudioVoiceEngine /** Client calls this to allocate a Submix for gathering audio together for effects processing */ virtual std::unique_ptr allocateNewSubmix(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; + /** Client may use this to determine current speaker-setup */ virtual AudioChannelSet getAvailableSet()=0; diff --git a/lib/audiodev/ALSA.cpp b/lib/audiodev/ALSA.cpp index 5bb6d5f..6b03b80 100644 --- a/lib/audiodev/ALSA.cpp +++ b/lib/audiodev/ALSA.cpp @@ -153,11 +153,13 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine { bestRate = 96000; m_mixInfo.m_sampleRate = 96000.0; + m_5msFrames = 96000 * 5 / 1000; } else if (!snd_pcm_hw_params_test_rate(m_pcm, hwParams, 48000, 0)) { bestRate = 48000; m_mixInfo.m_sampleRate = 48000.0; + m_5msFrames = 48000 * 5 / 1000; } else { diff --git a/lib/audiodev/AudioVoice.cpp b/lib/audiodev/AudioVoice.cpp index 84839e8..9cc3311 100644 --- a/lib/audiodev/AudioVoice.cpp +++ b/lib/audiodev/AudioVoice.cpp @@ -20,7 +20,7 @@ void AudioVoice::_setPitchRatio(double ratio, bool slew) { if (m_dynamicRate) { - soxr_error_t err = soxr_set_io_ratio(m_src, ratio * m_sampleRateIn / m_sampleRateOut, slew ? m_5msFrames : 0); + soxr_error_t err = soxr_set_io_ratio(m_src, ratio * m_sampleRateIn / m_sampleRateOut, slew ? m_root.m_5msFrames : 0); if (err) { Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(err)); @@ -99,7 +99,6 @@ void AudioVoiceMono::_resetSampleRate(double sampleRate) m_sampleRateIn = sampleRate; m_sampleRateOut = rateOut; - m_5msFrames = rateOut * 5 / 1000; soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0); _setPitchRatio(m_pitchRatio, false); m_resetSampleRate = false; @@ -121,21 +120,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratch16.size() < frames) scratch16.resize(frames); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratch16.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratch16.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone); + + return oDone; } size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, @@ -145,21 +136,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratch32.size() < frames) scratch32.resize(frames); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratch32.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratch32.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone); + + return oDone; } size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, @@ -169,21 +152,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratchFlt.size() < frames) scratchFlt.resize(frames); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratchFlt.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone); + + return oDone; } void AudioVoiceMono::setDefaultMatrixCoefficients() @@ -263,21 +238,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratch16.size() < samples) scratch16.resize(samples); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratch16.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratch16.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone); + + return oDone; } size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, @@ -288,21 +255,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratch32.size() < samples) scratch32.resize(samples); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratch32.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratch32.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone); + + return oDone; } size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, @@ -313,21 +272,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, if (scratchFlt.size() < samples) scratchFlt.resize(samples); - size_t oDone; - size_t totalDone = 0; - while (frames) - { - oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames); - _midUpdate(); - if (oDone) - { - buf = m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone); - totalDone += oDone; - frames -= oDone; - } - } + size_t oDone = soxr_output(m_src, scratchFlt.data(), frames); + _midUpdate(); - return totalDone; + if (oDone) + m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone); + + return oDone; } void AudioVoiceStereo::setDefaultMatrixCoefficients() diff --git a/lib/audiodev/AudioVoice.hpp b/lib/audiodev/AudioVoice.hpp index 344c7ee..5e62cb6 100644 --- a/lib/audiodev/AudioVoice.hpp +++ b/lib/audiodev/AudioVoice.hpp @@ -36,7 +36,6 @@ protected: soxr_t m_src = nullptr; double m_sampleRateIn; double m_sampleRateOut; - size_t m_5msFrames; bool m_dynamicRate; /* Running bool */ diff --git a/lib/audiodev/AudioVoiceEngine.cpp b/lib/audiodev/AudioVoiceEngine.cpp index e5482c9..5b1d6ca 100644 --- a/lib/audiodev/AudioVoiceEngine.cpp +++ b/lib/audiodev/AudioVoiceEngine.cpp @@ -15,31 +15,94 @@ BaseAudioVoiceEngine::~BaseAudioVoiceEngine() void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut) { memset(dataOut, 0, sizeof(int16_t) * frames * m_mixInfo.m_channelMap.m_channelCount); - for (AudioVoice* vox : m_activeVoices) - if (vox->m_running) - vox->pumpAndMix(m_mixInfo, frames, dataOut); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, dataOut); + + size_t remFrames = frames; + while (remFrames) + { + size_t thisFrames; + if (remFrames < m_5msFrames) + { + thisFrames = remFrames; + if (m_5msCallback) + m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0); + } + else + { + thisFrames = m_5msFrames; + if (m_5msCallback) + m_5msCallback(5.0 / 1000.0); + } + + for (AudioVoice* vox : m_activeVoices) + if (vox->m_running) + vox->pumpAndMix(m_mixInfo, thisFrames, dataOut); + for (AudioSubmix* smx : m_activeSubmixes) + smx->_pumpAndMixVoices(thisFrames, dataOut); + remFrames -= thisFrames; + dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; + } } void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut) { memset(dataOut, 0, sizeof(int32_t) * frames * m_mixInfo.m_channelMap.m_channelCount); - for (AudioVoice* vox : m_activeVoices) - if (vox->m_running) - vox->pumpAndMix(m_mixInfo, frames, dataOut); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, dataOut); + + size_t remFrames = frames; + while (remFrames) + { + size_t thisFrames; + if (remFrames < m_5msFrames) + { + thisFrames = remFrames; + if (m_5msCallback) + m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0); + } + else + { + thisFrames = m_5msFrames; + if (m_5msCallback) + m_5msCallback(5.0 / 1000.0); + } + + for (AudioVoice* vox : m_activeVoices) + if (vox->m_running) + vox->pumpAndMix(m_mixInfo, thisFrames, dataOut); + for (AudioSubmix* smx : m_activeSubmixes) + smx->_pumpAndMixVoices(thisFrames, dataOut); + remFrames -= thisFrames; + dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; + } } void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut) { memset(dataOut, 0, sizeof(float) * frames * m_mixInfo.m_channelMap.m_channelCount); - for (AudioVoice* vox : m_activeVoices) - if (vox->m_running) - vox->pumpAndMix(m_mixInfo, frames, dataOut); - for (AudioSubmix* smx : m_activeSubmixes) - smx->_pumpAndMixVoices(frames, dataOut); + + size_t remFrames = frames; + while (remFrames) + { + size_t thisFrames; + if (remFrames < m_5msFrames) + { + thisFrames = remFrames; + if (m_5msCallback) + m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0); + } + else + { + thisFrames = m_5msFrames; + if (m_5msCallback) + m_5msCallback(5.0 / 1000.0); + } + + for (AudioVoice* vox : m_activeVoices) + if (vox->m_running) + vox->pumpAndMix(m_mixInfo, thisFrames, dataOut); + for (AudioSubmix* smx : m_activeSubmixes) + smx->_pumpAndMixVoices(thisFrames, dataOut); + remFrames -= thisFrames; + dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount; + } } void BaseAudioVoiceEngine::_unbindFrom(std::list::iterator it) @@ -86,6 +149,11 @@ BaseAudioVoiceEngine::allocateNewSubmix(IAudioSubmixCallback* cb) return ret; } +void BaseAudioVoiceEngine::register5MsCallback(std::function&& callback) +{ + m_5msCallback = std::move(callback); +} + const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::mixInfo() const { return m_mixInfo; diff --git a/lib/audiodev/AudioVoiceEngine.hpp b/lib/audiodev/AudioVoiceEngine.hpp index e29b9e7..f5f3d11 100644 --- a/lib/audiodev/AudioVoiceEngine.hpp +++ b/lib/audiodev/AudioVoiceEngine.hpp @@ -5,6 +5,7 @@ #include "AudioVoice.hpp" #include "AudioSubmix.hpp" #include "IAudioMix.hpp" +#include namespace boo { @@ -31,6 +32,8 @@ protected: AudioVoiceEngineMixInfo m_mixInfo; std::list m_activeVoices; std::list m_activeSubmixes; + size_t m_5msFrames = 0; + std::function m_5msCallback; /* Shared scratch buffers for accumulating audio data for resampling */ std::vector m_scratchIn; @@ -57,6 +60,8 @@ public: std::unique_ptr allocateNewSubmix(IAudioSubmixCallback* cb); + void register5MsCallback(std::function&& callback); + const AudioVoiceEngineMixInfo& mixInfo() const; AudioChannelSet getAvailableSet() {return m_mixInfo.m_channels;} void pumpAndMixVoices() {}