std1.0.0-beta-7 SafeC docs

Source dsp/minphase.sc

View in the repository

// SafeC Standard Library — Minimum-phase implementation (see minphase.h)
#pragma once
#include <std/dsp/minphase.h>
#include <std/dsp/complex_dsp.h>
#include <std/dsp/fft.h>
#include <std/math.h>
#include <std/mem.h>

namespace std {

void minimum_phase_n_(const &double x, unsigned long n, unsigned long fftLen, &double outMinPhase) {
    struct Complex* spec;
    unsafe { spec = (struct Complex*)alloc_raw_(fftLen * (unsigned long)sizeof(struct Complex)); }

    unsigned long i = 0UL;
    while (i < fftLen) {
        double v = 0.0;
        if (i < n) {
            unsafe { v = x[i]; }
        }
        unsafe { spec[i] = Complex::new(v, 0.0); }
        i = i + 1UL;
    }
    unsafe { fft_n_(spec, fftLen); }

    // Log-magnitude spectrum, in place.
    i = 0UL;
    while (i < fftLen) {
        struct Complex xi;
        unsafe { xi = spec[i]; }
        double mag = xi.abs();
        double logMag = log_d(mag + 1e-9);
        unsafe { spec[i] = Complex::new(logMag, 0.0); }
        i = i + 1UL;
    }

    // Real cepstrum = IFFT(log-magnitude), in place.
    unsafe { ifft_n_(spec, fftLen); }

    // Homomorphic ("minimum-phase") window: fold the anti-causal half of
    // the cepstrum onto the causal half by doubling it, and zero the
    // anti-causal half entirely — this is what makes the reconstructed
    // signal minimum-phase.
    unsigned long half = fftLen / 2UL;
    i = 0UL;
    while (i < fftLen) {
        struct Complex ci;
        unsafe { ci = spec[i]; }
        double cre;
        unsafe { cre = ci.re; }
        double wv = 0.0;
        if (i == 0UL) {
            wv = cre;
        } else if (i < half) {
            wv = 2.0 * cre;
        } else if (i == half) {
            wv = cre;
        }
        unsafe { spec[i] = Complex::new(wv, 0.0); }
        i = i + 1UL;
    }

    unsafe { fft_n_(spec, fftLen); }

    // Exponentiate the (now complex) cepstrum spectrum back into the
    // minimum-phase spectrum: exp(a+bi) = e^a * (cos(b) + i*sin(b)).
    i = 0UL;
    while (i < fftLen) {
        struct Complex wi;
        unsafe { wi = spec[i]; }
        double a;
        double b;
        unsafe { a = wi.re; b = wi.im; }
        double ea = exp_d(a);
        unsafe { spec[i] = Complex::new(ea * cos_d(b), ea * sin_d(b)); }
        i = i + 1UL;
    }

    unsafe { ifft_n_(spec, fftLen); }
    i = 0UL;
    while (i < fftLen) {
        struct Complex wi;
        unsafe { wi = spec[i]; }
        double re;
        unsafe { re = wi.re; }
        unsafe { outMinPhase[i] = re; }
        i = i + 1UL;
    }

    unsafe { dealloc(spec); }
}

// ── Slice API (the public signatures; the (reference, length) cores above
// are the *_n_ functions) ─────────────────────────────────────────────────────

int minimum_phase([]const double x, unsigned long fftLen, []double outMinPhase) {
    if (x.len == 0UL || x.len > fftLen || outMinPhase.len < fftLen) return 0;
    unsafe { minimum_phase_n_((const &double)x.ptr, x.len, fftLen, (&double)outMinPhase.ptr); }
    return 1;
}

} // namespace std