Path: blob/master/servers/audio/effects/audio_effect_delay.cpp
10278 views
/**************************************************************************/1/* audio_effect_delay.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_delay.h"3132#include "core/math/math_funcs.h"33#include "servers/audio_server.h"3435void AudioEffectDelayInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {36int todo = p_frame_count;3738while (todo) {39int to_mix = MIN(todo, 256); //can't mix too much4041_process_chunk(p_src_frames, p_dst_frames, to_mix);4243p_src_frames += to_mix;44p_dst_frames += to_mix;4546todo -= to_mix;47}48}4950void AudioEffectDelayInstance::_process_chunk(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {51float main_level_f = base->dry;5253float mix_rate = AudioServer::get_singleton()->get_mix_rate();5455float tap_1_level_f = base->tap_1_active ? Math::db_to_linear(base->tap_1_level) : 0.0;56int tap_1_delay_frames = int((base->tap_1_delay_ms / 1000.0) * mix_rate);5758float tap_2_level_f = base->tap_2_active ? Math::db_to_linear(base->tap_2_level) : 0.0;59int tap_2_delay_frames = int((base->tap_2_delay_ms / 1000.0) * mix_rate);6061float feedback_level_f = base->feedback_active ? Math::db_to_linear(base->feedback_level) : 0.0;62unsigned int feedback_delay_frames = int((base->feedback_delay_ms / 1000.0) * mix_rate);6364AudioFrame tap1_vol = AudioFrame(tap_1_level_f, tap_1_level_f);6566tap1_vol.left *= CLAMP(1.0 - base->tap_1_pan, 0, 1);67tap1_vol.right *= CLAMP(1.0 + base->tap_1_pan, 0, 1);6869AudioFrame tap2_vol = AudioFrame(tap_2_level_f, tap_2_level_f);7071tap2_vol.left *= CLAMP(1.0 - base->tap_2_pan, 0, 1);72tap2_vol.right *= CLAMP(1.0 + base->tap_2_pan, 0, 1);7374// feedback lowpass here75float lpf_c = std::exp(-Math::TAU * base->feedback_lowpass / mix_rate); // 0 .. 10khz76float lpf_ic = 1.0 - lpf_c;7778const AudioFrame *src = p_src_frames;79AudioFrame *dst = p_dst_frames;80AudioFrame *rb_buf = ring_buffer.ptrw();81AudioFrame *fb_buf = feedback_buffer.ptrw();8283for (int i = 0; i < p_frame_count; i++) {84rb_buf[ring_buffer_pos & ring_buffer_mask] = src[i];8586AudioFrame main_val = src[i] * main_level_f;87AudioFrame tap_1_val = rb_buf[(ring_buffer_pos - tap_1_delay_frames) & ring_buffer_mask] * tap1_vol;88AudioFrame tap_2_val = rb_buf[(ring_buffer_pos - tap_2_delay_frames) & ring_buffer_mask] * tap2_vol;8990AudioFrame out = main_val + tap_1_val + tap_2_val;9192out += fb_buf[feedback_buffer_pos];9394//apply lowpass and feedback gain95AudioFrame fb_in = out * feedback_level_f * lpf_ic + h * lpf_c;96fb_in.undenormalize(); //avoid denormals9798h = fb_in;99fb_buf[feedback_buffer_pos] = fb_in;100101dst[i] = out;102103ring_buffer_pos++;104105if ((++feedback_buffer_pos) >= feedback_delay_frames) {106feedback_buffer_pos = 0;107}108}109}110111Ref<AudioEffectInstance> AudioEffectDelay::instantiate() {112Ref<AudioEffectDelayInstance> ins;113ins.instantiate();114ins->base = Ref<AudioEffectDelay>(this);115116float ring_buffer_max_size = MAX_DELAY_MS + 100; //add 100ms of extra room, just in case117ring_buffer_max_size /= 1000.0; //convert to seconds118ring_buffer_max_size *= AudioServer::get_singleton()->get_mix_rate();119120int ringbuff_size = ring_buffer_max_size;121122int bits = 0;123124while (ringbuff_size > 0) {125bits++;126ringbuff_size /= 2;127}128129ringbuff_size = 1 << bits;130ins->ring_buffer_mask = ringbuff_size - 1;131ins->ring_buffer_pos = 0;132133ins->ring_buffer.resize(ringbuff_size);134ins->feedback_buffer.resize(ringbuff_size);135136ins->feedback_buffer_pos = 0;137138ins->h = AudioFrame(0, 0);139140return ins;141}142143void AudioEffectDelay::set_dry(float p_dry) {144dry = p_dry;145}146147float AudioEffectDelay::get_dry() {148return dry;149}150151void AudioEffectDelay::set_tap1_active(bool p_active) {152tap_1_active = p_active;153}154155bool AudioEffectDelay::is_tap1_active() const {156return tap_1_active;157}158159void AudioEffectDelay::set_tap1_delay_ms(float p_delay_ms) {160tap_1_delay_ms = p_delay_ms;161}162163float AudioEffectDelay::get_tap1_delay_ms() const {164return tap_1_delay_ms;165}166167void AudioEffectDelay::set_tap1_level_db(float p_level_db) {168tap_1_level = p_level_db;169}170171float AudioEffectDelay::get_tap1_level_db() const {172return tap_1_level;173}174175void AudioEffectDelay::set_tap1_pan(float p_pan) {176tap_1_pan = p_pan;177}178179float AudioEffectDelay::get_tap1_pan() const {180return tap_1_pan;181}182183void AudioEffectDelay::set_tap2_active(bool p_active) {184tap_2_active = p_active;185}186187bool AudioEffectDelay::is_tap2_active() const {188return tap_2_active;189}190191void AudioEffectDelay::set_tap2_delay_ms(float p_delay_ms) {192tap_2_delay_ms = p_delay_ms;193}194195float AudioEffectDelay::get_tap2_delay_ms() const {196return tap_2_delay_ms;197}198199void AudioEffectDelay::set_tap2_level_db(float p_level_db) {200tap_2_level = p_level_db;201}202203float AudioEffectDelay::get_tap2_level_db() const {204return tap_2_level;205}206207void AudioEffectDelay::set_tap2_pan(float p_pan) {208tap_2_pan = p_pan;209}210211float AudioEffectDelay::get_tap2_pan() const {212return tap_2_pan;213}214215void AudioEffectDelay::set_feedback_active(bool p_active) {216feedback_active = p_active;217}218219bool AudioEffectDelay::is_feedback_active() const {220return feedback_active;221}222223void AudioEffectDelay::set_feedback_delay_ms(float p_delay_ms) {224feedback_delay_ms = p_delay_ms;225}226227float AudioEffectDelay::get_feedback_delay_ms() const {228return feedback_delay_ms;229}230231void AudioEffectDelay::set_feedback_level_db(float p_level_db) {232feedback_level = p_level_db;233}234235float AudioEffectDelay::get_feedback_level_db() const {236return feedback_level;237}238239void AudioEffectDelay::set_feedback_lowpass(float p_lowpass) {240feedback_lowpass = p_lowpass;241}242243float AudioEffectDelay::get_feedback_lowpass() const {244return feedback_lowpass;245}246247void AudioEffectDelay::_bind_methods() {248ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectDelay::set_dry);249ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectDelay::get_dry);250251ClassDB::bind_method(D_METHOD("set_tap1_active", "amount"), &AudioEffectDelay::set_tap1_active);252ClassDB::bind_method(D_METHOD("is_tap1_active"), &AudioEffectDelay::is_tap1_active);253254ClassDB::bind_method(D_METHOD("set_tap1_delay_ms", "amount"), &AudioEffectDelay::set_tap1_delay_ms);255ClassDB::bind_method(D_METHOD("get_tap1_delay_ms"), &AudioEffectDelay::get_tap1_delay_ms);256257ClassDB::bind_method(D_METHOD("set_tap1_level_db", "amount"), &AudioEffectDelay::set_tap1_level_db);258ClassDB::bind_method(D_METHOD("get_tap1_level_db"), &AudioEffectDelay::get_tap1_level_db);259260ClassDB::bind_method(D_METHOD("set_tap1_pan", "amount"), &AudioEffectDelay::set_tap1_pan);261ClassDB::bind_method(D_METHOD("get_tap1_pan"), &AudioEffectDelay::get_tap1_pan);262263ClassDB::bind_method(D_METHOD("set_tap2_active", "amount"), &AudioEffectDelay::set_tap2_active);264ClassDB::bind_method(D_METHOD("is_tap2_active"), &AudioEffectDelay::is_tap2_active);265266ClassDB::bind_method(D_METHOD("set_tap2_delay_ms", "amount"), &AudioEffectDelay::set_tap2_delay_ms);267ClassDB::bind_method(D_METHOD("get_tap2_delay_ms"), &AudioEffectDelay::get_tap2_delay_ms);268269ClassDB::bind_method(D_METHOD("set_tap2_level_db", "amount"), &AudioEffectDelay::set_tap2_level_db);270ClassDB::bind_method(D_METHOD("get_tap2_level_db"), &AudioEffectDelay::get_tap2_level_db);271272ClassDB::bind_method(D_METHOD("set_tap2_pan", "amount"), &AudioEffectDelay::set_tap2_pan);273ClassDB::bind_method(D_METHOD("get_tap2_pan"), &AudioEffectDelay::get_tap2_pan);274275ClassDB::bind_method(D_METHOD("set_feedback_active", "amount"), &AudioEffectDelay::set_feedback_active);276ClassDB::bind_method(D_METHOD("is_feedback_active"), &AudioEffectDelay::is_feedback_active);277278ClassDB::bind_method(D_METHOD("set_feedback_delay_ms", "amount"), &AudioEffectDelay::set_feedback_delay_ms);279ClassDB::bind_method(D_METHOD("get_feedback_delay_ms"), &AudioEffectDelay::get_feedback_delay_ms);280281ClassDB::bind_method(D_METHOD("set_feedback_level_db", "amount"), &AudioEffectDelay::set_feedback_level_db);282ClassDB::bind_method(D_METHOD("get_feedback_level_db"), &AudioEffectDelay::get_feedback_level_db);283284ClassDB::bind_method(D_METHOD("set_feedback_lowpass", "amount"), &AudioEffectDelay::set_feedback_lowpass);285ClassDB::bind_method(D_METHOD("get_feedback_lowpass"), &AudioEffectDelay::get_feedback_lowpass);286287ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry");288289ADD_GROUP("Tap 1", "tap1_");290ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap1_active"), "set_tap1_active", "is_tap1_active");291ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap1_delay_ms", "get_tap1_delay_ms");292ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap1_level_db", "get_tap1_level_db");293ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap1_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap1_pan", "get_tap1_pan");294295ADD_GROUP("Tap 2", "tap2_");296ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tap2_active"), "set_tap2_active", "is_tap2_active");297ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_tap2_delay_ms", "get_tap2_delay_ms");298ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_tap2_level_db", "get_tap2_level_db");299ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tap2_pan", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_tap2_pan", "get_tap2_pan");300301ADD_GROUP("Feedback", "feedback_");302ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feedback_active"), "set_feedback_active", "is_feedback_active");303ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_delay_ms", PROPERTY_HINT_RANGE, "0,1500,1,suffix:ms"), "set_feedback_delay_ms", "get_feedback_delay_ms");304ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_level_db", PROPERTY_HINT_RANGE, "-60,0,0.01,suffix:dB"), "set_feedback_level_db", "get_feedback_level_db");305ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "feedback_lowpass", PROPERTY_HINT_RANGE, "1,16000,1"), "set_feedback_lowpass", "get_feedback_lowpass");306}307308309