1/**************************************************************************/
2/* reverb_filter.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "reverb_filter.h"
32
33#include "core/math/math_funcs.h"
34
35#include <math.h>
36
37const float Reverb::comb_tunings[MAX_COMBS] = {
38 //freeverb comb tunings
39 0.025306122448979593f,
40 0.026938775510204082f,
41 0.028956916099773241f,
42 0.03074829931972789f,
43 0.032244897959183672f,
44 0.03380952380952381f,
45 0.035306122448979592f,
46 0.036666666666666667f
47};
48
49const float Reverb::allpass_tunings[MAX_ALLPASS] = {
50 //freeverb allpass tunings
51 0.0051020408163265302f,
52 0.007732426303854875f,
53 0.01f,
54 0.012607709750566893f
55};
56
57void Reverb::process(float *p_src, float *p_dst, int p_frames) {
58 if (p_frames > INPUT_BUFFER_MAX_SIZE) {
59 p_frames = INPUT_BUFFER_MAX_SIZE;
60 }
61
62 int predelay_frames = lrint((params.predelay / 1000.0) * params.mix_rate);
63 if (predelay_frames < 10) {
64 predelay_frames = 10;
65 }
66 if (predelay_frames >= echo_buffer_size) {
67 predelay_frames = echo_buffer_size - 1;
68 }
69
70 for (int i = 0; i < p_frames; i++) {
71 if (echo_buffer_pos >= echo_buffer_size) {
72 echo_buffer_pos = 0;
73 }
74
75 int read_pos = echo_buffer_pos - predelay_frames;
76 while (read_pos < 0) {
77 read_pos += echo_buffer_size;
78 }
79
80 float in = undenormalize(echo_buffer[read_pos] * params.predelay_fb + p_src[i]);
81
82 echo_buffer[echo_buffer_pos] = in;
83
84 input_buffer[i] = in;
85
86 p_dst[i] = 0; //take the chance and clear this
87
88 echo_buffer_pos++;
89 }
90
91 if (params.hpf > 0) {
92 float hpaux = expf(-Math_TAU * params.hpf * 6000 / params.mix_rate);
93 float hp_a1 = (1.0 + hpaux) / 2.0;
94 float hp_a2 = -(1.0 + hpaux) / 2.0;
95 float hp_b1 = hpaux;
96
97 for (int i = 0; i < p_frames; i++) {
98 float in = input_buffer[i];
99 input_buffer[i] = in * hp_a1 + hpf_h1 * hp_a2 + hpf_h2 * hp_b1;
100 hpf_h2 = input_buffer[i];
101 hpf_h1 = in;
102 }
103 }
104
105 for (int i = 0; i < MAX_COMBS; i++) {
106 Comb &c = comb[i];
107
108 int size_limit = c.size - lrintf((float)c.extra_spread_frames * (1.0 - params.extra_spread));
109 for (int j = 0; j < p_frames; j++) {
110 if (c.pos >= size_limit) { //reset this now just in case
111 c.pos = 0;
112 }
113
114 float out = undenormalize(c.buffer[c.pos] * c.feedback);
115 out = out * (1.0 - c.damp) + c.damp_h * c.damp; //lowpass
116 c.damp_h = out;
117 c.buffer[c.pos] = input_buffer[j] + out;
118 p_dst[j] += out;
119 c.pos++;
120 }
121 }
122
123 static const float allpass_feedback = 0.7;
124 /* this one works, but the other version is just nicer....
125 int ap_size_limit[MAX_ALLPASS];
126
127 for (int i=0;i<MAX_ALLPASS;i++) {
128 AllPass &a=allpass[i];
129 ap_size_limit[i]=a.size-lrintf((float)a.extra_spread_frames*(1.0-params.extra_spread));
130 }
131
132 for (int i=0;i<p_frames;i++) {
133 float sample=p_dst[i];
134 float aux,in;
135 float AllPass*ap;
136
137#define PROCESS_ALLPASS(m_ap) \
138 ap=&allpass[m_ap]; \
139 if (ap->pos>=ap_size_limit[m_ap]) \
140 ap->pos=0; \
141 aux=undenormalize(ap->buffer[ap->pos]); \
142 in=sample; \
143 sample=-in+aux; \
144 ap->pos++;
145
146
147 PROCESS_ALLPASS(0);
148 PROCESS_ALLPASS(1);
149 PROCESS_ALLPASS(2);
150 PROCESS_ALLPASS(3);
151
152 p_dst[i]=sample;
153 }
154 */
155
156 for (int i = 0; i < MAX_ALLPASS; i++) {
157 AllPass &a = allpass[i];
158 int size_limit = a.size - lrintf((float)a.extra_spread_frames * (1.0 - params.extra_spread));
159
160 for (int j = 0; j < p_frames; j++) {
161 if (a.pos >= size_limit) {
162 a.pos = 0;
163 }
164
165 float aux = a.buffer[a.pos];
166 a.buffer[a.pos] = undenormalize(allpass_feedback * aux + p_dst[j]);
167 p_dst[j] = aux - allpass_feedback * a.buffer[a.pos];
168 a.pos++;
169 }
170 }
171
172 static const float wet_scale = 0.6;
173
174 for (int i = 0; i < p_frames; i++) {
175 p_dst[i] = p_dst[i] * params.wet * wet_scale + p_src[i] * params.dry;
176 }
177}
178
179void Reverb::set_room_size(float p_size) {
180 params.room_size = p_size;
181 update_parameters();
182}
183
184void Reverb::set_damp(float p_damp) {
185 params.damp = p_damp;
186 update_parameters();
187}
188
189void Reverb::set_wet(float p_wet) {
190 params.wet = p_wet;
191}
192
193void Reverb::set_dry(float p_dry) {
194 params.dry = p_dry;
195}
196
197void Reverb::set_predelay(float p_predelay) {
198 params.predelay = p_predelay;
199}
200
201void Reverb::set_predelay_feedback(float p_predelay_fb) {
202 params.predelay_fb = p_predelay_fb;
203}
204
205void Reverb::set_highpass(float p_frq) {
206 if (p_frq > 1) {
207 p_frq = 1;
208 }
209 if (p_frq < 0) {
210 p_frq = 0;
211 }
212 params.hpf = p_frq;
213}
214
215void Reverb::set_extra_spread(float p_spread) {
216 params.extra_spread = p_spread;
217}
218
219void Reverb::set_mix_rate(float p_mix_rate) {
220 params.mix_rate = p_mix_rate;
221 configure_buffers();
222}
223
224void Reverb::set_extra_spread_base(float p_sec) {
225 params.extra_spread_base = p_sec;
226 configure_buffers();
227}
228
229void Reverb::configure_buffers() {
230 clear_buffers(); //clear if necessary
231
232 for (int i = 0; i < MAX_COMBS; i++) {
233 Comb &c = comb[i];
234
235 c.extra_spread_frames = lrint(params.extra_spread_base * params.mix_rate);
236
237 int len = lrint(comb_tunings[i] * params.mix_rate) + c.extra_spread_frames;
238 if (len < 5) {
239 len = 5; //may this happen?
240 }
241
242 c.buffer = memnew_arr(float, len);
243 c.pos = 0;
244 for (int j = 0; j < len; j++) {
245 c.buffer[j] = 0;
246 }
247 c.size = len;
248 }
249
250 for (int i = 0; i < MAX_ALLPASS; i++) {
251 AllPass &a = allpass[i];
252
253 a.extra_spread_frames = lrint(params.extra_spread_base * params.mix_rate);
254
255 int len = lrint(allpass_tunings[i] * params.mix_rate) + a.extra_spread_frames;
256 if (len < 5) {
257 len = 5; //may this happen?
258 }
259
260 a.buffer = memnew_arr(float, len);
261 a.pos = 0;
262 for (int j = 0; j < len; j++) {
263 a.buffer[j] = 0;
264 }
265 a.size = len;
266 }
267
268 echo_buffer_size = (int)(((float)MAX_ECHO_MS / 1000.0) * params.mix_rate + 1.0);
269 echo_buffer = memnew_arr(float, echo_buffer_size);
270 for (int i = 0; i < echo_buffer_size; i++) {
271 echo_buffer[i] = 0;
272 }
273
274 echo_buffer_pos = 0;
275}
276
277void Reverb::update_parameters() {
278 //more freeverb derived constants
279 static const float room_scale = 0.28f;
280 static const float room_offset = 0.7f;
281
282 for (int i = 0; i < MAX_COMBS; i++) {
283 Comb &c = comb[i];
284 c.feedback = room_offset + params.room_size * room_scale;
285 if (c.feedback < room_offset) {
286 c.feedback = room_offset;
287 } else if (c.feedback > (room_offset + room_scale)) {
288 c.feedback = (room_offset + room_scale);
289 }
290
291 float auxdmp = params.damp / 2.0 + 0.5; //only half the range (0.5 .. 1.0 is enough)
292 auxdmp *= auxdmp;
293
294 c.damp = expf(-Math_TAU * auxdmp * 10000 / params.mix_rate); // 0 .. 10khz
295 }
296}
297
298void Reverb::clear_buffers() {
299 if (echo_buffer) {
300 memdelete_arr(echo_buffer);
301 }
302
303 for (int i = 0; i < MAX_COMBS; i++) {
304 if (comb[i].buffer) {
305 memdelete_arr(comb[i].buffer);
306 }
307
308 comb[i].buffer = nullptr;
309 }
310
311 for (int i = 0; i < MAX_ALLPASS; i++) {
312 if (allpass[i].buffer) {
313 memdelete_arr(allpass[i].buffer);
314 }
315
316 allpass[i].buffer = nullptr;
317 }
318}
319
320Reverb::Reverb() {
321 params.room_size = 0.8;
322 params.damp = 0.5;
323 params.dry = 1.0;
324 params.wet = 0.0;
325 params.mix_rate = 44100;
326 params.extra_spread_base = 0;
327 params.extra_spread = 1.0;
328 params.predelay = 150;
329 params.predelay_fb = 0.4;
330 params.hpf = 0;
331
332 input_buffer = memnew_arr(float, INPUT_BUFFER_MAX_SIZE);
333
334 configure_buffers();
335 update_parameters();
336}
337
338Reverb::~Reverb() {
339 memdelete_arr(input_buffer);
340 clear_buffers();
341}
342