Several CPU usage optimizations

This commit is contained in:
Jack Andersen 2016-05-21 22:35:55 -10:00
parent fbafa397fe
commit 654eccf82d
10 changed files with 65623 additions and 21 deletions

View File

@ -12,6 +12,7 @@ set(SOURCES
lib/SoundMacroState.cpp lib/SoundMacroState.cpp
lib/SongState.cpp lib/SongState.cpp
lib/Voice.cpp lib/Voice.cpp
lib/VolumeLUT.cpp
lib/Submix.cpp lib/Submix.cpp
lib/EffectBase.cpp lib/EffectBase.cpp
lib/EffectReverb.cpp lib/EffectReverb.cpp

View File

@ -778,7 +778,7 @@ struct AppCallback : boo::IApplicationCallback
/* Build voice engine */ /* Build voice engine */
std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine(); std::unique_ptr<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
amuse::BooBackendVoiceAllocator booBackend(*voxEngine); amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
m_engine.emplace(booBackend); m_engine.emplace(booBackend, amuse::AmplitudeMode::BlockLinearized);
/* Load group into engine */ /* Load group into engine */
const amuse::AudioGroup* group = m_engine->addAudioGroup(data); const amuse::AudioGroup* group = m_engine->addAudioGroup(data);

View File

@ -20,6 +20,12 @@ class AudioGroup;
class AudioGroupData; class AudioGroupData;
class IMIDIReader; class IMIDIReader;
enum class AmplitudeMode
{
PerSample, /**< Per-sample amplitude evaluation (dt = 1.0 / sampleRate, rather CPU demanding) */
BlockLinearized /**< Per-block lerp amplitude evaluation (dt = 160.0 / sampleRate) */
};
/** Main audio playback system for a single audio output */ /** Main audio playback system for a single audio output */
class Engine class Engine
{ {
@ -29,6 +35,7 @@ class Engine
friend struct Sequencer::ChannelState; friend struct Sequencer::ChannelState;
IBackendVoiceAllocator& m_backend; IBackendVoiceAllocator& m_backend;
AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader; std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups; std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<std::shared_ptr<Voice>> m_activeVoices; std::list<std::shared_ptr<Voice>> m_activeVoices;
@ -55,7 +62,7 @@ class Engine
void _5MsCallback(double dt); void _5MsCallback(double dt);
public: public:
~Engine(); ~Engine();
Engine(IBackendVoiceAllocator& backend); Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode=AmplitudeMode::PerSample);
/** Access voice backend of engine */ /** Access voice backend of engine */
IBackendVoiceAllocator& getBackend() {return m_backend;} IBackendVoiceAllocator& getBackend() {return m_backend;}

View File

@ -30,7 +30,7 @@ public:
void reset(const ADSR* adsr); void reset(const ADSR* adsr);
void reset(const ADSRDLS* adsr, int8_t note, int8_t vel); void reset(const ADSRDLS* adsr, int8_t note, int8_t vel);
void keyOff(); void keyOff();
float nextSample(double sampleRate); float advance(double dt);
bool isComplete() const {return m_phase == State::Complete;} bool isComplete() const {return m_phase == State::Complete;}
}; };

View File

@ -61,6 +61,9 @@ class Voice : public Entity
int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */ int16_t m_prev2 = 0; /**< DSPADPCM prev-prev sample */
double m_sampleRate = 32000.0; /**< Current sample rate computed from relative sample key or SETPITCH */ double m_sampleRate = 32000.0; /**< Current sample rate computed from relative sample key or SETPITCH */
double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */ double m_voiceTime = 0.0; /**< Current seconds of voice playback (per-sample resolution) */
uint32_t m_voiceSamples = 0; /**< Count of samples processed over voice's lifetime */
float m_lastLevel = 0.f; /**< Last computed level ([0,1] mapped to [-10,0] clamped decibels) */
float m_nextLevel = 0.f; /**< Next computed level used for lerp-mode amplitude */
VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */ VoiceState m_voxState = VoiceState::Dead; /**< Current high-level state of voice */
bool m_sustained = false; /**< Sustain pedal pressed for this voice */ bool m_sustained = false; /**< Sustain pedal pressed for this voice */

View File

