AtlatestRepositorysigil-dsp

sigil-dsp / tree / src / cfx4.c

1/*
2 * fx4.c - chorus, crush, compressor (see fx4.h)
3 */
4
5#include "fx4.h"
6#include <stdlib.h>
7#include <string.h>
8#include <math.h>
9
10/* ============================================================ chorus */
12#define CHORUS_CENTRE_MS 15.0f
14int sp_chorus_create(sp_chorus **p)
16 *p = (sp_chorus *)calloc(1, sizeof(sp_chorus));
17 return *p ? SP_OK : SP_NOT_OK;
20int sp_chorus_destroy(sp_chorus **p)
22 if (!*p) return SP_OK;
23 free((*p)->buf);
24 free(*p);
25 *p = NULL;
26 return SP_OK;
29int sp_chorus_init(sp_data *sp, sp_chorus *p)
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;
39/* Read `delay` samples back from the write position, linear interp. */
40static inline SPFLOAT chorus_tap(const sp_chorus *p, SPFLOAT delay)
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 add
48 * (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;
57/* Triangle in [-1, 1] from a 0..1 phase. */
58static inline SPFLOAT tri_lfo(SPFLOAT phase)
60 SPFLOAT t = phase < 0.5f ? phase * 4.0f - 1.0f : 3.0f - phase * 4.0f;
61 return t;
64int sp_chorus_compute(sp_data *sp, sp_chorus *p, SPFLOAT in, SPFLOAT rate,
65 SPFLOAT depth, SPFLOAT mix, SPFLOAT *out)
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;
90/* ============================================================ crush */
92int sp_crush_create(sp_crush **p)
94 *p = (sp_crush *)calloc(1, sizeof(sp_crush));
95 return *p ? SP_OK : SP_NOT_OK;
98int sp_crush_destroy(sp_crush **p)
100 if (!*p) return SP_OK;
101 free(*p);
102 *p = NULL;
103 return SP_OK;
106int sp_crush_init(sp_data *sp, sp_crush *p)
108 p->held = 0.0f;
109 p->counter = 1.0e9f; /* first sample is always sampled */
110 return SP_OK;
113int sp_crush_compute(sp_data *sp, sp_crush *p, SPFLOAT in, SPFLOAT drive,
114 SPFLOAT bits, SPFLOAT rate_div, SPFLOAT *out)
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;
146/* ============================================================ compressor */
148int sp_comp_create(sp_comp **p)
150 *p = (sp_comp *)calloc(1, sizeof(sp_comp));
151 return *p ? SP_OK : SP_NOT_OK;
154int sp_comp_destroy(sp_comp **p)
156 if (!*p) return SP_OK;
157 free(*p);
158 *p = NULL;
159 return SP_OK;
162static inline SPFLOAT onepole_coef(SPFLOAT seconds, SPFLOAT sr)
164 if (seconds <= 0.0f) return 0.0f;
165 return expf(-1.0f / (seconds * sr));
168int sp_comp_init(sp_data *sp, sp_comp *p, SPFLOAT attack, SPFLOAT release,
169 SPFLOAT makeup_db)
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;
182int sp_comp_compute(sp_data *sp, sp_comp *p, SPFLOAT in, SPFLOAT threshold_db,
183 SPFLOAT ratio, SPFLOAT *out)
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 else
189 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;