Skip to main content

TwoStageFFTConvolver

Struct TwoStageFFTConvolver 

Source
pub struct TwoStageFFTConvolver<F: FftNum> {
Show 14 fields ir_len: usize, head_block_size: usize, tail_block_size: usize, head_convolver: FFTConvolver<F>, tail_convolver0: FFTConvolver<F>, tail_convolver: FFTConvolver<F>, tail_input: Vec<F>, tail_input_fill: usize, tail_output0: Vec<F>, tail_precalculated0: Vec<F>, tail_output: Vec<F>, tail_precalculated: Vec<F>, processing_buffer: Vec<F>, precalculated_pos: usize,
}
Expand description

TwoStageFFTConvolver Implementation of a partitioned FFT convolution algorithm with non-uniform block sizes.

Uses two different block sizes: a small head block size for low latency and a larger tail block size for efficient processing of long impulse responses. Internally uses three FFTConvolver instances:

  • Head: Processes the first portion of the IR at the small block size (zero latency)
  • Tail0: Transition stage that bridges the latency gap between head and tail
  • Tail: Processes the remaining IR at the larger block size (efficient)

Some notes on how to use it:

  • After initialization with an impulse response, subsequent data portions of arbitrary length can be convolved. The convolver internally can handle this by using appropriate buffering.
  • The convolver works without “latency” (except for the required processing time, of course), i.e. the output always is the convolved input for each processing call.
  • The convolver is suitable for real-time processing which means that no “unpredictable” operations like allocations, locking, API calls, etc. are performed during processing (all necessary allocations and preparations take place during initialization).

Fields§

§ir_len: usize§head_block_size: usize§tail_block_size: usize§head_convolver: FFTConvolver<F>§tail_convolver0: FFTConvolver<F>§tail_convolver: FFTConvolver<F>§tail_input: Vec<F>§tail_input_fill: usize§tail_output0: Vec<F>§tail_precalculated0: Vec<F>§tail_output: Vec<F>§tail_precalculated: Vec<F>§processing_buffer: Vec<F>§precalculated_pos: usize

Implementations§

Source§

impl<F: FftNum> TwoStageFFTConvolver<F>

Source

pub fn init( &mut self, head_block_size: usize, tail_block_size: usize, impulse_response: &[F], ) -> Result<(), FFTConvolverError>

Initializes the two-stage convolver with an impulse response

This method sets up all internal buffers and prepares the convolver for processing. The head block size determines latency and will be rounded up to the next power of 2. The tail block size determines efficiency for the tail portion and will also be rounded up to the next power of 2.

Use compute_tail_block_size to calculate an optimal tail block size based on García’s formula.

All memory allocations happen during initialization, making subsequent processing operations real-time safe.

§Arguments
  • head_block_size - Block size for the head convolver (determines latency). Will be rounded up to the next power of 2. Must be > 0.
  • tail_block_size - Block size for the tail convolver (determines efficiency). Will be rounded up to the next power of 2. Must be > 0. If smaller than head_block_size, the two values are automatically swapped.
  • impulse_response - The impulse response to convolve with. Can be empty.
§Returns

Returns BlockSizeZero if either block size is 0.

§Example
use fft_convolver::{TwoStageFFTConvolver, compute_tail_block_size};

let ir = vec![0.5_f32; 10000];
let head_block_size = 64;
let tail_block_size = compute_tail_block_size(head_block_size, ir.len());

let mut convolver = TwoStageFFTConvolver::default();
convolver.init(head_block_size, tail_block_size, &ir).unwrap();
Source

pub fn init_default( &mut self, head_block_size: usize, impulse_response: &[F], ) -> Result<(), FFTConvolverError>

Initializes the two-stage convolver with an automatically computed tail block size

This is a convenience method that computes the optimal tail block size using García’s formula and then calls init.

