using System; using System.Linq; namespace NAudio.Dsp { // C# ADSR based on work by Nigel Redmon, EarLevel Engineering: earlevel.com // http://www.earlevel.com/main/2013/06/03/envelope-generators-adsr-code/ /// /// Envelope generator (ADSR) /// public class EnvelopeGenerator { private EnvelopeState state; private float output; private float attackRate; private float decayRate; private float releaseRate; private float attackCoef; private float decayCoef; private float releaseCoef; private float sustainLevel; private float targetRatioAttack; private float targetRatioDecayRelease; private float attackBase; private float decayBase; private float releaseBase; /// /// Envelope State /// public enum EnvelopeState { /// /// Idle /// Idle = 0, /// /// Attack /// Attack, /// /// Decay /// Decay, /// /// Sustain /// Sustain, /// /// Release /// Release }; /// /// Creates and Initializes an Envelope Generator /// public EnvelopeGenerator() { Reset(); AttackRate = 0; DecayRate = 0; ReleaseRate = 0; SustainLevel = 1.0f; SetTargetRatioAttack(0.3f); SetTargetRatioDecayRelease(0.0001f); } /// /// Attack Rate (seconds * SamplesPerSecond) /// public float AttackRate { get { return attackRate; } set { attackRate = value; attackCoef = CalcCoef(value, targetRatioAttack); attackBase = (1.0f + targetRatioAttack) * (1.0f - attackCoef); } } /// /// Decay Rate (seconds * SamplesPerSecond) /// public float DecayRate { get { return decayRate; } set { decayRate = value; decayCoef = CalcCoef(value, targetRatioDecayRelease); decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef); } } /// /// Release Rate (seconds * SamplesPerSecond) /// public float ReleaseRate { get { return releaseRate; } set { releaseRate = value; releaseCoef = CalcCoef(value, targetRatioDecayRelease); releaseBase = -targetRatioDecayRelease * (1.0f - releaseCoef); } } private static float CalcCoef(float rate, float targetRatio) { return (float)Math.Exp(-Math.Log((1.0f + targetRatio) / targetRatio) / rate); } /// /// Sustain Level (1 = 100%) /// public float SustainLevel { get { return sustainLevel; } set { sustainLevel = value; decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef); } } /// /// Sets the attack curve /// void SetTargetRatioAttack(float targetRatio) { if (targetRatio < 0.000000001f) targetRatio = 0.000000001f; // -180 dB targetRatioAttack = targetRatio; attackBase = (1.0f + targetRatioAttack) * (1.0f - attackCoef); } /// /// Sets the decay release curve /// void SetTargetRatioDecayRelease(float targetRatio) { if (targetRatio < 0.000000001f) targetRatio = 0.000000001f; // -180 dB targetRatioDecayRelease = targetRatio; decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef); releaseBase = -targetRatioDecayRelease * (1.0f - releaseCoef); } /// /// Read the next volume multiplier from the envelope generator /// /// A volume multiplier public float Process() { switch (state) { case EnvelopeState.Idle: break; case EnvelopeState.Attack: output = attackBase + output * attackCoef; if (output >= 1.0f) { output = 1.0f; state = EnvelopeState.Decay; } break; case EnvelopeState.Decay: output = decayBase + output * decayCoef; if (output <= sustainLevel) { output = sustainLevel; state = EnvelopeState.Sustain; } break; case EnvelopeState.Sustain: break; case EnvelopeState.Release: output = releaseBase + output * releaseCoef; if (output <= 0.0) { output = 0.0f; state = EnvelopeState.Idle; } break; } return output; } /// /// Trigger the gate /// /// If true, enter attack phase, if false enter release phase (unless already idle) public void Gate(bool gate) { if (gate) state = EnvelopeState.Attack; else if (state != EnvelopeState.Idle) state = EnvelopeState.Release; } /// /// Current envelope state /// public EnvelopeState State { get { return state; } } /// /// Reset to idle state /// public void Reset() { state = EnvelopeState.Idle; output = 0.0f; } /// /// Get the current output level /// public float GetOutput() { return output; } } }