mirror of https://github.com/AxioDL/amuse.git
Refactored audio supply dispatch across two passes
This commit is contained in:
parent
fe78a675d7
commit
52cba61f76
|
@ -22,6 +22,7 @@ class BooBackendVoice : public IBackendVoice
|
||||||
struct VoiceCallback : boo::IAudioVoiceCallback
|
struct VoiceCallback : boo::IAudioVoiceCallback
|
||||||
{
|
{
|
||||||
BooBackendVoice& m_parent;
|
BooBackendVoice& m_parent;
|
||||||
|
void preSupplyAudio(boo::IAudioVoice& voice, double dt);
|
||||||
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
|
size_t supplyAudio(boo::IAudioVoice& voice, size_t frames, int16_t* data);
|
||||||
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
|
VoiceCallback(BooBackendVoice& parent) : m_parent(parent) {}
|
||||||
} m_cb;
|
} m_cb;
|
||||||
|
|
|
@ -21,10 +21,10 @@ public:
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
State m_phase = State::Attack; /**< Current envelope state */
|
State m_phase = State::Attack; /**< Current envelope state */
|
||||||
double m_attackTime = 0.02; /**< Time of attack in seconds */
|
double m_attackTime = 0.0; /**< Time of attack in seconds */
|
||||||
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
double m_decayTime = 0.0; /**< Time of decay in seconds */
|
||||||
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
double m_sustainFactor = 1.0; /**< Evaluated sustain percentage */
|
||||||
double m_releaseTime = 0.02; /**< Time of release in seconds */
|
double m_releaseTime = 0.0; /**< Time of release in seconds */
|
||||||
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
double m_releaseStartFactor = 0.0; /**< Level at whenever release event occurs */
|
||||||
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
double m_curTime = 0.0; /**< Current time of envelope stage in seconds */
|
||||||
bool m_adsrSet = false;
|
bool m_adsrSet = false;
|
||||||
|
|
|
@ -86,6 +86,7 @@ class Voice : public Entity
|
||||||
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
|
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
|
||||||
int32_t m_curPitch; /**< Current base pitch in cents */
|
int32_t m_curPitch; /**< Current base pitch in cents */
|
||||||
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
|
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
|
||||||
|
bool m_needsSlew = false; /**< next _setTotalPitch will be slewed */
|
||||||
|
|
||||||
Envelope m_volAdsr; /**< Volume envelope */
|
Envelope m_volAdsr; /**< Volume envelope */
|
||||||
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
double m_envelopeTime = -1.f; /**< time since last ENVELOPE command, -1 for no active volume-sweep */
|
||||||
|
@ -137,7 +138,7 @@ class Voice : public Entity
|
||||||
void _doKeyOff();
|
void _doKeyOff();
|
||||||
void _macroKeyOff();
|
void _macroKeyOff();
|
||||||
void _macroSampleEnd();
|
void _macroSampleEnd();
|
||||||
bool _advanceSample(int16_t& samp, int32_t& curPitch);
|
void _advanceSample(int16_t& samp);
|
||||||
void _setTotalPitch(int32_t cents, bool slew);
|
void _setTotalPitch(int32_t cents, bool slew);
|
||||||
bool _isRecursivelyDead();
|
bool _isRecursivelyDead();
|
||||||
void _bringOutYourDead();
|
void _bringOutYourDead();
|
||||||
|
@ -166,6 +167,10 @@ public:
|
||||||
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, Submix* smx);
|
||||||
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx);
|
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Submix* smx);
|
||||||
|
|
||||||
|
/** Called before each supplyAudio invocation to prepare voice
|
||||||
|
* backend for possible parameter updates */
|
||||||
|
void preSupplyAudio(double dt);
|
||||||
|
|
||||||
/** Request specified count of audio frames (samples) from voice,
|
/** Request specified count of audio frames (samples) from voice,
|
||||||
* internally advancing the voice stream */
|
* internally advancing the voice stream */
|
||||||
size_t supplyAudio(size_t frames, int16_t* data);
|
size_t supplyAudio(size_t frames, int16_t* data);
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void BooBackendVoice::VoiceCallback::preSupplyAudio(boo::IAudioVoice&,
|
||||||
|
double dt)
|
||||||
|
{
|
||||||
|
m_parent.m_clientVox.preSupplyAudio(dt);
|
||||||
|
}
|
||||||
|
|
||||||
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&,
|
size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&,
|
||||||
size_t frames, int16_t* data)
|
size_t frames, int16_t* data)
|
||||||
{
|
{
|
||||||
|
|
164
lib/Voice.cpp
164
lib/Voice.cpp
|
@ -115,7 +115,7 @@ void Voice::_doKeyOff()
|
||||||
|
|
||||||
void Voice::_setTotalPitch(int32_t cents, bool slew)
|
void Voice::_setTotalPitch(int32_t cents, bool slew)
|
||||||
{
|
{
|
||||||
//fprintf(stderr, "PITCH %d\n", cents);
|
//fprintf(stderr, "PITCH %d %d \n", cents, slew);
|
||||||
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
int32_t interval = cents - m_curSample->first.m_pitch * 100;
|
||||||
double ratio = std::exp2(interval / 1200.0);
|
double ratio = std::exp2(interval / 1200.0);
|
||||||
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
m_sampleRate = m_curSample->first.m_sampleRate * ratio;
|
||||||
|
@ -199,7 +199,7 @@ static void ApplyVolume(float vol, int16_t& samp)
|
||||||
samp *= VolumeLUT[int(vol * 65536)];
|
samp *= VolumeLUT[int(vol * 65536)];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
void Voice::_advanceSample(int16_t& samp)
|
||||||
{
|
{
|
||||||
double dt;
|
double dt;
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
|
|
||||||
/* Apply total volume to sample using decibel scale */
|
/* Apply total volume to sample using decibel scale */
|
||||||
ApplyVolume(l, samp);
|
ApplyVolume(l, samp);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dt = 160.0 / m_sampleRate;
|
dt = 160.0 / m_sampleRate;
|
||||||
|
@ -232,7 +232,6 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_voiceTime += dt;
|
m_voiceTime += dt;
|
||||||
bool refresh = false;
|
|
||||||
|
|
||||||
/* Process active envelope */
|
/* Process active envelope */
|
||||||
if (m_envelopeTime >= 0.0)
|
if (m_envelopeTime >= 0.0)
|
||||||
|
@ -255,40 +254,6 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
/* Dynamically evaluate per-sample SoundMacro parameters */
|
/* Dynamically evaluate per-sample SoundMacro parameters */
|
||||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol;
|
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol;
|
||||||
|
|
||||||
bool panDirty = false;
|
|
||||||
if (m_state.m_panSel)
|
|
||||||
{
|
|
||||||
float evalPan = m_state.m_panSel.evaluate(*this, m_state);
|
|
||||||
if (evalPan != m_curPan)
|
|
||||||
{
|
|
||||||
m_curPan = evalPan;
|
|
||||||
panDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_state.m_spanSel)
|
|
||||||
{
|
|
||||||
float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
|
|
||||||
if (evalSpan != m_curSpan)
|
|
||||||
{
|
|
||||||
m_curSpan = evalSpan;
|
|
||||||
panDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_state.m_reverbSel)
|
|
||||||
{
|
|
||||||
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
|
|
||||||
if (evalRev != m_curReverbVol)
|
|
||||||
{
|
|
||||||
m_curReverbVol = evalRev;
|
|
||||||
panDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (panDirty)
|
|
||||||
_setPan(m_curPan);
|
|
||||||
|
|
||||||
if (m_state.m_pitchWheelSel)
|
|
||||||
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
|
|
||||||
|
|
||||||
/* Process user volume slew */
|
/* Process user volume slew */
|
||||||
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
||||||
{
|
{
|
||||||
|
@ -351,8 +316,70 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
|
|
||||||
/* Apply total volume to sample using decibel scale */
|
/* Apply total volume to sample using decibel scale */
|
||||||
ApplyVolume(m_nextLevel, samp);
|
ApplyVolume(m_nextLevel, samp);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt)
|
||||||
|
{
|
||||||
|
switch (fmt)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
case Voice::SampleFormat::DSP:
|
||||||
|
return 14;
|
||||||
|
case Voice::SampleFormat::N64:
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::preSupplyAudio(double dt)
|
||||||
|
{
|
||||||
|
/* Process SoundMacro; bootstrapping sample if needed */
|
||||||
|
bool dead = m_state.advance(*this, dt);
|
||||||
|
|
||||||
|
/* Process per-block evaluators here */
|
||||||
|
if (m_state.m_pedalSel)
|
||||||
|
{
|
||||||
|
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
|
||||||
|
if (pedal != m_sustained)
|
||||||
|
setPedal(pedal);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool panDirty = false;
|
||||||
|
if (m_state.m_panSel)
|
||||||
|
{
|
||||||
|
float evalPan = m_state.m_panSel.evaluate(*this, m_state);
|
||||||
|
if (evalPan != m_curPan)
|
||||||
|
{
|
||||||
|
m_curPan = evalPan;
|
||||||
|
panDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_state.m_spanSel)
|
||||||
|
{
|
||||||
|
float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
|
||||||
|
if (evalSpan != m_curSpan)
|
||||||
|
{
|
||||||
|
m_curSpan = evalSpan;
|
||||||
|
panDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_state.m_reverbSel)
|
||||||
|
{
|
||||||
|
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
|
||||||
|
if (evalRev != m_curReverbVol)
|
||||||
|
{
|
||||||
|
m_curReverbVol = evalRev;
|
||||||
|
panDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (panDirty)
|
||||||
|
_setPan(m_curPan);
|
||||||
|
|
||||||
|
if (m_state.m_pitchWheelSel)
|
||||||
|
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
|
||||||
|
|
||||||
/* Process active pan-sweep */
|
/* Process active pan-sweep */
|
||||||
|
bool refresh = false;
|
||||||
if (m_panningTime >= 0.f)
|
if (m_panningTime >= 0.f)
|
||||||
{
|
{
|
||||||
m_panningTime += dt;
|
m_panningTime += dt;
|
||||||
|
@ -383,7 +410,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate total pitch */
|
/* Calculate total pitch */
|
||||||
newPitch = m_curPitch;
|
int32_t newPitch = m_curPitch;
|
||||||
refresh |= m_pitchDirty;
|
refresh |= m_pitchDirty;
|
||||||
m_pitchDirty = false;
|
m_pitchDirty = false;
|
||||||
if (m_portamentoTime >= 0.f)
|
if (m_portamentoTime >= 0.f)
|
||||||
|
@ -423,20 +450,19 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
refresh = true;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* True if backend voice needs reconfiguration before next sample */
|
if (m_curSample && refresh)
|
||||||
return refresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Voice::_GetBlockSampleCount(SampleFormat fmt)
|
|
||||||
{
|
|
||||||
switch (fmt)
|
|
||||||
{
|
{
|
||||||
default:
|
_setTotalPitch(newPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal, m_needsSlew);
|
||||||
return 1;
|
m_needsSlew = true;
|
||||||
case Voice::SampleFormat::DSP:
|
}
|
||||||
return 14;
|
|
||||||
case Voice::SampleFormat::N64:
|
if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) &&
|
||||||
return 64;
|
m_sampleEndTrap.macroId == 0xffff &&
|
||||||
|
m_messageTrap.macroId == 0xffff &&
|
||||||
|
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
|
||||||
|
{
|
||||||
|
m_voxState = VoiceState::Dead;
|
||||||
|
m_backendVoice->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,23 +471,10 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
uint32_t samplesRem = samples;
|
uint32_t samplesRem = samples;
|
||||||
size_t samplesProc = 0;
|
size_t samplesProc = 0;
|
||||||
|
|
||||||
/* Process SoundMacro; bootstrapping sample if needed */
|
|
||||||
bool dead = m_state.advance(*this, samples / m_sampleRate);
|
|
||||||
|
|
||||||
/* Process per-block evaluators here */
|
|
||||||
if (m_state.m_pedalSel)
|
|
||||||
{
|
|
||||||
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
|
|
||||||
if (pedal != m_sustained)
|
|
||||||
setPedal(pedal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_curSample)
|
if (m_curSample)
|
||||||
{
|
{
|
||||||
uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat);
|
uint32_t blockSampleCount = _GetBlockSampleCount(m_curFormat);
|
||||||
uint32_t block;
|
uint32_t block;
|
||||||
int32_t curPitch = m_curPitch;
|
|
||||||
bool refresh = false;
|
|
||||||
|
|
||||||
bool looped = true;
|
bool looped = true;
|
||||||
while (looped && samplesRem)
|
while (looped && samplesRem)
|
||||||
|
@ -517,7 +530,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
{
|
{
|
||||||
++samplesProc;
|
++samplesProc;
|
||||||
++m_curSamplePos;
|
++m_curSamplePos;
|
||||||
refresh |= _advanceSample(data[i], curPitch);
|
_advanceSample(data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
|
@ -582,7 +595,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
{
|
{
|
||||||
++samplesProc;
|
++samplesProc;
|
||||||
++m_curSamplePos;
|
++m_curSamplePos;
|
||||||
refresh |= _advanceSample(data[i], curPitch);
|
_advanceSample(data[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesRem -= decSamples;
|
samplesRem -= decSamples;
|
||||||
|
@ -598,22 +611,12 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh)
|
|
||||||
_setTotalPitch(curPitch + m_pitchSweep1 + m_pitchSweep2 + m_pitchWheelVal, true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memset(data, 0, sizeof(int16_t) * samples);
|
memset(data, 0, sizeof(int16_t) * samples);
|
||||||
|
|
||||||
if (dead && (!m_curSample || m_voxState == VoiceState::KeyOff) &&
|
if (m_voxState == VoiceState::Dead)
|
||||||
m_sampleEndTrap.macroId == 0xffff &&
|
|
||||||
m_messageTrap.macroId == 0xffff &&
|
|
||||||
(!m_curSample || (m_curSample && m_volAdsr.isComplete())))
|
|
||||||
{
|
|
||||||
m_curSample = nullptr;
|
m_curSample = nullptr;
|
||||||
m_voxState = VoiceState::Dead;
|
|
||||||
m_backendVoice->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
@ -795,6 +798,7 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
||||||
m_pitchDirty = true;
|
m_pitchDirty = true;
|
||||||
_setPitchWheel(m_curPitchWheel);
|
_setPitchWheel(m_curPitchWheel);
|
||||||
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
m_backendVoice->resetSampleRate(m_curSample->first.m_sampleRate);
|
||||||
|
m_needsSlew = false;
|
||||||
|
|
||||||
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
|
int32_t numSamples = m_curSample->first.m_numSamples & 0xffffff;
|
||||||
if (offset)
|
if (offset)
|
||||||
|
@ -1001,7 +1005,7 @@ void Voice::setPitchSweep1(uint8_t times, int16_t add)
|
||||||
{
|
{
|
||||||
m_pitchSweep1 = 0;
|
m_pitchSweep1 = 0;
|
||||||
m_pitchSweep1It = 0;
|
m_pitchSweep1It = 0;
|
||||||
m_pitchSweep1Times = times * 160;
|
m_pitchSweep1Times = times;
|
||||||
m_pitchSweep1Add = add;
|
m_pitchSweep1Add = add;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,7 +1013,7 @@ void Voice::setPitchSweep2(uint8_t times, int16_t add)
|
||||||
{
|
{
|
||||||
m_pitchSweep2 = 0;
|
m_pitchSweep2 = 0;
|
||||||
m_pitchSweep2It = 0;
|
m_pitchSweep2It = 0;
|
||||||
m_pitchSweep2Times = times * 160;
|
m_pitchSweep2Times = times;
|
||||||
m_pitchSweep2Add = add;
|
m_pitchSweep2Add = add;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue