
Product
Socket Firewall Now Blocks Malicious VS Code and Open VSX Extensions
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.
audio-filter
Advanced tools
Canonical audio filter implementations.
|
Weighting Auditory |
Speech EQ Effect |
npm install audio-filter
// import everything
import * as filter from 'audio-filter'
// import by domain
import { aWeighting, kWeighting } from 'audio-filter/weighting'
import { gammatone, melBank } from 'audio-filter/auditory'
import { moogLadder, oberheim } from 'audio-filter/analog'
import { vocoder, lpcAnalysis } from 'audio-filter/speech'
import { parametricEq, crossover, baxandall, tilt, lowShelf, highShelf } from 'audio-filter/eq'
import { dcBlocker, notch, lowpass, highpass, bandpass, resonator } from 'audio-filter/effect'
All filters share one shape:
filter(buffer, params) // → buffer (modified in-place)
Takes an Array/Float32Array/Float64Array, modifies it in-place, returns it. Pass the same params object on every call to persist state across blocks automatically:
let params = { fc: 1000, resonance: 0.5, fs: 44100 }
for (let buf of stream) moogLadder(buf, params)
For frequency analysis, weighting filters expose a .coefs(fs) method returning a second-order sections (SOS) array — [{b0, b1, b2, a1, a2}, ...], one biquad per section — for use with digital-filter:
import { freqz, mag2db } from 'digital-filter/core'
let sos = aWeighting.coefs(44100)
let resp = freqz(sos, 2048, 44100)
let db = mag2db(resp.magnitude)
Standard measurement curves. Each is defined by a standards body to a specific curve shape and normalization.
| filter | standard | normalized |
|---|---|---|
aWeighting | IEC 61672-1:2013 | 0 dB at 1 kHz |
cWeighting | IEC 61672-1:2013 | 0 dB at 1 kHz |
kWeighting | ITU-R BS.1770-4:2015 | — |
itu468 | ITU-R BS.468-4:1986 | +12.2 dB at 6.3 kHz |
riaa | RIAA 1954 / IEC 60098 | 0 dB at 1 kHz |
Models how the ear perceives loudness — attenuates low and very high frequencies.
Transfer function: $H(s) = \frac{Ks^4}{(s+\omega_1)^2(s+\omega_2)(s+\omega_3)(s+\omega_4)^2}$
Poles: $\omega_1 = 2\pi \cdot 20.6,\text{Hz}$, $\omega_2 = 2\pi \cdot 107.7,\text{Hz}$, $\omega_3 = 2\pi \cdot 737.9,\text{Hz}$, $\omega_4 = 2\pi \cdot 12194,\text{Hz}$
Implementation: matched z-transform ($z_k = e^{s_k/f_s}$), 3 SOS sections — no frequency warping near Nyquist
Normalization: 0 dB at 1 kHz (IEC requirement)
import { aWeighting } from 'audio-filter/weighting'
let p = { fs: 44100 }
for (let buf of stream) aWeighting(buf, p) // A-weighted stream
Standard: IEC 61672-1:20131
Use when: measuring SPL, noise, OSHA compliance, audio quality
Not for: loudness in broadcast (use K-weighting), noise annoyance (use ITU-468)
Like A-weighting but flatter — less rolloff at low and high frequencies.
Transfer function: $H(s) = \frac{Ks^2}{(s+\omega_1)^2(s+\omega_4)^2}$
Poles: $\omega_1 = 2\pi \cdot 20.6,\text{Hz}$, $\omega_4 = 2\pi \cdot 12194,\text{Hz}$ (same as A-weighting outer poles)
Implementation: matched z-transform, 2 SOS sections
cWeighting(buffer, { fs: 44100 })
Standard: IEC 61672-1:20131
Use when: peak sound level measurement, where A-weighting over-penalizes bass
Compared to A: rolls off below 31.5 Hz and above 8 kHz; flat 31.5 Hz–8 kHz
The loudness measurement curve — a high shelf plus a highpass. Used to compute LUFS.
Stage 1: pre-filter — high shelf +4 dB above ~1.5 kHz (head diffraction simulation)
Stage 2: RLB highpass — 2nd-order Butterworth at ~38 Hz (removes sub-bass)
Exact coefficients at 48 kHz: specified in BS.1770 Annex 1; this implementation uses them verbatim
import { kWeighting } from 'audio-filter/weighting'
kWeighting(buffer, { fs: 48000 }) // exact ITU-R BS.1770 coefficients
kWeighting(buffer, { fs: 44100 }) // approximated via biquad design
Standard: ITU-R BS.1770-4:20152, EBU R128
Use when: computing integrated loudness (LUFS/LKFS), broadcast loudness normalization
Not for: A-weighted SPL measurement (different shape, different standard)
Peaked noise weighting — peaks at +12.2 dB near 6.3 kHz — models how humans actually perceive noise annoyance.
Shape: rises steeply from 31.5 Hz, peaks at +12.2 dB at 6.3 kHz, rolls off above 10 kHz
Implementation: practical IIR approximation via cascaded biquads, within ~1 dB of spec
itu468(buffer, { fs: 48000 })
Standard: ITU-R BS.468-4:19863 (original CCIR 468, 1968)
Rationale: human hearing is more sensitive to short noise bursts than sine tones; 468 weights accordingly
Use when: measuring noise in broadcast equipment, tape noise, hum and hiss
Compared to A-weighting: 6.3 kHz peak makes it harsher on hiss; preferred in European broadcast
Playback equalization for vinyl records — a shelving curve with three time constants.
Transfer function: $H(s) = \frac{1 + sT_2}{(1 + sT_1)(1 + sT_3)}$
Time constants: $T_1 = 3180,\mu\text{s}$ (50.05 Hz pole), $T_2 = 318,\mu\text{s}$ (500.5 Hz zero), $T_3 = 75,\mu\text{s}$ (2122 Hz pole)
Implementation: 1 SOS section via bilinear transform, normalized 0 dB at 1 kHz
import { riaa } from 'audio-filter/weighting'
riaa(phonoSignal, { fs: 44100 }) // correct vinyl playback
Standard: RIAA 1954, IEC 60098:19874
Purpose: playback de-emphasis undoes the mastering pre-emphasis applied during vinyl cutting
Shape: boosts bass ~+20 dB at 20 Hz, rolls off treble; at playback restores flat response
Models of the human auditory system — how the cochlea and brain decompose sound into frequency channels. Used in psychoacoustics, music information retrieval, and hearing aid design.
The cochlear filter — bandpass tuned to one frequency, decaying oscillation, mimics an inner hair cell.
Model: cascade of complex one-pole filters; 4th-order is the standard cochlear approximation
Bandwidth: $\text{ERB} = 24.7\left(\frac{4.37 f_c}{1000} + 1\right),\text{Hz}$
Implementation: complex resonator with gain normalization to 0 dB at $f_c$
import { gammatone } from 'audio-filter/auditory'
let params = { fc: 1000, fs: 44100 }
gammatone(buffer, params) // bandpass at 1 kHz with cochlear envelope
Origin: Patterson et al. (1992)5
Use when: cochlear modeling, auditory scene analysis, psychoacoustic feature extraction
Compared to Butterworth bandpass: gammatone has asymmetric temporal envelope matching biological data
Reuse params across blocks — state in params._s, gain cached in params._gain.
ISO/IEC fractional-octave filter bank — the standard for acoustic measurement and spectrum analysis.
Center frequencies: ISO 266 series — $f_c = 1000 \cdot G^{k/n}$, $G = 10^{3/10}$
Bandwidth: each band spans $f_c \cdot G^{-1/(2n)}$ to $f_c \cdot G^{+1/(2n)}$
1/1 octave: 10 bands (31.5–16 kHz) — coarse; 1/3 octave: 30 bands — standard; 1/6+: psychoacoustics
Returns: array of { fc, coefs } — each band is a biquad bandpass section
import { octaveBank } from 'audio-filter/auditory'
import { filter } from 'digital-filter'
let bands = octaveBank(3, 44100) // 1/3-octave, 30+ bands
for (let band of bands) {
let buf = Float64Array.from(signal)
filter(buf, { coefs: band.coefs })
spectrum.push({ fc: band.fc, energy: rms(buf) })
}
Standard: IEC 61260-1:20146, ANSI S1.11:2004
Use when: acoustic measurement, noise assessment, spectrum visualization
Equivalent Rectangular Bandwidth scale — how the auditory system actually spaces its channels.
ERB formula: $\text{ERB}(f_c) = 24.7\left(\frac{4.37 f_c}{1000} + 1\right)$
Spacing: ~1 ERB between adjacent channels — logarithmic above 1 kHz, more linear below
Returns: array of { fc, erb, bw } descriptors; apply gammatone at each fc for the filter bank
import { erbBank, gammatone } from 'audio-filter/auditory'
let bands = erbBank(44100)
let states = bands.map(b => ({ fc: b.fc, fs: 44100 }))
for (let buf of stream) {
let channels = bands.map((_, i) => {
let b = Float64Array.from(buf)
gammatone(b, states[i])
return b
})
}
Origin: Moore & Glasberg (1983, 1990)7
Use when: speech processing, hearing models, auditory feature extraction
Compared to Bark: ERB is more accurate above 500 Hz; Bark is the psychoacoustic masking model
Zwicker's 24 critical bands — the psychoacoustic foundation of perceptual audio coding.
Scale: 24 bands spanning 20 Hz–20 kHz; named after Heinrich Barkhausen
Band widths: ~100 Hz wide below 500 Hz; ~20% of center frequency above
Returns: array of { bark, fLow, fHigh, fc, coefs } — each band is a biquad bandpass section
import { barkBank } from 'audio-filter/auditory'
import { filter } from 'digital-filter'
let bands = barkBank(44100) // 24 critical bands
for (let band of bands) {
let buf = Float64Array.from(signal)
filter(buf, { coefs: band.coefs })
excitation[band.bark] = rms(buf)
}
Origin: Zwicker (1961)8
Use when: perceptual audio coding (MP3/AAC use Bark-like groupings), loudness models, masking
Compared to ERB: Bark bands are wider and fewer; ERB is more accurate for hearing science
Mel-frequency triangular filter bank — the standard front-end for speech recognition and music information retrieval.
Scale: $\text{mel}(f) = 2595 \log_{10}(1 + f/700)$ (O'Shaughnessy variant)9
Bands: equally spaced in mel scale; each band is a triangle spanning 3 adjacent mel points
Returns: array of { fc, fLow, fHigh, mel } — band descriptors for MFCC computation
import { melBank } from 'audio-filter/auditory'
let bands = melBank(44100) // 26 bands (default)
let bands = melBank(16000, { nFilters: 40 }) // 40 bands, telephony rate
let bands = melBank(44100, { fmin: 300, fmax: 8000 })
Use when: MFCC feature extraction, speech recognition, music genre classification, audio fingerprinting
Compared to ERB/Bark: mel is the most widely used in ML; ERB is more physiologically accurate
Discrete-time models of analog circuits — each named after the hardware it replicates. Nonlinear, stateful, process in-place. The filters in synthesizers.
Robert Moog's 4-pole transistor ladder, 1965 — the most imitated filter in electronic music.
Circuit: 4 cascaded one-pole transistor ladder sections, global feedback from output to input
Implementation: Zero-delay feedback (ZDF) via trapezoidal integration — Zavalishin (2012)10, Ch. 6
Response: $-24,\text{dB/oct}$ lowpass; resonance peak at $f_c$; self-oscillation (sine wave) at resonance=1
Nonlinearity: $\tanh$ saturation at input (transistor ladder characteristic)
import { moogLadder } from 'audio-filter/analog'
let params = { fc: 800, resonance: 0.7, fs: 44100 }
moogLadder(buffer, params)
// Self-oscillation — runs indefinitely from a single impulse
let silent = new Float64Array(4096); silent[0] = 0.01
moogLadder(silent, { fc: 1000, resonance: 1, fs: 44100 })
Patent: Moog (1965) US347562311
vs Diode ladder: Moog saturates only at input; diode saturates at each stage — different character at high resonance
Roland TB-303 / EMS VCS3 style — per-stage saturation gives the characteristic acid "squelch".
Circuit: Roland TB-303, EMS VCS3, EDP Wasp
Key difference from Moog: $\tanh$ nonlinearity at each of 4 stages, not just input; feedback is a weighted sum of all stage outputs
Character: preserves bass at high resonance; more "squelchy" and aggressive than Moog
Implementation: ZDF — Zavalishin (2012)10; Pirkle (2019)12, Ch. 10
Stability: stable up to resonance=0.95; bounded output
import { diodeLadder } from 'audio-filter/analog'
let params = { fc: 500, resonance: 0.8, fs: 44100 }
diodeLadder(buffer, params)
Korg MS-10/MS-20, 1978 — 2-pole filter with lowpass and complementary highpass outputs.
Topology: 2 cascaded one-pole sections with nonlinear feedback; HP = input − LP
Response: $-12,\text{dB/oct}$; aggressive resonance due to nonlinear feedback; both LP and HP from one circuit
import { korg35 } from 'audio-filter/analog'
korg35(buffer, { fc: 1000, resonance: 0.5, type: 'lowpass', fs: 44100 })
korg35(buffer, { fc: 1000, resonance: 0.5, type: 'highpass', fs: 44100 })
Circuit: Korg MS-10/MS-20 (1978)
Analysis: Stilson & Smith (1996)13; Zavalishin (2012)10, Ch. 5
vs Moog ladder: 2-pole ($-12,\text{dB/oct}$) vs 4-pole ($-24,\text{dB/oct}$); Korg35 has complementary HP mode
Oberheim SEM (1974) — 2-pole state-variable filter with four modes from one circuit.
Topology: 2 trapezoidal integrators with nonlinear feedback; multimode output (LP/HP/BP/notch)
Response: $-12,\text{dB/oct}$; warm, musical resonance; continuous mode morphing
Implementation: ZDF — Zavalishin (2012)10, Ch. 4–5; $\tanh$ saturation on integrator states
import { oberheim } from 'audio-filter/analog'
oberheim(buffer, { fc: 1000, resonance: 0.5, type: 'lowpass', fs: 44100 })
oberheim(buffer, { fc: 1000, resonance: 0.5, type: 'highpass', fs: 44100 })
oberheim(buffer, { fc: 1000, resonance: 0.5, type: 'bandpass', fs: 44100 })
oberheim(buffer, { fc: 1000, resonance: 0.5, type: 'notch', fs: 44100 })
Circuit: Oberheim SEM (1974), Two Voice, Four Voice, Eight Voice
vs Moog/Korg: 2-pole like Korg35 but true state-variable topology; LP/HP/BP/notch from one circuit; warmer resonance character
Filters that model or process the human vocal tract — from vowel synthesis to spectral voice coding.
Parallel resonator bank — each peak models one vocal tract resonance (formant).
Model: parallel combination of second-order resonators, each modeling one vocal tract mode
Formant frequencies: determined by vocal tract shape; F1 controls vowel openness, F2 controls front/back
Typical ranges: F1: 250–850 Hz, F2: 850–2500 Hz, F3: 1700–3500 Hz
Implementation: uses resonator internally — constant peak-gain bandpass per formant
Defaults: F1=730 Hz, F2=1090 Hz, F3=2440 Hz (open vowel /a/)
import { formant } from 'audio-filter/speech'
formant(excitation, { fs: 44100 }) // vowel /a/ (default)
formant(excitation, {
formants: [{ fc: 270, bw: 60, gain: 1 }, { fc: 2290, bw: 90, gain: 0.5 }],
fs: 44100
}) // vowel /i/
Use when: speech synthesis, singing synthesis, vocal effects, acoustic phonetics
Not a substitute for: LPC synthesis, which estimates formants automatically from a speech signal
Channel vocoder — transfers the spectral envelope of one sound onto the pitched content of another.
Note: takes two separate buffers, returns a new buffer (does not modify in-place).
Principle: analyze modulator into N bands → extract envelope per band → multiply with filtered carrier → sum
Implementation: N parallel bandpass filters on both signals; envelope follower per modulator band
Band count: 8 = robotic effect; 16 = classic vocoder sound; 32+ = more speech intelligibility
import { vocoder } from 'audio-filter/speech'
// carrier: pitched source (sawtooth, buzz, noise...)
// modulator: signal whose spectral shape to impose (voice, instrument...)
let output = vocoder(carrier, modulator, { bands: 16, fs: 44100 })
Inventor: Dudley (1939)14, Bell Labs
Use when: voice effects, talkbox simulation, cross-synthesis, spectral morphing
Linear Predictive Coding — estimates the vocal tract transfer function from a speech signal.
Analysis: autocorrelation method + Levinson-Durbin recursion → LPC coefficients + residual
Synthesis: all-pole filter reconstructs signal from residual excitation
Round-trip: lpcAnalysis → lpcSynthesize recovers the original signal exactly
import { lpcAnalysis, lpcSynthesize } from 'audio-filter/speech'
// Analysis: extract vocal tract model
let { coefs, gain, residual } = lpcAnalysis(speechFrame, { order: 12 })
// Synthesis: reconstruct from residual
lpcSynthesize(residual, { coefs, gain }) // residual → reconstructed speech
// Modify pitch: replace residual with different excitation
let buzz = generatePulseTrainAtNewPitch()
lpcSynthesize(buzz, { coefs, gain }) // speech at new pitch
Origin: Atal & Hanauer (1971)15; foundation of CELP, GSM, and modern speech codecs
Use when: speech coding, pitch modification, voice conversion, formant estimation, speech analysis
Equalization and frequency routing — from parametric studio EQ to speaker crossover networks.
10-band ISO octave equalizer — fixed center frequencies, gain per band.
Implementation: parallel biquad peaking filters, one per band; gains combined additively
Band spacing: 1-octave intervals — $f_k = 1000 \cdot 2^k,\text{Hz}$
Bands: 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz
import { graphicEq } from 'audio-filter/eq'
graphicEq(buffer, {
gains: { 125: -3, 1000: +6, 8000: +2 },
fs: 44100
})
Standard: ISO 266:1997 center frequencies
Use when: quick tonal shaping, DJ mixers, consumer audio, live sound
vs Parametric EQ: fixed centers but simpler — no per-band frequency or Q control
N-band EQ with fully adjustable frequency, Q, and gain per band.
Implementation: cascaded biquad sections — one per band; peak uses peaking EQ biquad, shelves use Zölzer shelf design16
Band types: peak (bell curve at $f_c$), lowshelf (boost/cut below $f_c$), highshelf (boost/cut above $f_c$)
import { parametricEq } from 'audio-filter/eq'
parametricEq(buffer, {
bands: [
{ fc: 80, Q: 0.7, gain: +4, type: 'lowshelf' },
{ fc: 1000, Q: 2.0, gain: -3, type: 'peak' },
{ fc: 8000, Q: 0.7, gain: +2, type: 'highshelf' },
],
fs: 44100
})
Use when: studio mixing, mastering, precise tonal correction
vs Graphic EQ: fully adjustable $f_c$, Q, and gain per band; no fixed centers
Linkwitz-Riley crossover network — splits audio into N frequency bands with flat magnitude sum.
Filter type: cascade of two Butterworth filters of half the specified order
Property: LR4 (order=4) bands sum to flat magnitude response with correct phase alignment
Orders: LR2 ($-12,\text{dB/oct}$), LR4 ($-24,\text{dB/oct}$, most common), LR8 ($-48,\text{dB/oct}$)
Returns: SOS[][] — one SOS array per band
import { crossover } from 'audio-filter/eq'
import { filter } from 'digital-filter'
let bands = crossover([500, 5000], 4, 44100) // 3 bands: lo / mid / hi
let lo = Float64Array.from(buffer); filter(lo, { coefs: bands[0] })
let mid = Float64Array.from(buffer); filter(mid, { coefs: bands[1] })
let hi = Float64Array.from(buffer); filter(hi, { coefs: bands[2] })
Designers: Linkwitz & Riley (1976)17
Use when: speaker system design, multi-band dynamics, band splitting for separate processing
Headphone crossfeed — mixes a filtered copy of each channel into the other to reduce in-head localization.
Takes two separate channel buffers, modifies both in-place.
Problem: speaker playback has inter-channel crosstalk and head shadowing; headphones remove these, causing an unnatural "in-head" stereo image
Solution: add a lowpass-filtered, attenuated copy of each channel to the opposite channel, simulating crosstalk and head diffraction
fc: models the head-shadow lowpass (~700 Hz is typical); level: 0.3 = mild, 0.5 = strong
import { crossfeed } from 'audio-filter/eq'
crossfeed(left, right, { fc: 700, level: 0.3, fs: 44100 })
Origin: Bauer (1961)18; BS2B (Bauer Stereophonic-to-Binaural) algorithm
Standalone low-shelf and high-shelf filters — boost or cut below/above a corner frequency.
Low shelf: $H(s) = A \cdot \frac{s/\omega_c + \sqrt{A}}{s/(\omega_c\sqrt{A}) + 1}$ — RBJ biquad shelf design
High shelf: same topology, mirrored in frequency
Q / slope: $Q = 0.707$ gives maximally-flat transition; lower Q gives a gentler, wider slope
import { lowShelf, highShelf } from 'audio-filter/eq'
lowShelf(buffer, { fc: 200, gain: +6, Q: 0.707, fs: 44100 }) // bass boost
highShelf(buffer, { fc: 4000, gain: -3, Q: 0.707, fs: 44100 }) // treble cut
Use when: correcting speaker/room low-end buildup, air-band top-end addition, mastering bus
vs Parametric EQ: shelf is a single-band operation with a cleaner API — use when you don't need bell curves
Bass/treble tone control — the canonical two-knob EQ in amplifiers, mixers, and guitar pedals since 1952.
Bass: low shelf around fBass (default 250 Hz)
Treble: high shelf around fTreble (default 4 kHz)
Independence: bass and treble controls are cascaded, not interactive — each shelf is independent
import { baxandall } from 'audio-filter/eq'
baxandall(buffer, { bass: +6, treble: -3, fs: 44100 }) // default pivot freqs
baxandall(buffer, { bass: +4, treble: +2, fBass: 300, fTreble: 6000, fs: 44100 }) // custom pivots
Origin: Peter Baxandall (1952)19
Use when: amp/mixer tone stack simulation, consumer audio tone controls, guitar pedal EQ
vs Parametric EQ: intentionally limited to two knobs — the constraint is the point
See-saw around a pivot frequency — one knob trades bass for treble symmetrically.
Positive gain: bass up / treble down — warms up a bright signal
Negative gain: treble up / bass down — brightens a dull signal
Pivot: frequency that stays at 0 dB (default 1 kHz)
import { tilt } from 'audio-filter/eq'
tilt(buffer, { gain: +4, pivot: 1000, fs: 44100 }) // warm up
tilt(buffer, { gain: -3, pivot: 1000, fs: 44100 }) // brighten
Use when: quick tonal correction on a mix bus or stereo source with a single parameter
vs Baxandall: tilt is one knob not two — bass and treble always move equal and opposite
Signal conditioning and spectral shaping — single-purpose filters with well-defined transfer functions.
Removes DC offset — the simplest useful filter.
$H(z) = \dfrac{1 - z^{-1}}{1 - Rz^{-1}}$
Topology: zero at $z = 1$ (DC), pole at $z = R$
Cutoff: $f_c \approx \frac{(1-R) f_s}{2\pi}$ — $R = 0.995$ gives ~22 Hz at 44.1 kHz
import { dcBlocker } from 'audio-filter/effect'
let params = { R: 0.995 }
dcBlocker(buffer, params)
Use when: removing DC bias before processing, preventing lowpass filter saturation
Adds a delayed copy of the signal to itself — notches and peaks at harmonics of $f_s / D$.
Feedforward: $H(z) = 1 + g \cdot z^{-D}$ — notches at $f = \frac{(2k+1) f_s}{2D}$
Feedback: $H(z) = \dfrac{1}{1 - g \cdot z^{-D}}$ — peaks at $f = \frac{k \cdot f_s}{D}$
import { comb } from 'audio-filter/effect'
comb(buffer, { delay: 100, gain: 0.6, type: 'feedback' })
Use when: flanging, chorus (with modulated delay), Karplus-Strong string synthesis, room mode modeling
Unity magnitude at all frequencies — shifts phase only. First and second order.
First order: $H(z) = \dfrac{a + z^{-1}}{1 + a z^{-1}}$ — pole at $z = -a$, 180° phase shift at Nyquist
Second order: $H(z) = \dfrac{d - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}{1 - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}$ — 360° phase shift around $\omega_0$
import { allpass } from 'audio-filter/effect'
allpass.first(buffer, { a: 0.5 }) // coefficient a
allpass.second(buffer, { fc: 1000, Q: 1, fs: 44100 }) // center fc, quality Q
Use when: phase equalization, reverb building blocks (Schroeder reverb), stereo widening
First-order highpass (emphasis) and its inverse (de-emphasis) — used before and after coding or transmission.
$H(z) = 1 - \alpha z^{-1}$ (emphasis) / $H(z) = \dfrac{1}{1 - \alpha z^{-1}}$ (de-emphasis)
Rolloff: emphasis boosts above $f_c = \frac{(1-\alpha) f_s}{2\pi}$ — $\alpha = 0.97$ gives ~420 Hz at 44.1 kHz
Inverse pair: deemphasis exactly cancels emphasis — $H_e(z) \cdot H_d(z) = 1$
import { emphasis, deemphasis } from 'audio-filter/effect'
emphasis(buffer, { alpha: 0.97 }) // before encoding
deemphasis(buffer, { alpha: 0.97 }) // after decoding — exact inverse
Use when: speech coding (GSM, AMR uses $\alpha = 0.97$), tape recording, FM broadcasting
Removes everything above cutoff frequency — the most common filter in audio.
Order 2 (default): RBJ biquad lowpass — $-12,\text{dB/oct}$
Order 4+: Butterworth cascaded SOS — $-6n,\text{dB/oct}$ where $n$ = order
import { lowpass } from 'audio-filter/effect'
lowpass(buffer, { fc: 2000, fs: 44100 }) // 2nd-order (default)
lowpass(buffer, { fc: 2000, order: 4, fs: 44100 }) // 4th-order Butterworth
lowpass(buffer, { fc: 2000, Q: 1.5, fs: 44100 }) // resonant
Use when: anti-aliasing, smoothing, removing hiss, synth subtractive filtering
vs Moog ladder: lowpass is a clean linear filter; Moog adds nonlinear saturation and self-oscillation
Removes everything below cutoff frequency — DC removal, rumble elimination.
Order 2 (default): RBJ biquad highpass — $-12,\text{dB/oct}$
Order 4+: Butterworth cascaded SOS — $-6n,\text{dB/oct}$ where $n$ = order
import { highpass } from 'audio-filter/effect'
highpass(buffer, { fc: 80, fs: 44100 }) // rumble filter
highpass(buffer, { fc: 80, order: 4, fs: 44100 }) // steeper rolloff
Use when: removing rumble, mic handling noise, subsonic content before dynamics processing
vs DC blocker: highpass has adjustable cutoff and slope; DC blocker is simpler, lighter, fixed near 0 Hz
Passes frequencies around center frequency, rejects the rest — constant 0 dB peak gain.
Implementation: RBJ biquad bandpass (constant peak, Q controls width)
import { bandpass } from 'audio-filter/effect'
bandpass(buffer, { fc: 1000, Q: 5, fs: 44100 }) // narrow
bandpass(buffer, { fc: 1000, Q: 0.5, fs: 44100 }) // wide
Use when: isolating frequency regions, radio effect, walkie-talkie simulation, band splitting
vs Resonator: bandpass is RBJ biquad (standard); resonator has constant peak gain — use resonator for modal synthesis
Constant peak-gain bandpass — peak amplitude stays fixed regardless of bandwidth.
$H(z) = \dfrac{1 - R^2}{1 - 2R\cos(\omega_0)z^{-1} + R^2 z^{-2}}$
Pole radius: $R = e^{-\pi \cdot bw / f_s}$ — controls bandwidth; $bw \to 0$ gives infinite Q
Peak gain: always 0 dB by construction — $(1 - R^2)$ normalizes the peak
import { resonator } from 'audio-filter/effect'
resonator(buffer, { fc: 440, bw: 20, fs: 44100 })
Use when: additive synthesis (bells, gongs), modal synthesis, formant bank building
vs Peaking EQ: resonator has fixed 0 dB peak; peaking EQ has variable gain — use resonator for synthesis, EQ for mixing
Band-reject filter — unity gain everywhere except a deep null at fc.
$H(z) = \dfrac{1 - 2\cos(\omega_0)z^{-1} + z^{-2}}{1 - 2\cos(\omega_0)z^{-1}(1-\alpha) + (1-2\alpha)z^{-2}}$
Q: controls notch width — $Q = 30$ is narrow (hum removal); $Q = 5$ is wider (resonance suppression)
Zeros: on the unit circle at $\pm\omega_0$ — exact null, independent of Q
import { notch } from 'audio-filter/effect'
notch(buffer, { fc: 50, Q: 30, fs: 44100 }) // remove 50 Hz mains hum
notch(buffer, { fc: 1000, Q: 10, fs: 44100 }) // suppress a resonance
Use when: mains hum removal (50/60 Hz), feedback cancellation, room mode suppression
vs Parametric EQ with negative gain: notch reaches −∞ dB exactly at fc; peaking EQ has finite attenuation
Shapes white noise to $1/f$ spectrum — equal energy per octave.
Spectrum: power spectral density $S(f) \propto 1/f$ — $-3,\text{dB/oct}$ slope, equal energy per octave
Implementation: Voss-McCartney algorithm — sum of white noise sources at octave-spaced update rates; approximated by cascaded first-order IIR filters
import { pinkNoise } from 'audio-filter/effect'
let buf = new Float64Array(1024)
for (let i = 0; i < buf.length; i++) buf[i] = Math.random() * 2 - 1
pinkNoise(buf, {}) // white → pink (−3 dB/oct spectral slope)
Use when: noise testing, psychoacoustic masking reference, procedural audio, natural-sounding noise
vs White noise: white noise has equal energy per Hz ($-0,\text{dB/oct}$); pink is perceptually flat
Applies a constant dB/octave slope — tilts the entire spectrum.
Model: first-order IIR approximation of fractional power-law spectrum $S(f) \propto f^\alpha$
slope: $\alpha = -3,\text{dB/oct}$ gives pink noise character; $-6,\text{dB/oct}$ gives brownian/red noise
import { spectralTilt } from 'audio-filter/effect'
spectralTilt(buffer, { slope: -3, fs: 44100 }) // −3 dB/oct: brownian noise character
spectralTilt(buffer, { slope: +3, fs: 44100 }) // +3 dB/oct: pre-emphasis for coding
Use when: matching microphone/speaker frequency responses, spectral coloring, noise synthesis
Lowpass with continuously variable bandwidth — smooth parameter automation without discontinuities.
Implementation: biquad lowpass with per-sample coefficient update using smooth interpolation
Property: no discontinuity when $f_c$ or $Q$ change — avoids clicks from abrupt coefficient jumps
import { variableBandwidth } from 'audio-filter/effect'
variableBandwidth(buffer, { fc: 2000, Q: 1.0, fs: 44100 })
Use when: LFO-modulated filter cutoff, automated EQ sweeps, smooth filter animation
vs Direct biquad: recalculating biquad coefficients per sample causes zipper noise; variable bandwidth avoids this
| I need to... | Use |
|---|---|
| Measure SPL or noise level | aWeighting (general), cWeighting (peak), itu468 (broadcast noise) |
| Measure loudness (LUFS/LU) | kWeighting |
| Decode vinyl audio | riaa |
| Model the cochlea / auditory system | gammatone, erbBank |
| Analyze a spectrum in octave bands | octaveBank |
| Psychoacoustic analysis / masking model | barkBank |
| MFCC / speech recognition features | melBank |
| Synth filter — warmth and resonance | moogLadder |
| Synth filter — acid / squelch | diodeLadder |
| Synth filter — 2-pole LP + HP | korg35 |
| Synth filter — multimode SVF | oberheim |
| Synthesize vowel sounds | formant |
| Transfer one sound's spectral shape to another | vocoder |
| Analyze/resynthesize speech, change pitch | lpcAnalysis / lpcSynthesize |
| Studio EQ at fixed ISO frequencies | graphicEq |
| Studio EQ with full per-band control | parametricEq |
| Split audio for multi-way speakers | crossover |
| Improve headphone stereo imaging | crossfeed |
| Bass/treble tone control | baxandall |
| One-knob tonal tilt | tilt |
| Standalone bass or treble shelf | lowShelf / highShelf |
| Remove DC offset | dcBlocker |
| Remove mains hum / suppress resonance | notch |
| Clean lowpass / anti-alias | lowpass |
| Remove rumble / subsonic content | highpass |
| Isolate a frequency band | bandpass |
| Create resonant combing | comb |
| Phase-shift without changing magnitude | allpass.first, allpass.second |
| Pre-process for audio coding | emphasis / deemphasis |
| Modal synthesis (bells, drums, rooms) | resonator |
| Generate pink / brown noise | pinkNoise + spectralTilt |
| Tilt spectrum for noise synthesis | spectralTilt |
| Smooth automated filter sweeps | variableBandwidth |
Why does my filter click when I change fc or Q?
Biquad coefficients change discontinuously between samples. Use variableBandwidth for smooth automated sweeps, or crossfade.
Why does my Moog/Diode filter blow up?
resonance=1 on Moog is intentional self-oscillation. Diode ladder is stable up to 0.95. Limit input gain before high resonance.
Does mutating params between calls reset state?
No — mutating the same object (params.fc = newFc) preserves state. Replacing the object (params = { fc: newFc }) loses it.
Why does .coefs(fs) return an SOS array instead of one biquad?
A-weighting needs 3 second-order sections; a single biquad can't represent a 6-pole response. Pass SOS arrays to digital-filter's filter() or freqz().
What sample rate should I use for accurate A-weighting? 96 kHz for IEC Class 1 across the full 20 Hz–20 kHz range. At 48 kHz error grows above 10 kHz (~1 dB at 10 kHz, ~4 dB at 20 kHz).
Chain filters
let p1 = { fc: 200, fs: 44100 }
let p2 = { R: 0.995 }
for (let buf of stream) {
dcBlocker(buf, p2) // DC removal first
moogLadder(buf, p1)
}
Stereo — independent state per channel
let pL = { fc: 1000, fs: 44100 }
let pR = { fc: 1000, fs: 44100 }
for (let [L, R] of stereoStream) {
moogLadder(L, pL)
moogLadder(R, pR)
}
Frequency analysis
import { freqz, mag2db } from 'digital-filter'
let sos = aWeighting.coefs(44100)
let { magnitude } = freqz(sos, 4096, 44100)
let db = mag2db(magnitude) // dB at 4096 frequencies, 20 Hz–Nyquist
Multi-band split
let bands = crossover([500, 5000], 4, 44100) // lo / mid / hi
let [lo, mid, hi] = bands.map(coefs => {
let buf = Float64Array.from(input) // copy — filter is in-place
filter(buf, { coefs })
return buf
})
// process independently, then sum
Notch out mains hum
let p = { fc: 50, Q: 30, fs: 44100 }
for (let buf of stream) notch(buf, p) // removes 50 Hz hum, flat elsewhere
Automate cutoff without clicks
let p = { fc: 200, Q: 1.0, fs: 44100 }
for (let buf of stream) {
p.fc = 200 + lfo() * 1800 // mutate in-place — state preserved
variableBandwidth(buf, p)
}
New params object on every call — state resets each block
// Wrong
for (let buf of stream) moogLadder(buf, { fc: 1000, fs: 44100 })
// Right — create once, reuse
let p = { fc: 1000, fs: 44100 }
for (let buf of stream) moogLadder(buf, p)
Shared params for stereo — channels corrupt each other's state
// Wrong
let p = { fc: 1000, fs: 44100 }
for (let [L, R] of stream) { moogLadder(L, p); moogLadder(R, p) }
// Right — one object per channel
let pL = { fc: 1000, fs: 44100 }, pR = { fc: 1000, fs: 44100 }
for (let [L, R] of stream) { moogLadder(L, pL); moogLadder(R, pR) }
Filtering the same buffer twice for multi-band — second band sees pre-filtered input
// Wrong
filter(buffer, { coefs: bands[0] })
filter(buffer, { coefs: bands[1] }) // input already filtered!
// Right — copy per band
let bufs = bands.map(b => { let c = Float64Array.from(buffer); filter(c, { coefs: b.coefs }); return c })
Omitting fs — silently uses 44100 Hz math on 48000 Hz audio
// Wrong — wrong cutoffs at 48 kHz
moogLadder(buffer, { fc: 1000 })
// Right
moogLadder(buffer, { fc: 1000, fs: 48000 })
AudioContextIEC 61672-1:2013, Electroacoustics — Sound level meters — Part 1: Specifications. Supersedes IEC 651:1979. ↩ ↩2
ITU-R BS.1770-4:2015, Algorithms to measure audio programme loudness and true-peak audio level. Adopted by EBU R128. ↩
ITU-R BS.468-4:1986, Measurement of audio-frequency noise voltage level in sound broadcasting. Originally CCIR 468, 1968. ↩
RIAA standard (1954); IEC 60098:1987, Analogue audio disk records and reproducing equipment. ↩
Patterson, R.D., Robinson, K., Holdsworth, J., McKeown, D., Zhang, C. & Allerhand, M. (1992). "Complex sounds and auditory images." Auditory Physiology and Perception, Pergamon, pp. 429–446. ↩
IEC 61260-1:2014, Electroacoustics — Octave-band and fractional-octave-band filters — Part 1: Specifications. ANSI S1.11:2004. ↩
Moore, B.C.J. & Glasberg, B.R. (1983). "Suggested formulae for calculating auditory-filter bandwidths and excitation patterns." JASA 74(3), pp. 750–753. Updated 1990. ↩
Zwicker, E. (1961). "Subdivision of the audible frequency range into critical bands." JASA 33(2), p. 248. ↩
O'Shaughnessy, D. (2000). Speech Communications: Human and Machine, 2nd ed. IEEE Press. ↩
Zavalishin, V. (2012). The Art of VA Filter Design. Native Instruments. ↩ ↩2 ↩3 ↩4
Moog, R.A. (1965). Voltage controlled electronic music modules. Patent US3475623. ↩
Pirkle, W.C. (2019). Designing Audio Effect Plugins in C++, 2nd ed. Routledge. ↩
Stilson, T. & Smith, J.O. (1996). "Analyzing the Moog VCF with considerations for digital implementation." Proc. ICMC. ↩
Dudley, H. (1939). "The vocoder." Bell Laboratories Record 17, pp. 122–126. Patent US2151091. ↩
Atal, B.S. & Hanauer, S.L. (1971). "Speech Analysis and Synthesis by Linear Prediction of the Speech Wave." JASA 50(2B), pp. 637–655. ↩
Zölzer, U. (2011). DAFX: Digital Audio Effects, 2nd ed. Wiley. ↩
Linkwitz, S. & Riley, R. (1976). "Active Crossover Networks for Non-Coincident Drivers." JAES 24(1), pp. 2–8. ↩
Bauer, B.B. (1961). "Stereophonic Earphones and Binaural Loudspeakers." JAES 9(2), pp. 148–151. ↩
Baxandall, P.J. (1952). "Transistor Tone-Control Design." Wireless World 58(10), pp. 402–405. ↩
FAQs
Audio filters – weighting, auditory, analog, speech, eq, effects
The npm package audio-filter receives a total of 63 weekly downloads. As such, audio-filter popularity was classified as not popular.
We found that audio-filter demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket Firewall blocks malicious VS Code and Open VSX extensions before install, protecting developers from compromised editor marketplaces.

Research
More than 140 Mastra npm packages were compromised in a supply chain attack that used a typosquatted dependency to deliver a cross-platform infostealer during installation.

Research
/Security News
A new npm package tests AI malware scanners with prompt injection, safety-triggering comments, context flooding, and obfuscated JavaScript.