Commit | Line | Data |
---|---|---|
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 | ||
30 | struct comb | |
31 | { | |
32 | float *buffer; | |
33 | unsigned bufsize; | |
34 | unsigned bufidx; | |
35 | ||
36 | float feedback; | |
37 | float filterstore; | |
38 | float damp1, damp2; | |
39 | }; | |
40 | ||
41 | struct allpass | |
42 | { | |
43 | float *buffer; | |
44 | float feedback; | |
45 | unsigned bufsize; | |
46 | unsigned bufidx; | |
47 | }; | |
48 | ||
49 | static 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 | ||
63 | static 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 | |
78 | static const float muted = 0; | |
79 | static const float fixedgain = 0.015f; | |
80 | static const float scalewet = 3; | |
81 | static const float scaledry = 2; | |
82 | static const float scaledamp = 0.4f; | |
83 | static const float scaleroom = 0.28f; | |
84 | static const float offsetroom = 0.7f; | |
85 | static const float initialroom = 0.5f; | |
86 | static const float initialdamp = 0.5f; | |
87 | static const float initialwet = 1.0f / 3.0f; | |
88 | static const float initialdry = 0; | |
89 | static const float initialwidth = 1; | |
90 | static const float initialmode = 0; | |
91 | static const float freezemode = 0.5f; | |
92 | ||
93 | struct 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 | ||
110 | static 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 | ||
126 | static 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 | ||
152 | static void revmodel_setroomsize(struct revmodel *rev, float value) | |
153 | { | |
154 | rev->roomsize = value * scaleroom + offsetroom; | |
155 | revmodel_update(rev); | |
156 | } | |
157 | ||
158 | static void revmodel_setdamp(struct revmodel *rev, float value) | |
159 | { | |
160 | rev->damp = value * scaledamp; | |
161 | revmodel_update(rev); | |
162 | } | |
163 | ||
164 | static void revmodel_setwet(struct revmodel *rev, float value) | |
165 | { | |
166 | rev->wet = value * scalewet; | |
167 | revmodel_update(rev); | |
168 | } | |
169 | ||
170 | static void revmodel_setdry(struct revmodel *rev, float value) | |
171 | { | |
172 | rev->dry = value * scaledry; | |
173 | revmodel_update(rev); | |
174 | } | |
175 | ||
176 | static void revmodel_setwidth(struct revmodel *rev, float value) | |
177 | { | |
178 | rev->width = value; | |
179 | revmodel_update(rev); | |
180 | } | |
181 | ||
182 | static void revmodel_setmode(struct revmodel *rev, float value) | |
183 | { | |
184 | rev->mode = value; | |
185 | revmodel_update(rev); | |
186 | } | |
187 | ||
188 | static 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 | ||
221 | struct reverb_data | |
222 | { | |
223 | struct revmodel left, right; | |
224 | }; | |
225 | ||
226 | static 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 | ||
243 | static 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 | ||
263 | static 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 | ||
296 | static 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 | ||
310 | const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | |
311 | { | |
312 | return &reverb_plug; | |
313 | } | |
314 | ||
315 | #undef dspfilter_get_implementation |