From 17425ce70f8155257793f36dae8e8b8fc06e0bc1 Mon Sep 17 00:00:00 2001 From: Markus Schmidt Date: Sun, 25 Oct 2009 10:04:14 +0100 Subject: [PATCH] New FX: Sidechain compressor (cherry picked from commit c5c8786de3e410c64d9c9d4cd70fd03a4c697a2e) --- plugins/ladspa_effect/calf/calf/giface.h | 1 + plugins/ladspa_effect/calf/calf/metadata.h | 11 + plugins/ladspa_effect/calf/calf/modulelist.h | 1 + plugins/ladspa_effect/calf/calf/modules.h | 80 +++- plugins/ladspa_effect/calf/src/modules.cpp | 45 +++ .../ladspa_effect/calf/src/modules_dsp.cpp | 356 +++++++++++++++++- 6 files changed, 475 insertions(+), 19 deletions(-) diff --git a/plugins/ladspa_effect/calf/calf/giface.h b/plugins/ladspa_effect/calf/calf/giface.h index 4a23300ca..56305bcb9 100644 --- a/plugins/ladspa_effect/calf/calf/giface.h +++ b/plugins/ladspa_effect/calf/calf/giface.h @@ -69,6 +69,7 @@ enum parameter_flags PF_CTL_BUTTON = 0x0600, ///< push button PF_CTL_METER = 0x0700, ///< volume meter PF_CTL_LED = 0x0800, ///< light emitting diode + PF_CTL_LABEL = 0x0900, ///< label PF_CTLOPTIONS = 0x00F000, ///< bit mask for control (widget) options PF_CTLO_HORIZ = 0x001000, ///< horizontal version of the control (unused) diff --git a/plugins/ladspa_effect/calf/calf/metadata.h b/plugins/ladspa_effect/calf/calf/metadata.h index 4ce663961..79d440140 100644 --- a/plugins/ladspa_effect/calf/calf/metadata.h +++ b/plugins/ladspa_effect/calf/calf/metadata.h @@ -136,6 +136,17 @@ struct compressor_metadata: public plugin_metadata PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor") }; +/// Markus's sidechain compressor - metadata +struct sidechaincompressor_metadata: public plugin_metadata +{ + enum { in_count = 2, out_count = 2, 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_threshold, param_ratio, param_attack, param_release, param_makeup, param_knee, param_detection, param_stereo_link, param_compression, + 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("sidechaincompressor", "sidechaincompressor", "Sidechain Compressor") +}; + /// Markus's multibandcompressor - metadata struct multibandcompressor_metadata: public plugin_metadata { diff --git a/plugins/ladspa_effect/calf/calf/modulelist.h b/plugins/ladspa_effect/calf/calf/modulelist.h index ca990654b..904e1b3ba 100644 --- a/plugins/ladspa_effect/calf/calf/modulelist.h +++ b/plugins/ladspa_effect/calf/calf/modulelist.h @@ -11,6 +11,7 @@ PER_MODULE_ITEM(multichorus, false, "multichorus") PER_MODULE_ITEM(compressor, false, "compressor") PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor") + PER_MODULE_ITEM(sidechaincompressor, false, "sidechaincompressor") #ifdef ENABLE_EXPERIMENTAL PER_MODULE_ITEM(fluidsynth, true, "fluidsynth") PER_MODULE_ITEM(wavetable, true, "wavetable") diff --git a/plugins/ladspa_effect/calf/calf/modules.h b/plugins/ladspa_effect/calf/calf/modules.h index fd3f27eb3..7933aa0f7 100644 --- a/plugins/ladspa_effect/calf/calf/modules.h +++ b/plugins/ladspa_effect/calf/calf/modules.h @@ -926,8 +926,8 @@ class gain_reduction_audio_module { private: float linSlope, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop; float compressedKneeStop, adjKneeStart, thres; - float attack, release, threshold, ratio, knee, makeup, detection, bypass, mute, meter_out, meter_comp; - float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection; + float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_comp; + float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection, old_stereo_link; int last_generation; uint32_t srate; bool is_active; @@ -935,8 +935,8 @@ private: inline float output_gain(float linSlope, bool rms); public: gain_reduction_audio_module(); - void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float byp, float mu); - void process(float &left, float &right); + void set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu); + void process(float &left, float &right, float det_left = NULL, float det_right = NULL); void activate(); void deactivate(); int id; @@ -949,6 +949,78 @@ public: virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline); }; + +/// Sidecain Compressor by Markus Schmidt (based on Thor's compressor and Krzysztof's filters) +class sidechaincompressor_audio_module: public audio_module, public frequency_response_line_graph { +private: + enum CalfScModes { + WIDEBAND, + DEESSER_WIDE, + DEESSER_SPLIT, + DERUMBLER_WIDE, + DERUMBLER_SPLIT, + WEIGHTED_1, + WEIGHTED_2, + WEIGHTED_3, + BANDPASS_1, + BANDPASS_2 + }; + float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old; + CalfScModes sc_mode; + float f1_active, f2_active; + uint32_t clip_in, clip_out; + float meter_in, meter_out; + gain_reduction_audio_module compressor; + biquad_d2 f1L, f1R, f2L, f2R; +public: + typedef std::complex cfloat; + float *ins[in_count]; + float *outs[out_count]; + float *params[param_count]; + uint32_t srate; + bool is_active; + sidechaincompressor_audio_module(); + void activate(); + void deactivate(); + void params_changed(); + inline cfloat h_z(const cfloat &z) { + switch (sc_mode) { + default: + case WIDEBAND: + return false; + break; + case DEESSER_WIDE: + case DERUMBLER_WIDE: + case WEIGHTED_1: + case WEIGHTED_2: + case WEIGHTED_3: + case BANDPASS_2: + return f1L.h_z(z) * f2L.h_z(z); + break; + case DEESSER_SPLIT: + case DERUMBLER_SPLIT: + case BANDPASS_1: + return f1L.h_z(z); + break; + } + + } + float freq_gain(int index, double freq, uint32_t sr) + { + 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 set_sample_rate(uint32_t sr); + uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask); + virtual bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context); + virtual bool get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context); + virtual bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context); + virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline); +}; + /// Multibandcompressor by Markus Schmidt class multibandcompressor_audio_module: public audio_module, public line_graph_iface { private: diff --git a/plugins/ladspa_effect/calf/src/modules.cpp b/plugins/ladspa_effect/calf/src/modules.cpp index db40b3993..5d6c5d78a 100644 --- a/plugins/ladspa_effect/calf/src/modules.cpp +++ b/plugins/ladspa_effect/calf/src/modules.cpp @@ -240,6 +240,51 @@ CALF_PLUGIN_INFO(compressor) = { 0x8502, "Compressor", "Calf Compressor", "Thor //////////////////////////////////////////////////////////////////////////// +CALF_PORT_NAMES(sidechaincompressor) = {"In L", "In R", "Out L", "Out R"}; + +const char *sidechaincompressor_detection_names[] = { "RMS", "Peak" }; +const char *sidechaincompressor_stereo_link_names[] = { "Average", "Maximum" }; +const char *sidechaincompressor_mode_names[] = {"Wideband (F1:off / F2:off)", + "Deesser wide (F1:HP / F2:Bell)", + "Deesser split (F1:HP / F2:off)", + "Derumbler wide (F1:Bell / F2:LP)", + "Derumbler split (F1:off / F2:LP)", + "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)"}; + +CALF_PORT_PROPS(sidechaincompressor) = { + { 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" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "0dB" }, + { 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" }, + { 2, 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, sidechaincompressor_detection_names, "detection", "Detection" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, sidechaincompressor_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, "compression", "Gain Reduction" }, + { 0, 0, 9, 0, PF_ENUM | PF_CTL_COMBO, sidechaincompressor_mode_names, "sc_mode", "Sidechain Mode" }, + { 200, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "f1_freq", "Freq" }, + { 4000, 10,20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "f2_freq", "Freq" }, + { 1, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f1_level", "Level" }, + { 1, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f2_level", "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", "active" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "f2_active", "active" }, +}; + +CALF_PLUGIN_INFO(sidechaincompressor) = { 0x8502, "Sidechaincompressor", "Calf Sidechain Compressor", "Thor Harald Johansen / Markus Schmidt", calf_plugins::calf_copyright_info, "CompressorPlugin" }; + +//////////////////////////////////////////////////////////////////////////// + CALF_PORT_NAMES(multibandcompressor) = {"In L", "In R", "Out L", "Out R"}; const char *multibandcompressor_detection_names[] = { "RMS", "Peak" }; diff --git a/plugins/ladspa_effect/calf/src/modules_dsp.cpp b/plugins/ladspa_effect/calf/src/modules_dsp.cpp index 5f54654d0..c1a8464f7 100644 --- a/plugins/ladspa_effect/calf/src/modules_dsp.cpp +++ b/plugins/ladspa_effect/calf/src/modules_dsp.cpp @@ -716,16 +716,16 @@ void multibandcompressor_audio_module::params_changed() for (int j = 0; j < strips; j ++) { switch (j) { case 0: - strip[j].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], *params[param_bypass0], *params[param_mute0]); + strip[j].set_params(*params[param_attack0], *params[param_release0], *params[param_threshold0], *params[param_ratio0], *params[param_knee0], *params[param_makeup0], *params[param_detection0], 1.f, *params[param_bypass0], *params[param_mute0]); break; case 1: - strip[j].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], *params[param_bypass1], *params[param_mute1]); + strip[j].set_params(*params[param_attack1], *params[param_release1], *params[param_threshold1], *params[param_ratio1], *params[param_knee1], *params[param_makeup1], *params[param_detection1], 1.f, *params[param_bypass1], *params[param_mute1]); break; case 2: - strip[j].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], *params[param_bypass2], *params[param_mute2]); + strip[j].set_params(*params[param_attack2], *params[param_release2], *params[param_threshold2], *params[param_ratio2], *params[param_knee2], *params[param_makeup2], *params[param_detection2], 1.f, *params[param_bypass2], *params[param_mute2]); break; case 3: - strip[j].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], *params[param_bypass3], *params[param_mute3]); + strip[j].set_params(*params[param_attack3], *params[param_release3], *params[param_threshold3], *params[param_ratio3], *params[param_knee3], *params[param_makeup3], *params[param_detection3], 1.f, *params[param_bypass3], *params[param_mute3]); break; } } @@ -1049,6 +1049,328 @@ int multibandcompressor_audio_module::get_changed_offsets(int index, int generat } return 0; } + +/// Sidecain Compressor by Markus Schmidt +/// +/// This module splits the signal in a sidechain- and a process signal. +/// The sidechain is processed through Krzystofs filters and compresses +/// the process signal via Thor's compression routine afterwards. +/////////////////////////////////////////////////////////////////////////////////////////////// + +sidechaincompressor_audio_module::sidechaincompressor_audio_module() +{ + is_active = false; + srate = 0; +} + +void sidechaincompressor_audio_module::activate() +{ + is_active = true; + // set all filters and strips + compressor.activate(); + params_changed(); + meter_in = 0.f; + meter_out = 0.f; + clip_in = 0.f; + clip_out = 0.f; +} +void sidechaincompressor_audio_module::deactivate() +{ + is_active = false; + compressor.deactivate(); +} + +void sidechaincompressor_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_sc_mode] != sc_mode + or *params[param_f2_freq] != f2_freq_old or *params[param_f2_level] != f2_level_old) { + switch ((int)*params[param_sc_mode]) { + default: + case WIDEBAND: + f1L.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f1R.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f2L.set_lp_rbj((float)*params[param_f2_freq], 0.707, (float)srate); + f2R.set_lp_rbj((float)*params[param_f2_freq], 0.707, (float)srate); + f1_active = 0.f; + f2_active = 0.f; + break; + case DEESSER_WIDE: + f1L.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f1R.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f2L.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 1.f; + f2_active = 0.5f; + break; + case DEESSER_SPLIT: + f1L.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), 0.707, (float)srate); + f1R.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), 0.707, (float)srate); + f2L.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), 0.707, (float)srate); + f2R.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), 0.707, (float)srate); + f1_active = 1.f; + f2_active = 0.f; + break; + case DERUMBLER_WIDE: + f1L.set_lp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f1R.set_lp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f2L.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 1.f; + f2_active = 0.5f; + break; + case DERUMBLER_SPLIT: + f1L.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), 0.707, (float)srate); + f1R.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), 0.707, (float)srate); + f2L.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), 0.707, (float)srate); + f2R.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), 0.707, (float)srate); + f1_active = 1.f; + f2_active = 0.f; + break; + case WEIGHTED_1: + f1L.set_lowshelf_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f1R.set_lowshelf_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f2L.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case WEIGHTED_2: + f1L.set_lowshelf_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f1R.set_lowshelf_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f2L.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_peakeq_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case WEIGHTED_3: + f1L.set_peakeq_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f1R.set_peakeq_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f2L.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 0.5f; + f2_active = 0.5f; + break; + case BANDPASS_1: + f1L.set_bp_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f1R.set_bp_rbj((float)*params[param_f1_freq], 0.707, *params[param_f1_level], (float)srate); + f2L.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f2R.set_highshelf_rbj((float)*params[param_f2_freq], 0.707, *params[param_f2_level], (float)srate); + f1_active = 1.f; + f2_active = 0.f; + break; + case BANDPASS_2: + f1L.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f1R.set_hp_rbj((float)*params[param_f1_freq], 0.707, (float)srate); + f2L.set_lp_rbj((float)*params[param_f2_freq], 0.707, (float)srate); + f2R.set_lp_rbj((float)*params[param_f2_freq], 0.707, (float)srate); + 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 compressor module + compressor.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); +} + +void sidechaincompressor_audio_module::set_sample_rate(uint32_t sr) +{ + srate = sr; + compressor.set_sample_rate(srate); +} + +uint32_t sidechaincompressor_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); + + 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: + compressor.process(leftAC, rightAC, leftSC, rightSC); + break; + case DEESSER_WIDE: + case DERUMBLER_WIDE: + case WEIGHTED_1: + case WEIGHTED_2: + case WEIGHTED_3: + case BANDPASS_2: + leftSC = f2L.process(f1L.process(leftSC)); + rightSC = f2L.process(f1L.process(rightSC)); + leftMC = leftSC; + rightMC = rightSC; + compressor.process(leftAC, rightAC, leftSC, rightSC); + break; + case DEESSER_SPLIT: + case DERUMBLER_SPLIT: + leftSC = f1L.process(leftSC); + rightSC = f1L.process(rightSC); + leftMC = leftSC; + rightMC = rightSC; + compressor.process(leftSC, rightSC, leftSC, rightSC); + leftAC = f2L.process(leftAC); + rightAC = f2L.process(rightAC); + leftAC += leftSC; + rightAC += rightSC; + break; + case BANDPASS_1: + leftSC = f1L.process(leftSC); + rightSC = f1L.process(rightSC); + leftMC = leftSC; + rightMC = rightSC; + compressor.process(leftAC, rightAC, leftSC, rightSC); + break; + } + f1L.sanitize(); + f1R.sanitize(); + f2L.sanitize(); + f2R.sanitize(); + + 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 + } + // 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; + } + // draw strip meter + if(bypass > 0.5f) { + if(params[param_compression] != NULL) { + *params[param_compression] = 1.0f; + } + } else { + if(params[param_compression] != NULL) { + *params[param_compression] = compressor.get_comp_level(); + } + } + // whatever has to be returned x) + return outputs_mask; +} +bool sidechaincompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context) +{ + 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_compression) { + return compressor.get_graph(subindex, data, points, context); + } + return false; +} + +bool sidechaincompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context) +{ + if (!is_active) + return false; + if (index == param_compression) { + return compressor.get_dot(subindex, x, y, size, context); + } + return false; +} + +bool sidechaincompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) +{ + if (!is_active) + return false; + if (index == param_compression) { + return compressor.get_gridline(subindex, pos, vertical, legend, context); + } else { + return get_freq_gridline(subindex, pos, vertical, legend, context); + } +// return false; +} + +int sidechaincompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) +{ + if (!is_active) + return false; + if(index == param_compression) { + return compressor.get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline); + } + return false; +} + /// Gain reduction module implemented by Markus Schmidt /// Nearly all functions of this module are originally written /// by Thor, while some features have been stripped (mainly stereo linking @@ -1080,14 +1402,20 @@ void gain_reduction_audio_module::deactivate() is_active = false; } -void gain_reduction_audio_module::process(float &left, float &right) +void gain_reduction_audio_module::process(float &left, float &right, float det_left, float det_right) { + if(!det_left) { + det_left = left; + } + if(!det_right) { + det_right = right; + } float gain = 1.f; - float maxLR = 0.f; if(bypass < 0.5f) { // this routine is mainly copied from thor's compressor module // greatest sounding compressor I've heard! bool rms = detection == 0; + bool average = stereo_link == 0; float linThreshold = threshold; float attack_coeff = std::min(1.f, 1.f / (attack * srate / 4000.f)); float release_coeff = std::min(1.f, 1.f / (release * srate / 4000.f)); @@ -1100,7 +1428,7 @@ void gain_reduction_audio_module::process(float &left, float &right) kneeStop = log(linKneeStop); compressedKneeStop = (kneeStop - thres) / ratio + thres; - float absample = (fabs(left) + fabs(right)) * 0.5f; + 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); @@ -1109,15 +1437,12 @@ void gain_reduction_audio_module::process(float &left, float &right) gain = output_gain(linSlope, rms); } - gain *= makeup; - - left *= gain; - right *= gain; - maxLR = std::max(fabs(left), fabs(right)); + left *= gain * makeup; + right *= gain * makeup; + meter_out = std::max(fabs(left), fabs(right));; + meter_comp = gain; detected = rms ? sqrt(linSlope) : linSlope; } - meter_out = maxLR; - meter_comp = gain; } float gain_reduction_audio_module::output_level(float slope) { @@ -1154,7 +1479,7 @@ void gain_reduction_audio_module::set_sample_rate(uint32_t sr) { srate = sr; } -void gain_reduction_audio_module::set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float byp, float mu) +void gain_reduction_audio_module::set_params(float att, float rel, float thr, float rat, float kn, float mak, float det, float stl, float byp, float mu) { // set all params attack = att; @@ -1164,6 +1489,7 @@ void gain_reduction_audio_module::set_params(float att, float rel, float thr, fl knee = kn; makeup = mak; detection = det; + stereo_link = stl; bypass = byp; mute = mu; if(mute > 0.f) {