2016-03-24 00:01:57 +00:00
|
|
|
#include "AudioVoice.hpp"
|
|
|
|
#include "AudioVoiceEngine.hpp"
|
|
|
|
#include "logvisor/logvisor.hpp"
|
|
|
|
|
|
|
|
namespace boo
|
|
|
|
{
|
|
|
|
static logvisor::Module Log("boo::AudioVoice");
|
|
|
|
|
2016-05-07 22:11:45 +00:00
|
|
|
AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, IAudioMix& parent,
|
|
|
|
IAudioVoiceCallback* cb, bool dynamicRate)
|
|
|
|
: m_root(root), m_parent(parent), m_cb(cb), m_dynamicRate(dynamicRate) {}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
|
|
|
AudioVoice::~AudioVoice()
|
|
|
|
{
|
|
|
|
unbindVoice();
|
|
|
|
soxr_delete(m_src);
|
|
|
|
}
|
|
|
|
|
2016-05-17 03:46:03 +00:00
|
|
|
void AudioVoice::_setPitchRatio(double ratio, bool slew)
|
2016-03-24 00:01:57 +00:00
|
|
|
{
|
|
|
|
if (m_dynamicRate)
|
|
|
|
{
|
2016-05-17 03:46:03 +00:00
|
|
|
soxr_error_t err = soxr_set_io_ratio(m_src, ratio * m_sampleRateIn / m_sampleRateOut, slew ? m_5msFrames : 0);
|
2016-03-24 00:01:57 +00:00
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Fatal, "unable to set resampler rate: %s", soxr_strerror(err));
|
2016-05-11 21:29:11 +00:00
|
|
|
m_setPitchRatio = false;
|
2016-03-24 00:01:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-05-11 21:29:11 +00:00
|
|
|
m_setPitchRatio = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoice::_midUpdate()
|
|
|
|
{
|
|
|
|
if (m_resetSampleRate)
|
|
|
|
_resetSampleRate(m_deferredSampleRate);
|
|
|
|
if (m_setPitchRatio)
|
2016-05-17 03:46:03 +00:00
|
|
|
_setPitchRatio(m_pitchRatio, m_slew);
|
2016-05-11 21:29:11 +00:00
|
|
|
}
|
|
|
|
|
2016-05-17 03:46:03 +00:00
|
|
|
void AudioVoice::setPitchRatio(double ratio, bool slew)
|
2016-05-11 21:29:11 +00:00
|
|
|
{
|
|
|
|
m_setPitchRatio = true;
|
|
|
|
m_pitchRatio = ratio;
|
2016-05-17 03:46:03 +00:00
|
|
|
m_slew = slew;
|
2016-05-11 21:29:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoice::resetSampleRate(double sampleRate)
|
|
|
|
{
|
|
|
|
m_resetSampleRate = true;
|
|
|
|
m_deferredSampleRate = sampleRate;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoice::start()
|
|
|
|
{
|
|
|
|
m_running = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoice::stop()
|
|
|
|
{
|
|
|
|
m_running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoice::unbindVoice()
|
|
|
|
{
|
|
|
|
if (m_bound)
|
|
|
|
{
|
2016-05-07 04:28:32 +00:00
|
|
|
m_parent._unbindFrom(m_parentIt);
|
2016-03-24 00:01:57 +00:00
|
|
|
m_bound = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-07 22:11:45 +00:00
|
|
|
AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
|
2016-03-24 00:01:57 +00:00
|
|
|
double sampleRate, bool dynamicRate)
|
2016-05-07 22:11:45 +00:00
|
|
|
: AudioVoice(root, parent, cb, dynamicRate)
|
2016-03-24 00:01:57 +00:00
|
|
|
{
|
2016-05-11 21:29:11 +00:00
|
|
|
_resetSampleRate(sampleRate);
|
2016-05-11 04:50:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
void AudioVoiceMono::_resetSampleRate(double sampleRate)
|
2016-05-11 04:50:26 +00:00
|
|
|
{
|
|
|
|
soxr_delete(m_src);
|
|
|
|
|
2016-05-16 02:41:11 +00:00
|
|
|
double rateOut = m_parent.mixInfo().m_sampleRate;
|
2016-05-11 04:50:26 +00:00
|
|
|
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, m_parent.mixInfo().m_sampleFormat);
|
|
|
|
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
|
2016-03-24 00:01:57 +00:00
|
|
|
|
|
|
|
soxr_error_t err;
|
2016-05-16 02:41:11 +00:00
|
|
|
m_src = soxr_create(sampleRate, rateOut, 1,
|
2016-03-24 00:01:57 +00:00
|
|
|
&err, &ioSpec, &qSpec, nullptr);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
|
2016-05-11 21:29:11 +00:00
|
|
|
m_resetSampleRate = false;
|
2016-03-24 00:01:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-16 02:41:11 +00:00
|
|
|
m_sampleRateIn = sampleRate;
|
|
|
|
m_sampleRateOut = rateOut;
|
2016-05-16 20:21:20 +00:00
|
|
|
m_5msFrames = rateOut * 5 / 1000;
|
2016-03-24 00:01:57 +00:00
|
|
|
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
|
2016-05-17 03:46:03 +00:00
|
|
|
_setPitchRatio(m_pitchRatio, false);
|
2016-05-11 21:29:11 +00:00
|
|
|
m_resetSampleRate = false;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
|
2016-03-24 00:01:57 +00:00
|
|
|
if (scratchIn.size() < frames)
|
|
|
|
scratchIn.resize(frames);
|
|
|
|
*data = scratchIn.data();
|
|
|
|
return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, int16_t* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int16_t>& scratch16 = m_root.m_scratch16;
|
2016-03-24 00:01:57 +00:00
|
|
|
if (scratch16.size() < frames)
|
|
|
|
scratch16.resize(frames);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratch16.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixMonoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, int32_t* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int32_t>& scratch32 = m_root.m_scratch32;
|
2016-03-24 00:01:57 +00:00
|
|
|
if (scratch32.size() < frames)
|
|
|
|
scratch32.resize(frames);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratch32.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixMonoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, float* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<float>& scratchFlt = m_root.m_scratchFlt;
|
2016-03-24 00:01:57 +00:00
|
|
|
if (scratchFlt.size() < frames)
|
|
|
|
scratchFlt.resize(frames);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixMonoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceMono::setDefaultMatrixCoefficients()
|
|
|
|
{
|
|
|
|
m_matrix.setDefaultMatrixCoefficients(m_parent.mixInfo().m_channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceMono::setMonoMatrixCoefficients(const float coefs[8])
|
|
|
|
{
|
|
|
|
m_matrix.setMatrixCoefficients(coefs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceMono::setStereoMatrixCoefficients(const float coefs[8][2])
|
|
|
|
{
|
|
|
|
float newCoefs[8] =
|
|
|
|
{
|
|
|
|
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_matrix.setMatrixCoefficients(newCoefs);
|
|
|
|
}
|
|
|
|
|
2016-05-07 22:11:45 +00:00
|
|
|
AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
|
2016-03-24 00:01:57 +00:00
|
|
|
double sampleRate, bool dynamicRate)
|
2016-05-07 22:11:45 +00:00
|
|
|
: AudioVoice(root, parent, cb, dynamicRate)
|
2016-03-24 00:01:57 +00:00
|
|
|
{
|
2016-05-11 21:29:11 +00:00
|
|
|
_resetSampleRate(sampleRate);
|
2016-05-11 04:50:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
void AudioVoiceStereo::_resetSampleRate(double sampleRate)
|
2016-05-11 04:50:26 +00:00
|
|
|
{
|
|
|
|
soxr_delete(m_src);
|
|
|
|
|
2016-05-16 02:41:11 +00:00
|
|
|
double rateOut = m_parent.mixInfo().m_sampleRate;
|
2016-05-11 04:50:26 +00:00
|
|
|
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, m_parent.mixInfo().m_sampleFormat);
|
|
|
|
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, m_dynamicRate ? SOXR_VR : 0);
|
2016-03-24 00:01:57 +00:00
|
|
|
|
|
|
|
soxr_error_t err;
|
2016-05-16 02:41:11 +00:00
|
|
|
m_src = soxr_create(sampleRate, rateOut, 2,
|
2016-03-24 00:01:57 +00:00
|
|
|
&err, &ioSpec, &qSpec, nullptr);
|
|
|
|
|
|
|
|
if (!m_src)
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Fatal, "unable to create soxr resampler: %s", soxr_strerror(err));
|
2016-05-11 21:29:11 +00:00
|
|
|
m_resetSampleRate = false;
|
2016-03-24 00:01:57 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-16 02:41:11 +00:00
|
|
|
m_sampleRateIn = sampleRate;
|
|
|
|
m_sampleRateOut = rateOut;
|
2016-03-24 00:01:57 +00:00
|
|
|
soxr_set_input_fn(m_src, soxr_input_fn_t(SRCCallback), this, 0);
|
2016-05-17 03:46:03 +00:00
|
|
|
_setPitchRatio(m_pitchRatio, false);
|
2016-05-11 21:29:11 +00:00
|
|
|
m_resetSampleRate = false;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
|
2016-03-24 00:01:57 +00:00
|
|
|
size_t samples = frames * 2;
|
|
|
|
if (scratchIn.size() < samples)
|
|
|
|
scratchIn.resize(samples);
|
|
|
|
*data = scratchIn.data();
|
|
|
|
return ctx->m_cb->supplyAudio(*ctx, frames, scratchIn.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, int16_t* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int16_t>& scratch16 = m_root.m_scratch16;
|
2016-03-24 00:01:57 +00:00
|
|
|
size_t samples = frames * 2;
|
|
|
|
if (scratch16.size() < samples)
|
|
|
|
scratch16.resize(samples);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratch16.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixStereoSampleData(mixInfo, scratch16.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, int32_t* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<int32_t>& scratch32 = m_root.m_scratch32;
|
2016-03-24 00:01:57 +00:00
|
|
|
size_t samples = frames * 2;
|
|
|
|
if (scratch32.size() < samples)
|
|
|
|
scratch32.resize(samples);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratch32.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixStereoSampleData(mixInfo, scratch32.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
|
|
|
|
size_t frames, float* buf)
|
|
|
|
{
|
2016-05-07 22:11:45 +00:00
|
|
|
std::vector<float>& scratchFlt = m_root.m_scratchFlt;
|
2016-03-24 00:01:57 +00:00
|
|
|
size_t samples = frames * 2;
|
|
|
|
if (scratchFlt.size() < samples)
|
|
|
|
scratchFlt.resize(samples);
|
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
size_t oDone;
|
|
|
|
size_t totalDone = 0;
|
|
|
|
while (frames)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
oDone = soxr_output(m_src, scratchFlt.data(), m_5msFrames);
|
2016-05-11 21:29:11 +00:00
|
|
|
_midUpdate();
|
|
|
|
if (oDone)
|
|
|
|
{
|
2016-05-16 20:21:20 +00:00
|
|
|
buf = m_matrix.mixStereoSampleData(mixInfo, scratchFlt.data(), buf, oDone);
|
2016-05-11 21:29:11 +00:00
|
|
|
totalDone += oDone;
|
|
|
|
frames -= oDone;
|
|
|
|
}
|
|
|
|
}
|
2016-03-24 00:01:57 +00:00
|
|
|
|
2016-05-11 21:29:11 +00:00
|
|
|
return totalDone;
|
2016-03-24 00:01:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceStereo::setDefaultMatrixCoefficients()
|
|
|
|
{
|
|
|
|
m_matrix.setDefaultMatrixCoefficients(m_parent.mixInfo().m_channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceStereo::setMonoMatrixCoefficients(const float coefs[8])
|
|
|
|
{
|
|
|
|
float newCoefs[8][2] =
|
|
|
|
{
|
|
|
|
{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_matrix.setMatrixCoefficients(newCoefs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioVoiceStereo::setStereoMatrixCoefficients(const float coefs[8][2])
|
|
|
|
{
|
|
|
|
m_matrix.setMatrixCoefficients(coefs);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|