mirror of https://github.com/AxioDL/amuse.git
Default volume and pan CC values, MIDI controller ADSR mode
This commit is contained in:
parent
8930e005df
commit
e8c6418633
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
class Voice;
|
||||||
|
|
||||||
/** Per-sample state tracker for ADSR envelope data */
|
/** Per-sample state tracker for ADSR envelope data */
|
||||||
class Envelope
|
class Envelope
|
||||||
|
@ -30,7 +31,9 @@ private:
|
||||||
public:
|
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(const Voice& vox);
|
||||||
void keyOff();
|
void keyOff();
|
||||||
|
float advance(double dt, const Voice& vox);
|
||||||
float advance(double dt);
|
float advance(double dt);
|
||||||
bool isComplete() const {return m_phase == State::Complete;}
|
bool isComplete() const {return m_phase == State::Complete;}
|
||||||
bool isAdsrSet() const {return m_adsrSet;}
|
bool isAdsrSet() const {return m_adsrSet;}
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Voice;
|
||||||
class SoundMacroState
|
class SoundMacroState
|
||||||
{
|
{
|
||||||
friend class Voice;
|
friend class Voice;
|
||||||
|
friend class Envelope;
|
||||||
|
|
||||||
/** SoundMacro header */
|
/** SoundMacro header */
|
||||||
struct Header
|
struct Header
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Voice : public Entity
|
||||||
friend class Engine;
|
friend class Engine;
|
||||||
friend class Sequencer;
|
friend class Sequencer;
|
||||||
friend class SoundMacroState;
|
friend class SoundMacroState;
|
||||||
|
friend class Envelope;
|
||||||
int m_vid; /**< VoiceID of this voice instance */
|
int m_vid; /**< VoiceID of this voice instance */
|
||||||
bool m_emitter; /**< Voice is part of an Emitter */
|
bool m_emitter; /**< Voice is part of an Emitter */
|
||||||
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
Submix* m_submix = nullptr; /**< Submix this voice outputs to (or NULL for the main output mix) */
|
||||||
|
@ -71,7 +72,8 @@ class Voice : public Entity
|
||||||
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
bool m_sustainKeyOff = false; /**< Keyoff event occured while sustained */
|
||||||
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
uint8_t m_curAftertouch = 0; /**< Aftertouch value (key pressure when 'bottoming out') */
|
||||||
|
|
||||||
float m_userVol = 1.f; /**< User volume of voice */
|
float m_targetUserVol = 1.f; /**< Target user volume of voice (slewed to prevent audible aliasing) */
|
||||||
|
float m_curUserVol = 1.f; /**< Current user volume of voice */
|
||||||
float m_curVol = 1.f; /**< Current volume of voice */
|
float m_curVol = 1.f; /**< Current volume of voice */
|
||||||
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
|
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
|
||||||
float m_userPan = 0.f; /**< User pan of voice */
|
float m_userPan = 0.f; /**< User pan of voice */
|
||||||
|
|
151
lib/Envelope.cpp
151
lib/Envelope.cpp
|
@ -1,8 +1,26 @@
|
||||||
#include "amuse/Envelope.hpp"
|
#include "amuse/Envelope.hpp"
|
||||||
|
#include "amuse/Voice.hpp"
|
||||||
|
|
||||||
namespace amuse
|
namespace amuse
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static int32_t MIDItoTIME[104] =
|
||||||
|
{ /* [0..103] -> milliseconds */
|
||||||
|
0, 10, 20, 30, 40, 50, 60, 70,
|
||||||
|
80, 90, 100, 110, 110, 120, 130, 140,
|
||||||
|
150, 160, 170, 190, 200, 220, 230, 250,
|
||||||
|
270, 290, 310, 330, 350, 380, 410, 440,
|
||||||
|
470, 500, 540, 580, 620, 660, 710, 760,
|
||||||
|
820, 880, 940, 1000, 1000, 1100, 1200, 1300,
|
||||||
|
1400, 1500, 1600, 1700, 1800, 2000, 2100, 2300,
|
||||||
|
2400, 2600, 2800, 3000, 3200, 3500, 3700, 4000,
|
||||||
|
4300, 4600, 4900, 5300, 5700, 6100, 6500, 7000,
|
||||||
|
7500, 8100, 8600, 9300, 9900, 10000, 11000, 12000,
|
||||||
|
13000, 14000, 15000, 16000, 17000, 18000, 19000, 21000,
|
||||||
|
22000, 24000, 26000, 28000, 30000, 32000, 34000, 37000,
|
||||||
|
39000, 42000, 45000, 49000, 50000, 55000, 60000, 65000
|
||||||
|
};
|
||||||
|
|
||||||
void Envelope::reset(const ADSR* adsr)
|
void Envelope::reset(const ADSR* adsr)
|
||||||
{
|
{
|
||||||
m_phase = State::Attack;
|
m_phase = State::Attack;
|
||||||
|
@ -27,13 +45,23 @@ void Envelope::reset(const ADSRDLS* adsr, int8_t note, int8_t vel)
|
||||||
m_adsrSet = true;
|
m_adsrSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Envelope::keyOff(const Voice& vox)
|
||||||
|
{
|
||||||
|
double releaseTime = m_releaseTime;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
releaseTime = MIDItoTIME[clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiRelease)), 103)] / 1000.0;
|
||||||
|
|
||||||
|
m_phase = (releaseTime != 0.0) ? State::Release : State::Complete;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
void Envelope::keyOff()
|
void Envelope::keyOff()
|
||||||
{
|
{
|
||||||
m_phase = (m_releaseTime != 0.0) ? State::Release : State::Complete;
|
m_phase = (m_releaseTime != 0.0) ? State::Release : State::Complete;
|
||||||
m_curTime = 0.0;
|
m_curTime = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Envelope::advance(double dt)
|
float Envelope::advance(double dt, const Voice& vox)
|
||||||
{
|
{
|
||||||
double thisTime = m_curTime;
|
double thisTime = m_curTime;
|
||||||
m_curTime += dt;
|
m_curTime += dt;
|
||||||
|
@ -42,14 +70,18 @@ float Envelope::advance(double dt)
|
||||||
{
|
{
|
||||||
case State::Attack:
|
case State::Attack:
|
||||||
{
|
{
|
||||||
if (m_attackTime == 0.0)
|
double attackTime = m_attackTime;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
attackTime = MIDItoTIME[clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiAttack)), 103)] / 1000.0;
|
||||||
|
|
||||||
|
if (attackTime == 0.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Decay;
|
m_phase = State::Decay;
|
||||||
m_curTime = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = 1.f;
|
m_releaseStartFactor = 1.f;
|
||||||
return 1.f;
|
return 1.f;
|
||||||
}
|
}
|
||||||
double attackFac = thisTime / m_attackTime;
|
double attackFac = thisTime / attackTime;
|
||||||
if (attackFac >= 1.0)
|
if (attackFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Decay;
|
m_phase = State::Decay;
|
||||||
|
@ -62,22 +94,115 @@ float Envelope::advance(double dt)
|
||||||
}
|
}
|
||||||
case State::Decay:
|
case State::Decay:
|
||||||
{
|
{
|
||||||
if (m_decayTime == 0.0)
|
double decayTime = m_decayTime;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
decayTime = MIDItoTIME[clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiDecay)), 103)] / 1000.0;
|
||||||
|
|
||||||
|
double sustainFactor = m_sustainFactor;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
sustainFactor = clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiSustain)), 127) / 127.0;
|
||||||
|
|
||||||
|
if (decayTime == 0.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Sustain;
|
m_phase = State::Sustain;
|
||||||
m_curTime = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = m_sustainFactor;
|
m_releaseStartFactor = sustainFactor;
|
||||||
return m_sustainFactor;
|
return sustainFactor;
|
||||||
}
|
}
|
||||||
double decayFac = thisTime / m_decayTime;
|
double decayFac = thisTime / decayTime;
|
||||||
if (decayFac >= 1.0)
|
if (decayFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Sustain;
|
m_phase = State::Sustain;
|
||||||
m_curTime = 0.0;
|
m_curTime = 0.0;
|
||||||
m_releaseStartFactor = m_sustainFactor;
|
m_releaseStartFactor = sustainFactor;
|
||||||
return m_sustainFactor;
|
return sustainFactor;
|
||||||
}
|
}
|
||||||
m_releaseStartFactor = (1.0 - decayFac) + decayFac * m_sustainFactor;
|
m_releaseStartFactor = (1.0 - decayFac) + decayFac * sustainFactor;
|
||||||
|
return m_releaseStartFactor;
|
||||||
|
}
|
||||||
|
case State::Sustain:
|
||||||
|
{
|
||||||
|
double sustainFactor = m_sustainFactor;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
sustainFactor = clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiSustain)), 127) / 127.0;
|
||||||
|
|
||||||
|
return sustainFactor;
|
||||||
|
}
|
||||||
|
case State::Release:
|
||||||
|
{
|
||||||
|
double releaseTime = m_releaseTime;
|
||||||
|
if (vox.m_state.m_useAdsrControllers)
|
||||||
|
releaseTime = MIDItoTIME[clamp(0, int(vox.getCtrlValue(vox.m_state.m_midiRelease)), 103)] / 1000.0;
|
||||||
|
|
||||||
|
if (releaseTime == 0.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Complete;
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
double releaseFac = thisTime / releaseTime;
|
||||||
|
if (releaseFac >= 1.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Complete;
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
return std::min(m_releaseStartFactor, 1.0 - releaseFac);
|
||||||
|
}
|
||||||
|
case State::Complete:
|
||||||
|
default:
|
||||||
|
return 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Envelope::advance(double dt)
|
||||||
|
{
|
||||||
|
double thisTime = m_curTime;
|
||||||
|
m_curTime += dt;
|
||||||
|
|
||||||
|
switch (m_phase)
|
||||||
|
{
|
||||||
|
case State::Attack:
|
||||||
|
{
|
||||||
|
double attackTime = m_attackTime;
|
||||||
|
|
||||||
|
if (attackTime == 0.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Decay;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
m_releaseStartFactor = 1.f;
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
double attackFac = thisTime / attackTime;
|
||||||
|
if (attackFac >= 1.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Decay;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
m_releaseStartFactor = 1.f;
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
|
m_releaseStartFactor = attackFac;
|
||||||
|
return attackFac;
|
||||||
|
}
|
||||||
|
case State::Decay:
|
||||||
|
{
|
||||||
|
double decayTime = m_decayTime;
|
||||||
|
double sustainFactor = m_sustainFactor;
|
||||||
|
|
||||||
|
if (decayTime == 0.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Sustain;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
m_releaseStartFactor = sustainFactor;
|
||||||
|
return sustainFactor;
|
||||||
|
}
|
||||||
|
double decayFac = thisTime / decayTime;
|
||||||
|
if (decayFac >= 1.0)
|
||||||
|
{
|
||||||
|
m_phase = State::Sustain;
|
||||||
|
m_curTime = 0.0;
|
||||||
|
m_releaseStartFactor = sustainFactor;
|
||||||
|
return sustainFactor;
|
||||||
|
}
|
||||||
|
m_releaseStartFactor = (1.0 - decayFac) + decayFac * sustainFactor;
|
||||||
return m_releaseStartFactor;
|
return m_releaseStartFactor;
|
||||||
}
|
}
|
||||||
case State::Sustain:
|
case State::Sustain:
|
||||||
|
@ -86,12 +211,14 @@ float Envelope::advance(double dt)
|
||||||
}
|
}
|
||||||
case State::Release:
|
case State::Release:
|
||||||
{
|
{
|
||||||
if (m_releaseTime == 0.0)
|
double releaseTime = m_releaseTime;
|
||||||
|
|
||||||
|
if (releaseTime == 0.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
double releaseFac = thisTime / m_releaseTime;
|
double releaseFac = thisTime / releaseTime;
|
||||||
if (releaseFac >= 1.0)
|
if (releaseFac >= 1.0)
|
||||||
{
|
{
|
||||||
m_phase = State::Complete;
|
m_phase = State::Complete;
|
||||||
|
|
|
@ -96,6 +96,8 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
|
||||||
|
|
||||||
m_curVol = m_setup.volume / 127.f;
|
m_curVol = m_setup.volume / 127.f;
|
||||||
m_curPan = m_setup.panning / 64.f - 1.f;
|
m_curPan = m_setup.panning / 64.f - 1.f;
|
||||||
|
m_ctrlVals[7] = 127;
|
||||||
|
m_ctrlVals[10] = 64;
|
||||||
m_ctrlVals[0x5b] = m_setup.reverb;
|
m_ctrlVals[0x5b] = m_setup.reverb;
|
||||||
m_ctrlVals[0x5d] = m_setup.chorus;
|
m_ctrlVals[0x5d] = m_setup.chorus;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,14 +100,14 @@ void Voice::_doKeyOff()
|
||||||
{
|
{
|
||||||
if (m_state.m_inWait && m_state.m_keyoffWait)
|
if (m_state.m_inWait && m_state.m_keyoffWait)
|
||||||
{
|
{
|
||||||
if (m_volAdsr.isAdsrSet())
|
if (m_volAdsr.isAdsrSet() || m_state.m_useAdsrControllers)
|
||||||
m_volAdsr.keyOff();
|
m_volAdsr.keyOff(*this);
|
||||||
if (m_pitchAdsr.isAdsrSet())
|
if (m_pitchAdsr.isAdsrSet())
|
||||||
m_pitchAdsr.keyOff();
|
m_pitchAdsr.keyOff();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_volAdsr.keyOff();
|
m_volAdsr.keyOff(*this);
|
||||||
m_pitchAdsr.keyOff();
|
m_pitchAdsr.keyOff();
|
||||||
}
|
}
|
||||||
m_state.keyoffNotify(*this);
|
m_state.keyoffNotify(*this);
|
||||||
|
@ -289,10 +289,39 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
|
||||||
if (m_state.m_pitchWheelSel)
|
if (m_state.m_pitchWheelSel)
|
||||||
setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state) / 127.f);
|
setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state) / 127.f);
|
||||||
|
|
||||||
|
/* Process user volume slew */
|
||||||
|
if (m_engine.m_ampMode == AmplitudeMode::PerSample)
|
||||||
|
{
|
||||||
|
if (m_targetUserVol != m_curUserVol)
|
||||||
|
{
|
||||||
|
float samplesPer5Ms = m_sampleRate * 5.f / 1000.f;
|
||||||
|
if (samplesPer5Ms > 1.f)
|
||||||
|
{
|
||||||
|
float adjRate = 1.f / samplesPer5Ms;
|
||||||
|
if (m_targetUserVol < m_curUserVol)
|
||||||
|
{
|
||||||
|
m_curUserVol -= adjRate;
|
||||||
|
if (m_targetUserVol > m_curUserVol)
|
||||||
|
m_curUserVol = m_targetUserVol;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_curUserVol += adjRate;
|
||||||
|
if (m_targetUserVol < m_curUserVol)
|
||||||
|
m_curUserVol = m_targetUserVol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_curUserVol = m_targetUserVol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_curUserVol = m_targetUserVol;
|
||||||
|
|
||||||
/* Factor in ADSR envelope state */
|
/* Factor in ADSR envelope state */
|
||||||
float adsr = m_volAdsr.advance(dt);
|
float adsr = m_volAdsr.advance(dt, *this);
|
||||||
m_lastLevel = m_nextLevel;
|
m_lastLevel = m_nextLevel;
|
||||||
m_nextLevel = m_userVol * evalVol * adsr * (m_state.m_curVel / 127.f);
|
m_nextLevel = m_curUserVol * evalVol * 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))
|
||||||
|
@ -585,6 +614,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
|
||||||
m_voxState = VoiceState::Dead;
|
m_voxState = VoiceState::Dead;
|
||||||
m_backendVoice->stop();
|
m_backendVoice->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,7 +636,7 @@ std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep,
|
||||||
_destroyVoice(vox);
|
_destroyVoice(vox);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
(*vox)->setVolume(m_userVol);
|
(*vox)->setVolume(m_targetUserVol);
|
||||||
(*vox)->setPan(m_userPan);
|
(*vox)->setPan(m_userPan);
|
||||||
(*vox)->setSurroundPan(m_userSpan);
|
(*vox)->setSurroundPan(m_userSpan);
|
||||||
return *vox;
|
return *vox;
|
||||||
|
@ -821,7 +851,7 @@ void Voice::stopSample()
|
||||||
|
|
||||||
void Voice::setVolume(float vol)
|
void Voice::setVolume(float vol)
|
||||||
{
|
{
|
||||||
m_userVol = clamp(0.f, vol, 1.f);
|
m_targetUserVol = clamp(0.f, vol, 1.f);
|
||||||
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
for (std::shared_ptr<Voice>& vox : m_childVoices)
|
||||||
vox->setVolume(vol);
|
vox->setVolume(vol);
|
||||||
}
|
}
|
||||||
|
@ -1000,7 +1030,7 @@ void Voice::setAdsr(ObjectId adsrId, bool dls)
|
||||||
{
|
{
|
||||||
m_volAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
|
m_volAdsr.reset(adsr, m_state.m_initKey, m_state.m_initVel);
|
||||||
if (m_voxState == VoiceState::KeyOff)
|
if (m_voxState == VoiceState::KeyOff)
|
||||||
m_volAdsr.keyOff();
|
m_volAdsr.keyOff(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1010,7 +1040,7 @@ void Voice::setAdsr(ObjectId adsrId, bool dls)
|
||||||
{
|
{
|
||||||
m_volAdsr.reset(adsr);
|
m_volAdsr.reset(adsr);
|
||||||
if (m_voxState == VoiceState::KeyOff)
|
if (m_voxState == VoiceState::KeyOff)
|
||||||
m_volAdsr.keyOff();
|
m_volAdsr.keyOff(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue