AUDIO
Source snapshot · 12 October 2026

Prepared signal processing

Native-precision convolution schedules and banks

Convolver32 and Convolver64 own one prepared finite response of 1..=262,144 taps and run every operation (direct head, FFT butterflies, spectral products and sums, overlap and output) in the named native sample precision. FFT twiddles are computed in f64 during preparation and, for f32, explicitly rounded to Complex32. ConvolutionPartitioning fixes the schedule at preparation:

info() reports original taps, direct taps, each stage's offset, taps, partition count and FFT size, the exact latency, the tail (taps - 1 + latency) and declared and actual numeric bytes. stage_work() reports completed FFT blocks per stage (saturating counters, no timing). Every processed source frame produces one output frame; supply explicit zeros to drain the tail. Reset clears all history and keeps the response and schedule.

Preparation checks the schedule, response finiteness, a maximum block of 1..=65,536 frames and a caller numeric byte budget of 1 byte..=256 MiB before any allocation, reserves storage fallibly, and rechecks actual capacities. Allocator overhead is excluded from the budget. Processing and reset do not allocate or deallocate. Calls whose frame count exceeds the prepared maximum, or whose slices are shorter than the requested prefix, are refused before any history or output changes; unused slice suffixes are untouched.

ConvolutionBank32/ConvolutionBank64 own 1..=64 independent diagonal channels sharing one schedule, each with its own response. The aggregate budget and every response are checked before any channel is built. Planar and in-place calls are admitted for the whole bank (channel count, maximum, all prefixes) before any channel runs; reset_channel affects one channel only. There is no stereo crossfeed, true-stereo matrix or linked normalization.

ravet-signal does not implement the Foundation Processor trait for these types. A processor wrapper reports info().latency_samples and the largest channel tail_samples so Graph latency compensation uses the actual algorithmic delay; engine_mut gives per-channel access for frame-interleaved processing. The crate tests include such a wrapper in an actual Graph.

Nonfinite and overflowing values propagate until reset; there is no denormal, gain, trim or sample-rate policy. Direct-head cost is O(head) per sample and FFT work is concentrated at partition boundaries; no real-time deadline is claimed. The engines do not load, convert, trim or normalize responses, and do not replace them live. Explicit preparation and live transitions are described below.

Tests compare against independent f64 direct sums and an analytic frequency response, check bit-identical output across arbitrary block sizes including 1 and odd sizes, single taps at every stage boundary, maximum-length responses, empty stage intervals, zero and subnormal responses, bank refusals and channel isolation, allocation-free processing and reset, and an actual Graph with a downstream FIR and a latency-compensated dry path. See decision 0270.

Native f32 FFT

FftPlan32::try_new(size, maximum_twiddle_bytes) prepares a 1..=1,048,576-point radix-two complex f32 FFT with the same sign and 1/N inverse convention as FftPlan. FftPlan::try_new is an additive bounded constructor for the f64 plan; FftPlan::new and all f64 transform results are unchanged. Both return FftPreparationError::{InvalidSize, Capacity, Allocation}. At the maximum size, the f32 plan shows about 3.2e-11 coherent leakage on an exact quarter-cycle tone because the f64 value of cos(pi/2) is not exactly zero before rounding; this is recorded, not corrected.