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
| Buffer | Size | Purpose |
|---|---|---|
h_fdl_re/im | K × (P+1) | Pre-computed tail IR spectra (flat) |
fdl_re/im | K × (P+1) | Circular frequency delay line (flat) |
input_buf | 2P | Input window for forward RFFT |
fft_re/im | P+1 | Forward RFFT output (compact spectrum) |
acc_re/im | P+1 | Complex MAC accumulation |
output_buf | 2P | IFFT output (time domain) |
tail_output_buf | P | Valid 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: usizePartition size P (head length = tail block size). Must be a power
of two ≤ N.
n: usizeTotal receptive field N (= IR length).
num_partitions: usizeNumber of tail partitions K = ceil((N-P)/P).
num_bins: usizeNumber 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: usizeCircular 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: usizeCurrent 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: InstructionSetInstruction set captured at construction time to avoid runtime CPU feature checks in the audio hot path.
Implementations§
Source§impl LinearFftState
impl LinearFftState
Sourcepub fn reset(&mut self)
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).
Sourcepub fn process_tail_block(&mut self, input_window: &[f32])
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)
- Compute forward RFFT of the
2P-sample input window. - 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. - Store the new input spectrum in the FDL and advance the write
index (overwrites the oldest entry, now
K+1blocks ago). - Inverse RFFT the accumulated spectrum back to time domain.
- Extract the valid
Poutput samples (overlap-save: indicesP..2P-1) intotail_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
impl LinearFftState
Sourcepub fn new(p: usize, weights: &[f32]) -> Result<Self, NamErrorCode>
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().
Sourcepub fn h_fdl_re_partition(&self, k: usize) -> &[f32]
pub fn h_fdl_re_partition(&self, k: usize) -> &[f32]
Trait Implementations§
Auto Trait Implementations§
impl Freeze for LinearFftState
impl RefUnwindSafe for LinearFftState
impl Send for LinearFftState
impl Sync for LinearFftState
impl Unpin for LinearFftState
impl UnsafeUnpin for LinearFftState
impl UnwindSafe for LinearFftState
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.