@ -23,8 +23,8 @@ Engine::~Engine()
vox->_destroy(); vox->_destroy();
} }
Engine::Engine(IBackendVoiceAllocator& backend) Engine::Engine(IBackendVoiceAllocator& backend, AmplitudeMode ampMode)
: m_backend(backend) : m_backend(backend), m_ampMode(ampMode)
{ {
backend.register5MsCallback(std::bind(&Engine::_5MsCallback, this, std::placeholders::_1)); backend.register5MsCallback(std::bind(&Engine::_5MsCallback, this, std::placeholders::_1));
m_midiReader = backend.allocateMIDIReader(*this); m_midiReader = backend.allocateMIDIReader(*this);

View File

@ -31,9 +31,9 @@ void Envelope::keyOff()
m_curTime = 0.0; m_curTime = 0.0;
} }
float Envelope::nextSample(double sampleRate) float Envelope::advance(double dt)
{ {
m_curTime += 1.0 / sampleRate; m_curTime += dt;
switch (m_phase) switch (m_phase)
{ {

View File

@ -211,10 +211,16 @@ void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
for (const auto& vox : m_keyoffVoxs) for (const auto& vox : m_keyoffVoxs)
vox->_notifyCtrlChange(ctrl, val); vox->_notifyCtrlChange(ctrl, val);
if (ctrl == 7) switch (ctrl)
{
case 7:
setVolume(val / 127.f); setVolume(val / 127.f);
else if (ctrl == 8 || ctrl == 10) break;
case 10:
setPan(val / 64.f - 1.f); setPan(val / 64.f - 1.f);
break;
default: break;
}
} }
bool Sequencer::ChannelState::programChange(int8_t prog) bool Sequencer::ChannelState::programChange(int8_t prog)

View File

@ -11,6 +11,7 @@
namespace amuse namespace amuse
{ {
extern "C" const float VolumeLUT[];
void Voice::_destroy() void Voice::_destroy()
{ {
@ -201,14 +202,41 @@ std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(Voice* voice)
static void ApplyVolume(float vol, int16_t& samp) static void ApplyVolume(float vol, int16_t& samp)
{ {
/* -10dB to 0dB mapped to full volume range */ /* -10dB to 0dB mapped to full volume range */
float factor = std::sqrt(std::pow(10.f, (vol - 1.f))) - 0.317f; samp *= VolumeLUT[int(vol * 65536)];
if (factor < 0.f) factor = 0.f;
samp *= factor;
} }
bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch) bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
{ {
double dt = 1.0 / m_sampleRate; double dt;
/* Block linearized will use a larger `dt` for amplitude sampling;
* significantly reducing the processing expense */
switch (m_engine.m_ampMode)
{
case AmplitudeMode::PerSample:
m_voiceSamples += 1;
dt = 1.0 / m_sampleRate;
break;
case AmplitudeMode::BlockLinearized:
{
uint32_t rem = m_voiceSamples % 160;
m_voiceSamples += 1;
if (rem != 0)
{
/* Lerp within 160-sample block */
float t = rem / 160.f;
float l = m_lastLevel * (1.f - t) + m_nextLevel * t;
/* Apply total volume to sample using decibel scale */
ApplyVolume(l, samp);
return false;
}
dt = 160.0 / m_sampleRate;
break;
}
}
m_voiceTime += dt; m_voiceTime += dt;
bool refresh = false; bool refresh = false;
@ -229,8 +257,9 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
/* Factor in ADSR envelope state */ /* Factor in ADSR envelope state */
float adsr = m_volAdsr.nextSample(m_sampleRate); float adsr = m_volAdsr.advance(dt);
float totalVol = m_userVol * m_curVol * adsr * (m_state.m_curVel / 127.f); m_lastLevel = m_nextLevel;
m_nextLevel = m_userVol * m_curVol * adsr * (m_state.m_curVel / 127.f);
/* Apply tremolo */ /* Apply tremolo */
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale)) if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
@ -241,23 +270,25 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
float fac = (1.0f - t) + (m_tremoloScale * t); float fac = (1.0f - t) + (m_tremoloScale * t);
float modT = getModWheel() / 127.f; float modT = getModWheel() / 127.f;
float modFac = (1.0f - modT) + (m_tremoloModScale * modT); float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
totalVol *= fac * modFac; m_nextLevel *= fac * modFac;
} }
else if (m_tremoloScale) else if (m_tremoloScale)
{ {
float fac = (1.0f - t) + (m_tremoloScale * t); float fac = (1.0f - t) + (m_tremoloScale * t);
totalVol *= fac; m_nextLevel *= fac;
} }
else if (m_tremoloModScale) else if (m_tremoloModScale)
{ {
float modT = getModWheel() / 127.f; float modT = getModWheel() / 127.f;
float modFac = (1.0f - modT) + (m_tremoloModScale * modT); float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
totalVol *= modFac; m_nextLevel *= modFac;
} }
} }
/* Apple total volume to sample using decibel scale */ m_nextLevel = ClampFull<float>(m_nextLevel);
ApplyVolume(ClampFull<float>(totalVol), samp);
/* Apply total volume to sample using decibel scale */
ApplyVolume(m_nextLevel, samp);
/* Process active pan-sweep */ /* Process active pan-sweep */
if (m_panningTime >= 0.f) if (m_panningTime >= 0.f)
@ -295,7 +326,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
m_pitchDirty = false; m_pitchDirty = false;
if (m_pitchEnv) if (m_pitchEnv)
{ {
newPitch = m_curPitch * m_pitchAdsr.nextSample(m_sampleRate); newPitch = m_curPitch * m_pitchAdsr.advance(dt);
refresh = true; refresh = true;
} }
@ -452,7 +483,9 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
m_messageTrap.macroId == 0xffff && m_messageTrap.macroId == 0xffff &&
(!m_curSample || (m_curSample && m_volAdsr.isComplete()))) (!m_curSample || (m_curSample && m_volAdsr.isComplete())))
{ {
m_curSample = nullptr;
m_voxState = VoiceState::Dead; m_voxState = VoiceState::Dead;
m_backendVoice->stop();
} }
return samples; return samples;
} }

65552
lib/VolumeLUT.cpp Normal file

File diff suppressed because it is too large Load Diff