Skip to content

Filters

Filters selectively pass or reject frequencies. nanodsp provides four filter families at different abstraction levels.

Biquad filters (signalsmith)

The biquad is a second-order IIR filter -- two poles and two zeros give enough flexibility for lowpass, highpass, bandpass, notch, peak EQ, and shelving responses with minimal computation.

Four design methods trade off different characteristics:

Design Best for
"bilinear" General-purpose LP/HP, matches analog response
"cookbook" Classic EQ curves (Robert Bristow-Johnson)
"one_sided" Bandpass, notch, peak, allpass
"vicanek" Accurate magnitude at high frequencies
from nanodsp.buffer import AudioBuffer
from nanodsp.effects.filters import (
    lowpass, highpass, bandpass, notch,
    peak_db, high_shelf_db, low_shelf_db, allpass,
)

buf = AudioBuffer.from_file("input.wav")

# Basic filtering
lp = lowpass(buf, cutoff_hz=2000.0)
hp = highpass(buf, cutoff_hz=80.0)
bp = bandpass(buf, cutoff_hz=1000.0, octaves=1.0)
nch = notch(buf, cutoff_hz=60.0)  # remove hum

# EQ: boost 3 kHz by 4 dB, cut lows, add air
eq = (
    buf
    .pipe(peak_db, cutoff_hz=3000.0, db=4.0)
    .pipe(low_shelf_db, cutoff_hz=200.0, db=-2.0)
    .pipe(high_shelf_db, cutoff_hz=10000.0, db=2.0)
)

# Choose a design method
lp_vicanek = lowpass(buf, cutoff_hz=5000.0, design="vicanek")

# Narrow Q via octave bandwidth
narrow_peak = peak_db(buf, cutoff_hz=1000.0, db=6.0, octaves=0.5)

DaisySP filters

Several filter topologies beyond the basic biquad:

from nanodsp.effects.filters import (
    svf_lowpass, svf_highpass, svf_bandpass, svf_notch, svf_peak,
    ladder_filter, moog_ladder,
    tone_lowpass, tone_highpass,
    modal_bandpass, comb_filter,
)

buf = AudioBuffer.from_file("input.wav")

# State Variable Filter -- resonant, with optional drive
svf = svf_lowpass(buf, freq_hz=1000.0, resonance=0.5, drive=0.3)
svf_hp = svf_highpass(buf, freq_hz=500.0, resonance=0.7)

# Ladder filter -- 24 dB/oct with mode selection
lad = ladder_filter(buf, freq_hz=800.0, resonance=0.6, mode="lp24")
lad_bp = ladder_filter(buf, freq_hz=1200.0, mode="bp24", drive=2.0)

# Moog ladder -- simpler interface
moog = moog_ladder(buf, freq_hz=600.0, resonance=0.8)

# Tone filters -- cheap 1-pole LP/HP
gentle_lp = tone_lowpass(buf, freq_hz=3000.0)
dc_block = tone_highpass(buf, freq_hz=20.0)

# Modal bandpass -- high-Q resonator for resonant bodies
resonance = modal_bandpass(buf, freq_hz=440.0, q=500.0)

# Comb filter -- evenly-spaced peaks/notches
comb = comb_filter(buf, freq_hz=200.0, rev_time=0.3)

Virtual analog filters (Faust)

Zero-delay feedback filter models derived from analog circuit schematics. These accurately model the nonlinear behavior of classic synthesizer filters.

from nanodsp.effects.filters import (
    va_moog_ladder, va_moog_half_ladder, va_diode_ladder,
    va_korg35_lpf, va_korg35_hpf, va_oberheim,
)

buf = AudioBuffer.from_file("input.wav")

# Classic Moog -- warm, fat
moog = va_moog_ladder(buf, cutoff_hz=800.0, q=3.0)

# Half ladder -- gentler 12 dB/oct rolloff
half = va_moog_half_ladder(buf, cutoff_hz=1200.0, q=2.0)

# Diode ladder -- acidic TB-303 character
acid = va_diode_ladder(buf, cutoff_hz=600.0, q=5.0)

# Korg MS-20 -- aggressive, screamy at high resonance
korg_lp = va_korg35_lpf(buf, cutoff_hz=1000.0, q=8.0)
korg_hp = va_korg35_hpf(buf, cutoff_hz=500.0, q=4.0)

# Oberheim SEM -- clean, precise, multi-mode
ob_lp = va_oberheim(buf, cutoff_hz=2000.0, q=2.0, mode="lpf")
ob_bp = va_oberheim(buf, cutoff_hz=1500.0, q=3.0, mode="bpf")
ob_notch = va_oberheim(buf, cutoff_hz=1000.0, q=1.5, mode="bsf")

Multi-order IIR filters (DspFilters)

For sharper transitions than a biquad can provide. Orders 1--16, implemented as cascaded second-order sections (SOS).

Family Character
Butterworth Maximally flat passband, no ripple
Chebyshev I Passband ripple, sharper transition
Chebyshev II Flat passband, stopband ripple
Elliptic Sharpest transition, ripple in both bands
Bessel Maximally flat group delay (best phase linearity)
from nanodsp.effects.filters import iir_filter, iir_design

buf = AudioBuffer.from_file("input.wav")

# 8th-order Butterworth lowpass at 2 kHz
butter = iir_filter(buf, family="butterworth", filter_type="lowpass",
                    order=8, freq_hz=2000.0)

# 4th-order Chebyshev Type I highpass with 1 dB ripple
cheby = iir_filter(buf, family="chebyshev1", filter_type="highpass",
                   order=4, freq_hz=200.0, ripple_db=1.0)

# 6th-order Elliptic bandpass
ellip = iir_filter(buf, family="elliptic", filter_type="bandpass",
                   order=6, freq_hz=1000.0, bandwidth_hz=500.0, ripple_db=0.5)

# Get raw SOS coefficients for external use
sos = iir_design(family="butterworth", filter_type="lowpass",
                 order=4, freq_hz=1000.0, sample_rate=48000.0)
# sos shape: [n_sections, 6] -- each row is [b0, b1, b2, a0, a1, a2]