From 62ba7d676bbae6f2e363187cb58e334a39db1aa9 Mon Sep 17 00:00:00 2001 From: Markus Schmidt Date: Sun, 22 Aug 2010 22:16:57 +0100 Subject: [PATCH] Add Sidechain Gate, rename Gate to Expander, cleanups etc. (cherry picked from commit fd70348c38a139e6f90a7a20f833b5eb2b926694) --- .../ladspa_effect/calf/src/calf/metadata.h | 31 +- .../ladspa_effect/calf/src/calf/modulelist.h | 3 +- .../calf/src/calf/modules_comp.h | 133 ++- plugins/ladspa_effect/calf/src/metadata.cpp | 108 ++- .../ladspa_effect/calf/src/modules_comp.cpp | 856 ++++++++++++++---- 5 files changed, 873 insertions(+), 258 deletions(-) diff --git a/plugins/ladspa_effect/calf/src/calf/metadata.h b/plugins/ladspa_effect/calf/src/calf/metadata.h index de17490c3..1c2e1f92f 100644 --- a/plugins/ladspa_effect/calf/src/calf/metadata.h +++ b/plugins/ladspa_effect/calf/src/calf/metadata.h @@ -197,6 +197,28 @@ struct deesser_metadata: public plugin_metadata PLUGIN_NAME_ID_LABEL("deesser", "deesser", "Deesser") }; +/// Damiens' Gate - metadata +/// Added some meters and stripped the weighting part +struct gate_metadata: public plugin_metadata +{ + enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true }; + enum { param_bypass, param_level_in, param_meter_in, param_meter_out, param_clip_in, param_clip_out, + param_range, param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_gating, + param_count }; + PLUGIN_NAME_ID_LABEL("gate", "gate", "Gate") +}; + +/// Markus's sidechain gate - metadata +struct sidechaingate_metadata: public plugin_metadata +{ + enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true }; + enum { param_bypass, param_level_in, param_meter_in, param_meter_out, param_clip_in, param_clip_out, + param_range, param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_gating, + param_sc_mode, param_f1_freq, param_f2_freq, param_f1_level, param_f2_level, + param_sc_listen, param_f1_active, param_f2_active, param_count }; + PLUGIN_NAME_ID_LABEL("sidechaingate", "sidechaingate", "Sidechain Gate") +}; + /// Markus's 5-band EQ - metadata struct equalizer5band_metadata: public plugin_metadata { @@ -295,15 +317,6 @@ struct bassenhancer_metadata: public plugin_metadata PLUGIN_NAME_ID_LABEL("bassenhancer", "bassenhancer", "Bass Enhancer") }; -/// Damien's gate - metadata -struct gate_metadata: public plugin_metadata -{ - enum { in_count = 3, out_count = 2, ins_optional = 1, outs_optional = 0, support_midi = false, require_midi = false, rt_capable = true }; - enum { param_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_aweighting, param_gating, param_peak, param_clip, param_bypass, param_range, param_mono, param_trigger, // param_freq, param_bw, - param_count }; - PLUGIN_NAME_ID_LABEL("gate", "gate", "Gate") -}; - /// Organ - enums for parameter IDs etc. (this mess is caused by organ split between plugin and generic class - which was /// a bad design decision and should be sorted out some day) XXXKF @todo struct organ_enums diff --git a/plugins/ladspa_effect/calf/src/calf/modulelist.h b/plugins/ladspa_effect/calf/src/calf/modulelist.h index baaa5d90f..85dead895 100644 --- a/plugins/ladspa_effect/calf/src/calf/modulelist.h +++ b/plugins/ladspa_effect/calf/src/calf/modulelist.h @@ -13,6 +13,8 @@ PER_MODULE_ITEM(sidechaincompressor, false, "sidechaincompressor") PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor") PER_MODULE_ITEM(deesser, false, "deesser") + PER_MODULE_ITEM(gate, false, "gate") + PER_MODULE_ITEM(sidechaingate, false, "sidechaingate") PER_MODULE_ITEM(pulsator, false, "pulsator") PER_MODULE_ITEM(equalizer5band, false, "eq5") PER_MODULE_ITEM(equalizer8band, false, "eq8") @@ -20,7 +22,6 @@ PER_MODULE_ITEM(saturator, false, "saturator") PER_MODULE_ITEM(exciter, false, "exciter") PER_MODULE_ITEM(bassenhancer, false, "bassenhancer") - PER_MODULE_ITEM(gate, false, "gate") #ifdef ENABLE_EXPERIMENTAL PER_MODULE_ITEM(fluidsynth, true, "fluidsynth") PER_MODULE_ITEM(wavetable, true, "wavetable") diff --git a/plugins/ladspa_effect/calf/src/calf/modules_comp.h b/plugins/ladspa_effect/calf/src/calf/modules_comp.h index 1c85820ec..934b289b1 100644 --- a/plugins/ladspa_effect/calf/src/calf/modules_comp.h +++ b/plugins/ladspa_effect/calf/src/calf/modules_comp.h @@ -33,6 +33,8 @@ namespace calf_plugins { /// Not a true _audio_module style class, just pretends to be one! +/// Main gain reduction routine by Thor called by various audio modules + class gain_reduction_audio_module { private: @@ -62,6 +64,35 @@ public: int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; }; +/// Not a true _audio_module style class, just pretends to be one! +/// Main gate routine by Damien called by various audio modules +class expander_audio_module { +private: + float linSlope, peak, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop, linKneeStop; + float compressedKneeStop, adjKneeStart, range, thres, attack_coeff, release_coeff; + float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_gate; + mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_range, old_trigger, old_mute, old_detection, old_stereo_link; + mutable volatile int last_generation; + inline float output_level(float slope) const; + inline float output_gain(float linSlope, bool rms) const; +public: + uint32_t srate; + bool is_active; + expander_audio_module(); + void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu, float ran); + void update_curve(); + void process(float &left, float &right, const float *det_left = NULL, const float *det_right = NULL); + void activate(); + void deactivate(); + void set_sample_rate(uint32_t sr); + float get_output_level(); + float get_expander_level(); + bool get_graph(int subindex, float *data, int points, cairo_iface *context) const; + bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const; + bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; + int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; +}; + /// Compressor by Thor class compressor_audio_module: public audio_module, public line_graph_iface { private: @@ -130,7 +161,7 @@ public: int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; }; -/// Multibandcompressor by Markus Schmidt +/// Multibandcompressor by Markus Schmidt (based on Thor's compressor and Krzysztof's filters) class multibandcompressor_audio_module: public audio_module, public line_graph_iface { private: typedef multibandcompressor_audio_module AM; @@ -190,59 +221,73 @@ public: int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; }; - -class gate_audio_module: public audio_module, public line_graph_iface { +/// Gate by Damien +class gate_audio_module: public audio_module, public line_graph_iface { private: - float linSlope, peak, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop, linKneeStop, threshold, ratio, knee, makeup, compressedKneeStop, adjKneeStart, range; - mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_range, old_trigger, old_mono; - mutable volatile int last_generation; - uint32_t clip; - dsp::aweighter awL, awR; - dsp::biquad_d2 bpL, bpR; + typedef gate_audio_module AM; + uint32_t clip_in, clip_out; + float meter_in, meter_out; + expander_audio_module gate; public: + typedef std::complex cfloat; uint32_t srate; bool is_active; + mutable volatile int last_generation, last_calculated_generation; gate_audio_module(); void activate(); void deactivate(); - uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); - - inline float output_level(float slope) const { - bool rms = *params[param_detection] == 0; - return slope * output_gain(rms ? slope*slope : slope, rms) * makeup; - } - - inline float output_gain(float linSlope, bool rms) const { - if(linSlope < linKneeStop) { - float slope = log(linSlope); - - //float tratio = rms ? sqrt(ratio) : ratio; - float tratio = ratio; - float gain = 0.f; - float delta = 0.f; - if(IS_FAKE_INFINITY(ratio)) - tratio = 1000.f; - gain = (slope-threshold) * tratio + threshold; - delta = tratio; - - if(knee > 1.f && slope > kneeStart ) { - gain = dsp::hermite_interpolation(slope, kneeStart, kneeStop, ((kneeStart - threshold) * tratio + threshold), kneeStop, delta,1.f); - } - return std::max(range, expf(gain-slope)); - } - - - return 1.f; - } - + void params_changed(); void set_sample_rate(uint32_t sr); - - virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const; - virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const; - virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; - virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; + uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); + bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const; + bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const; + bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; + int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; }; +/// Sidecain Gate by Markus Schmidt (based on Damiens's gate and Krzysztof's filters) +class sidechaingate_audio_module: public audio_module, public frequency_response_line_graph { +private: + typedef sidechaingate_audio_module AM; + enum CalfScModes { + WIDEBAND, + HIGHGATE_WIDE, + HIGHGATE_SPLIT, + LOWGATE_WIDE, + LOWGATE_SPLIT, + WEIGHTED_1, + WEIGHTED_2, + WEIGHTED_3, + BANDPASS_1, + BANDPASS_2 + }; + mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old; + mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1; + CalfScModes sc_mode; + mutable CalfScModes sc_mode_old, sc_mode_old1; + float f1_active, f2_active; + uint32_t clip_in, clip_out; + float meter_in, meter_out; + expander_audio_module gate; + dsp::biquad_d2 f1L, f1R, f2L, f2R; +public: + typedef std::complex cfloat; + uint32_t srate; + bool is_active; + mutable volatile int last_generation, last_calculated_generation; + sidechaingate_audio_module(); + void activate(); + void deactivate(); + void params_changed(); + cfloat h_z(const cfloat &z) const; + float freq_gain(int index, double freq, uint32_t sr) const; + void set_sample_rate(uint32_t sr); + uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); + bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const; + bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const; + bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const; + int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const; +}; }; diff --git a/plugins/ladspa_effect/calf/src/metadata.cpp b/plugins/ladspa_effect/calf/src/metadata.cpp index 4bfc37793..d655a486f 100644 --- a/plugins/ladspa_effect/calf/src/metadata.cpp +++ b/plugins/ladspa_effect/calf/src/metadata.cpp @@ -399,6 +399,83 @@ CALF_PORT_PROPS(deesser) = { CALF_PLUGIN_INFO(deesser) = { 0x8515, "Deesser", "Calf Deesser", "Markus Schmidt / Thor Harald Johansen", calf_plugins::calf_copyright_info, "CompressorPlugin" }; +//////////////////////////////////////////////////////////////////////////// + +CALF_PORT_NAMES(gate) = {"In L", "In R", "Out L", "Out R"}; + +const char *gate_detection_names[] = { "RMS", "Peak" }; +const char *gate_stereo_link_names[] = { "Average", "Maximum" }; + +CALF_PORT_PROPS(gate) = { + { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" }, + { 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" }, + { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" }, + { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_out", "Output" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB-In" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB-Out" }, + { 0.06125, 0.000015849, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "range", "Max Gain Reduction" }, + { 0.125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold", "Threshold" }, + { 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio", "Ratio" }, + { 20, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Attack" }, + { 250, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" }, + { 1, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup", "Makeup Gain" }, + { 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee", "Knee" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, gate_detection_names, "detection", "Detection" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, gate_stereo_link_names, "stereo_link", "Stereo Link" }, + { 0, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "gating", "Gating" }, +}; + +CALF_PLUGIN_INFO(gate) = { 0x8503, "Gate", "Calf Gate", "Damien Zammit / Thor Harald Johansen", calf_plugins::calf_copyright_info, "ExpanderPlugin" }; + +//////////////////////////////////////////////////////////////////////////// + +CALF_PORT_NAMES(sidechaingate) = {"In L", "In R", "Out L", "Out R"}; + +const char *sidechaingate_detection_names[] = { "RMS", "Peak" }; +const char *sidechaingate_stereo_link_names[] = { "Average", "Maximum" }; +const char *sidechaingate_mode_names[] = {"Wideband (F1:off / F2:off)", + "High gate wide (F1:Bell / F2:HP)", + "High gate split (F1:off / F2:HP)", + "Low Gate wide (F1:LP / F2:Bell)", + "Low gate split (F1:LP / F2:off)", + "Weighted #1 (F1:Shelf / F2:Shelf)", + "Weighted #2 (F1:Shelf / F2:Bell)", + "Weighted #3 (F1:Bell / F2:Shelf)", + "Bandpass #1 (F1:BP / F2:off)", + "Bandpass #2 (F1:HP / F2:LP)"}; +const char *sidechaingate_filter_choices[] = { "12dB", "24dB", "36dB"}; + + +CALF_PORT_PROPS(sidechaingate) = { + { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" }, + { 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_in", "Input" }, + { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_in", "Input" }, + { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "meter_out", "Output" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_in", "0dB-In" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB-Out" }, + { 0.06125, 0.000015849, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "range", "Max Gain Reduction" }, + { 0.125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold", "Threshold" }, + { 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio", "Ratio" }, + { 20, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Attack" }, + { 250, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" }, + { 1, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup", "Makeup Gain" }, + { 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee", "Knee" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, sidechaingate_detection_names, "detection", "Detection" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, sidechaingate_stereo_link_names, "stereo_link", "Stereo Link" }, + { 0, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_CTLO_REVERSE | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "gating", "Gating" }, + { 0, 0, 9, 0, PF_ENUM | PF_CTL_COMBO, sidechaingate_mode_names, "sc_mode", "Sidechain Mode" }, + { 250, 10,18000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "f1_freq", "F1 Freq" }, + { 4500, 10,18000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "f2_freq", "F2 Freq" }, + { 1, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f1_level", "F1 Level" }, + { 1, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f2_level", "F2 Level" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "sc_listen", "S/C-Listen" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "f1_active", "F1 Active" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "f2_active", "F2 Active" }, +}; + +CALF_PLUGIN_INFO(sidechaingate) = { 0x8504, "Sidechaingate", "Calf Sidechain Gate", "Markus Schmidt / Damien Zammit / Thor Harald Johansen", calf_plugins::calf_copyright_info, "ExpanderPlugin" }; + + //////////////////////////////////////////////////////////////////////////// // A few macros to make @@ -592,37 +669,6 @@ CALF_PORT_PROPS(bassenhancer) = { CALF_PLUGIN_INFO(bassenhancer) = { 0x8532, "BassEnhancer", "Calf Bass Enhancer", "Markus Schmidt / Krzysztof Foltman", calf_plugins::calf_copyright_info, "DistortionPlugin" }; - -//////////////////////////////////////////////////////////////////////////// - -CALF_PORT_NAMES(gate) = {"In L", "In R", "In Trigger", "Out L", "Out R"}; - -const char *gate_detection_names[] = { "RMS", "Peak" }; -const char *gate_stereo_link_names[] = { "Average", "Maximum" }; -const char *gate_weighting_names[] = { "Normal", "A-weighted", "Deesser (low)", "Deesser (med)", "Deesser (high)" }; - -CALF_PORT_PROPS(gate) = { - { 0.125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold", "Threshold" }, - { 2, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio", "Ratio" }, - { 20, 1, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack", "Attack" }, - { 250, 1, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release", "Release" }, - { 1, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup", "Makeup Gain" }, - { 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee", "Knee" }, - { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, gate_detection_names, "detection", "Detection" }, - { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, gate_stereo_link_names, "stereo_link", "Stereo Link" }, - { 0, 0, 4, 0, PF_ENUM | PF_CTL_COMBO, gate_weighting_names, "aweighting", "Weighting" }, - { 0, 0.03125, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL| PF_PROP_GRAPH, NULL, "gating", "Gating" }, - { 0, 0, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_METER | PF_CTLO_LABEL | PF_UNIT_DB | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "peak", "Peak Output" }, - { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip", "0dBFS" }, - { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" }, - { 0.01, 0.000015849, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "range", "Max Gain Reduction" }, - { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mono", "Mono (L only)" }, - { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "trigger", "Sidechain (Mono In)" }, - -}; - -CALF_PLUGIN_INFO(gate) = { 0x8503, "Gate", "Calf Gate", "Damien Zammit / Thor Harald Johansen", calf_plugins::calf_copyright_info, "ExpanderPlugin" }; - //////////////////////////////////////////////////////////////////////////// CALF_PORT_NAMES(monosynth) = { diff --git a/plugins/ladspa_effect/calf/src/modules_comp.cpp b/plugins/ladspa_effect/calf/src/modules_comp.cpp index a5d58e2e5..15f96a8bb 100644 --- a/plugins/ladspa_effect/calf/src/modules_comp.cpp +++ b/plugins/ladspa_effect/calf/src/modules_comp.cpp @@ -1118,6 +1118,540 @@ int deesser_audio_module::get_changed_offsets(int index, int generation, int &su return false; } +/// Gate originally by Damien +/// +/// This module provides Damien's original expander based on Thor's compressor +/// without any weighting +/////////////////////////////////////////////////////////////////////////////////////////////// + +gate_audio_module::gate_audio_module() +{ + is_active = false; + srate = 0; + last_generation = 0; +} + +void gate_audio_module::activate() +{ + is_active = true; + // set all filters and strips + gate.activate(); + params_changed(); + meter_in = 0.f; + meter_out = 0.f; + clip_in = 0.f; + clip_out = 0.f; +} +void gate_audio_module::deactivate() +{ + is_active = false; + gate.deactivate(); +} + +void gate_audio_module::params_changed() +{ + gate.set_params(*params[param_attack], *params[param_release], *params[param_threshold], *params[param_ratio], *params[param_knee], *params[param_makeup], *params[param_detection], *params[param_stereo_link], *params[param_bypass], 0.f, *params[param_range]); +} + +void gate_audio_module::set_sample_rate(uint32_t sr) +{ + srate = sr; + gate.set_sample_rate(srate); +} + +uint32_t gate_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) +{ + bool bypass = *params[param_bypass] > 0.5f; + numsamples += offset; + if(bypass) { + // everything bypassed + while(offset < numsamples) { + outs[0][offset] = ins[0][offset]; + outs[1][offset] = ins[1][offset]; + ++offset; + } + // displays, too + clip_in = 0.f; + clip_out = 0.f; + meter_in = 0.f; + meter_out = 0.f; + } else { + // process + clip_in -= std::min(clip_in, numsamples); + clip_out -= std::min(clip_out, numsamples); + + gate.update_curve(); + + while(offset < numsamples) { + // cycle through samples + float outL = 0.f; + float outR = 0.f; + float inL = ins[0][offset]; + float inR = ins[1][offset]; + // in level + inR *= *params[param_level_in]; + inL *= *params[param_level_in]; + + float leftAC = inL; + float rightAC = inR; + + gate.process(leftAC, rightAC); + + outL = leftAC; + outR = rightAC; + + // send to output + outs[0][offset] = outL; + outs[1][offset] = outR; + + // clip LED's + if(std::max(fabs(inL), fabs(inR)) > 1.f) { + clip_in = srate >> 3; + } + if(std::max(fabs(outL), fabs(outR)) > 1.f) { + clip_out = srate >> 3; + } + // rise up out meter + meter_in = std::max(fabs(inL), fabs(inR));; + meter_out = std::max(fabs(outL), fabs(outR));; + + // next sample + ++offset; + } // cycle trough samples + } + // draw meters + SET_IF_CONNECTED(clip_in) + SET_IF_CONNECTED(clip_out) + SET_IF_CONNECTED(meter_in) + SET_IF_CONNECTED(meter_out) + // draw strip meter + if(bypass > 0.5f) { + if(params[param_gating] != NULL) { + *params[param_gating] = 1.0f; + } + } else { + if(params[param_gating] != NULL) { + *params[param_gating] = gate.get_expander_level(); + } + } + // whatever has to be returned x) + return outputs_mask; +} +bool gate_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const +{ + if (!is_active) + return false; + return gate.get_graph(subindex, data, points, context); +} + +bool gate_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const +{ + if (!is_active) + return false; + return gate.get_dot(subindex, x, y, size, context); +} + +bool gate_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const +{ + if (!is_active) + return false; + return gate.get_gridline(subindex, pos, vertical, legend, context); +} + +int gate_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const +{ + if (!is_active) + return false; + return gate.get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline); +} + +/// Sidecain Gate by Markus Schmidt +/// +/// This module splits the signal in a sidechain- and a process signal. +/// The sidechain is processed through Krzystofs filters and gates +/// the process signal via Damiens's gating routine afterwards. +/////////////////////////////////////////////////////////////////////////////////////////////// + +sidechaingate_audio_module::sidechaingate_audio_module() +{ + is_active = false; + srate = 0; + last_generation = 0; +} + +void sidechaingate_audio_module::activate() +{ + is_active = true; + // set all filters and strips + gate.activate(); + params_changed(); + meter_in = 0.f; + meter_out = 0.f; + clip_in = 0.f; + clip_out = 0.f; +} +void sidechaingate_audio_module::deactivate() +{ + is_active = false; + gate.deactivate(); +} + +sidechaingate_audio_module::cfloat sidechaingate_audio_module::h_z(const cfloat &z) const +{ + switch (sc_mode) { + default: + case WIDEBAND: + return false; + break; + case HIGHGATE_WIDE: + case LOWGATE_WIDE: + case WEIGHTED_1: + case WEIGHTED_2: + case WEIGHTED_3: + case BANDPASS_2: + return f1L.h_z(z) * f2L.h_z(z); + break; + case HIGHGATE_SPLIT: + return f2L.h_z(z); + break; + case LOWGATE_SPLIT: + case BANDPASS_1: + return f1L.h_z(z); + break; + } +} + +float sidechaingate_audio_module::freq_gain(int index, double freq, uint32_t sr) const +{ + typedef std::complex cfloat; + freq *= 2.0 * M_PI / sr; + cfloat z = 1.0 / exp(cfloat(0.0, freq)); + + return std::abs(h_z(z)); +} + +void sidechaingate_audio_module::params_changed() +{ + // set the params of all filters + if(*params[param_f1_freq] != f1_freq_old or *params[param_f1_level] != f1_level_old + or *params[param_f2_freq] != f2_freq_old or *params[param_f2_level] != f2_level_old + or *params[param_sc_mode] != sc_mode) { + float q = 0.707; + switch ((int)*params[param_sc_mode]) { + default: + case WIDEBAND: + f1L.set_hp_rbj((float)*params[param_f1_freq], q, (float)srate, *params[param_f1_level]); + f1R.copy_coeffs(f1L); + f2L.set_lp_rbj((float)*params[param_f2_freq], q, (float)srate, *params[param_f2_level]); + f2R.copy_coeffs(f2L); + f1_active = 0.f; + f2_active = 0.f; + break; + case HIGHGATE_WIDE: + f1L.set_peakeq_rbj((float)*params[param_f1_freq], q, *params[param_f1_level], (float)srate); + f1R.copy_coeffs(f1L); + f2L.set_hp_rbj((float)*params[param_f2_freq], q, (float)srate, *params[param_f2_level]); + f2R.copy_coeffs(f2L); + f1_active = 0.5f; + f2_active = 1.f; + break; + case HIGHGATE_SPLIT: + f1L.set_lp_rbj((float)*params[param_f2_freq] * (1 + 0.17), q, (float)srate); + f1R.copy_coeffs(f1L); + f2L.set_hp_rbj((float)*params[param_f2_freq] * (1 - 0.17), q, (float)srate, *params[param_f2_level]); + f2R.copy_coeffs(f2L); + f1_active = 0.f; + f2_active = 1.f; + break; + case LOWGATE_WIDE: + f1L.set_lp_rbj((float)*params[param_f1_freq], q, (float)srate, *params[param_f1_level]); + f1R.copy_coeffs(f1L); + f2L.set_peakeq_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 1.f; + f2_active = 0.5f; + break; + case LOWGATE_SPLIT: + f1L.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), q, (float)srate, *params[param_f1_level]); + f1R.copy_coeffs(f1L); + f2L.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), q, (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 1.f; + f2_active = 0.f; + break; + case WEIGHTED_1: + f1L.set_lowshelf_rbj((float)*params[param_f1_freq], q, *params[param_f1_level], (float)srate); + f1R.copy_coeffs(f1L); + f2L.set_highshelf_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case WEIGHTED_2: + f1L.set_lowshelf_rbj((float)*params[param_f1_freq], q, *params[param_f1_level], (float)srate); + f1R.copy_coeffs(f1L); + f2L.set_peakeq_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case WEIGHTED_3: + f1L.set_peakeq_rbj((float)*params[param_f1_freq], q, *params[param_f1_level], (float)srate); + f1R.copy_coeffs(f1L); + f2L.set_highshelf_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case BANDPASS_1: + f1L.set_bp_rbj((float)*params[param_f1_freq], q, (float)srate, *params[param_f1_level]); + f1R.copy_coeffs(f1L); + f2L.set_hp_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + f2R.copy_coeffs(f2L); + f1_active = 1.f; + f2_active = 0.f; + break; + case BANDPASS_2: + f1L.set_hp_rbj((float)*params[param_f1_freq], q, (float)srate, *params[param_f1_level]); + f1R.copy_coeffs(f1L); + f2L.set_lp_rbj((float)*params[param_f2_freq], q, (float)srate, *params[param_f2_level]); + f2R.copy_coeffs(f2L); + f1_active = 1.f; + f2_active = 1.f; + break; + } + f1_freq_old = *params[param_f1_freq]; + f1_level_old = *params[param_f1_level]; + f2_freq_old = *params[param_f2_freq]; + f2_level_old = *params[param_f2_level]; + sc_mode = (CalfScModes)*params[param_sc_mode]; + } + // light LED's + if(params[param_f1_active] != NULL) { + *params[param_f1_active] = f1_active; + } + if(params[param_f2_active] != NULL) { + *params[param_f2_active] = f2_active; + } + // and set the expander module + gate.set_params(*params[param_attack], *params[param_release], *params[param_threshold], *params[param_ratio], *params[param_knee], *params[param_makeup], *params[param_detection], *params[param_stereo_link], *params[param_bypass], 0.f, *params[param_range]); +} + +void sidechaingate_audio_module::set_sample_rate(uint32_t sr) +{ + srate = sr; + gate.set_sample_rate(srate); +} + +uint32_t sidechaingate_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) +{ + bool bypass = *params[param_bypass] > 0.5f; + numsamples += offset; + if(bypass) { + // everything bypassed + while(offset < numsamples) { + outs[0][offset] = ins[0][offset]; + outs[1][offset] = ins[1][offset]; + ++offset; + } + // displays, too + clip_in = 0.f; + clip_out = 0.f; + meter_in = 0.f; + meter_out = 0.f; + } else { + // process + + clip_in -= std::min(clip_in, numsamples); + clip_out -= std::min(clip_out, numsamples); + gate.update_curve(); + + while(offset < numsamples) { + // cycle through samples + float outL = 0.f; + float outR = 0.f; + float inL = ins[0][offset]; + float inR = ins[1][offset]; + // in level + inR *= *params[param_level_in]; + inL *= *params[param_level_in]; + + + float leftAC = inL; + float rightAC = inR; + float leftSC = inL; + float rightSC = inR; + float leftMC = inL; + float rightMC = inR; + + switch ((int)*params[param_sc_mode]) { + default: + case WIDEBAND: + gate.process(leftAC, rightAC); + break; + case HIGHGATE_WIDE: + case LOWGATE_WIDE: + case WEIGHTED_1: + case WEIGHTED_2: + case WEIGHTED_3: + case BANDPASS_2: + leftSC = f2L.process(f1L.process(leftSC)); + rightSC = f2R.process(f1R.process(rightSC)); + leftMC = leftSC; + rightMC = rightSC; + gate.process(leftAC, rightAC, &leftSC, &rightSC); + break; + case HIGHGATE_SPLIT: + leftSC = f2L.process(leftSC); + rightSC = f2R.process(rightSC); + leftMC = leftSC; + rightMC = rightSC; + gate.process(leftSC, rightSC, &leftSC, &rightSC); + leftAC = f1L.process(leftAC); + rightAC = f1R.process(rightAC); + leftAC += leftSC; + rightAC += rightSC; + break; + case LOWGATE_SPLIT: + leftSC = f1L.process(leftSC); + rightSC = f1R.process(rightSC); + leftMC = leftSC; + rightMC = rightSC; + gate.process(leftSC, rightSC); + leftAC = f2L.process(leftAC); + rightAC = f2R.process(rightAC); + leftAC += leftSC; + rightAC += rightSC; + break; + case BANDPASS_1: + leftSC = f1L.process(leftSC); + rightSC = f1R.process(rightSC); + leftMC = leftSC; + rightMC = rightSC; + gate.process(leftAC, rightAC, &leftSC, &rightSC); + break; + } + + if(*params[param_sc_listen] > 0.f) { + outL = leftMC; + outR = rightMC; + } else { + outL = leftAC; + outR = rightAC; + } + + // send to output + outs[0][offset] = outL; + outs[1][offset] = outR; + + // clip LED's + if(std::max(fabs(inL), fabs(inR)) > 1.f) { + clip_in = srate >> 3; + } + if(std::max(fabs(outL), fabs(outR)) > 1.f) { + clip_out = srate >> 3; + } + // rise up out meter + meter_in = std::max(fabs(inL), fabs(inR));; + meter_out = std::max(fabs(outL), fabs(outR));; + + // next sample + ++offset; + } // cycle trough samples + f1L.sanitize(); + f1R.sanitize(); + f2L.sanitize(); + f2R.sanitize(); + + } + // draw meters + SET_IF_CONNECTED(clip_in) + SET_IF_CONNECTED(clip_out) + SET_IF_CONNECTED(meter_in) + SET_IF_CONNECTED(meter_out) + // draw strip meter + if(bypass > 0.5f) { + if(params[param_gating] != NULL) { + *params[param_gating] = 1.0f; + } + } else { + if(params[param_gating] != NULL) { + *params[param_gating] = gate.get_expander_level(); + } + } + // whatever has to be returned x) + return outputs_mask; +} +bool sidechaingate_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const +{ + if (!is_active) + return false; + if (index == param_f1_freq && !subindex) { + context->set_line_width(1.5); + return ::get_graph(*this, subindex, data, points); + } else if(index == param_gating) { + return gate.get_graph(subindex, data, points, context); + } + return false; +} + +bool sidechaingate_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const +{ + if (!is_active) + return false; + if (index == param_gating) { + return gate.get_dot(subindex, x, y, size, context); + } + return false; +} + +bool sidechaingate_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const +{ + if (!is_active) + return false; + if (index == param_gating) { + return gate.get_gridline(subindex, pos, vertical, legend, context); + } else { + return get_freq_gridline(subindex, pos, vertical, legend, context); + } +// return false; +} + +int sidechaingate_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const +{ + if (!is_active) + return false; + if(index == param_gating) { + return gate.get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline); + } else { + // (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f) + if (*params[param_f1_freq] != f1_freq_old1 + or *params[param_f2_freq] != f2_freq_old1 + or *params[param_f1_level] != f1_level_old1 + or *params[param_f2_level] != f2_level_old1 + or *params[param_sc_mode] !=sc_mode_old1) + { + f1_freq_old1 = *params[param_f1_freq]; + f2_freq_old1 = *params[param_f2_freq]; + f1_level_old1 = *params[param_f1_level]; + f2_level_old1 = *params[param_f2_level]; + sc_mode_old1 = (CalfScModes)*params[param_sc_mode]; + last_generation++; + subindex_graph = 0; + subindex_dot = INT_MAX; + subindex_gridline = INT_MAX; + } + else { + subindex_graph = 0; + subindex_dot = subindex_gridline = generation ? INT_MAX : 0; + } + if (generation == last_calculated_generation) + subindex_graph = INT_MAX; + return last_generation; + } + return false; +} + /// Gain reduction module by Thor /// All functions of this module are originally written /// by Thor, while some features have been stripped (mainly stereo linking @@ -1127,6 +1661,7 @@ int deesser_audio_module::get_changed_offsets(int index, int generation, int &su gain_reduction_audio_module::gain_reduction_audio_module() { is_active = false; + srate = 0; last_generation = 0; } @@ -1170,7 +1705,6 @@ void gain_reduction_audio_module::process(float &left, float &right, const float if(!det_right) { det_right = &right; } - float gain = 1.f; if(bypass < 0.5f) { // this routine is mainly copied from thor's compressor module // greatest sounding compressor I've heard! @@ -1183,7 +1717,7 @@ void gain_reduction_audio_module::process(float &left, float &right, const float if(rms) absample *= absample; linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff); - + float gain = 1.f; if(linSlope > 0.f) { gain = output_gain(linSlope, rms); } @@ -1344,37 +1878,143 @@ int gain_reduction_audio_module::get_changed_offsets(int generation, int &subind } - -/////////////////////////////////////////////////////////////////////////////////////////////// - -gate_audio_module::gate_audio_module() +/// Gate module by Damien +/// All functions of this module are originally written +/// by Damien, while some features have been stripped (mainly stereo linking +/// and frequency correction as implemented in Sidechain Gate above) +/// To save some CPU. +//////////////////////////////////////////////////////////////////////////////// +expander_audio_module::expander_audio_module() { - is_active = false; - srate = 0; + is_active = false; + srate = 0; last_generation = 0; } -void gate_audio_module::activate() +void expander_audio_module::activate() { is_active = true; - linSlope = 0.f; - peak = 0.f; - clip = 0.f; + linSlope = 0.f; + meter_out = 0.f; + meter_gate = 1.f; + float l, r; + l = r = 0.f; + float byp = bypass; + bypass = 0.0; + process(l, r); + bypass = byp; } -void gate_audio_module::deactivate() +void expander_audio_module::deactivate() { is_active = false; } -void gate_audio_module::set_sample_rate(uint32_t sr) +void expander_audio_module::update_curve() { - srate = sr; - awL.set(sr); - awR.set(sr); + bool rms = detection == 0; + float linThreshold = threshold; + if (rms) + linThreshold = linThreshold * linThreshold; + attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f)); + release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f)); + float linKneeSqrt = sqrt(knee); + linKneeStart = linThreshold / linKneeSqrt; + adjKneeStart = linKneeStart*linKneeStart; + linKneeStop = linThreshold * linKneeSqrt; + thres = log(linThreshold); + kneeStart = log(linKneeStart); + kneeStop = log(linKneeStop); + compressedKneeStop = (kneeStop - thres) / ratio + thres; } -bool gate_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const +void expander_audio_module::process(float &left, float &right, const float *det_left, const float *det_right) +{ + if(!det_left) { + det_left = &left; + } + if(!det_right) { + det_right = &right; + } + if(bypass < 0.5f) { + // this routine is mainly copied from Damien's expander module based on Thor's compressor + bool rms = detection == 0; + bool average = stereo_link == 0; + float absample = average ? (fabs(*det_left) + fabs(*det_right)) * 0.5f : std::max(fabs(*det_left), fabs(*det_right)); + if(rms) absample *= absample; + + linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff); + float gain = 1.f; + if(linSlope > 0.f) { + gain = output_gain(linSlope, rms); + } + left *= gain * makeup; + right *= gain * makeup; + meter_out = std::max(fabs(left), fabs(right)); + meter_gate = gain; + detected = linSlope; + } +} + +float expander_audio_module::output_level(float slope) const { + bool rms = detection == 0; + return slope * output_gain(rms ? slope*slope : slope, rms) * makeup; +} + +float expander_audio_module::output_gain(float linSlope, bool rms) const { + //this calculation is also Damiens's work based on Thor's compressor + if(linSlope < linKneeStop) { + float slope = log(linSlope); + //float tratio = rms ? sqrt(ratio) : ratio; + float tratio = ratio; + float gain = 0.f; + float delta = 0.f; + if(IS_FAKE_INFINITY(ratio)) + tratio = 1000.f; + gain = (slope-thres) * tratio + thres; + delta = tratio; + + if(knee > 1.f && slope > kneeStart ) { + gain = dsp::hermite_interpolation(slope, kneeStart, kneeStop, ((kneeStart - thres) * tratio + thres), kneeStop, delta,1.f); + } + return std::max(range, expf(gain-slope)); + } + return 1.f; +} + +void expander_audio_module::set_sample_rate(uint32_t sr) +{ + srate = sr; +} +void expander_audio_module::set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu, float ran) +{ + // set all params + attack = att; + release = rel; + threshold = thr; + ratio = rat; + knee = kn; + makeup = mak; + detection = det; + stereo_link = stl; + bypass = byp; + mute = mu; + range = ran; + if(mute > 0.f) { + meter_out = 0.f; + meter_gate = 1.f; + } +} +float expander_audio_module::get_output_level() { + // returns output level (max(left, right)) + return meter_out; +} +float expander_audio_module::get_expander_level() { + // returns amount of gating + return meter_gate; +} + +bool expander_audio_module::get_graph(int subindex, float *data, int points, cairo_iface *context) const { if (!is_active) return false; @@ -1383,13 +2023,14 @@ bool gate_audio_module::get_graph(int index, int subindex, float *data, int poin for (int i = 0; i < points; i++) { float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1)); - float output = output_level(input); if (subindex == 0) data[i] = dB_grid(input); - else + else { + float output = output_level(input); data[i] = dB_grid(output); + } } - if (subindex == (*params[param_bypass] > 0.f ? 1 : 0)) + if (subindex == (bypass > 0.5f ? 1 : 0) or mute > 0.1f) context->set_source_rgba(0.35, 0.4, 0.2, 0.3); else { context->set_source_rgba(0.35, 0.4, 0.2, 1); @@ -1398,22 +2039,26 @@ bool gate_audio_module::get_graph(int index, int subindex, float *data, int poin return true; } -bool gate_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) const +bool expander_audio_module::get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context) const { if (!is_active) return false; if (!subindex) { - bool rms = *params[param_detection] == 0; - float det = rms ? sqrt(detected) : detected; - x = 0.5 + 0.5 * dB_grid(det); - y = dB_grid(*params[param_bypass] > 0.f ? det : output_level(det)); - return *params[param_bypass] > 0.f ? false : true; + if(bypass > 0.5f or mute > 0.f) { + return false; + } else { + bool rms = detection == 0; + float det = rms ? sqrt(detected) : detected; + x = 0.5 + 0.5 * dB_grid(det); + y = dB_grid(bypass > 0.5f or mute > 0.f ? det : output_level(det)); + return true; + } } return false; } -bool gate_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const +bool expander_audio_module::get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const { bool tmp; vertical = (subindex & 1) != 0; @@ -1432,161 +2077,26 @@ bool gate_audio_module::get_gridline(int index, int subindex, float &pos, bool & return result; } -uint32_t gate_audio_module::process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask) -{ - bool bypass = *params[param_bypass] > 0.f; - - if(bypass) { - int count = numsamples * sizeof(float); - memcpy(outs[0], ins[0], count); - memcpy(outs[1], ins[1], count); - - if(params[param_gating] != NULL) { - *params[param_gating] = 1.f; - } - - if(params[param_clip] != NULL) { - *params[param_clip] = 0.f; - } - - if(params[param_peak] != NULL) { - *params[param_peak] = 0.f; - } - - return inputs_mask; - } - - bool rms = *params[param_detection] == 0; - bool average = *params[param_stereo_link] == 0; - int aweighting = fastf2i_drm(*params[param_aweighting]); - float linThreshold = *params[param_threshold]; - if (rms) - linThreshold = linThreshold * linThreshold; - ratio = *params[param_ratio]; - float attack = *params[param_attack]; - float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f)); - float release = *params[param_release]; - float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f)); - makeup = *params[param_makeup]; - knee = *params[param_knee]; - - range = *params[param_range]; - - float linKneeSqrt = sqrt(knee); - linKneeStart = linThreshold / linKneeSqrt; - adjKneeStart = linKneeStart*linKneeStart; - linKneeStop = linThreshold * linKneeSqrt; - - threshold = log(linThreshold); - kneeStart = log(linKneeStart); - kneeStop = log(linKneeStop); - compressedKneeStop = (kneeStop - threshold) / ratio + threshold; - - if (aweighting >= 2) - { - bpL.set_highshelf_rbj(5000, 0.707, 10 << (aweighting - 2), srate); - bpR.copy_coeffs(bpL); - bpL.sanitize(); - bpR.sanitize(); - } - - numsamples += offset; - - float gating = 1.f; - - peak -= peak * 5.f * numsamples / srate; - - clip -= std::min(clip, numsamples); - float left; - float right; - - while(offset < numsamples) { - if(*params[param_trigger]) { - left = ins[2][offset]; // Use sidechain to trigger input - right = left; // (mono sidechain) - } else { - left = ins[0][offset]; - right = ins[1][offset]; - } - - if(aweighting == 1) { - left = awL.process(left); - right = awR.process(right); - } - else if(aweighting >= 2) { - left = bpL.process(left); - right = bpR.process(right); - } - - float absample = average ? (fabs(left) + fabs(right)) * 0.5f : std::max(fabs(left), fabs(right)); - if(rms) absample *= absample; - - linSlope += (absample - linSlope) * (absample > linSlope ? attack_coeff : release_coeff); - - float gain = 1.f; - - if(linSlope > 0.f) { - gain = output_gain(linSlope, rms); - } - - gating = gain; - gain *= makeup; - - float outL = ins[0][offset] * gain; - float outR = ins[1][offset] * gain; - - outs[0][offset] = outL; - outs[1][offset] = outR; - - ++offset; - - float maxLR = std::max(fabs(outL), fabs(outR)); - - if(maxLR > 1.f) clip = srate >> 3; /* blink clip LED for 125 ms */ - - if(maxLR > peak) { - peak = maxLR; - } - } - - detected = linSlope; - - if(params[param_gating] != NULL) { - *params[param_gating] = gating; - } - - if(params[param_clip] != NULL) { - *params[param_clip] = clip; - } - - if(params[param_peak] != NULL) { - *params[param_peak] = peak; - } - - return inputs_mask; -} - -int gate_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const +int expander_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) const { subindex_graph = 0; subindex_dot = 0; subindex_gridline = generation ? INT_MAX : 0; - if (fabs(range-old_range) + fabs(threshold-old_threshold) + fabs(ratio - old_ratio) + fabs(knee - old_knee) + fabs( makeup - old_makeup) + fabs( *params[param_bypass] - old_bypass) > 0.01f) + if (fabs(range-old_range) + fabs(threshold-old_threshold) + fabs(ratio - old_ratio) + fabs(knee - old_knee) + fabs(makeup - old_makeup) + fabs(detection - old_detection) + fabs(bypass - old_bypass) + fabs(mute - old_mute) > 0.000001f) { - old_range = range; + old_range = range; old_threshold = threshold; - old_ratio = ratio; - old_knee = knee; - old_makeup = makeup; - old_bypass = *params[param_bypass]; - old_trigger = *params[param_trigger]; - old_mono = *params[param_mono]; + old_ratio = ratio; + old_knee = knee; + old_makeup = makeup; + old_detection = detection; + old_bypass = bypass; + old_mute = mute; last_generation++; } - old_trigger = *params[param_trigger]; - if (generation == last_generation) subindex_graph = 2; return last_generation; } +