mirror of
https://codeberg.org/armin/chromaflock.git
synced 2026-09-01 04:10:47 +02:00
87 lines
2.4 KiB
C
87 lines
2.4 KiB
C
|
|
#pragma once
|
||
|
|
#include <cmath>
|
||
|
|
#include <algorithm>
|
||
|
|
|
||
|
|
enum class ADSRStage { Idle = 0, Attack, Decay, Sustain, Release };
|
||
|
|
|
||
|
|
class ADSREnvelope {
|
||
|
|
public:
|
||
|
|
void prepare(double sr) {
|
||
|
|
sampleRate = sr;
|
||
|
|
stage = ADSRStage::Idle;
|
||
|
|
level = 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
void setAttack(float a) { attackTime = std::max(a, 0.001f); }
|
||
|
|
void setDecay(float d) { decayTime = std::max(d, 0.001f); }
|
||
|
|
void setSustain(float s) { sustainLevel = std::clamp(s, 0.0f, 1.0f); }
|
||
|
|
void setRelease(float r) { releaseTime = std::max(r, 0.001f); }
|
||
|
|
|
||
|
|
void noteOn() {
|
||
|
|
stage = ADSRStage::Attack;
|
||
|
|
level = 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
void noteOff() {
|
||
|
|
if (stage != ADSRStage::Idle)
|
||
|
|
stage = ADSRStage::Release;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool isActive() const { return stage != ADSRStage::Idle; }
|
||
|
|
|
||
|
|
float getNextSample() {
|
||
|
|
switch (stage) {
|
||
|
|
case ADSRStage::Idle:
|
||
|
|
level = 0.0f;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ADSRStage::Attack: {
|
||
|
|
float attackInc = 1.0f / (attackTime * static_cast<float>(sampleRate));
|
||
|
|
level += attackInc;
|
||
|
|
if (level >= 1.0f) {
|
||
|
|
level = 1.0f;
|
||
|
|
stage = ADSRStage::Decay;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case ADSRStage::Decay: {
|
||
|
|
float decayInc = (1.0f - sustainLevel) / (decayTime * static_cast<float>(sampleRate));
|
||
|
|
level -= decayInc;
|
||
|
|
if (level <= sustainLevel) {
|
||
|
|
level = sustainLevel;
|
||
|
|
stage = ADSRStage::Sustain;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case ADSRStage::Sustain:
|
||
|
|
level = sustainLevel;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case ADSRStage::Release: {
|
||
|
|
float releaseInc = level / (releaseTime * static_cast<float>(sampleRate));
|
||
|
|
level -= releaseInc;
|
||
|
|
if (level <= 0.0f) {
|
||
|
|
level = 0.0f;
|
||
|
|
stage = ADSRStage::Idle;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return level;
|
||
|
|
}
|
||
|
|
|
||
|
|
float getCurrentLevel() const { return level; }
|
||
|
|
ADSRStage getCurrentStage() const { return stage; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
double sampleRate = 44100.0;
|
||
|
|
float attackTime = 0.01f;
|
||
|
|
float decayTime = 0.3f;
|
||
|
|
float sustainLevel = 0.7f;
|
||
|
|
float releaseTime = 0.5f;
|
||
|
|
float level = 0.0f;
|
||
|
|
ADSRStage stage = ADSRStage::Idle;
|
||
|
|
};
|