Path: blob/master/servers/audio/effects/audio_effect_chorus.cpp
10278 views
/**************************************************************************/1/* audio_effect_chorus.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "audio_effect_chorus.h"31#include "core/math/math_funcs.h"32#include "servers/audio_server.h"3334void AudioEffectChorusInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {35int todo = p_frame_count;3637while (todo) {38int to_mix = MIN(todo, 256); //can't mix too much3940_process_chunk(p_src_frames, p_dst_frames, to_mix);4142p_src_frames += to_mix;43p_dst_frames += to_mix;4445todo -= to_mix;46}47}4849void AudioEffectChorusInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {50//fill ringbuffer51for (int i = 0; i < p_frame_count; i++) {52audio_buffer.write[(buffer_pos + i) & buffer_mask] = p_src_frames[i];53p_dst_frames[i] = p_src_frames[i] * base->dry;54}5556float mix_rate = AudioServer::get_singleton()->get_mix_rate();5758/* process voices */59for (int vc = 0; vc < base->voice_count; vc++) {60AudioEffectChorus::Voice &v = base->voice[vc];6162double time_to_mix = (float)p_frame_count / mix_rate;63double cycles_to_mix = time_to_mix * v.rate;6465unsigned int local_rb_pos = buffer_pos;66AudioFrame *dst_buff = p_dst_frames;67AudioFrame *rb_buff = audio_buffer.ptrw();6869double delay_msec = v.delay;70unsigned int delay_frames = Math::fast_ftoi((delay_msec / 1000.0) * mix_rate);71float max_depth_frames = (v.depth / 1000.0) * mix_rate;7273uint64_t local_cycles = cycles[vc];74uint64_t increment = std::rint(cycles_to_mix / (double)p_frame_count * (double)(1 << AudioEffectChorus::CYCLES_FRAC));7576//check the LFO doesn't read ahead of the write pos77if ((((unsigned int)max_depth_frames) + 10) > delay_frames) { //10 as some threshold to avoid precision stuff78delay_frames += (int)max_depth_frames - delay_frames;79delay_frames += 10; //threshold to avoid precision stuff80}8182//low pass filter83if (v.cutoff == 0) {84continue;85}86float auxlp = std::exp(-Math::TAU * v.cutoff / mix_rate);87float c1 = 1.0 - auxlp;88float c2 = auxlp;89AudioFrame h = filter_h[vc];90if (v.cutoff >= AudioEffectChorus::MS_CUTOFF_MAX) {91c1 = 1.0;92c2 = 0.0;93}9495//vol modifier9697AudioFrame vol_modifier = AudioFrame(base->wet, base->wet) * Math::db_to_linear(v.level);98vol_modifier.left *= CLAMP(1.0 - v.pan, 0, 1);99vol_modifier.right *= CLAMP(1.0 + v.pan, 0, 1);100101for (int i = 0; i < p_frame_count; i++) {102/** COMPUTE WAVEFORM **/103104float phase = (float)(local_cycles & AudioEffectChorus::CYCLES_MASK) / (float)(1 << AudioEffectChorus::CYCLES_FRAC);105106float wave_delay = std::sin(phase * Math::TAU) * max_depth_frames;107108int wave_delay_frames = std::rint(std::floor(wave_delay));109float wave_delay_frac = wave_delay - (float)wave_delay_frames;110111/** COMPUTE RINGBUFFER POS**/112113unsigned int rb_source = local_rb_pos;114rb_source -= delay_frames;115116rb_source -= wave_delay_frames;117118/** READ FROM RINGBUFFER, LINEARLY INTERPOLATE */119120AudioFrame val = rb_buff[rb_source & buffer_mask];121AudioFrame val_next = rb_buff[(rb_source - 1) & buffer_mask];122123val += (val_next - val) * wave_delay_frac;124125val = val * c1 + h * c2;126h = val;127128/** MIX VALUE TO OUTPUT **/129130dst_buff[i] += val * vol_modifier;131132local_cycles += increment;133local_rb_pos++;134}135136filter_h[vc] = h;137cycles[vc] += Math::fast_ftoi(cycles_to_mix * (double)(1 << AudioEffectChorus::CYCLES_FRAC));138}139140buffer_pos += p_frame_count;141}142143Ref<AudioEffectInstance> AudioEffectChorus::instantiate() {144Ref<AudioEffectChorusInstance> ins;145ins.instantiate();146ins->base = Ref<AudioEffectChorus>(this);147for (int i = 0; i < 4; i++) {148ins->filter_h[i] = AudioFrame(0, 0);149ins->cycles[i] = 0;150}151152float ring_buffer_max_size = AudioEffectChorus::MAX_DELAY_MS + AudioEffectChorus::MAX_DEPTH_MS + AudioEffectChorus::MAX_WIDTH_MS;153154ring_buffer_max_size *= 2; //just to avoid complications155ring_buffer_max_size /= 1000.0; //convert to seconds156ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();157158int ringbuff_size = ring_buffer_max_size;159160int bits = 0;161162while (ringbuff_size > 0) {163bits++;164ringbuff_size /= 2;165}166167ringbuff_size = 1 << bits;168ins->buffer_mask = ringbuff_size - 1;169ins->buffer_pos = 0;170ins->audio_buffer.resize(ringbuff_size);171for (int i = 0; i < ringbuff_size; i++) {172ins->audio_buffer.write[i] = AudioFrame(0, 0);173}174175return ins;176}177178void AudioEffectChorus::set_voice_count(int p_voices) {179ERR_FAIL_COND(p_voices < 1 || p_voices > MAX_VOICES);180voice_count = p_voices;181}182183int AudioEffectChorus::get_voice_count() const {184return voice_count;185}186187void AudioEffectChorus::set_voice_delay_ms(int p_voice, float p_delay_ms) {188ERR_FAIL_INDEX(p_voice, MAX_VOICES);189190voice[p_voice].delay = p_delay_ms;191}192193float AudioEffectChorus::get_voice_delay_ms(int p_voice) const {194ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);195return voice[p_voice].delay;196}197198void AudioEffectChorus::set_voice_rate_hz(int p_voice, float p_rate_hz) {199ERR_FAIL_INDEX(p_voice, MAX_VOICES);200201voice[p_voice].rate = p_rate_hz;202}203204float AudioEffectChorus::get_voice_rate_hz(int p_voice) const {205ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);206207return voice[p_voice].rate;208}209210void AudioEffectChorus::set_voice_depth_ms(int p_voice, float p_depth_ms) {211ERR_FAIL_INDEX(p_voice, MAX_VOICES);212213voice[p_voice].depth = p_depth_ms;214}215216float AudioEffectChorus::get_voice_depth_ms(int p_voice) const {217ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);218219return voice[p_voice].depth;220}221222void AudioEffectChorus::set_voice_level_db(int p_voice, float p_level_db) {223ERR_FAIL_INDEX(p_voice, MAX_VOICES);224225voice[p_voice].level = p_level_db;226}227228float AudioEffectChorus::get_voice_level_db(int p_voice) const {229ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);230231return voice[p_voice].level;232}233234void AudioEffectChorus::set_voice_cutoff_hz(int p_voice, float p_cutoff_hz) {235ERR_FAIL_INDEX(p_voice, MAX_VOICES);236237voice[p_voice].cutoff = p_cutoff_hz;238}239240float AudioEffectChorus::get_voice_cutoff_hz(int p_voice) const {241ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);242243return voice[p_voice].cutoff;244}245246void AudioEffectChorus::set_voice_pan(int p_voice, float p_pan) {247ERR_FAIL_INDEX(p_voice, MAX_VOICES);248249voice[p_voice].pan = p_pan;250}251252float AudioEffectChorus::get_voice_pan(int p_voice) const {253ERR_FAIL_INDEX_V(p_voice, MAX_VOICES, 0);254255return voice[p_voice].pan;256}257258void AudioEffectChorus::set_wet(float amount) {259wet = amount;260}261262float AudioEffectChorus::get_wet() const {263return wet;264}265266void AudioEffectChorus::set_dry(float amount) {267dry = amount;268}269270float AudioEffectChorus::get_dry() const {271return dry;272}273274void AudioEffectChorus::_validate_property(PropertyInfo &p_property) const {275if (p_property.name.begins_with("voice/")) {276int voice_idx = p_property.name.get_slicec('/', 1).to_int();277if (voice_idx > voice_count) {278p_property.usage = PROPERTY_USAGE_NONE;279}280}281}282283void AudioEffectChorus::_bind_methods() {284ClassDB::bind_method(D_METHOD("set_voice_count", "voices"), &AudioEffectChorus::set_voice_count);285ClassDB::bind_method(D_METHOD("get_voice_count"), &AudioEffectChorus::get_voice_count);286287ClassDB::bind_method(D_METHOD("set_voice_delay_ms", "voice_idx", "delay_ms"), &AudioEffectChorus::set_voice_delay_ms);288ClassDB::bind_method(D_METHOD("get_voice_delay_ms", "voice_idx"), &AudioEffectChorus::get_voice_delay_ms);289290ClassDB::bind_method(D_METHOD("set_voice_rate_hz", "voice_idx", "rate_hz"), &AudioEffectChorus::set_voice_rate_hz);291ClassDB::bind_method(D_METHOD("get_voice_rate_hz", "voice_idx"), &AudioEffectChorus::get_voice_rate_hz);292293ClassDB::bind_method(D_METHOD("set_voice_depth_ms", "voice_idx", "depth_ms"), &AudioEffectChorus::set_voice_depth_ms);294ClassDB::bind_method(D_METHOD("get_voice_depth_ms", "voice_idx"), &AudioEffectChorus::get_voice_depth_ms);295296ClassDB::bind_method(D_METHOD("set_voice_level_db", "voice_idx", "level_db"), &AudioEffectChorus::set_voice_level_db);297ClassDB::bind_method(D_METHOD("get_voice_level_db", "voice_idx"), &AudioEffectChorus::get_voice_level_db);298299ClassDB::bind_method(D_METHOD("set_voice_cutoff_hz", "voice_idx", "cutoff_hz"), &AudioEffectChorus::set_voice_cutoff_hz);300ClassDB::bind_method(D_METHOD("get_voice_cutoff_hz", "voice_idx"), &AudioEffectChorus::get_voice_cutoff_hz);301302ClassDB::bind_method(D_METHOD("set_voice_pan", "voice_idx", "pan"), &AudioEffectChorus::set_voice_pan);303ClassDB::bind_method(D_METHOD("get_voice_pan", "voice_idx"), &AudioEffectChorus::get_voice_pan);304305ClassDB::bind_method(D_METHOD("set_wet", "amount"), &AudioEffectChorus::set_wet);306ClassDB::bind_method(D_METHOD("get_wet"), &AudioEffectChorus::get_wet);307308ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectChorus::set_dry);309ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectChorus::get_dry);310311ADD_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");312ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");313ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet");314315ADD_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);316ADD_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);317ADD_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);318ADD_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);319ADD_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);320ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/1/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 0);321322ADD_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);323ADD_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);324ADD_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);325ADD_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);326ADD_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);327ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/2/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 1);328329ADD_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);330ADD_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);331ADD_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);332ADD_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);333ADD_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);334ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/3/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 2);335336ADD_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);337ADD_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);338ADD_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);339ADD_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);340ADD_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);341ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "voice/4/pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_voice_pan", "get_voice_pan", 3);342}343344AudioEffectChorus::AudioEffectChorus() {345voice_count = 2;346voice[0].delay = 15;347voice[1].delay = 20;348voice[0].rate = 0.8;349voice[1].rate = 1.2;350voice[0].depth = 2;351voice[1].depth = 3;352voice[0].cutoff = 8000;353voice[1].cutoff = 8000;354voice[0].pan = -0.5;355voice[1].pan = 0.5;356357wet = 0.5;358dry = 1.0;359}360361362