AtlatestRepositorysigil-dsp
1
/*2
* ks.c - Karplus-Strong plucked string3
*4
* Buffer sized from a ceiling on the lowest intended fundamental5
* (~20 Hz) so retuning up to any audible pitch is a pointer trick,6
* not a reallocation.7
*/9
#include "ks.h"10
#include <stdlib.h>11
#include <string.h>12
#include <math.h>14
#define KS_MIN_FREQ 20.0f16
static uint32_t xorshift32(uint32_t *s)17
{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;24
}26
static SPFLOAT ks_noise(sp_ks *p)27
{28
uint32_t r = xorshift32(&p->rng);29
/* map to [-1, 1] */30
return ((SPFLOAT)(int32_t)r) * (1.0f / 2147483648.0f);31
}33
int sp_ks_create(sp_ks **p)34
{35
*p = (sp_ks *)calloc(1, sizeof(sp_ks));36
return SP_OK;37
}39
int sp_ks_destroy(sp_ks **p)40
{41
if (!*p) return SP_OK;42
free((*p)->buf);43
free(*p);44
*p = NULL;45
return SP_OK;46
}48
int sp_ks_init(sp_data *sp, sp_ks *p, SPFLOAT freq, SPFLOAT decay,49
SPFLOAT excite_gain)50
{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;64
}66
int sp_ks_retune(sp_data *sp, sp_ks *p)67
{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;82
}84
int sp_ks_trigger(sp_data *sp, sp_ks *p)85
{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;93
}95
int sp_ks_compute(sp_data *sp, sp_ks *p, SPFLOAT *gate, SPFLOAT *out)96
{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;112
}