ADSR envelopes

An ADSR (attack, decay, sustain, release) envelope is used to shape a note. Typically it is used to control the gain of the note, but can also be used to control amount of brightness, bass, distortion, tremolo, vibrato, or anything else that varies over the course of a note.

There are shorter and longer variations of an ADSR. The AR envelope is often used for percussive sounds. The ASR envelope is used when there is no transient at the start of a note. The DADSR envelope has a delay stage at the beginning, and is used for parameters that don’t begin immediately at the start of a note (vibrato typically begins a little after the start of the note). There can be envelopes with more stages, and many hardware synths have envelopes of 8 or more stages.

Implementation

To implement an ADSR envelope, one has to keep track it’s state. Then the envelope can do the appropriate thing for the state.

  • Attack: during this state the envelope level will rise from 0.0 to 1.0 over the attack time. The envelope enters this state when the note starts. When the level reaches 1.0, the state will switch to decay.
  • Decay: during this state the envelope level will drop from 1.0 to the sustain level over the decay time. When the envelope reaches the sustain level, the state will switch to sustain.
  • Sustain: during this state the envelope level will be the sustain level. The envelope will stay in this state until the note ends (the key is lifted).
  • Release: during this state the envelope level will drop from it’s current level to 0.0 over the release time. The envelope enters this state when the note stops (this can be during the attack, decay or sustain state).
  • Off: This state is entered from the release state when the envelope level reaches 0.0. During this state, the envelope level stays at 0.0.

The code for an envelope which responds to MIDI velocity (1 to 127, note on, 0 is note off) could look like this. Notice what conditions cause the envelope to change from one state to another. Also notice that release can be entered from any state. This code should be improved in order to avoid divide by zero errors.

enum envState {
    kAttack,
    kDecay,
    kSustain,
    kRelease,
    kOff
};

long state = kOff;
float increment;
float envelopeLevel;
float samplerate = 44100;


float ADSR(float MIDIvelocity, float attacktime, float decaytime, float sustainlevel, float releasetime)
{
    switch(state)
    {
    case kOff:
        envelopeLevel = 0.0;
        if(MIDIvelocity > 0)
        {
            increment = (1.0 - envelopeLevel)/(attacktime * samplerate);
            state = kAttack;
        }
        break;
    case kAttack:
        if(envelopeLevel >= 1.0)
        {
            increment = (sustainLevel - envelopeLevel)/(decaytime * samplerate);
            state = kDecay;
        }
        break;
    case kDecay:
        if(envelopeLevel <= sustainlevel)
        {
            increment = 0.0;
            state = kSustain;
        }
    case kRelease:
        if(envelopeLevel <= 0.0)
        {
            envelopeLevel = 0.0;
            state = kOff;
        }
    }
    envelopeLevel = envelopeLevel + increment;
    if(MIDIvelocity = 0 && (state != kOff || state != kRelease)
    {
        increment = envelopeLevel/(releasetime * samplerate);
        state = kRelease;
    }
    // use MIDI velocity to change volume of envelope
    return(envelopeLevel * MIDIvelocity/127.0);  
}

Leave a Reply

Your email address will not be published.