| Crates.io | math-iir-fir |
| lib.rs | math-iir-fir |
| version | 0.3.1 |
| created_at | 2025-12-21 12:58:22.70934+00 |
| updated_at | 2026-01-02 09:57:40.926192+00 |
| description | Math-Audio IIR and FIR filters |
| homepage | |
| repository | https://github.com/pierreaubert/math-audio |
| max_upload_size | |
| id | 1997928 |
| size | 190,236 |
This crate provides IIR (Infinite Impulse Response) filter implementations for audio equalization.
BiquadFilterType::Lowpass: Low-pass filterBiquadFilterType::Highpass: High-pass filterBiquadFilterType::HighpassVariableQ: High-pass filter with variable QBiquadFilterType::Bandpass: Band-pass filterBiquadFilterType::Peak: Peak/parametric filterBiquadFilterType::Notch: Notch filterBiquadFilterType::Lowshelf: Low-shelf filterBiquadFilterType::Highshelf: High-shelf filteruse math_audio_iir_fir::{Biquad, BiquadFilterType};
// Create a peak filter at 1kHz with Q=1.0 and 3dB gain
let filter = Biquad::new(
BiquadFilterType::Peak,
1000.0, // frequency
48000.0, // sample rate
1.0, // Q factor
3.0 // gain in dB
);
// Apply filter to audio samples (requires mut for state updates)
// let mut filter = Biquad::new(...); // <- use mut if processing samples
// let output = filter.process(input_sample);
// Calculate frequency response at 1kHz
let response_db = filter.log_result(1000.0);
print!("Response at 1kHz: {:.2} dB", response_db);
use math_audio_iir_fir::{Biquad, BiquadFilterType, Peq, peq_spl, peq_preamp_gain, peq_format_apo};
use ndarray::Array1;
// Create a multi-band EQ
let mut peq: Peq = Vec::new();
// Add a high-pass filter at 80Hz
let hp = Biquad::new(BiquadFilterType::Highpass, 80.0, 48000.0, 0.707, 0.0);
peq.push((1.0, hp));
// Add a peak filter to boost mids at 1kHz
let peak = Biquad::new(BiquadFilterType::Peak, 1000.0, 48000.0, 1.5, 4.0);
peq.push((1.0, peak));
// Add a high-shelf to roll off highs
let hs = Biquad::new(BiquadFilterType::Highshelf, 8000.0, 48000.0, 0.8, -2.0);
peq.push((1.0, hs));
// Calculate frequency response
let freqs = Array1::logspace(10.0, 20.0_f64.log10(), 20000.0_f64.log10(), 1000);
let response = peq_spl(&freqs, &peq);
// Calculate preamp gain to prevent clipping
let preamp = peq_preamp_gain(&peq);
print!("Recommended preamp: {:.1} dB", preamp);
// Export to EqualizerAPO format
let apo_config = peq_format_apo("My Custom EQ", &peq);
print!("{}", apo_config);
use math_audio_iir_fir::{peq_butterworth_lowpass, peq_linkwitzriley_highpass};
// Create a 4th-order Butterworth lowpass at 2kHz
let lp_filter = peq_butterworth_lowpass(4, 2000.0, 48000.0);
print!("Butterworth LP has {} sections", lp_filter.len());
// Create a 4th-order Linkwitz-Riley highpass at 2kHz
let hp_filter = peq_linkwitzriley_highpass(4, 2000.0, 48000.0);
print!("LR HP has {} sections", hp_filter.len());
// These can be used for crossover design
peq_spl(freq, peq): Calculate SPL response across frequenciespeq_equal(left, right): Compare two PEQs for equalitypeq_preamp_gain(peq): Calculate recommended preamp gainpeq_preamp_gain_max(peq): Calculate conservative preamp gain with safety marginpeq_format_apo(comment, peq): Export PEQ to EqualizerAPO formatpeq_butterworth_q(order): Calculate Q values for Butterworth filterspeq_butterworth_lowpass(order, freq, srate): Create Butterworth lowpass filterpeq_butterworth_highpass(order, freq, srate): Create Butterworth highpass filterpeq_linkwitzriley_q(order): Calculate Q values for Linkwitz-Riley filterspeq_linkwitzriley_lowpass(order, freq, srate): Create Linkwitz-Riley lowpass filterpeq_linkwitzriley_highpass(order, freq, srate): Create Linkwitz-Riley highpass filterbw2q(bw): Convert bandwidth in octaves to Q factorq2bw(q): Convert Q factor to bandwidth in octavesuse math_audio_iir_fir::*;
use ndarray::Array1;
fn create_studio_eq() -> Peq {
let mut peq = Vec::new();
// High-pass filter to remove subsonic content
let hp = peq_butterworth_highpass(2, 20.0, 48000.0);
peq.extend(hp);
// Presence boost
let presence = Biquad::new(BiquadFilterType::Peak, 3000.0, 48000.0, 1.2, 2.5);
peq.push((1.0, presence));
// Air band enhancement
let air = Biquad::new(BiquadFilterType::Highshelf, 10000.0, 48000.0, 0.9, 1.5);
peq.push((1.0, air));
peq
}
fn analyze_eq(peq: &Peq) {
// Generate frequency sweep
let freqs = Array1::logspace(10.0, 20.0_f64.log10(), 20000.0_f64.log10(), 200);
// Calculate response
let response = peq_spl(&freqs, peq);
// Find peak response
let max_gain = response.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
let min_gain = response.iter().fold(f64::INFINITY, |a, &b| a.min(b));
println!("EQ Analysis:");
println!(" Peak gain: {:.2} dB", max_gain);
println!(" Min gain: {:.2} dB", min_gain);
println!(" Dynamic range: {:.2} dB", max_gain - min_gain);
println!(" Recommended preamp: {:.2} dB", peq_preamp_gain(peq));
}
fn main() {
let studio_eq = create_studio_eq();
analyze_eq(&studio_eq);
// Export for use in EqualizerAPO
let config = peq_format_apo("Studio EQ v1.0", &studio_eq);
println!("\nEqualizerAPO Configuration:");
println!("{}", config);
}
The Peq type is defined as Vec<(f64, Biquad)> where:
f64 is the weight/amplitude multiplier for each filterBiquad is the individual filter definitionWhen applying parametric EQ (PEQ) to audio, the spectral balance and perceived loudness changes. This document describes how to use the fast loudness compensation feature to maintain similar loudness without running full Replay Gain analysis.
Traditional approach (slow):
Fast analytical approach (microseconds):
use math_audio_iir_fir::{Biquad, BiquadFilterType, Peq, peq_loudness_gain, peq_preamp_gain};
// Create your PEQ filters
let bass_boost = Biquad::new(BiquadFilterType::Peak, 100.0, 48000.0, 1.0, 6.0);
let peq: Peq = vec![(1.0, bass_boost)];
// Get gain adjustments
let anti_clip = peq_preamp_gain(&peq); // Prevents clipping: -6.00 dB
let loudness_k = peq_loudness_gain(&peq, "k"); // K-weighted: -1.40 dB
let loudness_a = peq_loudness_gain(&peq, "a"); // A-weighted: -0.06 dB
// Apply to your audio pipeline:
// total_gain = anti_clip + loudness_k // or use loudness_a
K-weighting ("k"):
A-weighting ("a"):
From the test output:
Mid-range boost (+6 dB at 1 kHz):
Bass boost (+6 dB at 100 Hz):
Treble boost (+6 dB at 8 kHz):
V-shaped EQ (bass+4dB, mid-3dB, treble+3dB):
// Safest: prevent clipping AND maintain loudness
let total_gain = peq_preamp_gain(&peq) + peq_loudness_gain(&peq, "k");
// Apply PEQ filters + total_gain to audio
// If you know your PEQ won't clip (e.g., cuts only)
let total_gain = peq_loudness_gain(&peq, "k");
// Apply PEQ filters + total_gain to audio
# In your CamillaDSP config:
filters:
peq_with_compensation:
type: Conv
parameters:
# ... your PEQ biquads ...
- type: Gain
parameters:
gain: -1.55 # from peq_loudness_gain()
| Method | Time per file | Requires audio? | Accuracy |
|---|---|---|---|
| Replay Gain (EBU R128) | 1-10 seconds | Yes | 100% (reference) |
| peq_loudness_gain() | < 1 millisecond | No | ~90-95% |
The analytical method is 1000x faster while providing good perceptual accuracy.
peq_loudness_gain() when:K-weighting approximation:
A-weighting (IEC 61672-1):
Despite these limitations, the method provides good perceptual accuracy for practical use.
Potential enhancements:
src-iir/src/mod.rs: Implementationsrc-audio/src/replaygain.rs: Full Replay Gain implementationGPL-3.0-or-later