8.12.8. STRUDEL-08 — Effects & Filters
Strudel splits effects into two groups, distinguished by where they live in the signal graph (the same table appears in tutorial 07):
Scope |
Setters |
|---|---|
Per-voice (independent per note) |
|
Per-orbit (one shared bus per orbit) |
|
This split lets you give each layer of a stack its own reverb and delay character (per-orbit), while still personalising every note’s filter and modulation (per-voice). Every per-voice setter also has a pattern-valued overload — feed it a signal to make the parameter move over time; see tutorial 09.
8.12.8.1. Part A: per-voice filters — lpf and hpf
lpf(freq) cuts harmonics above freq Hz; hpf(freq) cuts below.
Compare a muffled sawtooth (lpf 200) against a thin one
(hpf 2000):
// Dark, all-bass:
let pat_lpf <- atom("sawtooth") |> note(48.0) |> sustain(1.0) |> lpf(200.0)
// Thin, all-treble:
let pat_hpf <- atom("sawtooth") |> note(48.0) |> sustain(1.0) |> hpf(2000.0)
Each voice carries its own filter coefficients, so two stacked voices can be filtered differently.
8.12.8.2. Part B: per-orbit reverb — room and roomsize
room(amount) sets the wet send to the orbit’s reverb bus;
roomsize(N) controls the room dimensions. Both live on the bus, so
all voices on the same orbit share one reverb instance:
let pat <- note("c4 ~ c4 ~", "sine") |> attack(0.001) |> decay(0.15) |> sustain(0.0) |> release(0.01) |> room(0.8) |> roomsize(2.0)
8.12.8.3. Part C: per-orbit delay — tempo-aware by default
delay(amount), delaytime(seconds) and delayfeedback(0..1)
drive the orbit’s delay line. delaytime defaults to a tempo-aware
3/16 cycle (resolved from delaysync and the current cps), so plain
delay(0.5) already locks to the tempo even without a delaytime
setter. Tutorial 10 covers the resolver in detail.
let pat <- note("c4 ~ e4 ~ g4 ~ c5 ~", "sine") |> attack(0.001) |> decay(0.15) |> sustain(0.0) |> release(0.01) |> delay(0.5) |> delaytime(0.5) |> delayfeedback(0.4)
8.12.8.4. Part D: per-voice phaser
phaser(rateHz) sweeps a notch through the spectrum at the given
rate. Because it is per-voice, every note carries its own phase — two
stacked voices stay independent, which is not how shared-bus
live-coding systems behave.
let pat <- run(8) |> scale("D:pentatonic") |> sound("sawtooth") |> sustain(1.0) |> release(0.5) |> phaser(2.0)
8.12.8.5. Part E: orbits — give each layer its own bus FX
Different orbits get independent bus FX instances. Below, orbit 0 sits in a tight small room while orbit 1 sits inside a cathedral:
let pat <- stack([
note("c4 ~ e4 ~", "sine") |> attack(0.001) |> decay(0.15) |> sustain(0.0) |> release(0.01) |> room(0.8) |> roomsize(0.5) |> orbit(0),
note("c3", "triangle") |> attack(0.5) |> decay(0.2) |> sustain(0.6) |> release(0.5) |> room(0.8) |> roomsize(8.0) |> orbit(1)
])
The pluck on orbit 0 sounds tight; the pad on orbit 1 swims in reverb — neither bleeds into the other.
8.12.8.6. Part F: crush & shape — per-voice waveshaping
crush(bits) reduces bit depth for lo-fi grit; coarse(n) decimates
the sample rate by a factor of n; shape(amount) is a waveshaper
that adds harmonic distortion. All per-voice:
note("c3 e3 g3 c4", "sawtooth") |> sustain(0.8) |> crush(4.0) |> coarse(4)
note("c3 e3 g3 c4", "sine") |> sustain(0.8) |> shape(0.6)
8.12.8.7. Part G: tremolo & compressor — per-voice amplitude FX
tremolo(rateHz) pulses the amplitude (tremolodepth sets the
depth); compressor(thresholdDb) tames dynamics so quiet and loud
notes sit closer together:
note("d3 d3 d#3 d3", "supersaw") |> sustain(1.0) |> release(0.5) |> tremolo(8.0) |> tremolodepth(0.8)
s("bd sd hh cp") |> compressor(-20.0)
8.12.8.8. Part H: bpf & djf — more per-voice filters
bpf(freq) is a band-pass filter (bpq sets resonance/Q); djf(x)
is a single DJ-style filter knob — below 0.5 it acts as a low-pass,
above 0.5 a high-pass, so one parameter sweeps muffled to thin:
note("c3 e3 g3 c4", "sawtooth") |> sustain(0.8) |> bpf(800.0) |> bpq(6.0)
note("c3 e3 g3 c4", "sawtooth") |> sustain(0.8) |> djf(0.8)
8.12.8.9. Part I: chorus — per-orbit bus
chorus(amount) thickens a sound with slightly detuned copies. Like
room and delay it lives on the orbit bus, so all voices on the
same orbit share one chorus instance:
note("<[c3,e3,g3] [f3,a3,c4] [g3,b3,d4] [a3,c4,e4]>", "sawtooth") |> chorus(0.5) |> attack(0.05) |> decay(0.1) |> sustain(0.6) |> release(0.3) |> gain(0.4)
See also
Full source: tutorials/daStrudel/daStrudel_08_effects_filters.das
Previous tutorial: STRUDEL-07 — Per-Voice FX & Combinators
Next tutorial: STRUDEL-09 — Signals & Modulation