fdn.h
A feedback delay network reverb.
#include <sonore/fdn.h>- 1type
- Guides: Getting started, MIDI and MPE
On this page
Design notes
effects.h's Reverb is Schroeder/Moorer: four combs into two allpasses, the 1962 topology, and it sounds like it -- a comb bank has a modal density set by four delay lengths and the tail carries their signature. Jot's feedback delay network (1991) is the modern answer: N delays feeding back through a UNITARY mixing matrix, so every line hears every other and the modal density grows with N² instead of N. Eight lines of it are the substrate of every plate, hall and shimmer written since.
What is decided here, and why:
The matrix is a Hadamard, applied as a fast Walsh-Hadamard transform: N log N adds, no multiplies, and unitary by construction (scaled by 1/sqrt(N)), which is the property that makes the network lossless before the per-line gains are applied -- the decay then comes ONLY from those gains, which is what lets the decay time be set exactly. Per-line gain from the delay length and the decay time, Jot's formula: g = 10^(-3 L / (T60 sr)). Every path through the network loses 60 dB in T60 seconds whichever lines it passes through, so the declared decay is the measured one. The unit test checks this against the Schroeder backward integral of the impulse response. Delay lengths are distinct PRIMES, picked from a logarithmic spread and rounded to the nearest prime at prepare(): distinct primes are coprime by definition, and coprime lengths are what stop the tail from ringing on the pitch of a common factor. Scaled by the sample rate, so the room keeps its size -- up to MaxDelay: the longest line is at most MaxDelay / sr seconds (42 ms at 192 kHz for the default 8192), and past that the whole spread shrinks together rather than the long lines piling up at the ceiling. A plugin that wants a full-size hall at 192 kHz asks for FdnReverb<8, 32768>. Damping is a one-pole lowpass inside each line's loop, so high frequencies decay faster than low ones the way air makes them. Modulation moves each line's read point by a fraction of a sample on its own slow LFO. With static lengths a network of this size still has a faint metallic signature at the lengths' beat frequencies; a few samples of drift smears it out. Read with readCubic(), because a delay that sweeps must -- see DelayLine.
Included by dsp.h.
Types 1
template <int Lines = 8, int MaxDelay = 32768> class FdnReverb
SIZE WARNING, the same one the Convolver carries: eight 32768-sample lines plus a pre-delay is ~1.1 MB of state, which CANNOT live on the stack in wasm (64 KB). A real plugin is fine -- the ABI puts SonoreDsp in static storage -- but a local FdnReverb in a test or a helper crashes there.
32768 holds the longest line of the largest room (160 ms) up to 204 kHz. The default was 8192, which held it to 51 kHz: past that every line was shortened by the same factor, which is more echoes a second at the same decay and more passes through the damping. The default network came out 4.7 dB louder in the low mids at 192 kHz and its top decayed 15% sooner, where one with room for its lines sounded as it does at 48. Past 204 kHz the lines shrink again; FdnReverb<8, 8192> is the smaller one, for a caller who never runs above 51 kHz.