An effects chain
Several processors wired into one graph — and what the wires mean.
Anyone whose patch has outgrown a single processor.
An effects chain is easier to understand when each processor has one job. Here the input passes through drive, filtering, delay and tremolo. The graph describes those connections; each processor still owns its own calculations and memory.
Read the graph at the bottom first. It is a map of the patch. Then return to the processor definitions to inspect the stages that interest you.
Controls exposed by the graph#
| Parameter | Default | Range | Destination |
|---|---|---|---|
drive | 2.0 | 1–12 | dist.amount: gain into the soft clipper. |
cutoff | 2,400 Hz | 60–7,500 Hz | flt.cutoff: filter frequency. |
resonance | 0.45 | 0–0.95 | flt.resonance: emphasis near cutoff. |
echo | 0.3 | 0–1 | dly.mix: amount of delayed audio. |
wobble | 0.35 | 0–1 | trem.depth: tremolo intensity. |
The graph's in and out are mono. Unconnected internal parameters keep
their defaults, so you can expose a small set of useful controls without
copying every internal knob to the host.
The complete patch#
processor Drive
{
input stream float in;
param float amount = 2.0f [1.0f, 12.0f] smooth 20.0f;
output stream float out;
void main()
{
loop
{
let x = in * amount;
let c = x > 1.0f ? 1.0f : (x < -1.0f ? -1.0f : x);
let odd = c * 1.5f - c * c * c * 0.5f;
out <- (odd + c * c * 0.12f) * 0.7f;
advance();
}
}
}
processor Filter
{
input stream float in;
param float cutoff = 1200.0f [60.0f, 7500.0f] smooth 15.0f;
param float resonance = 0.6f [0.0f, 0.95f];
output stream float out;
float lp = 0.0f;
float bp = 0.0f;
void main()
{
let sr = processor.frequency;
loop
{
let fc = 6.2831853f * min(cutoff, sr * 0.18f) / sr;
let q = 1.02f - resonance;
lp = lp + fc * bp;
let hp = in - lp - q * bp;
bp = bp + fc * hp;
out <- lp;
advance();
}
}
}
processor Delay
{
input stream float in;
param float time = 0.25f [0.02f, 0.33f];
param float feedback = 0.4f [0.0f, 0.85f];
param float mix = 0.3f [0.0f, 1.0f] smooth 30.0f;
output stream float out;
float buf[32768];
int pos = 0;
void main()
{
let sr = processor.frequency;
loop
{
let d = clamp(int(time * sr), 1, 32767);
let readPos = pos - d;
let echo = buf[readPos];
buf[pos] = in + echo * feedback;
pos = pos + 1;
out <- in * (1.0f - mix) + echo * mix;
advance();
}
}
}
processor Tremolo
{
input stream float in;
param float rate = 4.5f [0.05f, 20.0f];
param float depth = 0.5f [0.0f, 1.0f] smooth 25.0f;
output stream float out;
float phase = 0.0f;
void main()
{
let sr = processor.frequency;
loop
{
let step = rate / sr;
let p = phase + step;
phase = p > 1.0f ? p - 1.0f : p;
let lfo = 0.5f + 0.5f * sin(6.2831853f * phase);
out <- in * (1.0f - depth + depth * lfo) * 0.7f;
advance();
}
}
}
graph FxChain
{
input stream float in;
param float drive = 2.0f [1.0f, 12.0f];
param float cutoff = 2400.0f [60.0f, 7500.0f];
param float resonance = 0.45f [0.0f, 0.95f];
param float echo = 0.3f [0.0f, 1.0f];
param float wobble = 0.35f [0.0f, 1.0f];
output stream float out;
node dist = Drive;
node flt = Filter;
node dly = Delay;
node trem = Tremolo;
connection
{
in -> dist.in;
dist.out -> flt.in;
flt.out -> dly.in;
dly.out -> trem.in;
trem.out -> out;
drive -> dist.amount;
cutoff -> flt.cutoff;
resonance -> flt.resonance;
echo -> dly.mix;
wobble -> trem.depth;
}
}Walk through the graph#
in -> dist.in sends the incoming sample to the drive stage. Each subsequent
wire passes one stage's output into the next. The last wire, trem.out -> out,
returns the result to the host. Pole schedules the stages from these wires;
the order of the node declarations is not the execution order.
The other wires carry controls. echo -> dly.mix connects the graph's friendly
public name to the delay's internal parameter. It does not create another
audio path. The graph is the public interface, while its nodes are the
implementation.
Try changing the order#
Move the tremolo before the delay by rewriting the audio connections. After the delay, tremolo changes the loudness of both the source and its existing echoes together. Before the delay, the echoes repeat the loudness changes already recorded in the buffer. The same four stages make a different effect because the signal reaches them in a different order.
If the result is too loud: remember that gains multiply in a serial chain, while multiple wires entering one input add. Leave headroom between stages, especially around resonance and feedback.
Next: graphs covers the rules this file demonstrates.