1/**************************************************************************/
2/* audio_effect_chorus.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_chorus.h"
32#include "core/math/math_funcs.h"
33#include "servers/audio_server.h"
34
35void AudioEffectChorusInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
36 int todo = p_frame_count;
37
38 while (todo) {
39 int to_mix = MIN(todo, 256); //can't mix too much
40
41 _process_chunk(p_src_frames, p_dst_frames, to_mix);
42
43 p_src_frames += to_mix;
44 p_dst_frames += to_mix;
45
46 todo -= to_mix;
47 }
48}
49
50void AudioEffectChorusInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
51 //fill ringbuffer
52 for (int i = 0; i < p_frame_count; i++) {
53 audio_buffer.write[(buffer_pos + i) & buffer_mask] = p_src_frames[i];
54 p_dst_frames[i] = p_src_frames[i] * base->dry;
55 }
56
57 float mix_rate = AudioServer::get_singleton()->get_mix_rate();
58
59 /* process voices */
60 for (int vc = 0; vc < base->voice_count; vc++) {
61 AudioEffectChorus::Voice &v = base->voice[vc];
62
63 double time_to_mix = (float)p_frame_count / mix_rate;
64 double cycles_to_mix = time_to_mix * v.rate;
65
66 unsigned int local_rb_pos = buffer_pos;
67 AudioFrame *dst_buff = p_dst_frames;
68 AudioFrame *rb_buff = audio_buffer.ptrw();
69
70 double delay_msec = v.delay;
71 unsigned int delay_frames = Math::fast_ftoi((delay_msec / 1000.0) * mix_rate);
72 float max_depth_frames = (v.depth / 1000.0) * mix_rate;
73
74 uint64_t local_cycles = cycles[vc];
75 uint64_t increment = llrint(cycles_to_mix / (double)p_frame_count * (double)(1 << AudioEffectChorus::CYCLES_FRAC));
76
77 //check the LFO doesn't read ahead of the write pos
78 if ((((unsigned int)max_depth_frames) + 10) > delay_frames) { //10 as some threshold to avoid precision stuff
79 delay_frames += (int)max_depth_frames - delay_frames;
80 delay_frames += 10; //threshold to avoid precision stuff
81 }
82
83 //low pass filter
84 if (v.cutoff == 0) {
85 continue;
86 }
87 float auxlp = expf(-Math_TAU * v.cutoff / mix_rate);
88 float c1 = 1.0 - auxlp;
89 float c2 = auxlp;
90 AudioFrame h = filter_h[vc];
91 if (v.cutoff >= AudioEffectChorus::MS_CUTOFF_MAX) {
92 c1 = 1.0;
93 c2 = 0.0;
94 }
95
96 //vol modifier
97
98 AudioFrame vol_modifier = AudioFrame(base->wet, base->wet) * Math::db_to_linear(v.level);
99 vol_modifier.l *= CLAMP(1.0 - v.pan, 0, 1);
100 vol_modifier.r *= CLAMP(1.0 + v.pan, 0, 1);
101
102 for (int i = 0; i < p_frame_count; i++) {
103 /** COMPUTE WAVEFORM **/
104
105 float phase = (float)(local_cycles & AudioEffectChorus::CYCLES_MASK) / (float)(1 << AudioEffectChorus::CYCLES_FRAC);
106
107 float wave_delay = sinf(phase * Math_TAU) * max_depth_frames;
108
109 int wave_delay_frames = lrint(floor(wave_delay));
110 float wave_delay_frac = wave_delay - (float)wave_delay_frames;
111
112 /** COMPUTE RINGBUFFER POS**/
113
114 unsigned int rb_source = local_rb_pos;
115 rb_source -= delay_frames;
116
117 rb_source -= wave_delay_frames;
118
119 /** READ FROM RINGBUFFER, LINEARLY INTERPOLATE */
120
121 AudioFrame val = rb_buff[rb_source & buffer_mask];
122 AudioFrame val_next = rb_buff[(rb_source - 1) & buffer_mask];
123
124 val += (val_next - val) * wave_delay_frac;
125
126 val = val * c1 + h * c2;
127 h = val;
128
129 /** MIX VALUE TO OUTPUT **/
130
131 dst_buff[i] += val * vol_modifier;
132
133 local_cycles += increment;
134 local_rb_pos++;
135 }
136
137 filter_h[vc] = h;
138 cycles[vc] += Math::fast_ftoi(cycles_to_mix * (double)(1 << AudioEffectChorus::CYCLES_FRAC));
139 }
140
141 buffer_pos += p_frame_count;
142}
143
144Ref<AudioEffectInstance> AudioEffectChorus::instantiate() {
145 Ref<AudioEffectChorusInstance> ins;
146 ins.instantiate();
147 ins->base = Ref<AudioEffectChorus>(this);
148 for (int i = 0; i < 4; i++) {
149 ins->filter_h[i] = AudioFrame(0, 0);
150 ins->cycles[i] = 0;
151 }
152
153 float ring_buffer_max_size = AudioEffectChorus::MAX_DELAY_MS + AudioEffectChorus::MAX_DEPTH_MS + AudioEffectChorus::MAX_WIDTH_MS;
154
155 ring_buffer_max_size *= 2; //just to avoid complications
156 ring_buffer_max_size /= 1000.0; //convert to seconds
157 ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();
158
159 int ringbuff_size = ring_buffer_max_size;
160
161 int bits = 0;
162
163 while (ringbuff_size > 0) {
164 bits++;
165 ringbuff_size /= 2;
166 }
167
168 ringbuff_size = 1 << bits;
169 ins->buffer_mask = ringbuff_size - 1;
170 ins->buffer_pos = 0;
171 ins->audio_buffer.resize(ringbuff_size);
172 for (int i = 0; i < ringbuff_size; i++) {
173 ins->audio_buffer.write[i] = AudioFrame(0, 0);
174 }
175
176 return ins;
177}
178
179void AudioEffectChorus::set_voice_count(int p_voices) {
180 ERR_FAIL_COND(p_voices < 1 || p_voices > MAX_VOICES);
181 voice_count = p_voices;
182}
183
184int AudioEffectChorus::get_voice_count() const {
185 return voice_count;
186}
187
188void AudioEffectChorus::set_voice_delay_ms(int p_voice, float p_delay_ms) {
189 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
190
191 voice[p_voice].delay = p_delay_ms;
192}
193
194float AudioEffectChorus::get_voice_delay_ms(int p_voice) const {
195 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
196 return voice[p_voice].delay;
197}
198
199void AudioEffectChorus::set_voice_rate_hz(int p_voice, float p_rate_hz) {
200 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
201
202 voice[p_voice].rate = p_rate_hz;
203}
204
205float AudioEffectChorus::get_voice_rate_hz(int p_voice) const {
206 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
207
208 return voice[p_voice].rate;
209}
210
211void AudioEffectChorus::set_voice_depth_ms(int p_voice, float p_depth_ms) {
212 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
213
214 voice[p_voice].depth = p_depth_ms;
215}
216
217float AudioEffectChorus::get_voice_depth_ms(int p_voice) const {
218 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
219
220 return voice[p_voice].depth;
221}
222
223void AudioEffectChorus::set_voice_level_db(int p_voice, float p_level_db) {
224 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
225
226 voice[p_voice].level = p_level_db;
227}
228
229float AudioEffectChorus::get_voice_level_db(int p_voice) const {
230 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
231
232 return voice[p_voice].level;
233}
234
235void AudioEffectChorus::set_voice_cutoff_hz(int p_voice, float p_cutoff_hz) {
236 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
237
238 voice[p_voice].cutoff = p_cutoff_hz;
239}
240
241float AudioEffectChorus::get_voice_cutoff_hz(int p_voice) const {
242 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
243
244 return voice[p_voice].cutoff;
245}
246
247void AudioEffectChorus::set_voice_pan(int p_voice, float p_pan) {
248 ERR_FAIL_INDEX(p_voice, MAX_VOICES);
249
250 voice[p_voice].pan = p_pan;
251}
252
253float AudioEffectChorus::get_voice_pan(int p_voice) const {
254 ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);
255
256 return voice[p_voice].pan;
257}
258
259void AudioEffectChorus::set_wet(float amount) {
260 wet = amount;
261}
262
263float AudioEffectChorus::get_wet() const {
264 return wet;
265}
266
267void AudioEffectChorus::set_dry(float amount) {
268 dry = amount;
269}
270
271float AudioEffectChorus::get_dry() const {
272 return dry;
273}
274
275void AudioEffectChorus::_validate_property(PropertyInfo &p_property) const {
276 if (p_property.name.begins_with("voice/")) {
277 int voice_idx = p_property.name.get_slice("/", 1).to_int();
278 if (voice_idx > voice_count) {
279 p_property.usage = PROPERTY_USAGE_NONE;
280 }
281 }
282}
283
284void AudioEffectChorus::_bind_methods() {
285 ClassDB::bind_method(D_METHOD("set_voice_count", "voices"), &AudioEffectChorus::set_voice_count);
286 ClassDB::bind_method(D_METHOD("get_voice_count"), &AudioEffectChorus::get_voice_count);
287
288 ClassDB::bind_method(D_METHOD("set_voice_delay_ms", "voice_idx", "delay_ms"), &AudioEffectChorus::set_voice_delay_ms);
289 ClassDB::bind_method(D_METHOD("get_voice_delay_ms", "voice_idx"), &AudioEffectChorus::get_voice_delay_ms);
290
291 ClassDB::bind_method(D_METHOD("set_voice_rate_hz", "voice_idx", "rate_hz"), &AudioEffectChorus::set_voice_rate_hz);
292 ClassDB::bind_method(D_METHOD("get_voice_rate_hz", "voice_idx"), &AudioEffectChorus::get_voice_rate_hz);
293
294 ClassDB::bind_method(D_METHOD("set_voice_depth_ms", "voice_idx", "depth_ms"), &AudioEffectChorus::set_voice_depth_ms);
295 ClassDB::bind_method(D_METHOD("get_voice_depth_ms", "voice_idx"), &AudioEffectChorus::get_voice_depth_ms);
296
297 ClassDB::bind_method(D_METHOD("set_voice_level_db", "voice_idx", "level_db"), &AudioEffectChorus::set_voice_level_db);
298 ClassDB::bind_method(D_METHOD("get_voice_level_db", "voice_idx"), &AudioEffectChorus::get_voice_level_db);
299
300 ClassDB::bind_method(D_METHOD("set_voice_cutoff_hz", "voice_idx", "cutoff_hz"), &AudioEffectChorus::set_voice_cutoff_hz);
301 ClassDB::bind_method(D_METHOD("get_voice_cutoff_hz", "voice_idx"), &AudioEffectChorus::get_voice_cutoff_hz);
302
303 ClassDB::bind_method(D_METHOD("set_voice_pan", "voice_idx", "pan"), &AudioEffectChorus::set_voice_pan);
304 ClassDB::bind_method(D_METHOD("get_voice_pan", "voice_idx"), &AudioEffectChorus::get_voice_pan);
305
306 ClassDB::bind_method(D_METHOD("set_wet", "amount"), &AudioEffectChorus::set_wet);
307 ClassDB::bind_method(D_METHOD("get_wet"), &AudioEffectChorus::get_wet);
308
309 ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectChorus::set_dry);
310 ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectChorus::get_dry);
311
312 ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_voice_count", "get_voice_count");
313 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");
314 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet");
315
316 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 0);
317 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 0);
318 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 0);
319 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 0);
320 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 0);
321 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 0);
322
323 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 1);
324 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 1);
325 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 1);
326 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 1);
327 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 1);
328 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 1);
329
330 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 2);
331 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 2);
332 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 2);
333 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 2);
334 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 2);
335 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 2);
336
337 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/delay_ms", PROPERTY_HINT_RANGE, "0,50,0.01,suffix:ms"), "set_voice_delay_ms", "get_voice_delay_ms", 3);
338 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/rate_hz", PROPERTY_HINT_RANGE, "0.1,20,0.1,suffix:Hz"), "set_voice_rate_hz", "get_voice_rate_hz", 3);
339 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/depth_ms", PROPERTY_HINT_RANGE, "0,20,0.01,suffix:ms"), "set_voice_depth_ms", "get_voice_depth_ms", 3);
340 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/level_db", PROPERTY_HINT_RANGE, "-60,24,0.1,suffix:dB"), "set_voice_level_db", "get_voice_level_db", 3);
341 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1,suffix:Hz"), "set_voice_cutoff_hz", "get_voice_cutoff_hz", 3);
342 ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 3);
343}
344
345AudioEffectChorus::AudioEffectChorus() {
346 voice_count = 2;
347 voice[0].delay = 15;
348 voice[1].delay = 20;
349 voice[0].rate = 0.8;
350 voice[1].rate = 1.2;
351 voice[0].depth = 2;
352 voice[1].depth = 3;
353 voice[0].cutoff = 8000;
354 voice[1].cutoff = 8000;
355 voice[0].pan = -0.5;
356 voice[1].pan = 0.5;
357
358 wet = 0.5;
359 dry = 1.0;
360}
361