Fix some submix issues

This commit is contained in:
Jack Andersen 2016-05-07 12:11:45 -10:00
parent 4b969fd475
commit 08a4c5d7a8
9 changed files with 104 additions and 64 deletions

View File

@ -189,7 +189,7 @@ add_library(boo
lib/audiodev/AudioVoice.cpp
lib/audiodev/AudioSubmix.hpp
lib/audiodev/AudioSubmix.cpp
lib/audiodev/IAudioHost.hpp
lib/audiodev/IAudioMix.hpp
include/boo/inputdev/IHIDListener.hpp
include/boo/IGraphicsContext.hpp
include/boo/graphicsdev/IGraphicsDataFactory.hpp

View File

@ -28,6 +28,9 @@ struct IAudioSubmix
/** Same as the IAudioVoice allocator, but produces audio recursively within the submix */
virtual std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb=nullptr)=0;
/** Sets gain factors for each channel once accumulated by the submix */
virtual void setChannelGains(const float gains[8])=0;
};
struct IAudioSubmixCallback

View File

@ -2,16 +2,16 @@
#include "AudioVoiceEngine.hpp"
#include "AudioVoice.hpp"
#include <string.h>
#include <algorithm>
namespace boo
{
static std::vector<int16_t> scratch16;
static std::vector<int32_t> scratch32;
static std::vector<float> scratchFlt;
AudioSubmix::AudioSubmix(IAudioHost& parent, IAudioSubmixCallback* cb)
: m_parent(parent), m_cb(cb) {}
AudioSubmix::AudioSubmix(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioSubmixCallback* cb)
: m_root(root), m_parent(parent), m_cb(cb)
{
std::fill(std::begin(m_gains), std::end(m_gains), 1.f);
}
AudioSubmix::~AudioSubmix()
{
@ -22,90 +22,99 @@ void AudioSubmix::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
{
const AudioVoiceEngineMixInfo& info = mixInfo();
size_t sampleCount = frames * info.m_channelMap.m_channelCount;
if (scratch16.size() < sampleCount)
scratch16.resize(sampleCount);
if (m_scratch16.size() < sampleCount)
m_scratch16.resize(sampleCount);
/* Clear target buffer */
memset(scratch16.data(), 0, sizeof(int16_t) * sampleCount);
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, scratch16.data());
vox->pumpAndMix(m_parent.mixInfo(), frames, m_scratch16.data());
/* Pump child submixes */
for (AudioSubmix* smx : m_activeSubmixes)
smx->_pumpAndMixVoices(frames, scratch16.data());
smx->_pumpAndMixVoices(frames, m_scratch16.data());
/* Apply submix effect (if available) */
if (m_cb && m_cb->canApplyEffect())
m_cb->applyEffect(scratch16.data(), info.m_channelMap, info.m_sampleRate);
m_cb->applyEffect(m_scratch16.data(), info.m_channelMap, info.m_sampleRate);
/* Merge into output mix */
auto it = scratch16.begin();
auto it = m_scratch16.begin();
for (size_t f=0 ; f<frames ; ++f)
for (size_t c=0 ; c<info.m_channelMap.m_channelCount ; ++c)
*dataOut++ = Clamp16(*it++ * m_gains[c]);
{
*dataOut = Clamp16(*dataOut + *it++ * m_gains[c]);
++dataOut;
}
}
void AudioSubmix::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
{
const AudioVoiceEngineMixInfo& info = mixInfo();
size_t sampleCount = frames * info.m_channelMap.m_channelCount;
if (scratch32.size() < sampleCount)
scratch32.resize(sampleCount);
if (m_scratch32.size() < sampleCount)
m_scratch32.resize(sampleCount);
/* Clear target buffer */
memset(scratch32.data(), 0, sizeof(int32_t) * sampleCount);
memset(m_scratch32.data(), 0, sizeof(int32_t) * sampleCount);
/* Pump child voices */
for (AudioVoice* vox : m_activeVoices)
if (vox->m_running)
vox->pumpAndMix(m_parent.mixInfo(), frames, scratch32.data());
vox->pumpAndMix(m_parent.mixInfo(), frames, m_scratch32.data());
/* Pump child submixes */
for (AudioSubmix* smx : m_activeSubmixes)
smx->_pumpAndMixVoices(frames, scratch32.data());
smx->_pumpAndMixVoices(frames, m_scratch32.data());
/* Apply submix effect (if available) */
if (m_cb && m_cb->canApplyEffect())
m_cb->applyEffect(scratch32.data(), info.m_channelMap, info.m_sampleRate);
m_cb->applyEffect(m_scratch32.data(), info.m_channelMap, info.m_sampleRate);
/* Merge into output mix */
auto it = scratch32.begin();
auto it = m_scratch32.begin();
for (size_t f=0 ; f<frames ; ++f)
for (size_t c=0 ; c<info.m_channelMap.m_channelCount ; ++c)
*dataOut++ = Clamp32(*it++ * m_gains[c]);
{
*dataOut = Clamp32(*dataOut + *it++ * m_gains[c]);
++dataOut;
}
}
void AudioSubmix::_pumpAndMixVoices(size_t frames, float* dataOut)
{
const AudioVoiceEngineMixInfo& info = mixInfo();
size_t sampleCount = frames * info.m_channelMap.m_channelCount;
if (scratchFlt.size() < sampleCount)
scratchFlt.resize(sampleCount);
if (m_scratchFlt.size() < sampleCount)
m_scratchFlt.resize(sampleCount);
/* Clear target buffer */
memset(scratchFlt.data(), 0, sizeof(float) * sampleCount);
memset(m_scratchFlt.data(), 0, sizeof(float) * sampleCount);
/* Pump child voices */
for (AudioVoice* vox : m_activeVoices)
if (vox->m_running)
vox->pumpAndMix(m_parent.mixInfo(), frames, scratchFlt.data());
vox->pumpAndMix(m_parent.mixInfo(), frames, m_scratchFlt.data());
/* Pump child submixes */
for (AudioSubmix* smx : m_activeSubmixes)
smx->_pumpAndMixVoices(frames, scratchFlt.data());
smx->_pumpAndMixVoices(frames, m_scratchFlt.data());
/* Apply submix effect (if available) */
if (m_cb && m_cb->canApplyEffect())
m_cb->applyEffect(scratchFlt.data(), info.m_channelMap, info.m_sampleRate);
m_cb->applyEffect(m_scratchFlt.data(), info.m_channelMap, info.m_sampleRate);
/* Merge into output mix */
auto it = scratchFlt.begin();
auto it = m_scratchFlt.begin();
for (size_t f=0 ; f<frames ; ++f)
for (size_t c=0 ; c<info.m_channelMap.m_channelCount ; ++c)
*dataOut++ = ClampFlt(*it++ * m_gains[c]);
{
*dataOut = ClampFlt(*dataOut + *it++ * m_gains[c]);
++dataOut;
}
}
void AudioSubmix::_unbindFrom(std::list<AudioVoice*>::iterator it)
@ -123,7 +132,7 @@ std::unique_ptr<IAudioVoice> AudioSubmix::allocateNewMonoVoice(double sampleRate
bool dynamicPitch)
{
std::unique_ptr<IAudioVoice> ret =
std::make_unique<AudioVoiceMono>(*this, cb, sampleRate, dynamicPitch);
std::make_unique<AudioVoiceMono>(m_root, *this, cb, sampleRate, dynamicPitch);
AudioVoiceMono* retMono = static_cast<AudioVoiceMono*>(ret.get());
retMono->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retMono));
return ret;
@ -134,7 +143,7 @@ std::unique_ptr<IAudioVoice> AudioSubmix::allocateNewStereoVoice(double sampleRa
bool dynamicPitch)
{
std::unique_ptr<IAudioVoice> ret =
std::make_unique<AudioVoiceStereo>(*this, cb, sampleRate, dynamicPitch);
std::make_unique<AudioVoiceStereo>(m_root, *this, cb, sampleRate, dynamicPitch);
AudioVoiceStereo* retStereo = static_cast<AudioVoiceStereo*>(ret.get());
retStereo->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retStereo));
return ret;
@ -143,12 +152,18 @@ std::unique_ptr<IAudioVoice> AudioSubmix::allocateNewStereoVoice(double sampleRa
std::unique_ptr<IAudioSubmix> AudioSubmix::allocateNewSubmix(IAudioSubmixCallback* cb)
{
std::unique_ptr<IAudioSubmix> ret =
std::make_unique<AudioSubmix>(*this, cb);
std::make_unique<AudioSubmix>(m_root, *this, cb);
AudioSubmix* retIntern = static_cast<AudioSubmix*>(ret.get());
retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern));
return ret;
}
void AudioSubmix::setChannelGains(const float gains[8])
{
for (int i=0 ; i<8 ; ++i)
m_gains[i] = gains[i];
}
void AudioSubmix::unbindSubmix()
{
if (m_bound)

View File

@ -2,20 +2,22 @@
#define BOO_AUDIOSUBMIX_HPP
#include "boo/audiodev/IAudioSubmix.hpp"
#include "IAudioHost.hpp"
#include "IAudioMix.hpp"
#include <list>
#include <vector>
namespace boo
{
class BaseAudioVoiceEngine;
class AudioVoice;
class AudioSubmix : public IAudioSubmix, public IAudioHost
class AudioSubmix : public IAudioSubmix, public IAudioMix
{
friend class BaseAudioVoiceEngine;
/* Mixer-engine relationships */
IAudioHost& m_parent;
BaseAudioVoiceEngine& m_root;
IAudioMix& m_parent;
std::list<AudioSubmix*>::iterator m_parentIt;
bool m_bound = false;
void bindSubmix(std::list<AudioSubmix*>::iterator pIt)
@ -34,6 +36,11 @@ class AudioSubmix : public IAudioSubmix, public IAudioHost
/* Output gains for each channel */
float m_gains[8];
/* Temporary scratch buffers for accumulating submix audio */
std::vector<int16_t> m_scratch16;
std::vector<int32_t> m_scratch32;
std::vector<float> m_scratchFlt;
void _pumpAndMixVoices(size_t frames, int16_t* dataOut);
void _pumpAndMixVoices(size_t frames, int32_t* dataOut);
void _pumpAndMixVoices(size_t frames, float* dataOut);
@ -43,7 +50,7 @@ class AudioSubmix : public IAudioSubmix, public IAudioHost
public:
~AudioSubmix();
AudioSubmix(IAudioHost& parent, IAudioSubmixCallback* cb);
AudioSubmix(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioSubmixCallback* cb);
std::unique_ptr<IAudioVoice> allocateNewMonoVoice(double sampleRate,
IAudioVoiceCallback* cb,
@ -53,7 +60,9 @@ public:
IAudioVoiceCallback* cb,
bool dynamicPitch=false);
virtual std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb=nullptr);
std::unique_ptr<IAudioSubmix> allocateNewSubmix(IAudioSubmixCallback* cb=nullptr);
void setChannelGains(const float gains[8]);
void unbindSubmix();

View File

@ -6,13 +6,9 @@ namespace boo
{
static logvisor::Module Log("boo::AudioVoice");
static std::vector<int16_t> scratchIn;
static std::vector<int16_t> scratch16;
static std::vector<int32_t> scratch32;
static std::vector<float> scratchFlt;
AudioVoice::AudioVoice(IAudioHost& parent, IAudioVoiceCallback* cb, bool dynamicRate)
: m_parent(parent), m_cb(cb), m_dynamicRate(dynamicRate) {}
AudioVoice::AudioVoice(BaseAudioVoiceEngine& root, IAudioMix& parent,
IAudioVoiceCallback* cb, bool dynamicRate)
: m_root(root), m_parent(parent), m_cb(cb), m_dynamicRate(dynamicRate) {}
AudioVoice::~AudioVoice()
{
@ -52,9 +48,9 @@ void AudioVoice::unbindVoice()
}
}
AudioVoiceMono::AudioVoiceMono(IAudioHost& parent, IAudioVoiceCallback* cb,
AudioVoiceMono::AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate)
: AudioVoice(parent, cb, dynamicRate)
: AudioVoice(root, parent, cb, dynamicRate)
{
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, parent.mixInfo().m_sampleFormat);
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, dynamicRate ? SOXR_VR : 0);
@ -74,6 +70,7 @@ AudioVoiceMono::AudioVoiceMono(IAudioHost& parent, IAudioVoiceCallback* cb,
size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t frames)
{
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
if (scratchIn.size() < frames)
scratchIn.resize(frames);
*data = scratchIn.data();
@ -83,6 +80,7 @@ size_t AudioVoiceMono::SRCCallback(AudioVoiceMono* ctx, int16_t** data, size_t f
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, int16_t* buf)
{
std::vector<int16_t>& scratch16 = m_root.m_scratch16;
if (scratch16.size() < frames)
scratch16.resize(frames);
@ -95,6 +93,7 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, int32_t* buf)
{
std::vector<int32_t>& scratch32 = m_root.m_scratch32;
if (scratch32.size() < frames)
scratch32.resize(frames);
@ -107,6 +106,7 @@ size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t AudioVoiceMono::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, float* buf)
{
std::vector<float>& scratchFlt = m_root.m_scratchFlt;
if (scratchFlt.size() < frames)
scratchFlt.resize(frames);
@ -142,9 +142,9 @@ void AudioVoiceMono::setStereoMatrixCoefficients(const float coefs[8][2])
m_matrix.setMatrixCoefficients(newCoefs);
}
AudioVoiceStereo::AudioVoiceStereo(IAudioHost& parent, IAudioVoiceCallback* cb,
AudioVoiceStereo::AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate)
: AudioVoice(parent, cb, dynamicRate)
: AudioVoice(root, parent, cb, dynamicRate)
{
soxr_io_spec_t ioSpec = soxr_io_spec(SOXR_INT16_I, parent.mixInfo().m_sampleFormat);
soxr_quality_spec_t qSpec = soxr_quality_spec(SOXR_20_BITQ, dynamicRate ? SOXR_VR : 0);
@ -164,6 +164,7 @@ AudioVoiceStereo::AudioVoiceStereo(IAudioHost& parent, IAudioVoiceCallback* cb,
size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size_t frames)
{
std::vector<int16_t>& scratchIn = ctx->m_root.m_scratchIn;
size_t samples = frames * 2;
if (scratchIn.size() < samples)
scratchIn.resize(samples);
@ -174,6 +175,7 @@ size_t AudioVoiceStereo::SRCCallback(AudioVoiceStereo* ctx, int16_t** data, size
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, int16_t* buf)
{
std::vector<int16_t>& scratch16 = m_root.m_scratch16;
size_t samples = frames * 2;
if (scratch16.size() < samples)
scratch16.resize(samples);
@ -187,6 +189,7 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, int32_t* buf)
{
std::vector<int32_t>& scratch32 = m_root.m_scratch32;
size_t samples = frames * 2;
if (scratch32.size() < samples)
scratch32.resize(samples);
@ -200,6 +203,7 @@ size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t AudioVoiceStereo::pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo,
size_t frames, float* buf)
{
std::vector<float>& scratchFlt = m_root.m_scratchFlt;
size_t samples = frames * 2;
if (scratchFlt.size() < samples)
scratchFlt.resize(samples);

View File

@ -10,7 +10,7 @@ namespace boo
{
class BaseAudioVoiceEngine;
struct AudioVoiceEngineMixInfo;
class IAudioHost;
class IAudioMix;
class AudioVoice : public IAudioVoice
{
@ -19,7 +19,8 @@ class AudioVoice : public IAudioVoice
protected:
/* Mixer-engine relationships */
IAudioHost& m_parent;
BaseAudioVoiceEngine& m_root;
IAudioMix& m_parent;
std::list<AudioVoice*>::iterator m_parentIt;
bool m_bound = false;
void bindVoice(std::list<AudioVoice*>::iterator pIt)
@ -41,7 +42,7 @@ protected:
virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int16_t* buf)=0;
virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, int32_t* buf)=0;
virtual size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf)=0;
AudioVoice(IAudioHost& parent, IAudioVoiceCallback* cb, bool dynamicRate);
AudioVoice(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb, bool dynamicRate);
public:
~AudioVoice();
@ -64,7 +65,7 @@ class AudioVoiceMono : public AudioVoice
size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf);
public:
AudioVoiceMono(IAudioHost& parent, IAudioVoiceCallback* cb,
AudioVoiceMono(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate);
void setDefaultMatrixCoefficients();
void setMonoMatrixCoefficients(const float coefs[8]);
@ -83,7 +84,7 @@ class AudioVoiceStereo : public AudioVoice
size_t pumpAndMix(const AudioVoiceEngineMixInfo& mixInfo, size_t frames, float* buf);
public:
AudioVoiceStereo(IAudioHost& parent, IAudioVoiceCallback* cb,
AudioVoiceStereo(BaseAudioVoiceEngine& root, IAudioMix& parent, IAudioVoiceCallback* cb,
double sampleRate, bool dynamicRate);
void setDefaultMatrixCoefficients();
void setMonoMatrixCoefficients(const float coefs[8]);

View File

@ -50,7 +50,7 @@ BaseAudioVoiceEngine::allocateNewMonoVoice(double sampleRate,
bool dynamicPitch)
{
std::unique_ptr<IAudioVoice> ret =
std::make_unique<AudioVoiceMono>(*this, cb, sampleRate, dynamicPitch);
std::make_unique<AudioVoiceMono>(*this, *this, cb, sampleRate, dynamicPitch);
AudioVoiceMono* retMono = static_cast<AudioVoiceMono*>(ret.get());
retMono->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retMono));
return ret;
@ -62,7 +62,7 @@ BaseAudioVoiceEngine::allocateNewStereoVoice(double sampleRate,
bool dynamicPitch)
{
std::unique_ptr<IAudioVoice> ret =
std::make_unique<AudioVoiceStereo>(*this, cb, sampleRate, dynamicPitch);
std::make_unique<AudioVoiceStereo>(*this, *this, cb, sampleRate, dynamicPitch);
AudioVoiceStereo* retStereo = static_cast<AudioVoiceStereo*>(ret.get());
retStereo->bindVoice(m_activeVoices.insert(m_activeVoices.end(), retStereo));
return ret;
@ -72,7 +72,7 @@ std::unique_ptr<IAudioSubmix>
BaseAudioVoiceEngine::allocateNewSubmix(IAudioSubmixCallback* cb)
{
std::unique_ptr<IAudioSubmix> ret =
std::make_unique<AudioSubmix>(*this, cb);
std::make_unique<AudioSubmix>(*this, *this, cb);
AudioSubmix* retIntern = static_cast<AudioSubmix*>(ret.get());
retIntern->bindSubmix(m_activeSubmixes.insert(m_activeSubmixes.end(), retIntern));
return ret;

View File

@ -4,7 +4,7 @@
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "AudioVoice.hpp"
#include "AudioSubmix.hpp"
#include "IAudioHost.hpp"
#include "IAudioMix.hpp"
namespace boo
{
@ -21,15 +21,23 @@ struct AudioVoiceEngineMixInfo
};
/** Base class for managing mixing and sample-rate-conversion amongst active voices */
class BaseAudioVoiceEngine : public IAudioVoiceEngine, public IAudioHost
class BaseAudioVoiceEngine : public IAudioVoiceEngine, public IAudioMix
{
protected:
friend class AudioVoice;
friend class AudioSubmix;
friend class AudioVoiceMono;
friend class AudioVoiceStereo;
AudioVoiceEngineMixInfo m_mixInfo;
std::list<AudioVoice*> m_activeVoices;
std::list<AudioSubmix*> m_activeSubmixes;
/* Shared scratch buffers for accumulating audio data for resampling */
std::vector<int16_t> m_scratchIn;
std::vector<int16_t> m_scratch16;
std::vector<int32_t> m_scratch32;
std::vector<float> m_scratchFlt;
void _pumpAndMixVoices(size_t frames, int16_t* dataOut);
void _pumpAndMixVoices(size_t frames, int32_t* dataOut);
void _pumpAndMixVoices(size_t frames, float* dataOut);

View File

@ -1,5 +1,5 @@
#ifndef BOO_IAUDIOHOST_HPP
#define BOO_IAUDIOHOST_HPP
#ifndef BOO_IAUDIOMIX_HPP
#define BOO_IAUDIOMIX_HPP
#include <list>
@ -10,7 +10,7 @@ class AudioVoice;
class AudioSubmix;
/** Entity that mixes audio from several child sources (engine root or submix) */
class IAudioHost
class IAudioMix
{
friend class AudioVoice;
friend class AudioSubmix;
@ -22,4 +22,4 @@ public:
}
#endif // BOO_IAUDIOHOST_HPP
#endif // BOO_IAUDIOMIX_HPP