Skip to content

Custom DSP

Resonon lets you write audio effects and instruments directly in the language. Custom DSP code runs at the audio sample rate with zero overhead beyond the math you write — no plugins, no external tools.

A dsp effect processes audio in real time. The simplest effect is a gain control:

dsp effect Gain {
param level: 1.0 range(0, 2);
fn process(left, right) -> (out_l, out_r) {
return (left * level, right * level);
}
}

The process function runs once per audio sample. It receives the current left and right input and returns the processed output.

Load it onto a track like any built-in effect:

use "std/instruments" { Sampler, Kit };
let drums = AudioTrack("drums");
drums.load_instrument(Sampler(Kit("cr78")));
drums << [bd sd bd sd];
drums.load_effect(Gain());
PLAY;

param declares a controllable value with a default and an optional range:

dsp effect Tremolo {
param speed: 4.0 range(0.1, 20);
param depth: 0.5 range(0, 1);
state phase: 0.0;
fn process(left, right) -> (out_l, out_r) {
phase = fract(phase + speed * INV_SR);
let mod = 1.0 - depth * (0.5 + 0.5 * sin(phase * TWOPI));
return (left * mod, right * mod);
}
}
let trem = Tremolo();
drums.load_effect(trem);

Parameters are accessible as plain identifiers inside process. From outside, modulate them with signals:

use "std/signals" { Sine };
trem.Speed << Sine(0.25).range(2, 8); // Vary speed with an LFO
trem.Depth << 0.7; // Static value

state variables persist across samples — use them for anything that accumulates over time:

dsp effect EnvFollower {
param attack: 0.01 range(0.001, 0.5);
param release: 0.1 range(0.01, 2.0);
state env: 0.0;
fn process(left, right) -> (out_l, out_r) {
let input_level = abs(left) + abs(right);
if input_level > env {
env = env + (input_level - env) * attack;
} else {
env = env + (input_level - env) * release;
}
let gain = 1.0 / (1.0 + env * 4.0);
return (left * gain, right * gain);
}
}

buffer declares a fixed-size array for delay lines, wavetables, or any sample-indexed storage. Indices wrap automatically with modulo:

dsp effect Echo {
param time: 0.25 range(0.001, 1.0);
param feedback: 0.5 range(0, 0.99);
param mix: 0.4 range(0, 1);
buffer buf_l(48000);
buffer buf_r(48000);
state wp: 0;
fn process(left, right) -> (out_l, out_r) {
let delay_samples = time * SR;
let rp = wp + 48000.0 - delay_samples;
let delayed_l = buf_l[rp];
let delayed_r = buf_r[rp];
buf_l[wp] = left + delayed_l * feedback;
buf_r[wp] = right + delayed_r * feedback;
wp = (wp + 1.0) % 48000.0;
return (left + delayed_l * mix, right + delayed_r * mix);
}
}

SR is the sample rate (e.g. 48000). INV_SR is 1.0 / SR.

Define local helpers inside the effect block. They can read and write the parent’s state and buffers:

dsp effect SoftClipper {
param drive: 2.0 range(1, 10);
param mix: 0.5 range(0, 1);
fn soft_clip(x) -> out {
return x / (1.0 + abs(x));
}
fn process(left, right) -> (out_l, out_r) {
let clipped_l = soft_clip(left * drive);
let clipped_r = soft_clip(right * drive);
return (left * (1.0 - mix) + clipped_l * mix,
right * (1.0 - mix) + clipped_r * mix);
}
}

SVF (state variable filter) functions are available in any DSP block:

FunctionDescription
svf_lp(input, cutoff, resonance)Lowpass
svf_hp(input, cutoff, resonance)Highpass
svf_bp(input, cutoff, resonance)Bandpass
svf_notch(input, cutoff, resonance)Notch
svf_allpass(input, cutoff, resonance)Allpass
svf_peak(input, cutoff, resonance)Peak/bell
dsp effect AutoFilter {
param cutoff: 2000 range(20, 20000);
param resonance: 1.5 range(0.5, 10);
state phase: 0.0;
fn process(left, right) -> (out_l, out_r) {
phase = fract(phase + 0.5 * INV_SR);
let lfo = 0.5 + 0.5 * sin(phase * TWOPI);
let freq = 200.0 + lfo * cutoff;
return (svf_lp(left, freq, resonance),
svf_lp(right, freq, resonance));
}
}

A dsp instrument is a polyphonic synthesizer. The render function runs once per sample per active voice:

dsp instrument SineOsc {
voice state phase: 0.0;
fn render(note, velocity, gate) -> (out_l, out_r) {
let freq = mtof(note);
phase = fract(phase + freq * INV_SR);
let sample = sin(phase * TWOPI) * velocity * gate;
return (sample, sample);
}
}
let synth = AudioTrack("synth");
synth.load_instrument(SineOsc());
synth << [C4 E4 G4 C5];
PLAY;
ParameterTypeDescription
notef64MIDI note number (supports fractional values for microtonal)
velocityf64Note velocity, normalized 0.0–1.0
gatef641.0 while note held, 0.0 after note-off

mtof(note) converts a MIDI note number to frequency in Hz. voice state variables are independent per note and reset on each note-on.

Combine oscillator, envelope, and filter for a more complete synth:

dsp instrument FilteredSynth {
param attack: 0.01 range(0.001, 1.0);
param release: 0.2 range(0.01, 2.0);
param cutoff: 4000 range(20, 20000);
voice state phase: 0.0;
voice state env: 0.0;
fn render(note, velocity, gate) -> (out_l, out_r) {
let freq = mtof(note);
phase = fract(phase + freq * INV_SR);
let osc = sin(phase * TWOPI);
let target = gate * velocity;
if gate > 0.0 {
env = env + (target - env) * attack;
} else {
env = env + (target - env) * release;
}
let filtered = svf_lp(osc * env, cutoff, 1.5);
return (filtered, filtered);
}
}
let lead = AudioTrack("lead");
lead.load_instrument(FilteredSynth());
lead << [C4 _ E4 _ G4 _ C5 _];

Set instrument parameters from outside the DSP block:

let synth = FilteredSynth();
synth.params(); // Print all parameters
synth.set_param("cutoff", 2000); // Set by name
synth.set_param("attack", 0.1)
.set_param("release", 0.5); // Chainable
dsp instrument PulseOsc {
param duty: 0.5 range(0.01, 0.99);
voice state phase: 0.0;
fn pulse(p, d) -> out {
if p < d {
return 1.0;
} else {
return 0.0 - 1.0;
}
}
fn render(note, velocity, gate) -> (out_l, out_r) {
let freq = mtof(note);
phase = fract(phase + freq * INV_SR);
let sample = pulse(phase, duty) * velocity * gate;
return (sample, sample);
}
}
let pulse = AudioTrack("pulse");
pulse.load_instrument(PulseOsc());
pulse << [C4 E4 G4 C5];

These functions are available inside any dsp effect, dsp instrument, or dsp signal block:

CategoryFunctions
Mathsin, cos, tanh, abs, sqrt, pow, clamp, min, max, floor, fract
Filterssvf_lp, svf_hp, svf_bp, svf_notch, svf_allpass, svf_peak
Conversionmtof (MIDI to frequency)
ConstantsSR (sample rate), INV_SR (1/SR), TWOPI (2π)
Randomnoise (white noise sample)