std1.0.0-beta-7 SafeC docs

Source dsp/filter.h

View in the repository

// SafeC Standard Library — General FIR / IIR Filters
//
// Streaming (one-sample-at-a-time) difference-equation filters — the
// general form biquad.h's 2nd-order sections and dsp.h's array-level
// primitives both specialize:
//
//   FIR ("feedforward"): y[n] = sum_{k=0}^{M-1} b[k]*x[n-k]
//   IIR ("feedback"):     y[n] = sum_{k=0}^{M-1} b[k]*x[n-k]
//                              - sum_{k=1}^{N-1} a[k]*y[n-k]      (a[0] = 1)
//
// Both keep their own history/state internally (a delay line for FIR, a
// direct-form-I input+output history for IIR) so a caller can feed them
// one real-time sample at a time — e.g. inside std::Reactor's per-task
// processing — rather than needing the whole signal in memory up front
// the way convolution.h's conv_direct does.
#pragma once
#include <std/dsp/fixed.h>
#include <std/dsp/sample_filter.h>

namespace std {

// ── FIR ──────────────────────────────────────────────────────────────────────

struct FirFilter {
    const &double coeffs;    // b[0..numTaps-1], caller-owned, must outlive the filter
    unsigned long numTaps;
    &double       history;   // caller-provided ring buffer, numTaps elements, zero-initialized
    unsigned long pos;       // ring buffer write position

    // Processes one sample, returns y[n].
    double process(double x);

    // Resets the history to all zeros (numTaps elements).
    void   reset();

    // Processes 'n' samples, writing to 'out' (may alias 'in').
    void   process_block([]const double in, []double out);

    // coeffs/history as above — 'history' must have numTaps elements
    // (caller-allocated, typically zero-initialized).
    static struct FirFilter new([]const double coeffs, []double history);   // numTaps = min(coeffs.len, history.len)
};

// Single-precision FIR, same contract as FirFilter (float taps and history).
struct FirFilter32 {
    const &float  coeffs;
    unsigned long numTaps;
    &float        history;
    unsigned long pos;

    float process(float x);
    void  reset();
    void  process_block([]const float in, []float out);

    static struct FirFilter32 new([]const float coeffs, []float history);   // numTaps = min(coeffs.len, history.len)
};

struct FFirFilter {
    const &Fixed  coeffs;
    unsigned long numTaps;
    &Fixed        history;
    unsigned long pos;

    Fixed process(Fixed x);
    void  reset();
    void  process_block([]const Fixed in, []Fixed out);

    static struct FFirFilter new([]const Fixed coeffs, []Fixed history);   // numTaps = min(coeffs.len, history.len)
};

// ── IIR (general order, direct form I) ────────────────────────────────────────

struct IirFilter {
    const &double b;         // feedforward coeffs, b[0..numB-1]
    unsigned long numB;
    const &double a;         // feedback coeffs, a[1..numA-1] used (a[0] assumed 1 — pass your a[] with a[0]=1 anyway for clarity)
    unsigned long numA;
    &double       xHistory;  // caller-provided, numB elements, zero-initialized
    &double       yHistory;  // caller-provided, numA elements, zero-initialized
    unsigned long xPos;
    unsigned long yPos;

    double process(double x);
    void   reset();
    void   process_block([]const double in, []double out);

    static struct IirFilter new([]const double b, []const double a,
                                []double xHistory, []double yHistory);
};

// Single-precision IIR, same contract as IirFilter. Float rounding grows
// with the order; cascade Biquad32 sections for anything steep.
struct IirFilter32 {
    const &float  b;
    unsigned long numB;
    const &float  a;
    unsigned long numA;
    &float        xHistory;
    &float        yHistory;
    unsigned long xPos;
    unsigned long yPos;

    float process(float x);
    void  reset();
    void  process_block([]const float in, []float out);

    static struct IirFilter32 new([]const float b, []const float a,
                                  []float xHistory, []float yHistory);
};

struct FIirFilter {
    const &Fixed  b;
    unsigned long numB;
    const &Fixed  a;
    unsigned long numA;
    &Fixed        xHistory;
    &Fixed        yHistory;
    unsigned long xPos;
    unsigned long yPos;

    Fixed process(Fixed x);
    void  reset();
    void  process_block([]const Fixed in, []Fixed out);

    static struct FIirFilter new([]const Fixed b, []const Fixed a,
                                 []Fixed xHistory, []Fixed yHistory);
};

} // namespace std