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;
}
}
}