Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Envelopes & Modulators

Modulation sources shape how parameters change over time, creating movement and expression.

ADSR Envelope

The classic Attack-Decay-Sustain-Release envelope generator.

let env = patch.add("env", Adsr::new(44100.0));

Inputs

PortSignalRangeDescription
gateGate0/5VGate on/off
retrigTrigger0/5VRetrigger (restarts attack from the current level)
attackUnipolar CV0-10VAttack time, default 0.1
decayUnipolar CV0-10VDecay time (true segment duration), default 0.3
sustainUnipolar CV0-10VSustain level, default 0.7
releaseUnipolar CV0-10VRelease time (true segment duration), default 0.4
shapeGate0/5V0V = linear ramps, high = exponential one-pole

Outputs

PortSignalDescription
envUnipolar CVEnvelope output (0-10V)
invUnipolar CVInverted envelope
eocTriggerEnd-of-cycle trigger

decay and release are true segment durations — the envelope traverses its span in the set time regardless of the sustain level. The shape input toggles between linear and exponential curves.

Envelope Stages

Level
  5V ┤    ╱╲
     │   ╱  ╲____
     │  ╱        ╲
     │ ╱          ╲
  0V ┼╱────────────╲────
     A    D   S    R

Timing Curves

All stages use exponential curves:

Attack: $$v(t) = 5 \cdot (1 - e^{-t/\tau_a})$$

Decay/Release: $$v(t) = (v_{start} - v_{end}) \cdot e^{-t/\tau} + v_{end}$$

Typical Settings

SoundAttackDecaySustainRelease
Pluck5ms200ms0%100ms
Pad1s500ms80%2s
Brass50ms100ms70%200ms
Perc1ms50ms0%50ms

Envelope Follower

Extracts the amplitude envelope of an audio signal, with adjustable attack/release ballistics. type_id: envelope_follower.

let follower = patch.add("follow", EnvelopeFollower::new(44100.0));

Inputs

PortSignalRangeDescription
inAudio±5VAudio input
attackUnipolar CV0-10VDetector attack time, default 0.2
releaseUnipolar CV0-10VDetector release time, default 0.3
gainUnipolar CV0-10VOutput gain (×4), default 0.5

Outputs

PortSignalDescription
outUnipolar CVAmplitude envelope (0-10V)
invUnipolar CVInverted envelope

LFO (Low-Frequency Oscillator)

See Oscillators for full documentation.

Quick reference:

let lfo = patch.add("lfo", Lfo::new(44100.0));
patch.connect(lfo.out("sin"), vcf.in_("fm"))?;

Sample and Hold

Captures input value on trigger pulse.

let sh = patch.add("sh", SampleAndHold::new());

Inputs

PortSignalDescription
inCV/AudioSignal to sample
triggerTriggerWhen to sample

Output

PortSignalDescription
outCVHeld value

Classic Use: Random CV

// Random stepped modulation
patch.connect(noise.out("white"), sh.in_("in"))?;
patch.connect(clock.out("div_8"), sh.in_("trigger"))?;
patch.connect(sh.out("out"), vcf.in_("cutoff"))?;

Slew Limiter

Limits rate of change—creates portamento and smoothing.

let slew = patch.add("slew", SlewLimiter::new(44100.0));

Inputs

PortSignalDescription
inCVInput signal
riseUnipolar CVRise time (upward slew)
fallUnipolar CVFall time (downward slew)

Output

PortSignalDescription
outCVSlewed output

Applications

flowchart LR
    subgraph "Portamento"
        P1[Pitch CV] --> SLEW1[Slew] --> VCO1[VCO]
    end

    subgraph "Envelope Follower"
        P2[Audio] --> RECT[Rectify] --> SLEW2[Slew]
    end

    subgraph "Smooth Random"
        P3[S&H] --> SLEW3[Slew] --> MOD[Smooth CV]
    end

Quantizer

Snaps a V/Oct input to the nearest degree of a fixed scale. The scale is chosen at construction (not a port). type_id: quantizer.

let quant = patch.add("quant", Quantizer::major());
// Also: Quantizer::new(Scale::Dorian), Quantizer::chromatic(), Quantizer::minor()

Input

PortSignalDescription
inV/OctUnquantized pitch

Output

PortSignalDescription
outV/OctQuantized pitch

Available Scales

Scale: Chromatic, Major, Minor, PentatonicMajor, PentatonicMinor, Dorian, Mixolydian, Blues. Change at runtime with quantizer.set_scale(Scale::Minor).


Scale Quantizer

A quantizer with CV-selectable root and scale, boundary hysteresis, a note-change trigger, and optional microtuning. type_id: scale_quantizer.

let sq = patch.add("sq", ScaleQuantizer::new(44100.0));

Inputs

PortSignalRangeDescription
inV/Oct±5VPitch to quantize
rootUnipolar CV0-10VRoot note (0–11 semitones)
scaleUnipolar CV0-10VScale select (7 built-in scales)

Outputs

PortSignalDescription
outV/OctQuantized pitch
triggerTriggerFires on a committed note change

Microtuning (with the alloc feature)

// Install a custom scale from cents offsets within an octave:
sq_module.set_custom_scale(&[0.0, 200.0, 350.0, 700.0, 900.0]);

// Or load a Scala .scl file body:
sq_module.load_scala(scl_source)?;

Clock

Master timing generator.

let clock = patch.add("clock", Clock::new(44100.0));

Inputs

PortSignalDescription
tempoUnipolar CVBPM (0-10V = 20-300 BPM)
resetTriggerReset to beat 1

Outputs

PortSignalDescription
div_1TriggerWhole notes
div_2TriggerHalf notes
div_4TriggerQuarter notes
div_8TriggerEighth notes
div_16TriggerSixteenth notes
div_32Trigger32nd notes

Step Sequencer

8-step CV/gate sequencer.

let seq = patch.add("seq", StepSequencer::new());

Inputs

PortSignalDescription
clockTriggerAdvance to next step
resetTriggerReturn to step 1

Outputs

PortSignalDescription
cvV/OctStep CV value
gateGateStep gate state

Programming Steps

The sequencer holds 8 CV/gate pairs. In a full application, you’d set these via UI or MIDI.