mirror of https://github.com/AxioDL/amuse.git
Several CPU usage optimizations
This commit is contained in:
parent
fbafa397fe
commit
654eccf82d
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;}
|
||||||
|
|
|
@ -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;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue