AtlatestRepositorysigil-dsp
1
/*2
* fx4.c - chorus, crush, compressor (see fx4.h)3
*/5
#include "fx4.h"6
#include <stdlib.h>7
#include <string.h>8
#include <math.h>10
/* ============================================================ chorus */12
#define CHORUS_CENTRE_MS 15.0f14
int sp_chorus_create(sp_chorus **p)15
{16
*p = (sp_chorus *)calloc(1, sizeof(sp_chorus));17
return *p ? SP_OK : SP_NOT_OK;18
}20
int sp_chorus_destroy(sp_chorus **p)21
{22
if (!*p) return SP_OK;23
free((*p)->buf);24
free(*p);25
*p = NULL;26
return SP_OK;27
}29
int sp_chorus_init(sp_data *sp, sp_chorus *p)30
{31
p->sr = (SPFLOAT)sp->sr;32
p->bufsize = (int)(SP_CHORUS_MAX_MS * 0.001f * p->sr) + 4;33
p->buf = (SPFLOAT *)calloc((size_t)p->bufsize, sizeof(SPFLOAT));34
p->pos = 0;35
p->lfo_phase = 0.0f;36
return p->buf ? SP_OK : SP_NOT_OK;37
}39
/* Read `delay` samples back from the write position, linear interp. */40
static inline SPFLOAT chorus_tap(const sp_chorus *p, SPFLOAT delay)41
{42
SPFLOAT maxd = (SPFLOAT)(p->bufsize - 2);43
if (delay < 1.0f) delay = 1.0f;44
if (delay > maxd) delay = maxd;45
SPFLOAT rpos = (SPFLOAT)p->pos - delay;46
while (rpos < 0.0f) rpos += (SPFLOAT)p->bufsize;47
/* A tiny negative rpos rounds to exactly bufsize after the add48
* (float ulp at ~1768), which would index one past the buffer. */49
if (rpos >= (SPFLOAT)p->bufsize) rpos -= (SPFLOAT)p->bufsize;50
int i0 = (int)rpos;51
SPFLOAT frac = rpos - (SPFLOAT)i0;52
int i1 = i0 + 1;53
if (i1 >= p->bufsize) i1 -= p->bufsize;54
return p->buf[i0] + (p->buf[i1] - p->buf[i0]) * frac;55
}57
/* Triangle in [-1, 1] from a 0..1 phase. */58
static inline SPFLOAT tri_lfo(SPFLOAT phase)59
{60
SPFLOAT t = phase < 0.5f ? phase * 4.0f - 1.0f : 3.0f - phase * 4.0f;61
return t;62
}64
int sp_chorus_compute(sp_data *sp, sp_chorus *p, SPFLOAT in, SPFLOAT rate,65
SPFLOAT depth, SPFLOAT mix, SPFLOAT *out)66
{67
p->buf[p->pos] = in;69
SPFLOAT ms_per_sample = 0.001f * p->sr;70
SPFLOAT centre = CHORUS_CENTRE_MS * ms_per_sample;71
SPFLOAT dev = depth * ms_per_sample;72
SPFLOAT ph2 = p->lfo_phase + 0.5f;73
if (ph2 >= 1.0f) ph2 -= 1.0f;74
SPFLOAT tap1 = chorus_tap(p, centre + dev * tri_lfo(p->lfo_phase));75
SPFLOAT tap2 = chorus_tap(p, centre * 0.7f + dev * tri_lfo(ph2));76
SPFLOAT wet = 0.5f * (tap1 + tap2);78
if (mix < 0.0f) mix = 0.0f;79
if (mix > 1.0f) mix = 1.0f;80
*out = in * (1.0f - mix) + wet * mix;82
p->pos++;83
if (p->pos >= p->bufsize) p->pos = 0;84
if (rate < 0.0f) rate = 0.0f;85
p->lfo_phase += rate / p->sr;86
while (p->lfo_phase >= 1.0f) p->lfo_phase -= 1.0f;87
return SP_OK;88
}90
/* ============================================================ crush */92
int sp_crush_create(sp_crush **p)93
{94
*p = (sp_crush *)calloc(1, sizeof(sp_crush));95
return *p ? SP_OK : SP_NOT_OK;96
}98
int sp_crush_destroy(sp_crush **p)99
{100
if (!*p) return SP_OK;101
free(*p);102
*p = NULL;103
return SP_OK;104
}106
int sp_crush_init(sp_data *sp, sp_crush *p)107
{108
p->held = 0.0f;109
p->counter = 1.0e9f; /* first sample is always sampled */110
return SP_OK;111
}113
int sp_crush_compute(sp_data *sp, sp_crush *p, SPFLOAT in, SPFLOAT drive,114
SPFLOAT bits, SPFLOAT rate_div, SPFLOAT *out)115
{116
SPFLOAT y = in;118
/* Saturation: unity gain at |in| = 1, identity at drive 0. */119
if (drive > 0.0f) {120
SPFLOAT g = 1.0f + drive;121
y = tanhf(y * g) / tanhf(g);122
}124
/* Bit depth: 16 or more leaves the signal alone. */125
int b = (int)bits;126
if (b < 1) b = 1;127
if (b < 16) {128
SPFLOAT steps = (SPFLOAT)(1 << (b - 1));129
SPFLOAT q = y * steps;130
int qi = (int)(q + (q >= 0.0f ? 0.5f : -0.5f));131
y = (SPFLOAT)qi / steps;132
}134
/* Sample-rate reduction: hold the value for rate_div samples. */135
if (rate_div < 1.0f) rate_div = 1.0f;136
p->counter += 1.0f;137
if (p->counter >= rate_div) {138
p->held = y;139
p->counter -= rate_div;140
if (p->counter > rate_div) p->counter = 0.0f;141
}142
*out = p->held;143
return SP_OK;144
}146
/* ============================================================ compressor */148
int sp_comp_create(sp_comp **p)149
{150
*p = (sp_comp *)calloc(1, sizeof(sp_comp));151
return *p ? SP_OK : SP_NOT_OK;152
}154
int sp_comp_destroy(sp_comp **p)155
{156
if (!*p) return SP_OK;157
free(*p);158
*p = NULL;159
return SP_OK;160
}162
static inline SPFLOAT onepole_coef(SPFLOAT seconds, SPFLOAT sr)163
{164
if (seconds <= 0.0f) return 0.0f;165
return expf(-1.0f / (seconds * sr));166
}168
int sp_comp_init(sp_data *sp, sp_comp *p, SPFLOAT attack, SPFLOAT release,169
SPFLOAT makeup_db)170
{171
p->sr = (SPFLOAT)sp->sr;172
p->attack = attack;173
p->release = release;174
p->makeup = makeup_db;175
p->attack_coef = onepole_coef(attack, p->sr);176
p->release_coef = onepole_coef(release, p->sr);177
p->makeup_lin = powf(10.0f, makeup_db / 20.0f);178
p->env = 0.0f;179
return SP_OK;180
}182
int sp_comp_compute(sp_data *sp, sp_comp *p, SPFLOAT in, SPFLOAT threshold_db,183
SPFLOAT ratio, SPFLOAT *out)184
{185
SPFLOAT level = in < 0.0f ? -in : in;186
if (level > p->env)187
p->env = p->attack_coef * p->env + (1.0f - p->attack_coef) * level;188
else189
p->env = p->release_coef * p->env + (1.0f - p->release_coef) * level;191
SPFLOAT gain = 1.0f;192
if (ratio > 1.0f) {193
SPFLOAT thr = powf(10.0f, threshold_db / 20.0f);194
if (p->env > thr && thr > 0.0f) {195
/* Gain that maps the over-threshold amount through 1/ratio. */196
gain = powf(p->env / thr, 1.0f / ratio - 1.0f);197
}198
}199
*out = in * gain * p->makeup_lin;200
return SP_OK;201
}