pub struct OverlapSaveConvolver<T: Sample = f64> { /* private fields */ }Expand description
Streaming FFT convolution engine (overlap-save).
Designed for real-time block processing such as room-correction FIR filtering with long impulse responses (thousands of taps), where direct time-domain convolution is O(block · taps) and becomes the dominant cost. This engine is O(block · log N) per block and turns a multi-thousand-tap filter from a real-time bottleneck into a negligible one.
§How it works
The impulse response is FFT’d once at construction and its spectrum cached.
Each call to process_block transforms a window made of
the previous tail plus the new input block, multiplies by the cached spectrum,
inverse-transforms, and keeps only the alias-free tail — the standard
overlap-save method. After construction there are no heap allocations on
the processing path, so it is safe to call from an audio callback.
§Semantics
Computes true linear convolution y[n] = Σ_k h[k]·x[n−k], producing exactly
as many output samples as input samples (the filter’s own latency is carried
in h: ~taps/2 for a linear-phase IR, ~0 for a minimum-phase IR). The block
length is fixed at construction and every input slice must match it.
§Example
use spectrograms::OverlapSaveConvolver;
use std::num::NonZeroUsize;
// A trivial 3-tap moving-average impulse response.
let ir = [1.0f32 / 3.0; 3];
let block = NonZeroUsize::new(256).unwrap();
let mut conv = OverlapSaveConvolver::new(&ir, block).unwrap();
let input = vec![1.0f32; 256];
let mut output = vec![0.0f32; 256];
conv.process_block(&input, &mut output).unwrap();
// Steady-state output of a normalized 3-tap average of all-ones is ~1.0.
assert!((output[100] - 1.0).abs() < 1e-5);Implementations§
Source§impl<T: Sample> OverlapSaveConvolver<T>
impl<T: Sample> OverlapSaveConvolver<T>
Sourcepub fn new(ir: &[T], block: NonZeroUsize) -> SpectrogramResult<Self>
pub fn new(ir: &[T], block: NonZeroUsize) -> SpectrogramResult<Self>
Build a convolver for impulse response ir and a fixed block size.
The FFT size is next_power_of_two(block + ir.len() − 1). The impulse
response is transformed once here; this is the only place that allocates.
§Errors
Returns an error if ir is empty or the FFT plan cannot be built.
Sourcepub const fn block_size(&self) -> usize
pub const fn block_size(&self) -> usize
The fixed block size this convolver expects.
Sourcepub fn process_block(
&mut self,
input: &[T],
output: &mut [T],
) -> SpectrogramResult<()>
pub fn process_block( &mut self, input: &[T], output: &mut [T], ) -> SpectrogramResult<()>
Filter one block in place-free fashion: output[i] = (h * x)[i].
input and output must both have length block_size.
No allocation occurs.
§Errors
Returns an error if either slice length differs from the block size, or if an FFT step fails.