std1.0.0-beta-7 SafeC docs

Module dsp::sample_filter

#include <std/dsp/sample_filter.h> source

streaming DSP as composable traits.

std/dsp's streaming filters share one shape: process() one sample, process_block() a buffer (out may alias in), reset() the state to silence. The traits below name each capability on its own and compose into the roles generic code asks for (trait composition: 'trait SampleFilter : SampleProcessor + BlockProcessor + Resettable {}' requires all three). The generic helpers after them are written once and ask only for what they use; each call is resolved at compile time (no dispatch at run time, so a generic wrapper around a hand-tuned method costs nothing).

SampleProcessor  process(x): one sample in, one out
BlockProcessor   process_block(in, out)  (slices)
Resettable       reset(): back to silence
SampleFilter     SampleProcessor + BlockProcessor + Resettable:
                   double  FirFilter, IirFilter, Biquad, CombFF, CombFB
                   float   FirFilter32, IirFilter32, Biquad32,
                           CombFF32, CombFB32
                   Q8.24   FFirFilter, FIirFilter, FBiquad,
                           FCombFF, FCombFB
                   and FilterChain<S, A, B> of any two of one type
SampleGenerator  next(): one sample out, no input: KarplusStrong,
                 KarplusStrong32, FKarplusStrong

One set of traits covers all three sample types. A trait is satisfied by method name, so the signatures below are written with double, and a float or Q8.24 (Fixed) filter conforms the same way; the helpers take the sample type as a parameter of their own (S), inferred from the buffers. Your own filter works with them (and with any generic<F: SampleFilter> code) as soon as it has the methods.

struct Biquad hp = Biquad::highpass(48000.0, 80.0, 0.7071);
struct Biquad lp = Biquad::lowpass(48000.0, 8000.0, 0.7071);
struct FilterChain<double, struct Biquad, struct Biquad> band =
    FilterChain::of(hp, lp);
band.process_block(in, out);                     // band-limit a block (slices)

double h[64];
filter_impulse_response(&fir, h[0..64]);         // h[i] = taps, then 0

The per-type process_block bodies with a fast path of their own (FIR's sliding dot products, the biquad's look-ahead recurrence) stay hand- written; the others are filter_process_each over process().

Structs

FilterChain

Traits

SampleProcessor
BlockProcessor
SampleFilter
SampleGenerator

Functions

sample_one
filter_process_each
filter_impulse_response
filter_step_response
generate_block