Refactor metering code out of distortion plugins.

(cherry picked from commit 84d873861da7b4ad205061b00fcc9f73c47140b6)
This commit is contained in:
Krzysztof Foltman
2010-08-29 23:31:22 +01:00
committed by Tobias Doerffel
parent 017e382b35
commit d22dbc7d70
5 changed files with 207 additions and 147 deletions

View File

@@ -29,7 +29,6 @@
#include "giface.h"
#include "metadata.h"
#include "loudness.h"
#include "primitives.h"
namespace calf_plugins {

View File

@@ -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<saturator_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> lp[2][4], hp[2][4];
dsp::biquad_d2<float> p[2];
dsp::tap_distortion dist[2];
@@ -57,8 +58,8 @@ public:
class exciter_audio_module: public audio_module<exciter_metadata> {
private:
float freq_old;
uint32_t clip_in, clip_out;
float meter_in, meter_out, meter_drive;
stereo_in_out_metering<exciter_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> hp[2][4];
dsp::tap_distortion dist[2];
public:
@@ -76,8 +77,8 @@ public:
class bassenhancer_audio_module: public audio_module<bassenhancer_metadata> {
private:
float freq_old;
uint32_t clip_in, clip_out;
float meter_in, meter_out, meter_drive;
stereo_in_out_metering<exciter_metadata> meters;
float meter_drive;
dsp::biquad_d2<float> lp[2][4];
dsp::tap_distortion dist[2];
public:

View File

@@ -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 <config.h>
#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 Metadata>
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

View File

@@ -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

View File

@@ -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;
}