From d22dbc7d70a0365c09084ad923b2db30ff60dbdb Mon Sep 17 00:00:00 2001 From: Krzysztof Foltman Date: Sun, 29 Aug 2010 23:31:22 +0100 Subject: [PATCH] Refactor metering code out of distortion plugins. (cherry picked from commit 84d873861da7b4ad205061b00fcc9f73c47140b6) --- plugins/ladspa_effect/calf/src/calf/modules.h | 1 - .../calf/src/calf/modules_dist.h | 13 +- .../calf/src/calf/plugin_tools.h | 80 +++++++++ plugins/ladspa_effect/calf/src/calf/vumeter.h | 95 ++++++++++ .../ladspa_effect/calf/src/modules_dist.cpp | 165 +++--------------- 5 files changed, 207 insertions(+), 147 deletions(-) create mode 100644 plugins/ladspa_effect/calf/src/calf/plugin_tools.h create mode 100644 plugins/ladspa_effect/calf/src/calf/vumeter.h diff --git a/plugins/ladspa_effect/calf/src/calf/modules.h b/plugins/ladspa_effect/calf/src/calf/modules.h index d20421879..b9395c68d 100644 --- a/plugins/ladspa_effect/calf/src/calf/modules.h +++ b/plugins/ladspa_effect/calf/src/calf/modules.h @@ -29,7 +29,6 @@ #include "giface.h" #include "metadata.h" #include "loudness.h" -#include "primitives.h" namespace calf_plugins { diff --git a/plugins/ladspa_effect/calf/src/calf/modules_dist.h b/plugins/ladspa_effect/calf/src/calf/modules_dist.h index 6c49c372d..d7be7dd79 100644 --- a/plugins/ladspa_effect/calf/src/calf/modules_dist.h +++ b/plugins/ladspa_effect/calf/src/calf/modules_dist.h @@ -28,6 +28,7 @@ #include "audio_fx.h" #include "giface.h" #include "metadata.h" +#include "plugin_tools.h" namespace calf_plugins { @@ -37,8 +38,8 @@ private: float hp_pre_freq_old, lp_pre_freq_old; float hp_post_freq_old, lp_post_freq_old; float p_level_old, p_freq_old, p_q_old; - uint32_t clip_in, clip_out; - float meter_in, meter_out, meter_drive; + stereo_in_out_metering meters; + float meter_drive; dsp::biquad_d2 lp[2][4], hp[2][4]; dsp::biquad_d2 p[2]; dsp::tap_distortion dist[2]; @@ -57,8 +58,8 @@ public: class exciter_audio_module: public audio_module { private: float freq_old; - uint32_t clip_in, clip_out; - float meter_in, meter_out, meter_drive; + stereo_in_out_metering meters; + float meter_drive; dsp::biquad_d2 hp[2][4]; dsp::tap_distortion dist[2]; public: @@ -76,8 +77,8 @@ public: class bassenhancer_audio_module: public audio_module { private: float freq_old; - uint32_t clip_in, clip_out; - float meter_in, meter_out, meter_drive; + stereo_in_out_metering meters; + float meter_drive; dsp::biquad_d2 lp[2][4]; dsp::tap_distortion dist[2]; public: diff --git a/plugins/ladspa_effect/calf/src/calf/plugin_tools.h b/plugins/ladspa_effect/calf/src/calf/plugin_tools.h new file mode 100644 index 000000000..aad5cdf6b --- /dev/null +++ b/plugins/ladspa_effect/calf/src/calf/plugin_tools.h @@ -0,0 +1,80 @@ +/* Calf DSP plugin pack + * Tools to use in plugins + * + * Copyright (C) 2001-2010 Krzysztof Foltman, Markus Schmidt, Thor Harald Johansen and others + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02111-1307, USA. + */ +#ifndef CALF_PLUGIN_TOOLS_H +#define CALF_PLUGIN_TOOLS_H + +#include + +#include "giface.h" +#include "vumeter.h" + +namespace calf_plugins { + +/// Base class for universal stereo level metering +struct stereo_in_out_metering_base +{ + dsp::vumeter vumeter_in, vumeter_out; + + stereo_in_out_metering_base() + { + reset(); + } + void reset() + { + vumeter_in.reset(); + vumeter_out.reset(); + } +}; + +/// Universal stereo level metering for a specific plugin +template +class stereo_in_out_metering: public stereo_in_out_metering_base +{ +public: + inline void process(float *const *params, const float *const *inputs, const float *const *outputs, unsigned int offset, unsigned int nsamples) + { + if (params[Metadata::param_meter_in] || params[Metadata::param_clip_in]) { + if (inputs) + vumeter_in.update_stereo(inputs[0] ? inputs[0] + offset : NULL, inputs[1] ? inputs[1] + offset : NULL, nsamples); + else + vumeter_in.update_zeros(nsamples); + if (params[Metadata::param_meter_in]) + *params[Metadata::param_meter_in] = vumeter_in.level; + if (params[Metadata::param_clip_in]) + *params[Metadata::param_clip_in] = vumeter_in.clip > 0 ? 1.f : 0.f; + } + if (params[Metadata::param_meter_out] || params[Metadata::param_clip_out]) { + if (outputs) + vumeter_out.update_stereo(outputs[0] ? outputs[0] + offset : NULL, outputs[1] ? outputs[1] + offset : NULL, nsamples); + else + vumeter_out.update_zeros(nsamples); + if (params[Metadata::param_meter_out]) + *params[Metadata::param_meter_out] = vumeter_out.level; + if (params[Metadata::param_clip_out]) + *params[Metadata::param_clip_out] = vumeter_out.clip > 0 ? 1.f : 0.f; + } + } +}; + +}; + +#endif + diff --git a/plugins/ladspa_effect/calf/src/calf/vumeter.h b/plugins/ladspa_effect/calf/src/calf/vumeter.h new file mode 100644 index 000000000..6ef1e7622 --- /dev/null +++ b/plugins/ladspa_effect/calf/src/calf/vumeter.h @@ -0,0 +1,95 @@ +/* Calf DSP Library + * Peak metering facilities. + * + * Copyright (C) 2007 Krzysztof Foltman + * + * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __CALF_VUMETER_H +#define __CALF_VUMETER_H + +namespace dsp { + +/// Peak meter class +struct vumeter +{ + /// Measured signal level + float level; + /// Falloff of signal level (b1 coefficient of a 1-pole filter) + float falloff; + /// Clip indicator (set to 1 when |value| >= 1, fading otherwise) + float clip; + /// Falloff of clip indicator (b1 coefficient of a 1-pole filter); set to 1 if no falloff is required (manual reset of clip indicator) + float clip_falloff; + + vumeter() + { + falloff = 0.999f; + clip_falloff = 0.999f; + reset(); + } + + void reset() + { + level = 0; + clip = 0; + } + + /// Update peak meter based on input signal + inline void update(const float *src, unsigned int len) + { + update_stereo(src, NULL, len); + } + /// Update peak meter based on louder of two input signals + inline void update_stereo(const float *src1, const float *src2, unsigned int len) + { + // "Age" the old level by falloff^length + level *= pow(falloff, len); + // Same for clip level (using different fade constant) + clip *= pow(clip_falloff, len); + dsp::sanitize(level); + dsp::sanitize(clip); + // Process input samples - to get peak value, take a max of all values in the input signal and "aged" old peak + // Clip is set to 1 if any sample is out-of-range, if no clip occurs, the "aged" value is assumed + if (src1) + run_sample_loop(src1, len); + if (src2) + run_sample_loop(src2, len); + } + inline void run_sample_loop(const float *src, unsigned int len) + { + float tmp = level; + for (unsigned int i = 0; i < len; i++) { + float sig = fabs(src[i]); + tmp = std::max(tmp, sig); + if (sig >= 1.f) + clip = 1.f; + } + level = tmp; + } + /// Update clip meter as if update was called with all-zero input signal + inline void update_zeros(unsigned int len) + { + level *= pow((double)falloff, (double)len); + clip *= pow((double)clip_falloff, (double)len); + dsp::sanitize(level); + dsp::sanitize(clip); + } +}; + +}; + +#endif diff --git a/plugins/ladspa_effect/calf/src/modules_dist.cpp b/plugins/ladspa_effect/calf/src/modules_dist.cpp index aba40fc74..1592e3267 100644 --- a/plugins/ladspa_effect/calf/src/modules_dist.cpp +++ b/plugins/ladspa_effect/calf/src/modules_dist.cpp @@ -38,10 +38,6 @@ saturator_audio_module::saturator_audio_module() { is_active = false; srate = 0; - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; } @@ -50,6 +46,8 @@ void saturator_audio_module::activate() is_active = true; // set all filters params_changed(); + meters.reset(); + meter_drive = 0.f; } void saturator_audio_module::deactivate() { @@ -120,6 +118,8 @@ void saturator_audio_module::set_sample_rate(uint32_t sr) uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) { bool bypass = *params[param_bypass] > 0.5f; + uint32_t orig_offset = offset; + uint32_t orig_numsamples = numsamples; numsamples += offset; if(bypass) { // everything bypassed @@ -137,18 +137,9 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u } ++offset; } - // displays, too - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; - meter_drive = 0.f; + meters.reset(); + meters.process(params, NULL, NULL, 0, 0); } else { - - clip_in -= std::min(clip_in, numsamples); - clip_out -= std::min(clip_out, numsamples); - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; float in_avg[2] = {0.f, 0.f}; float out_avg[2] = {0.f, 0.f}; @@ -157,7 +148,6 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u while(offset < numsamples) { // cycle through samples float out[2], in[2] = {0.f, 0.f}; - float maxIn, maxOut = 0.f; int c = 0; if(in_count > 1 && out_count > 1) { @@ -178,6 +168,8 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u proc[0] = in[0] * *params[param_level_in]; proc[1] = in[1] * *params[param_level_in]; + float onedivlevelin = 1.0 / *params[param_level_in]; + for (int i = 0; i < c; ++i) { // all pre filters in chain proc[i] = lp[i][1].process(lp[i][0].process(proc[i])); @@ -200,7 +192,7 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u proc[i] = hp[i][2].process(hp[i][3].process(proc[i])); //subtract gain - proc[i] /= *params[param_level_in]; + proc[i] *= onedivlevelin; } if(in_count > 1 && out_count > 1) { @@ -209,42 +201,23 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u outs[0][offset] = out[0]; out[1] = ((proc[1] * *params[param_mix]) + in[1] * (1 - *params[param_mix])) * *params[param_level_out]; outs[1][offset] = out[1]; - maxIn = std::max(fabs(in[0]), fabs(in[1])); - maxOut = std::max(fabs(out[0]), fabs(out[1])); } else if(out_count > 1) { // mono -> pseudo stereo out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out]; outs[0][offset] = out[0]; out[1] = out[0]; outs[1][offset] = out[1]; - maxOut = fabs(out[0]); - maxIn = fabs(in[0]); } else { // stereo -> mono // or full mono out[0] = ((proc[0] * *params[param_mix]) + in[0] * (1 - *params[param_mix])) * *params[param_level_out]; outs[0][offset] = out[0]; - maxIn = fabs(in[0]); - maxOut = fabs(out[0]); } - - if(maxIn > 1.f) { - clip_in = srate >> 3; - } - if(maxOut > 1.f) { - clip_out = srate >> 3; - } - // set up in / out meters - if(maxIn > meter_in) { - meter_in = maxIn; - } - if(maxOut > meter_out) { - meter_out = maxOut; - } - + // next sample ++offset; } // cycle trough samples + meters.process(params, ins, outs, orig_offset, orig_numsamples); tube_avg = (sqrt(std::max(out_avg[0], out_avg[1])) / numsamples) - (sqrt(std::max(in_avg[0], in_avg[1])) / numsamples); meter_drive = (5.0f * fabs(tube_avg) * (float(*params[param_blend]) + 30.0f)); @@ -270,19 +243,6 @@ uint32_t saturator_audio_module::process(uint32_t offset, uint32_t numsamples, u p[1].sanitize(); } // draw meters - if(params[param_clip_in] != NULL) { - *params[param_clip_in] = clip_in; - } - if(params[param_clip_out] != NULL) { - *params[param_clip_out] = clip_out; - } - - if(params[param_meter_in] != NULL) { - *params[param_meter_in] = meter_in; - } - if(params[param_meter_out] != NULL) { - *params[param_meter_out] = meter_out; - } if(params[param_meter_drive] != NULL) { *params[param_meter_drive] = meter_drive; } @@ -300,10 +260,6 @@ exciter_audio_module::exciter_audio_module() { is_active = false; srate = 0; - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; } @@ -351,6 +307,8 @@ void exciter_audio_module::set_sample_rate(uint32_t sr) uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) { + uint32_t orig_offset = offset; + uint32_t orig_numsamples = numsamples; bool bypass = *params[param_bypass] > 0.5f; numsamples += offset; if(bypass) { @@ -369,25 +327,19 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin } ++offset; } + meters.reset(); + meters.process(params, NULL, NULL, 0, 0); // displays, too - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; } else { - clip_in -= std::min(clip_in, numsamples); - clip_out -= std::min(clip_out, numsamples); - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; // process while(offset < numsamples) { // cycle through samples float out[2], in[2] = {0.f, 0.f}; - float maxIn, maxOut, maxDrive = 0.f; + float maxDrive = 0.f; int c = 0; if(in_count > 1 && out_count > 1) { @@ -434,8 +386,6 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin else out[1] = (proc[1] * *params[param_amount] + in[1]) * *params[param_level_out]; outs[1][offset] = out[1]; - maxIn = std::max(fabs(in[0]), fabs(in[1])); - maxOut = std::max(fabs(out[0]), fabs(out[1])); maxDrive = std::max(dist[0].get_distortion_level() * *params[param_amount], dist[1].get_distortion_level() * *params[param_amount]); } else if(out_count > 1) { @@ -447,8 +397,6 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin outs[0][offset] = out[0]; out[1] = out[0]; outs[1][offset] = out[1]; - maxOut = fabs(out[0]); - maxIn = fabs(in[0]); maxDrive = dist[0].get_distortion_level() * *params[param_amount]; } else { // stereo -> mono @@ -458,24 +406,10 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin else out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out]; outs[0][offset] = out[0]; - maxIn = fabs(in[0]); - maxOut = fabs(out[0]); maxDrive = dist[0].get_distortion_level() * *params[param_amount]; } - if(maxIn > 1.f) { - clip_in = srate >> 3; - } - if(maxOut > 1.f) { - clip_out = srate >> 3; - } // set up in / out meters - if(maxIn > meter_in) { - meter_in = maxIn; - } - if(maxOut > meter_out) { - meter_out = maxOut; - } if(maxDrive > meter_drive) { meter_drive = maxDrive; } @@ -483,6 +417,7 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin // next sample ++offset; } // cycle trough samples + meters.process(params, ins, outs, orig_offset, orig_numsamples); // clean up hp[0][0].sanitize(); hp[1][0].sanitize(); @@ -494,19 +429,6 @@ uint32_t exciter_audio_module::process(uint32_t offset, uint32_t numsamples, uin hp[1][3].sanitize(); } // draw meters - if(params[param_clip_in] != NULL) { - *params[param_clip_in] = clip_in; - } - if(params[param_clip_out] != NULL) { - *params[param_clip_out] = clip_out; - } - - if(params[param_meter_in] != NULL) { - *params[param_meter_in] = meter_in; - } - if(params[param_meter_out] != NULL) { - *params[param_meter_out] = meter_out; - } if(params[param_meter_drive] != NULL) { *params[param_meter_drive] = meter_drive; } @@ -524,16 +446,14 @@ bassenhancer_audio_module::bassenhancer_audio_module() { is_active = false; srate = 0; - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; + meters.reset(); meter_drive = 0.f; } void bassenhancer_audio_module::activate() { is_active = true; + meters.reset(); // set all filters params_changed(); } @@ -575,6 +495,8 @@ void bassenhancer_audio_module::set_sample_rate(uint32_t sr) uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) { bool bypass = *params[param_bypass] > 0.5f; + uint32_t orig_offset = offset; + uint32_t orig_numsamples = numsamples; numsamples += offset; if(bypass) { // everything bypassed @@ -593,24 +515,17 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples ++offset; } // displays, too - clip_in = 0.f; - clip_out = 0.f; - meter_in = 0.f; - meter_out = 0.f; + meters.reset(); + meters.process(params, NULL, NULL, 0, 0); meter_drive = 0.f; } else { - - clip_in -= std::min(clip_in, numsamples); - clip_out -= std::min(clip_out, numsamples); - meter_in = 0.f; - meter_out = 0.f; meter_drive = 0.f; // process while(offset < numsamples) { // cycle through samples float out[2], in[2] = {0.f, 0.f}; - float maxIn, maxOut, maxDrive = 0.f; + float maxDrive = 0.f; int c = 0; if(in_count > 1 && out_count > 1) { @@ -657,8 +572,6 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples else out[1] = (proc[1] * *params[param_amount] + in[1]) * *params[param_level_out]; outs[1][offset] = out[1]; - maxIn = std::max(fabs(in[0]), fabs(in[1])); - maxOut = std::max(fabs(out[0]), fabs(out[1])); maxDrive = std::max(dist[0].get_distortion_level() * *params[param_amount], dist[1].get_distortion_level() * *params[param_amount]); } else if(out_count > 1) { @@ -670,8 +583,6 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples outs[0][offset] = out[0]; out[1] = out[0]; outs[1][offset] = out[1]; - maxOut = fabs(out[0]); - maxIn = fabs(in[0]); maxDrive = dist[0].get_distortion_level() * *params[param_amount]; } else { // stereo -> mono @@ -681,24 +592,10 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples else out[0] = (proc[0] * *params[param_amount] + in[0]) * *params[param_level_out]; outs[0][offset] = out[0]; - maxIn = fabs(in[0]); - maxOut = fabs(out[0]); maxDrive = dist[0].get_distortion_level() * *params[param_amount]; } - if(maxIn > 1.f) { - clip_in = srate >> 3; - } - if(maxOut > 1.f) { - clip_out = srate >> 3; - } // set up in / out meters - if(maxIn > meter_in) { - meter_in = maxIn; - } - if(maxOut > meter_out) { - meter_out = maxOut; - } if(maxDrive > meter_drive) { meter_drive = maxDrive; } @@ -706,6 +603,7 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples // next sample ++offset; } // cycle trough samples + meters.process(params, ins, outs, orig_offset, orig_numsamples); // clean up lp[0][0].sanitize(); lp[1][0].sanitize(); @@ -717,19 +615,6 @@ uint32_t bassenhancer_audio_module::process(uint32_t offset, uint32_t numsamples lp[1][3].sanitize(); } // draw meters - if(params[param_clip_in] != NULL) { - *params[param_clip_in] = clip_in; - } - if(params[param_clip_out] != NULL) { - *params[param_clip_out] = clip_out; - } - - if(params[param_meter_in] != NULL) { - *params[param_meter_in] = meter_in; - } - if(params[param_meter_out] != NULL) { - *params[param_meter_out] = meter_out; - } if(params[param_meter_drive] != NULL) { *params[param_meter_drive] = meter_drive; }