diff --git a/plugins/ladspa_effect/calf/calf/metadata.h b/plugins/ladspa_effect/calf/calf/metadata.h index 6ba14fbf8..c959e1940 100644 --- a/plugins/ladspa_effect/calf/calf/metadata.h +++ b/plugins/ladspa_effect/calf/calf/metadata.h @@ -168,6 +168,18 @@ struct multibandcompressor_metadata: public plugin_metadata +{ + enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true }; + enum { param_bypass, param_detected, param_compression, param_detected_led, param_clip_out, + param_detection, param_mode, + param_threshold, param_ratio, param_laxity, param_makeup, + param_f1_freq, param_f2_freq, param_f1_level, param_f2_level, param_f2_q, + param_sc_listen, param_count }; + PLUGIN_NAME_ID_LABEL("deesser", "deesser", "Deesser") +}; + /// Markus's 5-band EQ - metadata struct equalizer5band_metadata: public plugin_metadata { diff --git a/plugins/ladspa_effect/calf/calf/modulelist.h b/plugins/ladspa_effect/calf/calf/modulelist.h index 2fba14b3e..83c123cad 100644 --- a/plugins/ladspa_effect/calf/calf/modulelist.h +++ b/plugins/ladspa_effect/calf/calf/modulelist.h @@ -12,6 +12,7 @@ PER_MODULE_ITEM(compressor, false, "compressor") PER_MODULE_ITEM(sidechaincompressor, false, "sidechaincompressor") PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor") + PER_MODULE_ITEM(deesser, false, "deesser") PER_MODULE_ITEM(equalizer5band, false, "equalizer5band") PER_MODULE_ITEM(equalizer8band, false, "equalizer8band") PER_MODULE_ITEM(equalizer12band, false, "equalizer12band") diff --git a/plugins/ladspa_effect/calf/calf/modules.h b/plugins/ladspa_effect/calf/calf/modules.h index 5ba9ffff2..8612106bd 100644 --- a/plugins/ladspa_effect/calf/calf/modules.h +++ b/plugins/ladspa_effect/calf/calf/modules.h @@ -1052,6 +1052,41 @@ public: virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline); }; +/// Deesser by Markus Schmidt (based on Thor's compressor and Krzysztof's filters) +class deesser_audio_module: public audio_module, public frequency_response_line_graph { +private: + enum CalfDeessModes { + WIDE, + SPLIT + }; + float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old, f2_q_old; + float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1, f2_q_old1; + uint32_t detected_led; + float detected, clip_out; + gain_reduction_audio_module compressor; + biquad_d2 hpL, hpR, lpL, lpR, pL, pR; +public: + float *ins[in_count]; + float *outs[out_count]; + float *params[param_count]; + uint32_t srate; + bool is_active; + volatile int last_generation, last_calculated_generation; + deesser_audio_module(); + void activate(); + void deactivate(); + void params_changed(); + float freq_gain(int index, double freq, uint32_t sr) + { + return hpL.freq_gain(freq, sr) * pL.freq_gain(freq, sr); + } + 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); + bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context); + int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline); +}; + /// Equalizer 12 Band by Markus Schmidt (based on Krzysztof's filters) class equalizer12band_audio_module: public audio_module, public frequency_response_line_graph { private: diff --git a/plugins/ladspa_effect/calf/src/modules.cpp b/plugins/ladspa_effect/calf/src/modules.cpp index 9b1dc7b6c..eeeb6f073 100644 --- a/plugins/ladspa_effect/calf/src/modules.cpp +++ b/plugins/ladspa_effect/calf/src/modules.cpp @@ -283,7 +283,7 @@ CALF_PORT_PROPS(sidechaincompressor) = { { 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_PLUGIN_INFO(sidechaincompressor) = { 0x8502, "Sidechaincompressor", "Calf Sidechain Compressor", "Markus Schmidt / Thor Harald Johansen", calf_plugins::calf_copyright_info, "CompressorPlugin" }; //////////////////////////////////////////////////////////////////////////// @@ -373,6 +373,37 @@ CALF_PLUGIN_INFO(multibandcompressor) = { 0x8502, "Multibandcompressor", "Calf M //////////////////////////////////////////////////////////////////////////// +CALF_PORT_NAMES(deesser) = {"In L", "In R", "Out L", "Out R"}; + +const char *deesser_detection_names[] = { "RMS", "Peak" }; +const char *deesser_mode_names[] = { "Wide", "Split" }; + + +CALF_PORT_PROPS(deesser) = { + { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass", "Bypass" }, + { 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, "detected", "Detected" }, + { 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, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "detected_led", "Active" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_out", "Out" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, deesser_detection_names, "detection", "Detection" }, + { 0, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, deesser_mode_names, "mode", "Mode" }, + { 0.0625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold", "Threshold" }, + { 5, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio", "Ratio" }, + { 15, 1, 100, 1, PF_INT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "laxity", "Laxity" }, + { 1, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup", "Makeup" }, + + { 5000, 10, 18000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "f1_freq", "Split" }, + { 6000, 10, 18000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ, NULL, "f2_freq", "Peak" }, + { 1, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f1_level", "Gain" }, + { 4, 0.0625, 16, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "f2_level", "Level" }, + { 1, 0.1, 100,1, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "f2_q", "Peak Q" }, + { 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "sc_listen", "S/C-Listen" }, +}; + +CALF_PLUGIN_INFO(deesser) = { 0x8502, "Deesser", "Calf Deesser", "Markus Schmidt / Thor Harald Johansen", calf_plugins::calf_copyright_info, "CompressorPlugin" }; + +//////////////////////////////////////////////////////////////////////////// + CALF_PORT_NAMES(equalizer5band) = {"In L", "In R", "Out L", "Out R"}; CALF_PORT_PROPS(equalizer5band) = { diff --git a/plugins/ladspa_effect/calf/src/modules_dsp.cpp b/plugins/ladspa_effect/calf/src/modules_dsp.cpp index ca8922903..41ec5a6e1 100644 --- a/plugins/ladspa_effect/calf/src/modules_dsp.cpp +++ b/plugins/ladspa_effect/calf/src/modules_dsp.cpp @@ -1107,7 +1107,7 @@ void sidechaincompressor_audio_module::params_changed() f2_active = 1.f; break; case DEESSER_SPLIT: - f1L.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), q, (float)srate); + 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); @@ -1125,7 +1125,7 @@ void sidechaincompressor_audio_module::params_changed() case DERUMBLER_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_f2_freq] * (1 - 0.17), q, (float)srate); + 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; @@ -1157,7 +1157,7 @@ void sidechaincompressor_audio_module::params_changed() 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_highshelf_rbj((float)*params[param_f2_freq], q, *params[param_f2_level], (float)srate); + 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; @@ -1410,6 +1410,222 @@ int sidechaincompressor_audio_module::get_changed_offsets(int index, int generat return false; } +/// Deesser 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. +/////////////////////////////////////////////////////////////////////////////////////////////// + +deesser_audio_module::deesser_audio_module() +{ + is_active = false; + srate = 0; + last_generation = 0; +} + +void deesser_audio_module::activate() +{ + is_active = true; + // set all filters and strips + compressor.activate(); + params_changed(); + detected = 0.f; + detected_led = 0.f; + clip_out = 0.f; +} +void deesser_audio_module::deactivate() +{ + is_active = false; + compressor.deactivate(); +} + +void deesser_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_f2_q] != f2_q_old) { + float q = 0.707; + + hpL.set_hp_rbj((float)*params[param_f1_freq] * (1 - 0.17), q, (float)srate, *params[param_f1_level]); + hpR.copy_coeffs(hpL); + lpL.set_lp_rbj((float)*params[param_f1_freq] * (1 + 0.17), q, (float)srate); + lpR.copy_coeffs(lpL); + pL.set_peakeq_rbj((float)*params[param_f2_freq], *params[param_f2_q], *params[param_f2_level], (float)srate); + pR.copy_coeffs(pL); + 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]; + f2_q_old = *params[param_f2_q]; + } + // and set the compressor module + compressor.set_params((float)*params[param_laxity], (float)*params[param_laxity] * 1.33, *params[param_threshold], *params[param_ratio], 2.8, *params[param_makeup], *params[param_detection], 0.f, *params[param_bypass], 0.f); +} + +void deesser_audio_module::set_sample_rate(uint32_t sr) +{ + srate = sr; + compressor.set_sample_rate(srate); +} + +uint32_t deesser_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_out = 0.f; + detected = 0.f; + detected_led = 0.f; + } else { + // process + + detected_led -= std::min(detected_led, 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]; + + + float leftAC = inL; + float rightAC = inR; + float leftSC = inL; + float rightSC = inR; + float leftMC = inL; + float rightMC = inR; + + leftSC = pL.process(hpL.process(leftSC)); + rightSC = pR.process(hpR.process(rightSC)); + leftMC = leftSC; + rightMC = rightSC; + + switch ((int)*params[param_mode]) { + default: + case WIDE: + compressor.process(leftAC, rightAC, leftSC, rightSC); + break; + case SPLIT: + compressor.process(leftSC, rightSC, leftSC, rightSC); + leftAC = lpL.process(leftAC); + rightAC = lpR.process(rightAC); + leftAC += leftSC; + rightAC += 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; + + if(std::max(fabs(leftSC), fabs(rightSC)) > 0.1) { + detected_led = srate >> 3; + } + clip_out = std::max(fabs(outL), fabs(outR)); + detected = std::max(fabs(leftMC), fabs(rightMC)); + + // next sample + ++offset; + } // cycle trough samples + hpL.sanitize(); + hpR.sanitize(); + lpL.sanitize(); + lpR.sanitize(); + pL.sanitize(); + pR.sanitize(); + } + // draw meters + if(params[param_detected_led] != NULL) { + *params[param_detected_led] = detected_led; + } + if(params[param_clip_out] != NULL) { + *params[param_clip_out] = clip_out; + } + if(params[param_detected] != NULL) { + *params[param_detected] = detected; + } + // 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 deesser_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); + } + return false; +} + +bool deesser_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context) +{ + return get_freq_gridline(subindex, pos, vertical, legend, context); + +// return false; +} + +int deesser_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) +{ + if (!is_active) { + return false; + } 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_f2_q] !=f2_q_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]; + f2_q_old1 = *params[param_f2_q]; + 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 implemented by Markus Schmidt /// Nearly all functions of this module are originally written /// by Thor, while some features have been stripped (mainly stereo linking