miniDSP
A small C library for audio DSP
Loading...
Searching...
No Matches
Simple Effects

Three classic audio effects built from short delay lines:

These are simple enough to study sample-by-sample, but they are also the building blocks of many larger audio effects.


Delay line / echo

Delay/echo reads an older sample from a circular delay line and mixes it with the current input:

\[s[n] = x[n] + feedback \cdot s[n-D] \]

The output mixes dry input with the delayed state:

\[y[n] = dry \cdot x[n] + wet \cdot s[n-D] \]

where \(D\) is the delay in samples and \(|feedback| < 1\).

Reading the formula in C:

// x[n] -> in[n], s[n-D] -> d (delay[idx]), y[n] -> out[n], D -> delay_samples
double d = delay[idx];
out[n] = dry * in[n] + wet * d;
delay[idx] = in[n] + feedback * d;
idx = (idx + 1) % delay_samples;

API:

void MD_delay_echo(const double *in, double *out, unsigned N,
unsigned delay_samples, double feedback,
double dry, double wet);
void MD_delay_echo(const double *in, double *out, unsigned N, unsigned delay_samples, double feedback, double dry, double wet)
Delay line / echo effect using a circular buffer with feedback.

Quick example:

MD_delay_echo(delay_src, delay_out, N, 400, 0.45, 1.0, 0.6);

Audio (before/after):

Before (dry click train)

After (echo: delay=11025, feedback=0.45, dry=1.0, wet=0.6)

Spectrograms (before/after):


Tremolo

Tremolo is amplitude modulation by a low-frequency oscillator (LFO). The gain is:

\[g[n] = (1-depth) + depth \cdot \frac{1 + \sin(2\pi f_{LFO}n/f_s)}{2} \]

and output is:

\[y[n] = g[n] \cdot x[n] \]

So gain moves between \(1-depth\) and \(1\).

Reading the formula in C:

// f_LFO -> rate_hz, fs -> sample_rate, g[n] -> gain, x[n] -> in[n], y[n] -> out[n]
double lfo = 0.5 * (1.0 + sin(2.0 * M_PI * rate_hz * n / sample_rate));
double gain = (1.0 - depth) + depth * lfo;
out[n] = in[n] * gain;

API:

void MD_tremolo(const double *in, double *out, unsigned N,
double rate_hz, double depth, double sample_rate);
void MD_tremolo(const double *in, double *out, unsigned N, double rate_hz, double depth, double sample_rate)
Tremolo effect (amplitude modulation by a sinusoidal LFO).

Quick example:

MD_tremolo(trem_src, trem_out, N, 5.0, 0.8, sample_rate);

Audio (before/after):

Before (dry sine, 220 Hz)

After (tremolo: rate=5.0 Hz, depth=0.8)

Spectrograms (before/after):


Comb-filter reverb

A feedback comb filter reuses a delayed copy of its own output:

\[c[n] = x[n] + feedback \cdot c[n-D] \]

Then we blend dry input and comb output:

\[y[n] = dry \cdot x[n] + wet \cdot c[n] \]

The repeated, closely spaced echoes create a reverb-like resonant tail.

Reading the formula in C:

// c[n-D] -> delayed (comb[idx]), c[n] -> c, x[n] -> in[n], y[n] -> out[n], D -> delay_samples
double delayed = comb[idx];
double c = in[n] + feedback * delayed;
comb[idx] = c;
out[n] = dry * in[n] + wet * c;
idx = (idx + 1) % delay_samples;

API:

void MD_comb_reverb(const double *in, double *out, unsigned N,
unsigned delay_samples, double feedback,
double dry, double wet);
void MD_comb_reverb(const double *in, double *out, unsigned N, unsigned delay_samples, double feedback, double dry, double wet)
Comb-filter reverb (feedback comb filter with dry/wet mix).

Quick example:

MD_comb_reverb(comb_src, comb_out, N, 120, 0.75, 0.7, 0.6);

Audio (before/after):

Before (dry decaying tone burst)

After (comb reverb: delay=1323, feedback=0.75, dry=0.7, wet=0.6)

Spectrograms (before/after):


Verification tips

  • Delay echo impulse input should repeat every delay_samples, decaying by feedback^m.
  • Tremolo with depth = 0 should exactly match the input.
  • Comb reverb with dry = 0 and impulse input should produce a geometric series at delay multiples.

API reference