std1.0.0-beta-7 SafeC docs

Source dsp/complex_dsp.h

View in the repository

// SafeC Standard Library — DSP Complex Numbers
//
// Value-type complex numbers built for the rest of std/dsp/'s spectral
// code (fft.h/dft.h's butterflies and twiddle factors, convolution.h's
// frequency-domain multiply) — distinct from std/complex.h's C99-style
// API, which represents a complex number as a float[2]/double[2] out-
// array and writes results through a pointer on every operation. That
// shape matches C99 <complex.h> closely, but every FFT butterfly doing
// several arithmetic ops back-to-back would need a temporary out-array
// per intermediate result; a plain value type with operators (SafeC
// supports operator overloading — see reference/functions.md) reads the
// same way the math does: 'w * a + b', not a chain of cmul_d/cadd_d
// calls each writing through a pointer.
//
// Two variants, matching std/dsp's two numeric domains:
//   struct Complex  — double re/im, for the float-precision DSP path.
//   struct FComplex — Fixed (Q8.24) re/im, for the fixed-point path.
#pragma once
#include <std/dsp/fixed.h>

namespace std {

struct Complex {
    double re;
    double im;

    Complex operator+(Complex other) const;
    Complex operator-(Complex other) const;
    Complex operator*(Complex other) const;
    Complex operator/(Complex other) const;
    Complex neg() const; // negate — SafeC operator overloads aren't distinguished by arity, so unary '-' can't share the 'operator-' name with binary subtraction above

    double  abs() const;   // magnitude: sqrt(re^2 + im^2)
    double  arg() const;   // phase angle: atan2(im, re)
    Complex conj() const;  // complex conjugate: (re, -im)

    static Complex new(double re, double im);

    // magnitude * e^(i*theta) = magnitude * (cos(theta), sin(theta)) — the
    // form FFT twiddle factors and oscillator/DFT kernels are naturally
    // expressed in.
    static Complex polar(double magnitude, double theta);
};

// Single-precision complex: the float counterpart of Complex, with the same
// layout (re then im). The _f32 DSP functions (fft_f32, stft_forward_f32,
// ...) take it. Not to be confused with FComplex below, which is fixed point.
struct Complex32 {
    float re;
    float im;

    Complex32 operator+(Complex32 other) const;
    Complex32 operator-(Complex32 other) const;
    Complex32 operator*(Complex32 other) const;
    Complex32 operator/(Complex32 other) const;
    Complex32 neg() const;

    float     abs() const;
    float     arg() const;
    Complex32 conj() const;

    static Complex32 new(float re, float im);
    static Complex32 polar(float magnitude, float theta);
    static Complex32 from(Complex c);    // rounds each part to float
    Complex          to_complex() const; // back to double (exact)
};

struct FComplex {
    Fixed re;
    Fixed im;

    FComplex operator+(FComplex other) const;
    FComplex operator-(FComplex other) const;
    FComplex operator*(FComplex other) const;
    FComplex neg() const;

    Fixed     abs() const;
    FComplex  conj() const;

    static FComplex new(Fixed re, Fixed im);

    // Quantizes a double-precision Complex to Q8.24 (to_complex converts
    // back). Twiddle factors and other coefficients computed once
    // (at filter/FFT-plan design time) in double precision are quantized to
    // Fixed via these — the same design-time-float / runtime-fixed split
    // std/dsp/biquad.h's cookbook designers use (FBiquad::from), and
    // completely standard practice (even hardware FFT accelerators
    // precompute twiddle tables offline in floating point).
    static FComplex from(Complex c);
    Complex         to_complex() const;
};

} // namespace std