Skip to content

Instantly share code, notes, and snippets.

@sletz
Last active May 1, 2025 05:19
Show Gist options
  • Select an option

  • Save sletz/70d4a6b54e7c3336c3c207184d9fcdbe to your computer and use it in GitHub Desktop.

Select an option

Save sletz/70d4a6b54e7c3336c3c207184d9fcdbe to your computer and use it in GitHub Desktop.
Decramped peak filter algorithm in Faust
import("stdfaust.lib");
// =============================================================================
// Decramped Peaking EQ Filter (Second Order Biquad)
// Based on the Audio EQ Cookbook by Robert Bristow-Johnson
// https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
//
// This implementation uses the standard RBJ formulas which inherently
// account for frequency warping (i.e., they are "decramped").
// =============================================================================
peak_eq_decramped(fc, gain_db, q) = filter with {
// Filter parameters and intermediate variables
// Intermediate calculations based on RBJ formulas
A = pow(10.0, gain_db / 40.0); // Amplitude ratio from dB gain
// Note: /40 for peaking EQ, /20 for shelving
omega = 2.0 * ma.PI * fc / ma.SR; // Angular frequency (radians/sample)
// Pre-warping is implicitly handled by using tan() in alpha calc via sin/cos
// Alpha determines the filter bandwidth/Q shape
alpha = sin(omega) / (2.0 * q);
// --- Calculate Filter Coefficients ---
// Coefficients derived from RBJ's Peaking EQ formulas
// Numerator coefficients (b)
b0 = 1.0 + alpha * A;
b1 = -2.0 * cos(omega);
b2 = 1.0 - alpha * A;
// Denominator coefficients (a)
// a0 is the normalization factor
a0 = 1.0 + alpha / A;
a1 = -2.0 * cos(omega); // Same as b1
a2 = 1.0 - alpha / A;
// --- Normalization ---
// Divide all coefficients by a0 to get the standard biquad form
// where the coefficient for y[n] is 1.
// Add small epsilon to prevent division by zero if A is extremely small (large cut)
// or if alpha is zero (fc=0 or fc=SR/2).
a0_safe = max(ma.EPSILON, a0); // Use machine epsilon as the floor
norm_b0 = b0 / a0_safe;
norm_b1 = b1 / a0_safe;
norm_b2 = b2 / a0_safe;
norm_a1 = a1 / a0_safe; // Note: tf2 expects the negated coefficients a1, a2
// but the RBJ formulas directly give these negated versions
// for the y[n-1], y[n-2] terms.
norm_a2 = a2 / a0_safe; // So we use them directly here.
// --- Filter Implementation ---
// Use Faust's built-in second-order IIR filter (biquad) function
// tf2(b0, b1, b2, a1, a2) implements:
// y(n) = b0*x(n) + b1*x(n-1) + b2*x(n-2) - a1*y(n-1) - a2*y(n-2)
filter = fi.tf2(norm_b0, norm_b1, norm_b2, norm_a1, norm_a2);
};
// =============================================================================
// User Interface and Processing Block
// =============================================================================
// --- UI Controls ---
freq = hslider("peakF [unit:Hz] [style:log]", 1000, 20, 20000, 1) : si.smoo;
gain_db = hslider("peakGain [unit:dB]", 0, -24, 24, 0.1) : si.smoo;
q = hslider("peakQ [style:log]", 1.0, 0.1, 18, 0.01) : si.smoo;
// --- Processing ---
// Apply the decramped peak EQ filter to the stereo input signal
process = peak_eq_decramped(freq, gain_db, q);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment