Merge branch 'calf-updates'

* calf-updates:
  + DSP primitives: fix a rather stupid bug in clamping functions
  + Monosynth: fix type bug that made it impossible to compile with g++ 4.2
  + AutoHell: update AUTHORS file
  + GUI, Multiband Compressor, Toggle button: apply more fixes and additions by Markus Schmidt
  + Multiband Compressor: better metadata
  + Multiband Compressor: new module (first version, by Markus Schmidt, based on code by me and Thor) plus associated refactoring and graph colour scheme update
This commit is contained in:
Tobias Doerffel
2009-10-17 23:34:23 +02:00
9 changed files with 839 additions and 29 deletions

View File

@@ -4,6 +4,8 @@ Thor Harald Johansen <thj@thj.no>
Thorsten Wilms <t_w_@freenet.de>
Hans Baier <hansfbaier@googlemail.com>
Torben Hohn <torbenh@gmx.de>
Markus Schmidt <schmidt@boomshop.net>
Additional bugfixes/enhancement patches:
David Täht <d@teklibre.com>
Dave Robillard <dave@drobilla.net>

View File

@@ -45,7 +45,7 @@ enum parameter_flags
PF_TYPEMASK = 0x000F, ///< bit mask for type
PF_FLOAT = 0x0000, ///< any float value
PF_INT = 0x0001, ///< integer value (still represented as float)
PF_BOOL = 0x0002, ///< bool value (usually >=0.5f is treated as true, which is inconsistent with LV2 etc. which treats anything >0 as true)
PF_BOOL = 0x0002, ///< bool value (usually >=0.5f is treated as TRUE, which is inconsistent with LV2 etc. which treats anything >0 as TRUE)
PF_ENUM = 0x0003, ///< enum value (min, min+1, ..., max, only guaranteed to work when min = 0)
PF_ENUM_MULTI = 0x0004, ///< SET / multiple-choice
PF_STRING = 0x0005, ///< see: http://lv2plug.in/docs/index.php?title=String_port
@@ -190,12 +190,13 @@ struct line_graph_iface
virtual bool get_static_graph(int index, int subindex, float value, float *data, int points, cairo_iface *context) { return false; }
/// Return which graphs need to be redrawn and which can be cached for later reuse
/// @param index Parameter/graph number (usually tied to particular plugin control port)
/// @param generation 0 (at start) or the last value returned by the function (corresponds to a set of input values)
/// @param subindex_graph First graph that has to be redrawn (because it depends on values that might have changed)
/// @param subindex_dot First dot that has to be redrawn
/// @param subindex_gridline First gridline/legend that has to be redrawn
/// @retval Current generation (to pass when calling the function next time); if different than passed generation value, call the function again to retrieve which graph offsets should be put into cache
virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline) { subindex_graph = subindex_dot = subindex_gridline = 0; return 0; }
/// Standard destructor to make compiler happy
virtual ~line_graph_iface() {}

View File

@@ -136,6 +136,27 @@ struct compressor_metadata: public plugin_metadata<compressor_metadata>
PLUGIN_NAME_ID_LABEL("compressor", "compressor", "Compressor")
};
/// Markus's multibandcompressor - metadata
struct multibandcompressor_metadata: public plugin_metadata<multibandcompressor_metadata>
{
enum { in_count = 2, out_count = 2, support_midi = false, require_midi = false, rt_capable = true };
enum { param_bypass, param_level_in, param_level_out, param_meter_inL, param_meter_inR,
param_meter_outL, param_meter_outR, param_clip_inL, param_clip_inR, param_clip_outL, param_clip_outR,
param_freq0, param_freq1, param_freq2,
param_sep0, param_sep1, param_sep2,
param_q0, param_q1, param_q2,
param_threshold0, param_ratio0, param_attack0, param_release0, param_makeup0, param_knee0,
param_detection0, param_compression0, param_output0, param_bypass0, param_mute0,
param_threshold1, param_ratio1, param_attack1, param_release1, param_makeup1, param_knee1,
param_detection1, param_compression1, param_output1, param_bypass1, param_mute1,
param_threshold2, param_ratio2, param_attack2, param_release2, param_makeup2, param_knee2,
param_detection2, param_compression2, param_output2, param_bypass2, param_mute2,
param_threshold3, param_ratio3, param_attack3, param_release3, param_makeup3, param_knee3,
param_detection3, param_compression3, param_output3, param_bypass3, param_mute3,
param_count };
PLUGIN_NAME_ID_LABEL("multiband_compressor", "multibandcompressor", "Multiband Compressor")
};
/// 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

View File

@@ -10,6 +10,7 @@
PER_MODULE_ITEM(phaser, false, "phaser")
PER_MODULE_ITEM(multichorus, false, "multichorus")
PER_MODULE_ITEM(compressor, false, "compressor")
PER_MODULE_ITEM(multibandcompressor, false, "multibandcompressor")
#ifdef ENABLE_EXPERIMENTAL
PER_MODULE_ITEM(fluidsynth, true, "fluidsynth")
PER_MODULE_ITEM(wavetable, true, "wavetable")

View File

@@ -66,7 +66,7 @@ class frequency_response_line_graph: public line_graph_iface
{
public:
bool get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
};
class flanger_audio_module: public audio_module<flanger_metadata>, public frequency_response_line_graph
@@ -754,7 +754,7 @@ public:
}
bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context);
int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
};
/// A multitap stereo chorus thing - processing
@@ -875,18 +875,18 @@ public:
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 generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
virtual int get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
{
subindex_graph = 0;
subindex_dot = 0;
subindex_gridline = generation ? INT_MAX : 0;
subindex_graph = 0;
subindex_dot = 0;
subindex_gridline = generation ? INT_MAX : 0;
if (fabs(threshold-old_threshold) + fabs(ratio - old_ratio) + fabs(knee - old_knee) + fabs( makeup - old_makeup) + fabs( *params[param_bypass] - old_bypass) > 0.01f)
{
old_threshold = threshold;
old_ratio = ratio;
old_knee = knee;
old_makeup = makeup;
old_threshold = threshold;
old_ratio = ratio;
old_knee = knee;
old_makeup = makeup;
old_bypass = *params[param_bypass];
last_generation++;
}
@@ -897,6 +897,61 @@ public:
}
};
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;
int last_generation;
uint32_t srate;
bool is_active;
inline float output_level(float slope);
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 activate();
void deactivate();
int id;
void set_sample_rate(uint32_t sr);
float get_output_level();
float get_comp_level();
virtual bool get_graph(int subindex, float *data, int points, cairo_iface *context);
virtual bool get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context);
virtual bool get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context);
virtual int get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline);
};
/// Multibandcompressor by Markus Schmidt
class multibandcompressor_audio_module: public audio_module<multibandcompressor_metadata>, public line_graph_iface {
private:
static const int strips = 4;
bool mute[strips];
uint32_t clip_inL, clip_inR, clip_outL, clip_outR;
float meter_inL, meter_inR, meter_outL, meter_outR;
gain_reduction_audio_module strip[strips];
dsp::biquad_d2<float> lpL0, lpR0, lpL1, lpR1, lpL2, lpR2, hpL0, hpR0, hpL1, hpR1, hpL2, hpR2;
float freq_old[strips - 1], sep_old[strips - 1], q_old[strips - 1];
public:
float *ins[in_count];
float *outs[out_count];
float *params[param_count];
uint32_t srate;
bool is_active;
multibandcompressor_audio_module();
void activate();
void deactivate();
void params_changed();
uint32_t process(uint32_t offset, uint32_t numsamples, uint32_t inputs_mask, uint32_t outputs_mask);
void set_sample_rate(uint32_t sr);
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);
};
/// Filterclavier --- MIDI controlled filter by Hans Baier
class filterclavier_audio_module:
public audio_module<filterclavier_metadata>,

View File

@@ -224,14 +224,14 @@ inline T clip(T value, T min, T max) {
inline double clip11(double value) {
double a = fabs(value);
if (a<=1) return value;
return (a<0) ? -1.0 : 1.0;
return (value<0) ? -1.0 : 1.0;
}
/// Clip a float to [-1.0f, +1.0f]
inline float clip11(float value) {
float a = fabsf(value);
if (a<=1) return value;
return (a<0) ? -1.0f : 1.0f;
return (value<0) ? -1.0f : 1.0f;
}
/// Clip a double to [0.0, +1.0]
@@ -245,7 +245,7 @@ inline double clip01(double value) {
inline float clip01(float value) {
float a = fabsf(value-0.5f);
if (a<=0.5f) return value;
return (a<0) ? -0.0f : 1.0f;
return (value < 0) ? -0.0f : 1.0f;
}
// Linear interpolation (mix-way between v1 and v2).

View File

@@ -234,6 +234,94 @@ CALF_PLUGIN_INFO(compressor) = { 0x8502, "Compressor", "Calf Compressor", "Thor
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(multibandcompressor) = {"In L", "In R", "Out L", "Out R"};
const char *multibandcompressor_detection_names[] = { "RMS", "Peak" };
CALF_PORT_PROPS(multibandcompressor) = {
{ 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" },
{ 1, 0, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_NOBOUNDS, NULL, "level_out", "Output" },
{ 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_inL", "Input L" },
{ 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_inR", "Input R" },
{ 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_outL", "Output L" },
{ 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_outR", "Output R" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inL", "0dB" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_inR", "0dB" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outL", "0dB" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_LED | PF_PROP_OUTPUT | PF_PROP_OPTIONAL, NULL, "clip_outR", "0dB" },
{ 100, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq0", "Split 1/2" },
{ 1000, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq1", "Split 2/3" },
{ 6000, 10, 20000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_HZ | PF_PROP_GRAPH, NULL, "freq2", "Split 3/4" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep0", "S" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep1", "S" },
{ -0.17, -0.5, 0.5, 0, PF_FLOAT | PF_SCALE_LINEAR | PF_CTL_KNOB | PF_UNIT_COEF | PF_PROP_GRAPH, NULL, "sep2", "S" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q0", "Q" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q1", "Q" },
{ 0.895025, 0.25, 4, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB | PF_PROP_GRAPH, NULL, "q2", "Q" },
{ 0.0625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold0", "Threshold" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio0", "Ratio" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack0", "Attack" },
{ 100, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release0", "Release" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup0", "Makeup" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee0", "Knee" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection0", "Detection" },
{ 1, 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, "compression0", "Gain Reduction" },
{ 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, "output0", "Output" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass0", "Bypass" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute0", "Mute" },
{ 0.03125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold1", "Threshold" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio1", "Ratio" },
{ 25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack1", "Attack" },
{ 50, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release1", "Release" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup1", "Makeup" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee1", "Knee" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection1", "Detection" },
{ 1, 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, "compression1", "Gain Reduction" },
{ 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, "output1", "Output" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass1", "Bypass" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute1", "Mute" },
{ 0.015625, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold2", "Threshold" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio2", "Ratio" },
{ 12.5, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack2", "Attack" },
{ 25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release2", "Release" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup2", "Makeup" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee2", "Knee" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection2", "Detection" },
{ 1, 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, "compression2", "Gain Reduction" },
{ 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, "output2", "Output" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass2", "Bypass" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute2", "Mute" },
{ 0.0078125, 0.000976563, 1, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "threshold3", "Threshold" },
{ 3, 1, 20, 21, PF_FLOAT | PF_SCALE_LOG_INF | PF_CTL_KNOB | PF_UNIT_COEF, NULL, "ratio3", "Ratio" },
{ 6.25, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "attack3", "Attack" },
{ 12.5, 0.01, 2000, 0, PF_FLOAT | PF_SCALE_LOG | PF_CTL_KNOB | PF_UNIT_MSEC, NULL, "release3", "Release" },
{ 2, 1, 64, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "makeup3", "Makeup" },
{ 2.828427125, 1, 8, 0, PF_FLOAT | PF_SCALE_GAIN | PF_CTL_KNOB | PF_UNIT_DB, NULL, "knee3", "Knee" },
{ 1, 0, 1, 0, PF_ENUM | PF_CTL_COMBO, multibandcompressor_detection_names, "detection3", "Detection" },
{ 1, 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, "compression3", "Gain Reduction" },
{ 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, "output3", "Output" },
{ 1, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "bypass3", "Bypass" },
{ 0, 0, 1, 0, PF_BOOL | PF_CTL_TOGGLE, NULL, "mute3", "Mute" },
};
CALF_PLUGIN_INFO(multibandcompressor) = { 0x8502, "Multibandcompressor", "Calf Multiband Compressor", "Markus Schmidt / Thor Harald Johansen", calf_plugins::calf_copyright_info, "CompressorPlugin" };
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
CALF_PORT_NAMES(monosynth) = {
"Out L", "Out R",
};

View File

@@ -58,9 +58,9 @@ static inline float dB_grid_inv(float pos)
static void set_channel_color(cairo_iface *context, int channel)
{
if (channel & 1)
context->set_source_rgba(0.75, 1, 0);
context->set_source_rgba(0.35, 0.4, 0.2, 1);
else
context->set_source_rgba(0, 1, 0.75);
context->set_source_rgba(0.35, 0.4, 0.2, 0.5);
context->set_line_width(1.5);
}
@@ -87,9 +87,9 @@ static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::str
freq = 10000 * (subindex - 27 + 1);
pos = log(freq / 20.0) / log(1000);
if (!legend.empty())
context->set_source_rgba(0.25, 0.25, 0.25, 0.75);
context->set_source_rgba(0, 0, 0, 0.2);
else
context->set_source_rgba(0.25, 0.25, 0.25, 0.5);
context->set_source_rgba(0, 0, 0, 0.1);
return true;
}
subindex -= 28;
@@ -101,7 +101,7 @@ static bool get_freq_gridline(int subindex, float &pos, bool &vertical, std::str
if (pos < -1)
return false;
if (subindex != 4)
context->set_source_rgba(0.25, 0.25, 0.25, subindex & 1 ? 0.5 : 0.75);
context->set_source_rgba(0, 0, 0, subindex & 1 ? 0.1 : 0.2);
if (!(subindex & 1))
{
std::stringstream ss;
@@ -119,7 +119,7 @@ bool frequency_response_line_graph::get_gridline(int index, int subindex, float
return get_freq_gridline(subindex, pos, vertical, legend, context);
}
int frequency_response_line_graph::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
int frequency_response_line_graph::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
{
subindex_graph = 0;
subindex_dot = 0;
@@ -242,7 +242,7 @@ bool filter_audio_module::get_graph(int index, int subindex, float *data, int po
return false;
}
int filter_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
int filter_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
{
if (fabs(inertia_cutoff.get_last() - old_cutoff) + 100 * fabs(inertia_resonance.get_last() - old_resonance) + fabs(*params[par_mode] - old_mode) > 0.1f)
{
@@ -359,7 +359,7 @@ bool multichorus_audio_module::get_graph(int index, int subindex, float *data, i
if (subindex < 2)
set_channel_color(context, subindex);
else {
context->set_source_rgba(0, 1, 0);
context->set_source_rgba(0.35, 0.4, 0.2);
context->set_line_width(1.0);
}
return ::get_graph(*this, subindex, data, points);
@@ -470,9 +470,9 @@ bool compressor_audio_module::get_graph(int index, int subindex, float *data, in
data[i] = dB_grid(output);
}
if (subindex == (*params[param_bypass] > 0.5f ? 1 : 0))
context->set_source_rgba(0.5, 0.5, 0.5, 0.5);
context->set_source_rgba(0.35, 0.4, 0.2, 0.3);
else {
context->set_source_rgba(0, 1, 0, 1);
context->set_source_rgba(0.35, 0.4, 0.2, 1);
context->set_line_width(2);
}
return true;
@@ -519,9 +519,12 @@ uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples,
bool bypass = *params[param_bypass] > 0.5f;
if(bypass) {
int count = numsamples * sizeof(float);
memcpy(outs[0], ins[0], count);
memcpy(outs[1], ins[1], count);
numsamples += offset;
while(offset < numsamples) {
outs[0][offset] = ins[0][offset];
outs[1][offset] = ins[1][offset];
++offset;
}
if(params[param_compression] != NULL) {
*params[param_compression] = 1.f;
@@ -636,3 +639,642 @@ uint32_t compressor_audio_module::process(uint32_t offset, uint32_t numsamples,
return inputs_mask;
}
/// Multibandcompressor by Markus Schmidt
///
/// This module splits the signal in four different bands
/// and sends them through multiple filters (implemented by
/// Krzysztof). They are processed by a compressing routine
/// (implemented by Thor) afterwards and summed up to the
/// final output again.
///////////////////////////////////////////////////////////////////////////////////////////////
multibandcompressor_audio_module::multibandcompressor_audio_module()
{
is_active = false;
srate = 0;
// zero all displays
clip_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
}
void multibandcompressor_audio_module::activate()
{
is_active = true;
// set all filters and strips
params_changed();
// activate all strips
for (int j = 0; j < strips; j ++) {
strip[j].activate();
strip[j].id = j;
}
}
void multibandcompressor_audio_module::deactivate()
{
is_active = false;
// deactivate all strips
for (int j = 0; j < strips; j ++) {
strip[j].deactivate();
}
}
void multibandcompressor_audio_module::params_changed()
{
// set the params of all filters
if(*params[param_freq0] != freq_old[0] or *params[param_sep0] != sep_old[0] or *params[param_q0] != q_old[0]) {
lpL0.set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
lpR0.set_lp_rbj((float)(*params[param_freq0] * (1 - *params[param_sep0])), *params[param_q0], (float)srate);
hpL0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
hpR0.set_hp_rbj((float)(*params[param_freq0] * (1 + *params[param_sep0])), *params[param_q0], (float)srate);
freq_old[0] = *params[param_freq0];
sep_old[0] = *params[param_sep2];
q_old[0] = *params[param_q2];
}
if(*params[param_freq1] != freq_old[1] or *params[param_sep1] != sep_old[1] or *params[param_q1] != q_old[1]) {
lpL1.set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
lpR1.set_lp_rbj((float)(*params[param_freq1] * (1 - *params[param_sep1])), *params[param_q1], (float)srate);
hpL1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
hpR1.set_hp_rbj((float)(*params[param_freq1] * (1 + *params[param_sep1])), *params[param_q1], (float)srate);
freq_old[1] = *params[param_freq1];
sep_old[1] = *params[param_sep2];
q_old[1] = *params[param_q2];
}
if(*params[param_freq2] != freq_old[2] or *params[param_sep2] != sep_old[2] or *params[param_q2] != q_old[2]) {
lpL2.set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
lpR2.set_lp_rbj((float)(*params[param_freq2] * (1 - *params[param_sep2])), *params[param_q2], (float)srate);
hpL2.set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
hpR2.set_hp_rbj((float)(*params[param_freq2] * (1 + *params[param_sep2])), *params[param_q2], (float)srate);
freq_old[2] = *params[param_freq2];
sep_old[2] = *params[param_sep2];
q_old[2] = *params[param_q2];
}
// set the params of all strips
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]);
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]);
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]);
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]);
break;
}
}
}
void multibandcompressor_audio_module::set_sample_rate(uint32_t sr)
{
srate = sr;
// set srate of all strips
for (int j = 0; j < strips; j ++) {
strip[j].set_sample_rate(srate);
}
}
uint32_t multibandcompressor_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_inL = 0.f;
clip_inR = 0.f;
clip_outL = 0.f;
clip_outR = 0.f;
meter_inL = 0.f;
meter_inR = 0.f;
meter_outL = 0.f;
meter_outR = 0.f;
} else {
// process all strips
// determine mute state of strips
mute[0] = *params[param_mute0] > 0.f ? true : false;
mute[1] = *params[param_mute1] > 0.f ? true : false;
mute[2] = *params[param_mute2] > 0.f ? true : false;
mute[3] = *params[param_mute3] > 0.f ? true : false;
// let meters fall a bit
clip_inL -= std::min(clip_inL, numsamples);
clip_inR -= std::min(clip_inR, numsamples);
clip_outL -= std::min(clip_outL, numsamples);
clip_outR -= std::min(clip_outR, numsamples);
meter_inL -= meter_inL * 2.5 * numsamples / srate;
meter_inR -= meter_inR * 2.5 * numsamples / srate;
meter_outL -= meter_outL * 2.5 * numsamples / srate;
meter_outR -= meter_outR * 2.5 * numsamples / srate;
while(offset < numsamples) {
// cycle through samples
float inL = ins[0][offset];
float inR = ins[1][offset];
// in level
inR *= *params[param_level_in];
inL *= *params[param_level_in];
// out vars
float outL = 0.f;
float outR = 0.f;
for (int i = 0; i < strips; i ++) {
// cycle trough strips
if (!mute[i]) {
// strip unmuted
float left = inL;
float right = inR;
// send trough filters
switch (i) {
case 0:
left = lpL0.process(left);
right = lpR0.process(right);
lpL0.sanitize();
lpR0.sanitize();
break;
case 1:
left = lpL1.process(left);
right = lpR1.process(right);
left = hpL0.process(left);
right = hpR0.process(right);
lpL1.sanitize();
lpR1.sanitize();
hpL0.sanitize();
hpR0.sanitize();
break;
case 2:
left = lpL2.process(left);
right = lpR2.process(right);
left = hpL1.process(left);
right = hpR1.process(right);
lpL2.sanitize();
lpR2.sanitize();
hpL1.sanitize();
hpR1.sanitize();
break;
case 3:
left = hpL2.process(left);
right = hpR2.process(right);
hpL2.sanitize();
hpR2.sanitize();
break;
}
// process gain reduction
strip[i].process(left, right);
// sum up output
outL += left;
outR += right;
} else {
// strip muted
}
} // process single strip
// even out filters gain reduction
// 3dB - levelled manually (based on default sep and q settings)
outL *= 1.414213562;
outR *= 1.414213562;
// out level
outL *= *params[param_level_out];
outR *= *params[param_level_out];
// send to output
outs[0][offset] = outL;
outs[1][offset] = outR;
// clip LED's
if(inL > 1.f) {
clip_inL = srate >> 3;
}
if(inR > 1.f) {
clip_inR = srate >> 3;
}
if(outL > 1.f) {
clip_outL = srate >> 3;
}
if(outR > 1.f) {
clip_outR = srate >> 3;
}
// rise up in / out meters
if(inL > meter_inL) {
meter_inL = inL;
}
if(inR > meter_inR) {
meter_inR = inR;
}
if(outL > meter_outL) {
meter_outL = outL;
}
if(outR > meter_outR) {
meter_outR = outR;
}
// next sample
++offset;
} // cycle trough samples
} // process all strips (no bypass)
// draw meters
if(params[param_clip_inL] != NULL) {
*params[param_clip_inL] = clip_inL;
}
if(params[param_clip_inR] != NULL) {
*params[param_clip_inR] = clip_inR;
}
if(params[param_clip_outL] != NULL) {
*params[param_clip_outL] = clip_outL;
}
if(params[param_clip_outR] != NULL) {
*params[param_clip_outR] = clip_outR;
}
if(params[param_meter_inL] != NULL) {
*params[param_meter_inL] = meter_inL;
}
if(params[param_meter_inR] != NULL) {
*params[param_meter_inR] = meter_inR;
}
if(params[param_meter_outL] != NULL) {
*params[param_meter_outL] = meter_outL;
}
if(params[param_meter_outR] != NULL) {
*params[param_meter_outR] = meter_outR;
}
// draw strip meters
if(bypass > 0.5f) {
if(params[param_compression0] != NULL) {
*params[param_compression0] = 1.0f;
}
if(params[param_compression1] != NULL) {
*params[param_compression1] = 1.0f;
}
if(params[param_compression2] != NULL) {
*params[param_compression2] = 1.0f;
}
if(params[param_compression3] != NULL) {
*params[param_compression3] = 1.0f;
}
if(params[param_output0] != NULL) {
*params[param_output0] = 0.0f;
}
if(params[param_output1] != NULL) {
*params[param_output1] = 0.0f;
}
if(params[param_output2] != NULL) {
*params[param_output2] = 0.0f;
}
if(params[param_output3] != NULL) {
*params[param_output3] = 0.0f;
}
} else {
if(params[param_compression0] != NULL) {
*params[param_compression0] = strip[0].get_comp_level();
}
if(params[param_compression1] != NULL) {
*params[param_compression1] = strip[1].get_comp_level();
}
if(params[param_compression2] != NULL) {
*params[param_compression2] = strip[2].get_comp_level();
}
if(params[param_compression3] != NULL) {
*params[param_compression3] = strip[3].get_comp_level();
}
if(params[param_output0] != NULL) {
*params[param_output0] = strip[0].get_output_level();
}
if(params[param_output1] != NULL) {
*params[param_output1] = strip[1].get_output_level();
}
if(params[param_output2] != NULL) {
*params[param_output2] = strip[2].get_output_level();
}
if(params[param_output3] != NULL) {
*params[param_output3] = strip[3].get_output_level();
}
}
// whatever has to be returned x)
return outputs_mask;
}
bool multibandcompressor_audio_module::get_graph(int index, int subindex, float *data, int points, cairo_iface *context)
{
// let's handle by the corresponding strip
switch (index) {
case param_compression0:
return strip[0].get_graph(subindex, data, points, context);
break;
case param_compression1:
return strip[1].get_graph(subindex, data, points, context);
break;
case param_compression2:
return strip[2].get_graph(subindex, data, points, context);
break;
case param_compression3:
return strip[3].get_graph(subindex, data, points, context);
break;
}
return false;
}
bool multibandcompressor_audio_module::get_dot(int index, int subindex, float &x, float &y, int &size, cairo_iface *context)
{
// let's handle by the corresponding strip
switch (index) {
case param_compression0:
return strip[0].get_dot(subindex, x, y, size, context);
break;
case param_compression1:
return strip[1].get_dot(subindex, x, y, size, context);
break;
case param_compression2:
return strip[2].get_dot(subindex, x, y, size, context);
break;
case param_compression3:
return strip[3].get_dot(subindex, x, y, size, context);
break;
}
return false;
}
bool multibandcompressor_audio_module::get_gridline(int index, int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
{
// let's handle by the corresponding strip
switch (index) {
case param_compression0:
return strip[0].get_gridline(subindex, pos, vertical, legend, context);
break;
case param_compression1:
return strip[1].get_gridline(subindex, pos, vertical, legend, context);
break;
case param_compression2:
return strip[2].get_gridline(subindex, pos, vertical, legend, context);
break;
case param_compression3:
return strip[3].get_gridline(subindex, pos, vertical, legend, context);
break;
}
return false;
}
int multibandcompressor_audio_module::get_changed_offsets(int index, int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
{
// let's handle by the corresponding strip
switch (index) {
case param_compression0:
return strip[0].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
break;
case param_compression1:
return strip[1].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
break;
case param_compression2:
return strip[2].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
break;
case param_compression3:
return strip[3].get_changed_offsets(generation, subindex_graph, subindex_dot, subindex_gridline);
break;
}
return 0;
}
/// 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
/// and frequency correction as implemented in his Compressor above)
/// To save some CPU.
////////////////////////////////////////////////////////////////////////////////
gain_reduction_audio_module::gain_reduction_audio_module()
{
is_active = false;
last_generation = 0;
}
void gain_reduction_audio_module::activate()
{
is_active = true;
linSlope = 0.f;
meter_out = 0.f;
meter_comp = 1.f;
float l, r;
l = r = 0.f;
float byp = bypass;
bypass = 0.0;
process(l, r);
bypass = byp;
}
void gain_reduction_audio_module::deactivate()
{
is_active = false;
}
void gain_reduction_audio_module::process(float &left, float &right)
{
float compression = 1.f;
meter_out -= meter_out * 5.f * 1 / srate;
if(bypass < 0.5f) {
// this routine is mainly copied from thor's compressor module
// greatest sounding compressor I've heard!
bool rms = detection == 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));
float linKneeSqrt = sqrt(knee);
linKneeStart = linThreshold / linKneeSqrt;
adjKneeStart = linKneeStart*linKneeStart;
float linKneeStop = linThreshold * linKneeSqrt;
thres = log(linThreshold);
kneeStart = log(linKneeStart);
kneeStop = log(linKneeStop);
compressedKneeStop = (kneeStop - thres) / ratio + thres;
float absample = (fabs(left) + fabs(right)) * 0.5f;
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);
}
compression = gain;
gain *= makeup;
left *= gain;
right *= gain;
detected = rms ? sqrt(linSlope) : linSlope;
}
float maxLR = std::max(fabs(left), fabs(right));
if(maxLR > meter_out) {
meter_out = maxLR;
}
meter_comp = compression;
}
float gain_reduction_audio_module::output_level(float slope) {
return slope * output_gain(slope, false) * makeup;
}
float gain_reduction_audio_module::output_gain(float linSlope, bool rms) {
//this calculation is also thor's work
if(linSlope > (rms ? adjKneeStart : linKneeStart)) {
float slope = log(linSlope);
if(rms) slope *= 0.5f;
float gain = 0.f;
float delta = 0.f;
if(IS_FAKE_INFINITY(ratio)) {
gain = thres;
delta = 0.f;
} else {
gain = (slope - thres) / ratio + thres;
delta = 1.f / ratio;
}
if(knee > 1.f && slope < kneeStop) {
gain = hermite_interpolation(slope, kneeStart, kneeStop, kneeStart, compressedKneeStop, 1.f, delta);
}
return exp(gain - slope);
}
return 1.f;
}
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)
{
// set all params
attack = att;
release = rel;
threshold = thr;
ratio = rat;
knee = kn;
makeup = mak;
detection = det;
bypass = byp;
mute = mu;
if(mute > 0.f) {
meter_out = 0.f;
meter_comp = 1.f;
}
}
float gain_reduction_audio_module::get_output_level() {
// returns output level (max(left, right))
return meter_out;
}
float gain_reduction_audio_module::get_comp_level() {
// returns amount of compression
return meter_comp;
}
bool gain_reduction_audio_module::get_graph(int subindex, float *data, int points, cairo_iface *context)
{
if (!is_active)
return false;
if (subindex > 1) // 1
return false;
for (int i = 0; i < points; i++)
{
float input = dB_grid_inv(-1.0 + i * 2.0 / (points - 1));
if (subindex == 0)
data[i] = dB_grid(input);
else {
float output = output_level(input);
data[i] = dB_grid(output);
}
}
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);
context->set_line_width(1.5);
}
return true;
}
bool gain_reduction_audio_module::get_dot(int subindex, float &x, float &y, int &size, cairo_iface *context)
{
if (!is_active)
return false;
if (!subindex)
{
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 gain_reduction_audio_module::get_gridline(int subindex, float &pos, bool &vertical, std::string &legend, cairo_iface *context)
{
bool tmp;
vertical = (subindex & 1) != 0;
bool result = get_freq_gridline(subindex >> 1, pos, tmp, legend, context, false);
if (result && vertical) {
if ((subindex & 4) && !legend.empty()) {
legend = "";
}
else {
size_t pos = legend.find(" dB");
if (pos != std::string::npos)
legend.erase(pos);
}
pos = 0.5 + 0.5 * pos;
}
return result;
}
int gain_reduction_audio_module::get_changed_offsets(int generation, int &subindex_graph, int &subindex_dot, int &subindex_gridline)
{
subindex_graph = 0;
subindex_dot = 0;
subindex_gridline = generation ? INT_MAX : 0;
if (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_threshold = threshold;
old_ratio = ratio;
old_knee = knee;
old_makeup = makeup;
old_detection = detection;
old_bypass = bypass;
old_mute = mute;
last_generation++;
}
if (generation == last_generation)
subindex_graph = 2;
return last_generation;
}

View File

@@ -626,7 +626,7 @@ void monosynth_audio_module::set_frequency()
{
float detune_scaled = (detune - 1); // * log(freq / 440);
if (*params[par_scaledetune] > 0)
detune_scaled *= pow(20.0 / freq, *params[par_scaledetune]);
detune_scaled *= pow(20.0 / freq, (double)*params[par_scaledetune]);
float p1 = 1, p2 = 1;
if (moddest[moddest_o1detune] != 0)
p1 = pow(2.0, moddest[moddest_o1detune] * (1.0 / 1200.0));