mirror of https://github.com/AxioDL/boo.git
Add 5ms callback registration for precise audio updates
This commit is contained in:
parent
8ff2b9c404
commit
77507459cc
|
@ -35,6 +35,9 @@ struct IAudioVoiceEngine
|
||||||
/** Client calls this to allocate a Submix for gathering audio together for effects processing */
|
/** Client calls this to allocate a Submix for gathering audio together for effects processing */
|
||||||
virtual std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb=nullptr)=0;
|
virtual std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb=nullptr)=0;
|
||||||
|
|
||||||
|
/** Client may optionally register a 200-virtual-updates each second callback for the stream */
|
||||||
|
virtual void register5MsCallback(std::function<void(double dt)>&& callback)=0;
|
||||||
|
|
||||||
/** Client may use this to determine current speaker-setup */
|
/** Client may use this to determine current speaker-setup */
|
||||||
virtual AudioChannelSet getAvailableSet()=0;
|
virtual AudioChannelSet getAvailableSet()=0;
|
||||||
|
|
||||||
|
|
|
@ -153,11 +153,13 @@ struct ALSAAudioVoiceEngine : BaseAudioVoiceEngine
|
||||||
{
|
{
|
||||||
bestRate = 96000;
|
bestRate = 96000;
|
||||||
m_mixInfo.m_sampleRate = 96000.0;
|
m_mixInfo.m_sampleRate = 96000.0;
|
||||||
|
m_5msFrames = 96000 * 5 / 1000;
|
||||||
}
|
}
|
||||||
else if (!snd_pcm_hw_params_test_rate(m_pcm, hwParams, 48000, 0))
|
else if (!snd_pcm_hw_params_test_rate(m_pcm, hwParams, 48000, 0))
|
||||||
{
|
{
|
||||||
bestRate = 48000;
|
bestRate = 48000;
|
||||||
m_mixInfo.m_sampleRate = 48000.0;
|
m_mixInfo.m_sampleRate = 48000.0;
|
||||||
|
m_5msFrames = 48000 * 5 / 1000;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@ void AudioVoice::_setPitchRatio(double ratio, bool slew)
|
||||||
{
|
{
|
||||||
if (m_dynamicRate)
|
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)
|
if (err)
|
||||||
{
|
{
|
||||||
Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(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_sampleRateIn = sampleRate;
|
||||||
m_sampleRateOut = rateOut;
|
m_sampleRateOut = rateOut;
|
||||||
m_5msFrames = rateOut * 5 / 1000;
|
|
||||||
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
|
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
|
||||||
_setPitchRatio(m_pitchRatio, false);
|
_setPitchRatio(m_pitchRatio, false);
|
||||||
m_resetSampleRate = false;
|
m_resetSampleRate = false;
|
||||||
|
@ -121,21 +120,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratch16.size() < frames)
|
if (scratch16.size() < frames)
|
||||||
scratch16.resize(frames);
|
scratch16.resize(frames);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratch16.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratch16.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
|
@ -145,21 +136,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratch32.size() < frames)
|
if (scratch32.size() < frames)
|
||||||
scratch32.resize(frames);
|
scratch32.resize(frames);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratch32.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratch32.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
|
@ -169,21 +152,13 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratchFlt.size() < frames)
|
if (scratchFlt.size() < frames)
|
||||||
scratchFlt.resize(frames);
|
scratchFlt.resize(frames);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratchFlt.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVoiceMono::setDefaultMatrixCoefficients()
|
void AudioVoiceMono::setDefaultMatrixCoefficients()
|
||||||
|
@ -263,21 +238,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratch16.size() < samples)
|
if (scratch16.size() < samples)
|
||||||
scratch16.resize(samples);
|
scratch16.resize(samples);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratch16.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratch16.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
|
@ -288,21 +255,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratch32.size() < samples)
|
if (scratch32.size() < samples)
|
||||||
scratch32.resize(samples);
|
scratch32.resize(samples);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratch32.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratch32.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
|
@ -313,21 +272,13 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
||||||
if (scratchFlt.size() < samples)
|
if (scratchFlt.size() < samples)
|
||||||
scratchFlt.resize(samples);
|
scratchFlt.resize(samples);
|
||||||
|
|
||||||
size_t oDone;
|
size_t oDone = soxr_output(m_src, scratchFlt.data(), frames);
|
||||||
size_t totalDone = 0;
|
|
||||||
while (frames)
|
|
||||||
{
|
|
||||||
oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames);
|
|
||||||
_midUpdate();
|
_midUpdate();
|
||||||
if (oDone)
|
|
||||||
{
|
|
||||||
buf = m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
|
||||||
totalDone += oDone;
|
|
||||||
frames -= oDone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalDone;
|
if (oDone)
|
||||||
|
m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
||||||
|
|
||||||
|
return oDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioVoiceStereo::setDefaultMatrixCoefficients()
|
void AudioVoiceStereo::setDefaultMatrixCoefficients()
|
||||||
|
|
|
@ -36,7 +36,6 @@ protected:
|
||||||
soxr_t m_src = nullptr;
|
soxr_t m_src = nullptr;
|
||||||
double m_sampleRateIn;
|
double m_sampleRateIn;
|
||||||
double m_sampleRateOut;
|
double m_sampleRateOut;
|
||||||
size_t m_5msFrames;
|
|
||||||
bool m_dynamicRate;
|
bool m_dynamicRate;
|
||||||
|
|
||||||
/* Running bool */
|
/* Running bool */
|
||||||
|
|
|
@ -15,31 +15,94 @@ BaseAudioVoiceEngine::~BaseAudioVoiceEngine()
|
||||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
||||||
{
|
{
|
||||||
memset(dataOut, 0, sizeof(int16_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
memset(dataOut, 0, sizeof(int16_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||||
|
|
||||||
|
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)
|
for (AudioVoice* vox : m_activeVoices)
|
||||||
if (vox->m_running)
|
if (vox->m_running)
|
||||||
vox->pumpAndMix(m_mixInfo, frames, dataOut);
|
vox->pumpAndMix(m_mixInfo, thisFrames, dataOut);
|
||||||
for (AudioSubmix* smx : m_activeSubmixes)
|
for (AudioSubmix* smx : m_activeSubmixes)
|
||||||
smx->_pumpAndMixVoices(frames, dataOut);
|
smx->_pumpAndMixVoices(thisFrames, dataOut);
|
||||||
|
remFrames -= thisFrames;
|
||||||
|
dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
||||||
{
|
{
|
||||||
memset(dataOut, 0, sizeof(int32_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
memset(dataOut, 0, sizeof(int32_t) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||||
|
|
||||||
|
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)
|
for (AudioVoice* vox : m_activeVoices)
|
||||||
if (vox->m_running)
|
if (vox->m_running)
|
||||||
vox->pumpAndMix(m_mixInfo, frames, dataOut);
|
vox->pumpAndMix(m_mixInfo, thisFrames, dataOut);
|
||||||
for (AudioSubmix* smx : m_activeSubmixes)
|
for (AudioSubmix* smx : m_activeSubmixes)
|
||||||
smx->_pumpAndMixVoices(frames, dataOut);
|
smx->_pumpAndMixVoices(thisFrames, dataOut);
|
||||||
|
remFrames -= thisFrames;
|
||||||
|
dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
||||||
{
|
{
|
||||||
memset(dataOut, 0, sizeof(float) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
memset(dataOut, 0, sizeof(float) * frames * m_mixInfo.m_channelMap.m_channelCount);
|
||||||
|
|
||||||
|
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)
|
for (AudioVoice* vox : m_activeVoices)
|
||||||
if (vox->m_running)
|
if (vox->m_running)
|
||||||
vox->pumpAndMix(m_mixInfo, frames, dataOut);
|
vox->pumpAndMix(m_mixInfo, thisFrames, dataOut);
|
||||||
for (AudioSubmix* smx : m_activeSubmixes)
|
for (AudioSubmix* smx : m_activeSubmixes)
|
||||||
smx->_pumpAndMixVoices(frames, dataOut);
|
smx->_pumpAndMixVoices(thisFrames, dataOut);
|
||||||
|
remFrames -= thisFrames;
|
||||||
|
dataOut += thisFrames * m_mixInfo.m_channelMap.m_channelCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseAudioVoiceEngine::_unbindFrom(std::list<AudioVoice*>::iterator it)
|
void BaseAudioVoiceEngine::_unbindFrom(std::list<AudioVoice*>::iterator it)
|
||||||
|
@ -86,6 +149,11 @@ BaseAudioVoiceEngine::allocateNewSubmix(IAudioSubmixCallback* cb)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseAudioVoiceEngine::register5MsCallback(std::function<void(double dt)>&& callback)
|
||||||
|
{
|
||||||
|
m_5msCallback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::mixInfo() const
|
const AudioVoiceEngineMixInfo& BaseAudioVoiceEngine::mixInfo() const
|
||||||
{
|
{
|
||||||
return m_mixInfo;
|
return m_mixInfo;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "AudioVoice.hpp"
|
#include "AudioVoice.hpp"
|
||||||
#include "AudioSubmix.hpp"
|
#include "AudioSubmix.hpp"
|
||||||
#include "IAudioMix.hpp"
|
#include "IAudioMix.hpp"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
@ -31,6 +32,8 @@ protected:
|
||||||
AudioVoiceEngineMixInfo m_mixInfo;
|
AudioVoiceEngineMixInfo m_mixInfo;
|
||||||
std::list<AudioVoice*> m_activeVoices;
|
std::list<AudioVoice*> m_activeVoices;
|
||||||
std::list<AudioSubmix*> m_activeSubmixes;
|
std::list<AudioSubmix*> m_activeSubmixes;
|
||||||
|
size_t m_5msFrames = 0;
|
||||||
|
std::function<void(double dt)> m_5msCallback;
|
||||||
|
|
||||||
/* Shared scratch buffers for accumulating audio data for resampling */
|
/* Shared scratch buffers for accumulating audio data for resampling */
|
||||||
std::vector<int16_t> m_scratchIn;
|
std::vector<int16_t> m_scratchIn;
|
||||||
|
@ -57,6 +60,8 @@ public:
|
||||||
|
|
||||||
std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb);
|
std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb);
|
||||||
|
|
||||||
|
void register5MsCallback(std::function<void(double dt)>&& callback);
|
||||||
|
|
||||||
const AudioVoiceEngineMixInfo& mixInfo() const;
|
const AudioVoiceEngineMixInfo& mixInfo() const;
|
||||||
AudioChannelSet getAvailableSet() {return m_mixInfo.m_channels;}
|
AudioChannelSet getAvailableSet() {return m_mixInfo.m_channels;}
|
||||||
void pumpAndMixVoices() {}
|
void pumpAndMixVoices() {}
|
||||||
|
|
Loading…
Reference in New Issue