§Arguments
  • head_block_size - Block size for the head convolver (determines latency). Will be rounded up to the next power of 2. Must be > 0.
  • impulse_response - The impulse response to convolve with. Can be empty.
§Returns

Returns BlockSizeZero if head_block_size is 0.

§Example
use fft_convolver::TwoStageFFTConvolver;

let ir = vec![0.5_f32; 10000];
let mut convolver = TwoStageFFTConvolver::default();
convolver.init_default(64, &ir).unwrap();
Source

pub fn set_response( &mut self, impulse_response: &[F], ) -> Result<(), FFTConvolverError>

Updates the impulse response without reallocating buffers

This method allows changing the impulse response at runtime while maintaining real-time safety by avoiding allocations. The new impulse response must not exceed the length of the original impulse response used during initialization.

§Arguments
  • impulse_response - The new impulse response (must be ≤ original length)
§Returns

Returns ImpulseResponseExceedsCapacity if the new impulse response is longer than the original one.

§Example
use fft_convolver::{TwoStageFFTConvolver, compute_tail_block_size};

let ir1 = vec![0.5_f32; 10000];
let head_block_size = 64;
let tail_block_size = compute_tail_block_size(head_block_size, ir1.len());

let mut convolver = TwoStageFFTConvolver::default();
convolver.init(head_block_size, tail_block_size, &ir1).unwrap();

let ir2 = vec![0.8_f32; 5000];
convolver.set_response(&ir2).unwrap();
Source

pub fn process( &mut self, input: &[F], output: &mut [F], ) -> Result<(), FFTConvolverError>

Convolves the input samples with the impulse response and outputs the result

This is a real-time safe operation that performs no allocations. Internal buffering handles arbitrary sizes and ensures the output is always properly aligned with the input (zero latency except for processing time).

§Arguments
  • input - The input samples to convolve
  • output - Buffer to write the convolution result. Must have the same length as input.
§Returns

Returns InputOutputLengthMismatch if input and output have different lengths. Returns Fft error if an FFT operation fails.

§Example
use fft_convolver::{TwoStageFFTConvolver, compute_tail_block_size};

let ir = vec![0.5_f32; 10000];
let head_block_size = 64;
let tail_block_size = compute_tail_block_size(head_block_size, ir.len());

let mut convolver = TwoStageFFTConvolver::default();
convolver.init(head_block_size, tail_block_size, &ir).unwrap();

let input = vec![1.0_f32; 256];
let mut output = vec![0.0_f32; 256];
convolver.process(&input, &mut output).unwrap();
Source

pub fn reset(&mut self)

Clears the internal processing state while preserving the impulse response

This real-time safe operation resets all internal buffers that store the convolution state, effectively removing any “history” or “tail” from previous processing. The impulse response configuration remains intact, so processing can continue immediately.

This is useful when handling stream discontinuities such as:

  • Seeking in audio playback
  • Pause/resume operations with large time gaps
  • Switching between different audio sources

After calling reset(), the next process() call will produce output as if the convolver had just been initialized.

§Example
use fft_convolver::{TwoStageFFTConvolver, compute_tail_block_size};

let ir = vec![0.5_f32; 10000];
let head_block_size = 64;
let tail_block_size = compute_tail_block_size(head_block_size, ir.len());

let mut convolver = TwoStageFFTConvolver::default();
convolver.init(head_block_size, tail_block_size, &ir).unwrap();

let input = vec![1.0_f32; 256];
let mut output = vec![0.0_f32; 256];
convolver.process(&input, &mut output).unwrap();

convolver.reset();
convolver.process(&input, &mut output).unwrap();

Trait Implementations§

Source§

impl<F: Clone + FftNum> Clone for TwoStageFFTConvolver<F>

Source§

fn clone(&self) -> TwoStageFFTConvolver<F>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<F: Debug + FftNum> Debug for TwoStageFFTConvolver<F>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<F: FftNum> Default for TwoStageFFTConvolver<F>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.