git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / audio / dsp_filters / reverb.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (reverb.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include <math.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include <retro_inline.h>
28#include <libretro_dspfilter.h>
29
30struct comb
31{
32 float *buffer;
33 unsigned bufsize;
34 unsigned bufidx;
35
36 float feedback;
37 float filterstore;
38 float damp1, damp2;
39};
40
41struct allpass
42{
43 float *buffer;
44 float feedback;
45 unsigned bufsize;
46 unsigned bufidx;
47};
48
49static INLINE float comb_process(struct comb *c, float input)
50{
51 float output = c->buffer[c->bufidx];
52 c->filterstore = (output * c->damp2) + (c->filterstore * c->damp1);
53
54 c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);
55
56 c->bufidx++;
57 if (c->bufidx >= c->bufsize)
58 c->bufidx = 0;
59
60 return output;
61}
62
63static INLINE float allpass_process(struct allpass *a, float input)
64{
65 float bufout = a->buffer[a->bufidx];
66 float output = -input + bufout;
67 a->buffer[a->bufidx] = input + bufout * a->feedback;
68
69 a->bufidx++;
70 if (a->bufidx >= a->bufsize)
71 a->bufidx = 0;
72
73 return output;
74}
75
76#define numcombs 8
77#define numallpasses 4
78static const float muted = 0;
79static const float fixedgain = 0.015f;
80static const float scalewet = 3;
81static const float scaledry = 2;
82static const float scaledamp = 0.4f;
83static const float scaleroom = 0.28f;
84static const float offsetroom = 0.7f;
85static const float initialroom = 0.5f;
86static const float initialdamp = 0.5f;
87static const float initialwet = 1.0f / 3.0f;
88static const float initialdry = 0;
89static const float initialwidth = 1;
90static const float initialmode = 0;
91static const float freezemode = 0.5f;
92
93struct revmodel
94{
95 struct comb combL[numcombs];
96 struct allpass allpassL[numallpasses];
97
98 float *bufcomb[numcombs];
99 float *bufallpass[numallpasses];
100
101 float gain;
102 float roomsize, roomsize1;
103 float damp, damp1;
104 float wet, wet1, wet2;
105 float dry;
106 float width;
107 float mode;
108};
109
110static float revmodel_process(struct revmodel *rev, float in)
111{
112 int i;
113 float mono_out = 0.0f;
114 float mono_in = in;
115 float input = mono_in * rev->gain;
116
117 for (i = 0; i < numcombs; i++)
118 mono_out += comb_process(&rev->combL[i], input);
119
120 for (i = 0; i < numallpasses; i++)
121 mono_out = allpass_process(&rev->allpassL[i], mono_out);
122
123 return mono_in * rev->dry + mono_out * rev->wet1;
124}
125
126static void revmodel_update(struct revmodel *rev)
127{
128 int i;
129 rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
130
131 if (rev->mode >= freezemode)
132 {
133 rev->roomsize1 = 1.0f;
134 rev->damp1 = 0.0f;
135 rev->gain = muted;
136 }
137 else
138 {
139 rev->roomsize1 = rev->roomsize;
140 rev->damp1 = rev->damp;
141 rev->gain = fixedgain;
142 }
143
144 for (i = 0; i < numcombs; i++)
145 {
146 rev->combL[i].feedback = rev->roomsize1;
147 rev->combL[i].damp1 = rev->damp1;
148 rev->combL[i].damp2 = 1.0f - rev->damp1;
149 }
150}
151
152static void revmodel_setroomsize(struct revmodel *rev, float value)
153{
154 rev->roomsize = value * scaleroom + offsetroom;
155 revmodel_update(rev);
156}
157
158static void revmodel_setdamp(struct revmodel *rev, float value)
159{
160 rev->damp = value * scaledamp;
161 revmodel_update(rev);
162}
163
164static void revmodel_setwet(struct revmodel *rev, float value)
165{
166 rev->wet = value * scalewet;
167 revmodel_update(rev);
168}
169
170static void revmodel_setdry(struct revmodel *rev, float value)
171{
172 rev->dry = value * scaledry;
173 revmodel_update(rev);
174}
175
176static void revmodel_setwidth(struct revmodel *rev, float value)
177{
178 rev->width = value;
179 revmodel_update(rev);
180}
181
182static void revmodel_setmode(struct revmodel *rev, float value)
183{
184 rev->mode = value;
185 revmodel_update(rev);
186}
187
188static void revmodel_init(struct revmodel *rev,int srate)
189{
190
191 static const int comb_lengths[8] = { 1116,1188,1277,1356,1422,1491,1557,1617 };
192 static const int allpass_lengths[4] = { 225,341,441,556 };
193 double r = srate * (1 / 44100.0);
194 unsigned c;
195
196 for (c = 0; c < numcombs; ++c)
197 {
198 rev->bufcomb[c] = malloc(r*comb_lengths[c]*sizeof(float));
199 rev->combL[c].buffer = rev->bufcomb[c];
200 memset(rev->combL[c].buffer,0,r*comb_lengths[c]*sizeof(float));
201 rev->combL[c].bufsize=r*comb_lengths[c];
202 }
203
204 for (c = 0; c < numallpasses; ++c)
205 {
206 rev->bufallpass[c] = malloc(r*allpass_lengths[c]*sizeof(float));
207 rev->allpassL[c].buffer = rev->bufallpass[c];
208 memset(rev->allpassL[c].buffer,0,r*allpass_lengths[c]*sizeof(float));
209 rev->allpassL[c].bufsize=r*allpass_lengths[c];
210 rev->allpassL[c].feedback = 0.5f;
211 }
212
213 revmodel_setwet(rev, initialwet);
214 revmodel_setroomsize(rev, initialroom);
215 revmodel_setdry(rev, initialdry);
216 revmodel_setdamp(rev, initialdamp);
217 revmodel_setwidth(rev, initialwidth);
218 revmodel_setmode(rev, initialmode);
219}
220
221struct reverb_data
222{
223 struct revmodel left, right;
224};
225
226static void reverb_free(void *data)
227{
228 struct reverb_data *rev = (struct reverb_data*)data;
229 unsigned i;
230
231 for (i = 0; i < numcombs; i++) {
232 free(rev->left.bufcomb[i]);
233 free(rev->right.bufcomb[i]);
234 }
235
236 for (i = 0; i < numallpasses; i++) {
237 free(rev->left.bufallpass[i]);
238 free(rev->right.bufallpass[i]);
239 }
240 free(data);
241}
242
243static void reverb_process(void *data, struct dspfilter_output *output,
244 const struct dspfilter_input *input)
245{
246 unsigned i;
247 float *out;
248 struct reverb_data *rev = (struct reverb_data*)data;
249
250 output->samples = input->samples;
251 output->frames = input->frames;
252 out = output->samples;
253
254 for (i = 0; i < input->frames; i++, out += 2)
255 {
256 float in[2] = { out[0], out[1] };
257
258 out[0] = revmodel_process(&rev->left, in[0]);
259 out[1] = revmodel_process(&rev->right, in[1]);
260 }
261}
262
263static void *reverb_init(const struct dspfilter_info *info,
264 const struct dspfilter_config *config, void *userdata)
265{
266 float drytime, wettime, damping, roomwidth, roomsize;
267 struct reverb_data *rev = (struct reverb_data*)
268 calloc(1, sizeof(*rev));
269 if (!rev)
270 return NULL;
271
272 config->get_float(userdata, "drytime", &drytime, 0.43f);
273 config->get_float(userdata, "wettime", &wettime, 0.4f);
274 config->get_float(userdata, "damping", &damping, 0.8f);
275 config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
276 config->get_float(userdata, "roomsize", &roomsize, 0.56f);
277
278 revmodel_init(&rev->left,info->input_rate);
279 revmodel_init(&rev->right,info->input_rate);
280
281 revmodel_setdamp(&rev->left, damping);
282 revmodel_setdry(&rev->left, drytime);
283 revmodel_setwet(&rev->left, wettime);
284 revmodel_setwidth(&rev->left, roomwidth);
285 revmodel_setroomsize(&rev->left, roomsize);
286
287 revmodel_setdamp(&rev->right, damping);
288 revmodel_setdry(&rev->right, drytime);
289 revmodel_setwet(&rev->right, wettime);
290 revmodel_setwidth(&rev->right, roomwidth);
291 revmodel_setroomsize(&rev->right, roomsize);
292
293 return rev;
294}
295
296static const struct dspfilter_implementation reverb_plug = {
297 reverb_init,
298 reverb_process,
299 reverb_free,
300
301 DSPFILTER_API_VERSION,
302 "Reverb",
303 "reverb",
304};
305
306#ifdef HAVE_FILTERS_BUILTIN
307#define dspfilter_get_implementation reverb_dspfilter_get_implementation
308#endif
309
310const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
311{
312 return &reverb_plug;
313}
314
315#undef dspfilter_get_implementation