std1.0.0-beta-7 SafeC docs

Source dsp/comb.sc

View in the repository

// SafeC Standard Library — Comb filter / Karplus-Strong implementation
// (see comb.h)
#pragma once
#include <std/dsp/comb.h>
#include <std/dsp/fixed.h>

namespace std {

inline struct CombFF CombFF::new([]double history, double gain) {
    struct CombFF c;
    unsafe { c.history = (&double)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline double CombFF::process(double x) {
    double tapped;
    unsafe { tapped = self.history[self.pos]; }
    double out = x + self.gain * tapped;
    unsafe { self.history[self.pos] = x; }
    self.pos = (self.pos + 1UL) % self.delay;
    return out;
}

void CombFF::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = 0.0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void CombFF::process_block([]const double in_s, []double out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &double in;
    &double out;
    unsafe { in = (const &double)in_s.ptr; out = (&double)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct CombFB CombFB::new([]double history, double gain) {
    struct CombFB c;
    unsafe { c.history = (&double)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline double CombFB::process(double x) {
    double tapped;
    unsafe { tapped = self.history[self.pos]; }
    double y = x + self.gain * tapped;
    unsafe { self.history[self.pos] = y; }
    self.pos = (self.pos + 1UL) % self.delay;
    return y;
}

void CombFB::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = 0.0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void CombFB::process_block([]const double in_s, []double out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &double in;
    &double out;
    unsafe { in = (const &double)in_s.ptr; out = (&double)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct KarplusStrong KarplusStrong::new([]double buffer, double damping) {
    struct KarplusStrong ks;
    unsafe { ks.buffer = (&double)buffer.ptr; }
    ks.length = buffer.len;
    ks.pos = 0UL;
    ks.damping = damping;
    return ks;
}

inline double KarplusStrong::next() {
    unsigned long readPos = self.pos;
    unsigned long nextPos = (self.pos + 1UL) % self.length;
    double v0;
    double v1;
    unsafe { v0 = self.buffer[readPos]; v1 = self.buffer[nextPos]; }
    double newVal = self.damping * 0.5 * (v0 + v1);
    unsafe { self.buffer[readPos] = newVal; }
    self.pos = nextPos;
    return v0;
}

void KarplusStrong::pluck([]const double excitation) {
    unsigned long copyLen = excitation.len;
    if (copyLen > self.length) { copyLen = self.length; }
    unsigned long i = 0UL;
    while (i < copyLen) {
        double v;
        unsafe { v = excitation[i]; }
        unsafe { self.buffer[i] = v; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

// ── Single precision ─────────────────────────────────────────────────────────

inline struct CombFF32 CombFF32::new([]float history, float gain) {
    struct CombFF32 c;
    unsafe { c.history = (&float)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline float CombFF32::process(float x) {
    float tapped;
    unsafe { tapped = self.history[self.pos]; }
    float out = x + self.gain * tapped;
    unsafe { self.history[self.pos] = x; }
    self.pos = (self.pos + 1UL) % self.delay;
    return out;
}

void CombFF32::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = 0.0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void CombFF32::process_block([]const float in_s, []float out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &float in;
    &float out;
    unsafe { in = (const &float)in_s.ptr; out = (&float)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct CombFB32 CombFB32::new([]float history, float gain) {
    struct CombFB32 c;
    unsafe { c.history = (&float)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline float CombFB32::process(float x) {
    float tapped;
    unsafe { tapped = self.history[self.pos]; }
    float y = x + self.gain * tapped;
    unsafe { self.history[self.pos] = y; }
    self.pos = (self.pos + 1UL) % self.delay;
    return y;
}

void CombFB32::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = 0.0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void CombFB32::process_block([]const float in_s, []float out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &float in;
    &float out;
    unsafe { in = (const &float)in_s.ptr; out = (&float)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct KarplusStrong32 KarplusStrong32::new([]float buffer, float damping) {
    struct KarplusStrong32 ks;
    unsafe { ks.buffer = (&float)buffer.ptr; }
    ks.length = buffer.len;
    ks.pos = 0UL;
    ks.damping = damping;
    return ks;
}

inline float KarplusStrong32::next() {
    unsigned long readPos = self.pos;
    unsigned long nextPos = (self.pos + 1UL) % self.length;
    float v0;
    float v1;
    unsafe { v0 = self.buffer[readPos]; v1 = self.buffer[nextPos]; }
    float newVal = self.damping * (float)0.5 * (v0 + v1);
    unsafe { self.buffer[readPos] = newVal; }
    self.pos = nextPos;
    return v0;
}

void KarplusStrong32::pluck([]const float excitation) {
    unsigned long copyLen = excitation.len;
    if (copyLen > self.length) { copyLen = self.length; }
    unsigned long i = 0UL;
    while (i < copyLen) {
        float v;
        unsafe { v = excitation[i]; }
        unsafe { self.buffer[i] = v; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

inline struct FCombFF FCombFF::new([]Fixed history, Fixed gain) {
    struct FCombFF c;
    unsafe { c.history = (&Fixed)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline Fixed FCombFF::process(Fixed x) {
    Fixed tapped;
    unsafe { tapped = self.history[self.pos]; }
    Fixed out = fixed_add(x, fixed_mul(self.gain, tapped));
    unsafe { self.history[self.pos] = x; }
    self.pos = (self.pos + 1UL) % self.delay;
    return out;
}

void FCombFF::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = (Fixed)0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void FCombFF::process_block([]const Fixed in_s, []Fixed out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &Fixed in;
    &Fixed out;
    unsafe { in = (const &Fixed)in_s.ptr; out = (&Fixed)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct FCombFB FCombFB::new([]Fixed history, Fixed gain) {
    struct FCombFB c;
    unsafe { c.history = (&Fixed)history.ptr; }
    c.delay = history.len;
    c.pos = 0UL;
    c.gain = gain;
    return c;
}

inline Fixed FCombFB::process(Fixed x) {
    Fixed tapped;
    unsafe { tapped = self.history[self.pos]; }
    Fixed y = fixed_add(x, fixed_mul(self.gain, tapped));
    unsafe { self.history[self.pos] = y; }
    self.pos = (self.pos + 1UL) % self.delay;
    return y;
}

void FCombFB::reset() {
    unsigned long i = 0UL;
    while (i < self.delay) {
        unsafe { self.history[i] = (Fixed)0; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

void FCombFB::process_block([]const Fixed in_s, []Fixed out_s) {
    unsigned long n = in_s.len < out_s.len ? in_s.len : out_s.len;
    if (n == 0UL) return;
    const &Fixed in;
    &Fixed out;
    unsafe { in = (const &Fixed)in_s.ptr; out = (&Fixed)out_s.ptr; }
    filter_process_each_n_(self, in, out, n);
}

inline struct FKarplusStrong FKarplusStrong::new([]Fixed buffer, Fixed damping) {
    struct FKarplusStrong ks;
    unsafe { ks.buffer = (&Fixed)buffer.ptr; }
    ks.length = buffer.len;
    ks.pos = 0UL;
    ks.damping = damping;
    return ks;
}

inline Fixed FKarplusStrong::next() {
    unsigned long readPos = self.pos;
    unsigned long nextPos = (self.pos + 1UL) % self.length;
    Fixed v0;
    Fixed v1;
    unsafe { v0 = self.buffer[readPos]; v1 = self.buffer[nextPos]; }
    Fixed avg = fixed_mul(fixed_add(v0, v1), (Fixed)FIXED_HALF);
    Fixed newVal = fixed_mul(self.damping, avg);
    unsafe { self.buffer[readPos] = newVal; }
    self.pos = nextPos;
    return v0;
}

void FKarplusStrong::pluck([]const Fixed excitation) {
    unsigned long copyLen = excitation.len;
    if (copyLen > self.length) { copyLen = self.length; }
    unsigned long i = 0UL;
    while (i < copyLen) {
        Fixed v;
        unsafe { v = excitation[i]; }
        unsafe { self.buffer[i] = v; }
        i = i + 1UL;
    }
    self.pos = 0UL;
}

} // namespace std