Skip to content

Declare Latency

An effect that looks ahead — a limiter, a linear-phase filter — outputs each sample some time after it gets it. Declare that delay with latency:, in samples, and the engine delays the other paths of the mix by the same amount, so every track stays in time.

use "std/instruments" { Sampler, Kit };
dsp effect LookaheadLimiter {
latency: 512;
param ceiling: 0.8 range(0.1, 1);
param release: 0.0005 range(0.00001, 0.01);
buffer buf_l(512);
buffer buf_r(512);
state wp: 0;
state gain: 1.0;
state hold: 0;
fn process(left, right) -> (out_l, out_r) {
// The gain the incoming sample needs, from the louder channel.
let peak = __native("max", __native("abs", left), __native("abs", right));
let target = if peak > ceiling { ceiling / peak } else { 1.0 };
// Drop at once, hold until that sample is out, then recover.
if target < gain {
gain = target;
hold = 512.0;
} else {
if hold > 0.0 {
hold = hold - 1.0;
} else {
gain = gain + (target - gain) * release;
}
}
// Output the sample from 512 samples ago.
let delayed_l = buf_l[wp];
let delayed_r = buf_r[wp];
buf_l[wp] = left;
buf_r[wp] = right;
wp = (wp + 1.0) % 512.0;
return (delayed_l * gain, delayed_r * gain);
}
}
let bass = AudioTrack("bass");
bass.set_instrument(Sampler(Kit("CR-78")));
bass << [bd _ bd _];
bass.set_effects(#[LookaheadLimiter("limiter")]);
PLAY;

The limiter sees each peak 512 samples before it outputs it, so the gain is already down when the peak goes out. The output is 512 samples late, and the block says so.

The same line goes into a dsp instrument:

dsp instrument LatentSynth {
latency: 256;
voice state phase: 0.0;
fn render(note, velocity, gate) -> (out_l, out_r) {
let freq = __native("mtof", note);
phase = __native("fract", phase + freq * INV_SR);
let sample = __native("sin", phase * TWOPI) * velocity * gate;
return (sample, sample);
}
}

Declare the delay the block adds, and only that. The engine already knows the delay of what it builds for you, and adds your number to it. Without a latency: line the block declares 0.

A delay that changes with a parameter can’t be declared: latency: is a fixed number of samples. Declare the largest one, and pad the shorter settings to it.