Skip to main content

LinearFftState

Struct LinearFftState 

Source
pub struct LinearFftState {
Show 19 fields pub p: usize, pub n: usize, pub num_partitions: usize, pub num_bins: usize, pub rfft: RfftPlanner<f32>, pub h_fdl_re: AlignedVec<f32>, pub h_fdl_im: AlignedVec<f32>, pub fdl_re: AlignedVec<f32>, pub fdl_im: AlignedVec<f32>, pub fdl_write_idx: usize, pub input_buf: AlignedVec<f32>, pub fft_re: AlignedVec<f32>, pub fft_im: AlignedVec<f32>, pub acc_re: AlignedVec<f32>, pub acc_im: AlignedVec<f32>, pub output_buf: AlignedVec<f32>, pub tail_output_buf: AlignedVec<f32>, pub sample_counter: usize, pub isa: InstructionSet,
}
Expand description

State for zero-latency partitioned FFT (overlap-save) convolution.

Handles the tail portion of the impulse response (samples from partition size P to the end N-1). The head (samples 0..P) is computed directly in the time domain by LinearModel::process_sample.

§Buffer Layout

BufferSizePurpose
h_fdl_re/imK × (P+1)Pre-computed tail IR spectra (flat)
fdl_re/imK × (P+1)Circular frequency delay line (flat)
input_buf2PInput window for forward RFFT
fft_re/imP+1Forward RFFT output (compact spectrum)
acc_re/imP+1Complex MAC accumulation
output_buf2PIFFT output (time domain)
tail_output_bufPValid tail samples ready for consumption

where K = ceil((N-P)/P) is the number of tail partitions.

The spectrum buffers (h_fdl_* and fdl_*) are stored as flat AlignedVec<f32> with stride P+1 (number of bins). Partition k occupies indices [k * num_bins .. (k+1) * num_bins]. This flat layout avoids pointer indirection in the hot-path MAC loop and keeps all FDL data in a single contiguous region for cache locality.

Fields§

§p: usize

Partition size P (head length = tail block size). Must be a power of two ≤ N.

§n: usize

Total receptive field N (= IR length).

§num_partitions: usize

Number of tail partitions K = ceil((N-P)/P).

§num_bins: usize

Number of complex bins per partition = P + 1.

§rfft: RfftPlanner<f32>

Real-to-complex FFT planner for block size 2P.

§h_fdl_re: AlignedVec<f32>

Pre-computed real spectra of the tail IR partitions. Flat buffer of length K × num_bins. Partition k starts at index k * num_bins.

§h_fdl_im: AlignedVec<f32>

Pre-computed imaginary spectra of the tail IR partitions.

§fdl_re: AlignedVec<f32>

Frequency delay line — real part. Flat circular buffer of past input spectra, length K × num_bins. Partition k starts at k * num_bins.

§fdl_im: AlignedVec<f32>

Frequency delay line — imaginary part.

§fdl_write_idx: usize

Circular write index into fdl_re / fdl_im (0..K-1). Points to the next position that will be written. In process_tail_block, the FDL is read before writing: old spectra are consumed for the tail convolution, then the new input spectrum replaces the oldest entry.

§input_buf: AlignedVec<f32>

Input window buffer of size 2P for the forward RFFT. Filled from the MirroredBuffer history in LinearModel.

§fft_re: AlignedVec<f32>

Forward RFFT output — real bins (size P+1).

§fft_im: AlignedVec<f32>

Forward RFFT output — imaginary bins (size P+1).

§acc_re: AlignedVec<f32>

Complex MAC accumulation buffer — real part (size P+1).

§acc_im: AlignedVec<f32>

Complex MAC accumulation buffer — imaginary part (size P+1).

§output_buf: AlignedVec<f32>

IFFT output buffer (size 2P). Valid tail samples reside in indices P..2P-1.

§tail_output_buf: AlignedVec<f32>

Circular buffer holding the P valid tail output samples from the most recent process_tail_block call. Read sequentially by LinearModel::process_sample in the FFT path.

§sample_counter: usize

Current read position within tail_output_buf (0 ≤ sample_counter < P). Incremented by LinearModel::process_sample; triggers a new tail block computation when it reaches P.

§isa: InstructionSet

Instruction set captured at construction time to avoid runtime CPU feature checks in the audio hot path.

Implementations§

Source§

impl LinearFftState

Source

pub fn reset(&mut self)

Resets all runtime buffers to zero and re-initializes counters.

This operation is allocation-free: it only zero-fills the existing pre-allocated buffers. The pre-computed h_fdl_* spectra are not modified (they depend only on the IR, which is static).

Source

pub fn process_tail_block(&mut self, input_window: &[f32])

Processes one block of tail convolution using overlap-save FFT.

input_window must be a contiguous slice of the last 2P input samples (oldest to newest), typically obtained from the MirroredBuffer via history[write_pos - 2*P .. write_pos].

After this call completes, tail_output_buf contains P valid tail output samples ready for sequential per-sample consumption via sample_counter.

§Algorithm (overlap-save, zero-latency hybrid)
  1. Compute forward RFFT of the 2P-sample input window.
  2. Read past input spectra from the circular FDL (delays P, 2P, …, K×P), multiply by the corresponding pre-computed tail IR spectra in the frequency domain using SIMD complex MAC.
  3. Store the new input spectrum in the FDL and advance the write index (overwrites the oldest entry, now K+1 blocks ago).
  4. Inverse RFFT the accumulated spectrum back to time domain.
  5. Extract the valid P output samples (overlap-save: indices P..2P-1) into tail_output_buf.
§RT-Safety

Zero heap allocation, zero locks, zero panics in production (debug assertions only). All buffers were pre-allocated at construction time. The ISA for SIMD dispatch was captured once at construction time — no runtime CPU feature checks on the hot path.

Source§

impl LinearFftState

Source

pub fn new(p: usize, weights: &[f32]) -> Result<Self, NamErrorCode>

Creates a new LinearFftState for the given impulse response.

p is the partition size (head length), which must be a power of two and ≤ weights.len(). weights are the IR samples in forward-time order (i.e., weights[0] is the response at the current sample). They are read-only: only the tail portion (weights[P..N]) is used by this state; the head (weights[0..P]) is convolved directly by LinearModel.

All internal buffers are pre-allocated with 64-byte alignment (AlignedVec<f32>) at construction time. No further heap allocations occur during processing.

§Panics

Panics if p is not a power of two, or if p > weights.len().

Source

pub fn h_fdl_re_partition(&self, k: usize) -> &[f32]

Returns a slice over the real spectrum of tail partition k.

§Panics

Panics if k >= num_partitions.

Trait Implementations§

Source§

impl Debug for LinearFftState

Source§

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

Formats the value using the given formatter. 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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(value: T, _simd: S) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

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.