// ------------------------------------------------------------------------ // audiofx_amplitude.cpp: Amplitude effects and dynamic processors. // Copyright (C) 1999-2000,2003,2008,2009 Kai Vehmanen // // Attributes: // eca-style-version: 3 (see Ecasound Programmer's Guide) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // ------------------------------------------------------------------------ #include #include #include #include "samplebuffer_iterators.h" #include "audiofx_amplitude.h" #include "eca-logger.h" #include "eca-error.h" EFFECT_AMPLITUDE::~EFFECT_AMPLITUDE(void) { } EFFECT_AMPLITUDE::parameter_t EFFECT_AMPLITUDE::db_to_linear(parameter_t value) { return std::pow(10.0f, static_cast(value * 0.05f)); } void EFFECT_AMPLITUDE::init(SAMPLE_BUFFER* sbuf) { cur_sbuf_repp = sbuf; EFFECT_BASE::init(sbuf); } void EFFECT_AMPLITUDE::release(void) { cur_sbuf_repp = 0; EFFECT_BASE::release(); } EFFECT_AMPLIFY::EFFECT_AMPLIFY (EFFECT_AMPLITUDE::parameter_t multiplier_percent) { set_parameter(1, multiplier_percent); } EFFECT_AMPLIFY::~EFFECT_AMPLIFY(void) { } void EFFECT_AMPLIFY::set_parameter(int param, parameter_t value) { switch (param) { case 1: gain_rep = value / 100.0; break; } } CHAIN_OPERATOR::parameter_t EFFECT_AMPLIFY::get_parameter(int param) const { switch (param) { case 1: return gain_rep * 100.0; } return 0.0; } void EFFECT_AMPLIFY::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); } void EFFECT_AMPLIFY::init(SAMPLE_BUFFER* sbuf) { i.init(sbuf); sbuf_repp = sbuf; } void EFFECT_AMPLIFY::release(void) { sbuf_repp = 0; } void EFFECT_AMPLIFY::process(void) { sbuf_repp->multiply_by(gain_rep); } /** * Unoptimized version of process(). */ void EFFECT_AMPLIFY::process_ref(void) { i.begin(); while(!i.end()) { *i.current() = *i.current() * gain_rep; i.next(); } } EFFECT_AMPLIFY_DB::EFFECT_AMPLIFY_DB(parameter_t gain, int channel) : channel_rep(-1), sbuf_repp(0) { set_parameter(1, gain); set_parameter(2, channel); } EFFECT_AMPLIFY_DB::~EFFECT_AMPLIFY_DB(void) { } void EFFECT_AMPLIFY_DB::set_parameter(int param, parameter_t value) { switch (param) { case 1: gain_rep = EFFECT_AMPLITUDE::db_to_linear(value); gain_db_rep = value; break; case 2: { int ch = static_cast(value); /* note: ch==0 -> apply to all channels */ if (ch >= 0) { bool reinit = false; if (channel_rep != ch && sbuf_repp != 0) { reinit = true; } channel_rep = ch; /* note: must be done after 'channel_rep' is set */ if (reinit == true) init(sbuf_repp); } } break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_AMPLIFY_DB::get_parameter(int param) const { switch (param) { case 1: return gain_db_rep; case 2: return channel_rep; } DBC_NEVER_REACHED(); return 0.0; } void EFFECT_AMPLIFY_DB::init(SAMPLE_BUFFER* sbuf) { sbuf_repp = sbuf; if (channel_rep > 0) { i_ch.init(sbuf, channel_rep - 1); } else { i_all.init(sbuf); } EFFECT_BASE::init(sbuf); } void EFFECT_AMPLIFY_DB::release(void) { sbuf_repp = 0; } void EFFECT_AMPLIFY_DB::process(void) { if (channel_rep > 0 && channel_rep <= channels()) { sbuf_repp->multiply_by(gain_rep, channel_rep - 1); } else { sbuf_repp->multiply_by(gain_rep); } } /** * Unoptimized version of process(). */ void EFFECT_AMPLIFY_DB::process_ref(void) { if (channel_rep > 0 && channel_rep < channels()) { i_ch.begin(); while(!i_ch.end()) { *i_ch.current() *= gain_rep; i_ch.next(); } } else { i_all.begin(); while(!i_all.end()) { *i_all.current() *= gain_rep; i_all.next(); } } } int EFFECT_AMPLIFY_DB::output_channels(int i_channels) const { if (channel_rep > i_channels) return channel_rep; return i_channels; } EFFECT_AMPLIFY_CLIPCOUNT::EFFECT_AMPLIFY_CLIPCOUNT (parameter_t multiplier_percent, int max_clipped) { set_parameter(1, multiplier_percent); set_parameter(2, max_clipped); num_of_clipped = 0; } void EFFECT_AMPLIFY_CLIPCOUNT::set_parameter(int param, parameter_t value) { switch (param) { case 1: gain = value / 100.0; break; case 2: maxnum_of_clipped = (int)value; break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_AMPLIFY_CLIPCOUNT::get_parameter(int param) const { switch (param) { case 1: return gain * 100.0; case 2: return maxnum_of_clipped; } DBC_NEVER_REACHED(); return 0.0f; } void EFFECT_AMPLIFY_CLIPCOUNT::init(SAMPLE_BUFFER* sbuf) { i.init(sbuf); } void EFFECT_AMPLIFY_CLIPCOUNT::process(void) { i.begin(); while(!i.end()) { *i.current() = *i.current() * gain; if (*i.current() > SAMPLE_SPECS::impl_max_value || *i.current() < SAMPLE_SPECS::impl_min_value) { num_of_clipped++; } else { num_of_clipped = 0; } i.next(); } if (num_of_clipped > maxnum_of_clipped && maxnum_of_clipped != 0) { MESSAGE_ITEM otemp; otemp.setprecision(0); otemp << "(audiofx_amplitude) WARNING! Signal is clipping! "; otemp << num_of_clipped; otemp << " consecutive clipped samples."; ECA_LOG_MSG(ECA_LOGGER::info, otemp.to_string()); } } void EFFECT_AMPLIFY_CLIPCOUNT::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); } EFFECT_AMPLIFY_CHANNEL::EFFECT_AMPLIFY_CHANNEL (parameter_t multiplier_percent, int channel) { set_parameter(1, multiplier_percent); set_parameter(2, channel); } int EFFECT_AMPLIFY_CHANNEL::output_channels(int i_channels) const { if (channel_rep + 1 > i_channels) return channel_rep + 1; return i_channels; } void EFFECT_AMPLIFY_CHANNEL::set_parameter(int param, parameter_t value) { switch (param) { case 1: gain = value / 100.0; break; case 2: channel_rep = static_cast(value); channel_rep--; break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_AMPLIFY_CHANNEL::get_parameter(int param) const { switch (param) { case 1: return gain * 100.0; case 2: return static_cast(channel_rep + 1); } DBC_NEVER_REACHED(); return 0.0; } void EFFECT_AMPLIFY_CHANNEL::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); switch (param) { case 2: { pd->default_value = 1.0f; pd->bounded_above = false; pd->bounded_below = true; pd->lower_bound = 1.0f; pd->integer = true; } } } void EFFECT_AMPLIFY_CHANNEL::init(SAMPLE_BUFFER *insample) { i.init(insample); EFFECT_AMPLITUDE::init(insample); } void EFFECT_AMPLIFY_CHANNEL::process(void) { if (channel_rep >= 0 && channel_rep < channels()) { cur_sbuf_repp->multiply_by(gain, channel_rep); } } /** * Unoptimized version of process(). */ void EFFECT_AMPLIFY_CHANNEL::process_ref(void) { if (channel_rep >= 0 && channel_rep < channels()) { i.begin(channel_rep); while(!i.end()) { *i.current() = *i.current() * gain; i.next(); } } } EFFECT_LIMITER::EFFECT_LIMITER (parameter_t limiting_percent) { set_parameter(1, limiting_percent); } EFFECT_LIMITER::~EFFECT_LIMITER(void) { } void EFFECT_LIMITER::set_parameter(int param, parameter_t value) { switch (param) { case 1: limit_rep = value / 100.0; break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_LIMITER::get_parameter(int param) const { switch (param) { case 1: return limit_rep * 100.0; } DBC_NEVER_REACHED(); return 0.0; } void EFFECT_LIMITER::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); } void EFFECT_LIMITER::init(SAMPLE_BUFFER* sbuf) { i.init(sbuf); } void EFFECT_LIMITER::process(void) { i.begin(); while(!i.end()) { if (*i.current() < 0) { if ((-(*i.current())) > limit_rep) *i.current() = -limit_rep; } else { if (*i.current() > limit_rep) *i.current() = limit_rep; } i.next(); } } EFFECT_COMPRESS::EFFECT_COMPRESS (parameter_t compress_rate, parameter_t thold) { set_parameter(1, compress_rate); set_parameter(2, thold); first_time = true; } EFFECT_COMPRESS::EFFECT_COMPRESS (const EFFECT_COMPRESS& x) { crate = x.crate; threshold = x.threshold; delta = x.delta; ratio = x.ratio; first_time = x.first_time; lastin = x.lastin; lastout = x.lastout; } void EFFECT_COMPRESS::set_parameter(int param, parameter_t value) { switch (param) { case 1: crate = std::pow(2.0, value / 6.0); break; case 2: threshold = value / 100.0; break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_COMPRESS::get_parameter(int param) const { switch (param) { case 1: return (log (crate)) / (log (2.0f)) * 6.0; case 2: return threshold * 100.0; } DBC_NEVER_REACHED(); return 0.0; } void EFFECT_COMPRESS::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { switch (param) { case 1: pd->default_value = 1.0f; pd->description = "compression-rate-dB"; pd->bounded_above = true; pd->upper_bound = 99.0f; pd->bounded_below = true; pd->lower_bound = 0.0f; pd->toggled = false; pd->integer = false; pd->logarithmic = true; pd->output = false; break; case 2: pd->default_value = 30.0f; pd->description = "threshold-%"; pd->bounded_above = true; pd->upper_bound = 100.0f; pd->bounded_below = true; pd->lower_bound = 0.0f; pd->toggled = false; pd->integer = false; pd->logarithmic = false; pd->output = false; break; default: DBC_NEVER_REACHED(); } } void EFFECT_COMPRESS::init(SAMPLE_BUFFER *insample) { i.init(insample); set_channels(insample->number_of_channels()); set_samples_per_second(samples_per_second()); lastin.resize(insample->number_of_channels()); lastout.resize(insample->number_of_channels()); } void EFFECT_COMPRESS::process(void) { i.begin(); while(!i.end()) { if (first_time) { first_time = false; lastin[i.channel()] = lastout[i.channel()] = *i.current(); } else { if (fabs(*i.current()) > threshold) { delta = *i.current() - lastin[i.channel()]; delta /= crate; new_value = lastin[i.channel()] + delta; ratio = new_value / lastin[i.channel()]; new_value = lastout[i.channel()] * ratio; if (new_value > SAMPLE_SPECS::impl_max_value) new_value = SAMPLE_SPECS::impl_max_value; else if (new_value < SAMPLE_SPECS::impl_min_value) new_value = SAMPLE_SPECS::impl_min_value; lastin[i.channel()] = *i.current(); *i.current() = lastout[i.channel()] = new_value; } else { lastin[i.channel()] = lastout[i.channel()] = *i.current(); } } i.next(); } } EFFECT_NOISEGATE::EFFECT_NOISEGATE (parameter_t thlevel_percent, parameter_t thtime, parameter_t a, parameter_t h, parameter_t r) { // map_parameters(); set_parameter(1, thlevel_percent); set_parameter(2, thtime); set_parameter(3, a); set_parameter(4, h); set_parameter(5, r); } void EFFECT_NOISEGATE::set_parameter(int param, parameter_t value) { switch (param) { case 1: th_level = SAMPLE_SPECS::max_amplitude * (value / 100.0); break; case 2: th_time = (value * (parameter_t)samples_per_second() / 1000.0); break; case 3: atime = (value * (parameter_t)samples_per_second() / 1000.0); break; case 4: htime = (value * (parameter_t)samples_per_second() / 1000.0); break; case 5: rtime = (value * (parameter_t)samples_per_second() / 1000.0); break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_NOISEGATE::get_parameter(int param) const { switch (param) { case 1: return th_level * 100.0 / (parameter_t)SAMPLE_SPECS::max_amplitude; case 2: return th_time * 1000.0 / (parameter_t)samples_per_second(); case 3: return atime * 1000.0 / (parameter_t)samples_per_second(); case 4: return htime * 1000.0 / (parameter_t)samples_per_second(); case 5: return rtime * 1000.0 / (parameter_t)samples_per_second(); } DBC_NEVER_REACHED(); return 0.0; } void EFFECT_NOISEGATE::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); } void EFFECT_NOISEGATE::init(SAMPLE_BUFFER *insample) { i.init(insample); set_channels(insample->number_of_channels()); set_samples_per_second(samples_per_second()); th_time_lask.resize(insample->number_of_channels()); attack_lask.resize(insample->number_of_channels()); hold_lask.resize(insample->number_of_channels()); release_lask.resize(insample->number_of_channels()); gain.resize(insample->number_of_channels()); ng_status.resize(insample->number_of_channels(), int(ng_waiting)); } void EFFECT_NOISEGATE::process(void) { i.begin(); while(!i.end()) { bool below = fabs(*i.current()) <= th_level; switch(ng_status[i.channel()]) { case ng_waiting: // --- // phase 1 - waiting // --- { if (below) { th_time_lask[i.channel()]++; if (th_time_lask[i.channel()] >= th_time) { th_time_lask[i.channel()] = 0.0; ng_status[i.channel()] = ng_attacking; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from waiting to attacking"); } } else { th_time_lask[i.channel()] = 0; } break; } case ng_attacking: // --- // phase 2 - attack // --- { if (below) { attack_lask[i.channel()]++; gain[i.channel()] = (1.0 - (attack_lask[i.channel()] / atime)); if (attack_lask[i.channel()] >= atime) { attack_lask[i.channel()] = 0.0; ng_status[i.channel()] = ng_active; gain[i.channel()] = 0.0; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from attack to active"); } *i.current() = *i.current() * gain[i.channel()]; } else { attack_lask[i.channel()] = 0; ng_status[i.channel()] = ng_waiting; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from attack to waiting"); } break; } case ng_active: // --- // phase 3 - active // --- { if (below == false) { ng_status[i.channel()] = ng_holding; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from active to holding"); } *i.current() = *i.current() * 0.0; break; } case ng_holding: // --- // phase 4 - holding // --- { if (!below) { hold_lask[i.channel()]++; if (hold_lask[i.channel()] >= htime) { hold_lask[i.channel()] = 0.0; ng_status[i.channel()] = ng_releasing; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from holding to release"); } } *i.current() = *i.current() * 0.0; break; } case ng_releasing: // --- // phase 5 - releasing // --- { release_lask[i.channel()]++; gain[i.channel()] = release_lask[i.channel()] / rtime; if (release_lask[i.channel()] >= rtime) { release_lask[i.channel()] = 0.0; ng_status[i.channel()] = ng_waiting; ECA_LOG_MSG(ECA_LOGGER::user_objects,"(audiofx) noisegate - from releasing to waiting"); } *i.current() = *i.current() * gain[i.channel()]; break; } } i.next(); } } EFFECT_NORMAL_PAN::EFFECT_NORMAL_PAN (parameter_t right_percent) { set_parameter(1, right_percent); } void EFFECT_NORMAL_PAN::set_parameter(int param, parameter_t value) { switch (param) { case 1: right_percent_rep = value; if (value == 50.0) { l_gain = r_gain = 1.0; } else if (value < 50.0) { l_gain = 1.0; r_gain = value / 50.0; } else if (value > 50.0) { r_gain = 1.0; l_gain = (100.0 - value) / 50.0; } break; default: DBC_NEVER_REACHED(); } } CHAIN_OPERATOR::parameter_t EFFECT_NORMAL_PAN::get_parameter(int param) const { switch (param) { case 1: return right_percent_rep; } return 0.0; } void EFFECT_NORMAL_PAN::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const { OPERATOR::parameter_description(param, pd); switch (param) { case 1: { pd->default_value = 50.0f; pd->bounded_above = true; pd->upper_bound = 100.0f; pd->bounded_below = true; pd->lower_bound = 0.0f; } default: DBC_NEVER_REACHED(); } } void EFFECT_NORMAL_PAN::init(SAMPLE_BUFFER *insample) { i.init(insample); EFFECT_AMPLITUDE::init(insample); } void EFFECT_NORMAL_PAN::process(void) { /* to match with out_channels() */ cur_sbuf_repp->number_of_channels(2); cur_sbuf_repp->multiply_by(l_gain, SAMPLE_SPECS::ch_left); cur_sbuf_repp->multiply_by(r_gain, SAMPLE_SPECS::ch_right); } /** * Unoptimized version of process(). */ void EFFECT_NORMAL_PAN::process_ref(void) { i.begin(0); while(!i.end()) { *i.current() = *i.current() * l_gain; i.next(); } i.begin(1); while(!i.end()) { *i.current() = *i.current() * r_gain; i.next(); } }