#pragma once #include #include 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(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(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(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; };