1/**************************************************************************/
2/* audio_effect_delay.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 "audio_effect_delay.h"
32
33#include "core/math/math_funcs.h"
34#include "servers/audio_server.h"
35
36void AudioEffectDelayInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
37 int todo = p_frame_count;
38
39 while (todo) {
40 int to_mix = MIN(todo, 256); //can't mix too much
41
42 _process_chunk(p_src_frames, p_dst_frames, to_mix);
43
44 p_src_frames += to_mix;
45 p_dst_frames += to_mix;
46
47 todo -= to_mix;
48 }
49}
50
51void AudioEffectDelayInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
52 float main_level_f = base->dry;
53
54 float mix_rate = AudioServer::get_singleton()->get_mix_rate();
55
56 float tap_1_level_f = base->tap_1_active ? Math::db_to_linear(base->tap_1_level) : 0.0;
57 int tap_1_delay_frames = int((base->tap_1_delay_ms / 1000.0) * mix_rate);
58
59 float tap_2_level_f = base->tap_2_active ? Math::db_to_linear(base->tap_2_level) : 0.0;
60 int tap_2_delay_frames = int((base->tap_2_delay_ms / 1000.0) * mix_rate);
61
62 float feedback_level_f = base->feedback_active ? Math::db_to_linear(base->feedback_level) : 0.0;
63 unsigned int feedback_delay_frames = int((base->feedback_delay_ms / 1000.0) * mix_rate);
64
65 AudioFrame tap1_vol = AudioFrame(tap_1_level_f, tap_1_level_f);
66
67 tap1_vol.l *= CLAMP(1.0 - base->tap_1_pan, 0, 1);
68 tap1_vol.r *= CLAMP(1.0 + base->tap_1_pan, 0, 1);
69
70 AudioFrame tap2_vol = AudioFrame(tap_2_level_f, tap_2_level_f);
71
72 tap2_vol.l *= CLAMP(1.0 - base->tap_2_pan, 0, 1);
73 tap2_vol.r *= CLAMP(1.0 + base->tap_2_pan, 0, 1);
74
75 // feedback lowpass here
76 float lpf_c = expf(-Math_TAU * base->feedback_lowpass / mix_rate); // 0 .. 10khz
77 float lpf_ic = 1.0 - lpf_c;
78
79 const AudioFrame *src = p_src_frames;
80 AudioFrame *dst = p_dst_frames;
81 AudioFrame *rb_buf = ring_buffer.ptrw();
82 AudioFrame *fb_buf = feedback_buffer.ptrw();
83
84 for (int i = 0; i < p_frame_count; i++) {
85 rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];
86
87 AudioFrame main_val = src[i] * main_level_f;
88 AudioFrame tap_1_val = rb_buf[(ring_buffer_pos - tap_1_delay_frames) & ring_buffer_mask] * tap1_vol;
89 AudioFrame tap_2_val = rb_buf[(ring_buffer_pos - tap_2_delay_frames) & ring_buffer_mask] * tap2_vol;
90
91 AudioFrame out = main_val + tap_1_val + tap_2_val;
92
93 out += fb_buf[feedback_buffer_pos];
94
95 //apply lowpass and feedback gain
96 AudioFrame fb_in = out * feedback_level_f * lpf_ic + h * lpf_c;
97 fb_in.undenormalize(); //avoid denormals
98
99 h = fb_in;
100 fb_buf[feedback_buffer_pos] = fb_in;
101
102 dst[i] = out;
103
104 ring_buffer_pos++;
105
106 if ((++feedback_buffer_pos) >= feedback_delay_frames) {
107 feedback_buffer_pos = 0;
108 }
109 }
110}
111
112Ref<AudioEffectInstance> AudioEffectDelay::instantiate() {
113 Ref<AudioEffectDelayInstance> ins;
114 ins.instantiate();
115 ins->base = Ref<AudioEffectDelay>(this);
116
117 float ring_buffer_max_size = MAX_DELAY_MS + 100; //add 100ms of extra room, just in case
118 ring_buffer_max_size /= 1000.0; //convert to seconds
119 ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
120
121 int ringbuff_size = ring_buffer_max_size;
122
123 int bits = 0;
124
125 while (ringbuff_size > 0) {
126 bits++;
127 ringbuff_size /= 2;
128 }
129
130 ringbuff_size = 1 << bits;
131 ins->ring_buffer_mask = ringbuff_size - 1;
132 ins->ring_buffer_pos = 0;
133
134 ins->ring_buffer.resize(ringbuff_size);
135 ins->feedback_buffer.resize(ringbuff_size);
136
137 ins->feedback_buffer_pos = 0;
138
139 ins->h = AudioFrame(0, 0);
140
141 return ins;
142}
143
144void AudioEffectDelay::set_dry(float p_dry) {
145 dry = p_dry;
146}
147
148float AudioEffectDelay::get_dry() {
149 return dry;
150}
151
152void AudioEffectDelay::set_tap1_active(bool p_active) {
153 tap_1_active = p_active;
154}
155
156bool AudioEffectDelay::is_tap1_active() const {
157 return tap_1_active;
158}
159
160void AudioEffectDelay::set_tap1_delay_ms(float p_delay_ms) {
161 tap_1_delay_ms = p_delay_ms;
162}
163
164float AudioEffectDelay::get_tap1_delay_ms() const {
165 return tap_1_delay_ms;
166}
167
168void AudioEffectDelay::set_tap1_level_db(float p_level_db) {
169 tap_1_level = p_level_db;
170}
171
172float AudioEffectDelay::get_tap1_level_db() const {
173 return tap_1_level;
174}
175
176void AudioEffectDelay::set_tap1_pan(float p_pan) {
177 tap_1_pan = p_pan;
178}
179
180float AudioEffectDelay::get_tap1_pan() const {
181 return tap_1_pan;
182}
183
184void AudioEffectDelay::set_tap2_active(bool p_active) {
185 tap_2_active = p_active;
186}
187
188bool AudioEffectDelay::is_tap2_active() const {
189 return tap_2_active;
190}
191
192void AudioEffectDelay::set_tap2_delay_ms(float p_delay_ms) {
193 tap_2_delay_ms = p_delay_ms;
194}
195
196float AudioEffectDelay::get_tap2_delay_ms() const {
197 return tap_2_delay_ms;
198}
199
200void AudioEffectDelay::set_tap2_level_db(float p_level_db) {
201 tap_2_level = p_level_db;
202}
203
204float AudioEffectDelay::get_tap2_level_db() const {
205 return tap_2_level;
206}
207
208void AudioEffectDelay::set_tap2_pan(float p_pan) {
209 tap_2_pan = p_pan;
210}
211
212float AudioEffectDelay::get_tap2_pan() const {
213 return tap_2_pan;
214}
215
216void AudioEffectDelay::set_feedback_active(bool p_active) {
217 feedback_active = p_active;
218}
219
220bool AudioEffectDelay::is_feedback_active() const {
221 return feedback_active;
222}
223
224void AudioEffectDelay::set_feedback_delay_ms(float p_delay_ms) {
225 feedback_delay_ms = p_delay_ms;
226}
227
228float AudioEffectDelay::get_feedback_delay_ms() const {
229 return feedback_delay_ms;
230}
231
232void AudioEffectDelay::set_feedback_level_db(float p_level_db) {
233 feedback_level = p_level_db;
234}
235
236float AudioEffectDelay::get_feedback_level_db() const {
237 return feedback_level;
238}
239
240void AudioEffectDelay::set_feedback_lowpass(float p_lowpass) {
241 feedback_lowpass = p_lowpass;
242}
243
244float AudioEffectDelay::get_feedback_lowpass() const {
245 return feedback_lowpass;
246}
247
248void AudioEffectDelay::_bind_methods() {
249 ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectDelay::set_dry);
250 ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectDelay::get_dry);
251
252 ClassDB::bind_method(D_METHOD("set_tap1_active", "amount"), &AudioEffectDelay::set_tap1_active);
253 ClassDB::bind_method(D_METHOD("is_tap1_active"), &AudioEffectDelay::is_tap1_active);
254
255 ClassDB::bind_method(D_METHOD("set_tap1_delay_ms", "amount"), &AudioEffectDelay::set_tap1_delay_ms);
256 ClassDB::bind_method(D_METHOD("get_tap1_delay_ms"), &AudioEffectDelay::get_tap1_delay_ms);
257
258 ClassDB::bind_method(D_METHOD("set_tap1_level_db", "amount"), &AudioEffectDelay::set_tap1_level_db);
259 ClassDB::bind_method(D_METHOD("get_tap1_level_db"), &AudioEffectDelay::get_tap1_level_db);
260
261 ClassDB::bind_method(D_METHOD("set_tap1_pan", "amount"), &AudioEffectDelay::set_tap1_pan);
262 ClassDB::bind_method(D_METHOD("get_tap1_pan"), &AudioEffectDelay::get_tap1_pan);
263
264 ClassDB::bind_method(D_METHOD("set_tap2_active", "amount"), &AudioEffectDelay::set_tap2_active);
265 ClassDB::bind_method(D_METHOD("is_tap2_active"), &AudioEffectDelay::is_tap2_active);
266
267 ClassDB::bind_method(D_METHOD("set_tap2_delay_ms", "amount"), &AudioEffectDelay::set_tap2_delay_ms);
268 ClassDB::bind_method(D_METHOD("get_tap2_delay_ms"), &AudioEffectDelay::get_tap2_delay_ms);
269
270 ClassDB::bind_method(D_METHOD("set_tap2_level_db", "amount"), &AudioEffectDelay::set_tap2_level_db);
271 ClassDB::bind_method(D_METHOD("get_tap2_level_db"), &AudioEffectDelay::get_tap2_level_db);
272
273 ClassDB::bind_method(D_METHOD("set_tap2_pan", "amount"), &AudioEffectDelay::set_tap2_pan);
274 ClassDB::bind_method(D_METHOD("get_tap2_pan"), &AudioEffectDelay::get_tap2_pan);
275
276 ClassDB::bind_method(D_METHOD("set_feedback_active", "amount"), &AudioEffectDelay::set_feedback_active);
277 ClassDB::bind_method(D_METHOD("is_feedback_active"), &AudioEffectDelay::is_feedback_active);
278
279 ClassDB::bind_method(D_METHOD("set_feedback_delay_ms", "amount"), &AudioEffectDelay::set_feedback_delay_ms);
280 ClassDB::bind_method(D_METHOD("get_feedback_delay_ms"), &AudioEffectDelay::get_feedback_delay_ms);
281
282 ClassDB::bind_method(D_METHOD("set_feedback_level_db", "amount"), &AudioEffectDelay::set_feedback_level_db);
283 ClassDB::bind_method(D_METHOD("get_feedback_level_db"), &AudioEffectDelay::get_feedback_level_db);
284
285 ClassDB::bind_method(D_METHOD("set_feedback_lowpass", "amount"), &AudioEffectDelay::set_feedback_lowpass);
286 ClassDB::bind_method(D_METHOD("get_feedback_lowpass"), &AudioEffectDelay::get_feedback_lowpass);
287
288 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
289
290 ADD_GROUP("Tap 1", "tap1_");
291 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap1_active"), "set_tap1_active", "is_tap1_active");
292 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap1_delay_ms", "get_tap1_delay_ms");
293 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap1_level_db", "get_tap1_level_db");
294 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap1_pan", "get_tap1_pan");
295
296 ADD_GROUP("Tap 2", "tap2_");
297 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap2_active"), "set_tap2_active", "is_tap2_active");
298 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap2_delay_ms", "get_tap2_delay_ms");
299 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap2_level_db", "get_tap2_level_db");
300 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap2_pan", "get_tap2_pan");
301
302 ADD_GROUP("Feedback", "feedback_");
303 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feedback_active"), "set_feedback_active", "is_feedback_active");
304 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_feedback_delay_ms", "get_feedback_delay_ms");
305 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_feedback_level_db", "get_feedback_level_db");
306 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_lowpass", PROPERTY_HINT_RANGE, "1,16000,1"), "set_feedback_lowpass", "get_feedback_lowpass");
307}
308