EnvelopeBlock

ADSR envelope generator for amplitude and parameter modulation.

Overview

EnvelopeBlock generates time-varying control signals using the classic ADSR (Attack-Decay-Sustain-Release) model. It produces output values from 0 to 1 that can be used to shape amplitude, filter cutoff, or any other modulatable parameter.

Mathematical Foundation

The ADSR Model

An envelope is a time-varying amplitude contour that shapes how a sound evolves. The ADSR model breaks this into four stages:

  1. Attack: Signal rises from 0 to peak (1.0)
  2. Decay: Signal falls from peak to sustain level
  3. Sustain: Signal holds at a fixed level while key is held
  4. Release: Signal falls from current level to 0 when key is released
Level
  ^
  |    /\
  |   /  \______ Sustain
  |  /          \
  | /            \
  |/              \______
  +--A--D----S----R--> Time

State Machine

The envelope progresses through discrete states:

    ┌─────────────────────────────────────────────────┐
    │                                                 │
    ▼                                                 │
  IDLE ──note_on()──► ATTACK ──► DECAY ──► SUSTAIN ──┤
    ▲                                        │        │
    │                                   note_off()    │
    │                                        │        │
    └─────────────── RELEASE ◄───────────────┘        │
                        │                             │
                        └─────── (level < floor) ─────┘

Linear Ramp Equations

This implementation uses linear ramps for simplicity and predictability.

Attack Stage:

$$ L(t) = \frac{t}{t_A} $$

where $t_A$ is the attack time and $t$ is time since note-on.

Decay Stage:

$$ L(t) = 1 - (1 - S) \cdot \frac{t}{t_D} $$

where $S$ is the sustain level and $t_D$ is decay time.

Sustain Stage:

$$ L(t) = S $$

The level holds constant at the sustain value.

Release Stage:

$$ L(t) = L_r \cdot \left(1 - \frac{t}{t_R}\right) $$

where $L_r$ is the level when release started (captured at note-off) and $t_R$ is release time.

Linear vs Exponential Curves

Linear curves (used here):

  • Constant rate of change: $\frac{dL}{dt} = \text{const}$
  • Predictable timing
  • Sound transitions can feel abrupt at the endpoints

Exponential curves (alternative approach): $$ L(t) = L_{target} + (L_{start} - L_{target}) \cdot e^{-t/\tau} $$

  • Natural decay behavior (like capacitor discharge)
  • Sound transitions feel smoother
  • Asymptotic approach—never truly reaches target

Envelope Floor

The implementation uses a floor threshold of $10^{-6}$ (~-120 dB) to detect when release is effectively complete:

$$ \text{if } L < 10^{-6} \text{ then } L \leftarrow 0, \text{ state} \leftarrow \text{Idle} $$

This prevents floating-point precision issues from keeping the envelope in release state indefinitely.

Creating an Envelope

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::EnvelopeBlock, graph::GraphBuilder};

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);

// Parameters: attack(s), decay(s), sustain(0-1), release(s)
let env = builder.add(EnvelopeBlock::new(0.01, 0.2, 0.7, 0.3));
}

Port Layout

PortDirectionDescription
0OutputEnvelope value (0.0 to 1.0)

Parameters

ParameterTypeRangeDefaultDescription
attackf640.001 - 10.0 s0.01Time to reach peak
decayf640.001 - 10.0 s0.1Time to reach sustain
sustainf640.0 - 1.00.5Hold level (fraction of peak)
releasef640.001 - 10.0 s0.3Time to reach zero

Time values are clamped to [0.001, 10.0] seconds to prevent numerical issues.

Typical Settings

Sound TypeAttackDecaySustainRelease
Pluck/Stab0.0010.10.00.2
Piano0.0010.50.30.5
Pad0.50.20.81.0
Organ0.0010.01.00.1
Brass0.050.10.80.2

Usage Examples

Amplitude Envelope with VCA

The typical pattern for envelope-controlled amplitude uses a VCA:

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::{EnvelopeBlock, OscillatorBlock, VcaBlock}, graph::GraphBuilder, waveform::Waveform};

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);

let env = builder.add(EnvelopeBlock::new(0.01, 0.1, 0.7, 0.3));
let osc = builder.add(OscillatorBlock::new(440.0, Waveform::Sine, None));
let vca = builder.add(VcaBlock::new());

// Audio to VCA input 0, envelope to VCA input 1
builder.connect(osc, 0, vca, 0);
builder.connect(env, 0, vca, 1);
}

Pluck Sound (Short Decay)

#![allow(unused)]
fn main() {
let env = builder.add(EnvelopeBlock::new(
    0.001,  // Very fast attack
    0.2,    // Medium decay
    0.0,    // No sustain
    0.1,    // Short release
));
}

Pad Sound (Long Attack)

#![allow(unused)]
fn main() {
let env = builder.add(EnvelopeBlock::new(
    0.5,    // Slow attack
    0.3,    // Medium decay
    0.8,    // High sustain
    1.0,    // Long release
));
}

Percussive (No Sustain)

#![allow(unused)]
fn main() {
let env = builder.add(EnvelopeBlock::new(
    0.002,  // Instant attack
    0.5,    // Decay only
    0.0,    // No sustain
    0.0,    // No release
));
}

Filter Envelope

#![allow(unused)]
fn main() {
use bbx_dsp::{blocks::{EnvelopeBlock, LowPassFilterBlock, OscillatorBlock}, graph::GraphBuilder, waveform::Waveform};

let mut builder = GraphBuilder::<f32>::new(44100.0, 512, 2);

let osc = builder.add(OscillatorBlock::new(440.0, Waveform::Saw, None));
let filter = builder.add(LowPassFilterBlock::new(500.0, 2.0));
let env = builder.add(EnvelopeBlock::new(0.01, 0.3, 0.2, 0.5));

builder.connect(osc, 0, filter, 0);
builder.modulate(env, filter, "cutoff");
}

Control Methods

The envelope provides methods for note control:

#![allow(unused)]
fn main() {
// Trigger the envelope (start attack phase)
env.note_on();

// Release the envelope (start release phase from current level)
env.note_off();

// Reset to idle state immediately
env.reset();
}

Implementation Notes

  • Output range: 0.0 to 1.0
  • Linear ramps for all stages
  • Times clamped to [0.001, 10.0] seconds
  • Envelope floor at $10^{-6}$ for reliable release termination
  • Retrigger behavior: calling note_on() restarts attack from current level

Further Reading

  • Roads, C. (1996). The Computer Music Tutorial, Chapter 4: Synthesis. MIT Press.
  • Puckette, M. (2007). Theory and Techniques of Electronic Music, Chapter 4. World Scientific.
  • Smith, J.O. (2010). Physical Audio Signal Processing, Appendix E: Envelope Generators. online