AtlatestRepositorysigil-dsp

sigil-dsp / tree / src / cks.c

1/*
2 * ks.c - Karplus-Strong plucked string
3 *
4 * Buffer sized from a ceiling on the lowest intended fundamental
5 * (~20 Hz) so retuning up to any audible pitch is a pointer trick,
6 * not a reallocation.
7 */
8
9#include "ks.h"
10#include <stdlib.h>
11#include <string.h>
12#include <math.h>
14#define KS_MIN_FREQ 20.0f
16static uint32_t xorshift32(uint32_t *s)
18 uint32_t x = *s;
19 x ^= x << 13;
20 x ^= x >> 17;
21 x ^= x << 5;
22 *s = x ? x : 0x1u;
23 return *s;
26static SPFLOAT ks_noise(sp_ks *p)
28 uint32_t r = xorshift32(&p->rng);
29 /* map to [-1, 1] */
30 return ((SPFLOAT)(int32_t)r) * (1.0f / 2147483648.0f);
33int sp_ks_create(sp_ks **p)
35 *p = (sp_ks *)calloc(1, sizeof(sp_ks));
36 return SP_OK;
39int sp_ks_destroy(sp_ks **p)
41 if (!*p) return SP_OK;
42 free((*p)->buf);
43 free(*p);
44 *p = NULL;
45 return SP_OK;
48int sp_ks_init(sp_data *sp, sp_ks *p, SPFLOAT freq, SPFLOAT decay,
49 SPFLOAT excite_gain)
51 if (freq < KS_MIN_FREQ) freq = KS_MIN_FREQ;
52 if (decay <= 0.0f) decay = 0.5f;
53 p->freq = freq;
54 p->decay = decay;
55 p->excite_gain = excite_gain;
56 p->bufcap = (int)ceilf((SPFLOAT)sp->sr / KS_MIN_FREQ) + 2;
57 p->buf = (SPFLOAT *)calloc(p->bufcap, sizeof(SPFLOAT));
58 p->bufpos = 0;
59 p->last = 0.0f;
60 p->gate_prev = 0;
61 p->rng = 0xC0FFEE01u;
62 sp_ks_retune(sp, p);
63 return SP_OK;
66int sp_ks_retune(sp_data *sp, sp_ks *p)
68 SPFLOAT f = p->freq < KS_MIN_FREQ ? KS_MIN_FREQ : p->freq;
69 int n = (int)lroundf((SPFLOAT)sp->sr / f);
70 if (n < 2) n = 2;
71 if (n > p->bufcap) n = p->bufcap;
72 p->bufsize = n;
73 /* Per-trip gain so signal decays to -60 dB after `decay` seconds.
74 * After T seconds the loop runs T*freq times, want g^(T*freq)=1e-3. */
75 SPFLOAT d = p->decay > 0.0f ? p->decay : 0.001f;
76 SPFLOAT g = powf(10.0f, -3.0f / (d * f));
77 if (g > 0.99995f) g = 0.99995f;
78 if (g < 0.0f) g = 0.0f;
79 p->feedback = g;
80 if (p->bufpos >= p->bufsize) p->bufpos = 0;
81 return SP_OK;
84int sp_ks_trigger(sp_data *sp, sp_ks *p)
86 (void)sp;
87 SPFLOAT amp = p->excite_gain;
88 for (int i = 0; i < p->bufsize; i++)
89 p->buf[i] = ks_noise(p) * amp;
90 p->bufpos = 0;
91 p->last = 0.0f;
92 return SP_OK;
95int sp_ks_compute(sp_data *sp, sp_ks *p, SPFLOAT *gate, SPFLOAT *out)
97 /* Rising-edge retrigger on gate */
98 int g_now = (*gate > 0.5f) ? 1 : 0;
99 if (g_now && !p->gate_prev) sp_ks_trigger(sp, p);
100 p->gate_prev = g_now;
102 SPFLOAT y = p->buf[p->bufpos];
103 /* 2-tap averaging lowpass in feedback */
104 SPFLOAT avg = 0.5f * (y + p->last);
105 SPFLOAT newval = avg * p->feedback;
106 p->buf[p->bufpos] = newval;
107 p->last = y;
108 p->bufpos++;
109 if (p->bufpos >= p->bufsize) p->bufpos = 0;
110 *out = y;
111 return SP_OK;