4 releases
Uses new Rust 2024
| 0.3.2 | Jan 29, 2026 |
|---|---|
| 0.3.1 | Jan 2, 2026 |
| 0.3.0 | Jan 2, 2026 |
| 0.2.21 | Dec 21, 2025 |
#113 in Audio
Used in 5 crates
(2 directly)
200KB
3.5K
SLoC
IIR & FIR Filters
This crate provides IIR (Infinite Impulse Response) filter implementations for audio equalization.
Features
- Biquad Filters: Implementation of common biquad filter types
- Low-pass filters
- High-pass filters
- Peak/notch filters
- Low/high shelf filters
- Band-pass filters
- PEQ (Parametric Equalizer): Multi-band parametric equalization with advanced features
- SPL response computation
- Preamp gain calculation
- EqualizerAPO format export
- PEQ comparison and manipulation
- Filter Design: Specialized filter design algorithms
- Butterworth filters (lowpass/highpass)
- Linkwitz-Riley filters (lowpass/highpass)
- Response Computation: Calculate frequency and phase response
- Filter Conversion: Convert between different filter representations
Filter Types
Biquad Filter Types
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 filter
Usage Examples
Basic Biquad Filter
use 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);
Parametric EQ (PEQ)
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);
Filter Design
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 Functions Reference
Core PEQ Operations
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 format
Filter Design Functions
peq_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 filter
Utility Functions
bw2q(bw): Convert bandwidth in octaves to Q factorq2bw(q): Convert Q factor to bandwidth in octaves
Advanced Example: Building a Complete Audio Processor
use 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);
}
Key Concepts
PEQ Type
The Peq type is defined as Vec<(f64, Biquad)> where:
- The
f64is the weight/amplitude multiplier for each filter - The
Biquadis the individual filter definition - This allows for flexible filter chaining and weighting
Filter Order vs. Sections
- Butterworth filters: An Nth-order filter uses N/2 biquad sections (rounded up)
- Linkwitz-Riley filters: Special case of Butterworth designed for crossovers
- Higher orders provide steeper rolloff but more computational cost
Q Factor Guidelines
- Q < 0.5: Wide, gentle curves
- Q = 0.707: Butterworth response (maximally flat)
- Q = 1.0: Good compromise for most applications
- Q > 5: Very narrow, surgical corrections
- Q > 10: Extreme precision, potential ringing
Fast PEQ Loudness Compensation
Overview
When 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.
The Problem
Traditional approach (slow):
- Apply PEQ filters to audio
- Run full Replay Gain analysis (EBU R128)
- Decode entire audio file
- Process all samples through loudness meter
- Takes seconds for each file
The Solution
Fast analytical approach (microseconds):
- Analyze PEQ frequency response
- Apply perceptual weighting (K-weighting or A-weighting)
- Compute loudness change analytically
- Takes microseconds, no audio processing needed
API Usage
Basic Example
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
Choosing Weighting Method
K-weighting ("k"):
- Based on EBU R128 standard
- Similar to how Replay Gain works
- Better for general music playback
- Recommended for most use cases
A-weighting ("a"):
- Classic loudness measurement
- Emphasizes mid-range, de-emphasizes bass
- Better for voice/speech content
- More tolerant of bass boost
Real-World Examples
From the test output:
-
Mid-range boost (+6 dB at 1 kHz):
- K-weighted: -1.55 dB (compensate by reducing 1.55 dB)
- A-weighted: -1.36 dB
- Effect: Similar compensation since 1kHz is important in both curves
-
Bass boost (+6 dB at 100 Hz):
- K-weighted: -1.40 dB
- A-weighted: -0.06 dB
- Effect: A-weighting barely compensates (bass less important perceptually)
-
Treble boost (+6 dB at 8 kHz):
- K-weighted: -2.40 dB
- A-weighted: -0.99 dB
- Effect: K-weighting has high-frequency boost, so more compensation needed
-
V-shaped EQ (bass+4dB, mid-3dB, treble+3dB):
- K-weighted: -1.76 dB
- A-weighted: +0.14 dB (slight boost!)
- Effect: A-weighting sees net reduction in important frequencies
Integration with Audio Pipeline
Option 1: Combine with Anti-Clipping
// 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
Option 2: Loudness Compensation Only
// 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
Option 3: Use with CamillaDSP
# In your CamillaDSP config:
filters:
peq_with_compensation:
type: Conv
parameters:
# ... your PEQ biquads ...
- type: Gain
parameters:
gain: -1.55 # from peq_loudness_gain()
Performance Comparison
| 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.
When to Use Each Method
Use peq_loudness_gain() when:
- Real-time EQ adjustment
- Previewing EQ changes
- Batch processing many files
- Interactive audio applications
- You want instant results
Use full Replay Gain when:
- Normalizing existing audio files
- Maximum accuracy required
- One-time analysis acceptable
- Working with non-EQ'd audio
Technical Details
How It Works
- Generate 500 logarithmically-spaced frequency points (20 Hz - 20 kHz)
- Compute PEQ frequency response at each point
- Apply perceptual weighting curve (K or A)
- Integrate weighted energy change
- Convert to dB gain compensation
Weighting Curves
K-weighting approximation:
- High-pass: 4th order Butterworth at 38 Hz
- High-shelf: +4 dB above 1500 Hz
A-weighting (IEC 61672-1):
- Follows standard A-weighting formula
- Peak sensitivity ~4 kHz
- -19 dB at 100 Hz, -9 dB at 500 Hz
Limitations
- Assumes broadband audio: Works best with music/speech, not pure tones
- Ignores phase: Only analyzes magnitude response
- Approximation: Not identical to sample-by-sample EBU R128
- No masking effects: Doesn't model psychoacoustic masking
Despite these limitations, the method provides good perceptual accuracy for practical use.
Future Improvements
Potential enhancements:
- More accurate K-weighting filter implementation
- Support for custom weighting curves
- Integration with audio file metadata
- Automatic weighting selection based on content type
See Also
src-iir/src/mod.rs: Implementationsrc-audio/src/replaygain.rs: Full Replay Gain implementation- EBU R128 standard: https://tech.ebu.ch/docs/r/r128.pdf
- A-weighting standard: IEC 61672-1
License
GPL-3.0-or-later
Dependencies
~7MB
~132K SLoC