X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fparameter_descriptor.cc;h=4f26af997d843087dca66a5c4d4ea6f5a7620ee6;hb=84272b4e27e537bf2c38c9cd25675c61addea40a;hp=f6460a514efe96762f908016f32d7640a3b7f312;hpb=567c0a8f5321f70f13e8e0c4c78725ce4521608b;p=ardour.git diff --git a/libs/ardour/parameter_descriptor.cc b/libs/ardour/parameter_descriptor.cc index f6460a514e..4f26af997d 100644 --- a/libs/ardour/parameter_descriptor.cc +++ b/libs/ardour/parameter_descriptor.cc @@ -17,6 +17,7 @@ 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include "pbd/control_math.h" @@ -43,21 +44,17 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) , largestep(0) , integer_step(parameter.type() >= MidiCCAutomation && parameter.type() <= MidiChannelPressureAutomation) - , logarithmic(false) , sr_dependent(false) - , min_unbound(0) - , max_unbound(0) , enumeration(false) { ScalePoints sp; + /* Note: defaults in Evoral::ParameterDescriptor */ + switch((AutomationType)parameter.type()) { case GainAutomation: - upper = Config->get_max_gain(); - normal = 1.0f; - break; case BusSendLevel: - upper = Config->get_max_gain (); + upper = Config->get_max_gain(); normal = 1.0f; break; case BusSendEnable: @@ -68,6 +65,7 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) upper = 10; // +20dB lower = .1; // -20dB normal = 1.0f; + logarithmic = true; break; case PanAzimuthAutomation: normal = 0.5f; // there really is no _normal but this works for stereo, sort of @@ -84,7 +82,6 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) upper = 1.0; toggled = true; break; - case PluginAutomation: case FadeInAutomation: case FadeOutAutomation: case EnvelopeAutomation: @@ -104,24 +101,31 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) lower = 0.0; normal = 0.0; upper = 127.0; + print_fmt = "%.0f"; break; case MidiPitchBenderAutomation: lower = 0.0; normal = 8192.0; upper = 16383.0; + print_fmt = "%.0f"; break; case PhaseAutomation: toggled = true; + scale_points = boost::shared_ptr(new ScalePoints()); + scale_points->insert (std::make_pair (_("Normal"), 0)); + scale_points->insert (std::make_pair (_("Invert"), 1)); break; case MonitoringAutomation: enumeration = true; integer_step = true; lower = MonitorAuto; upper = MonitorDisk; /* XXX bump when we add MonitorCue */ + scale_points = boost::shared_ptr(new ScalePoints()); + scale_points->insert (std::make_pair (_("Auto"), MonitorAuto)); + scale_points->insert (std::make_pair (_("Input"), MonitorInput)); + scale_points->insert (std::make_pair (_("Disk"), MonitorDisk)); break; case SoloIsolateAutomation: - toggled = true; - break; case SoloSafeAutomation: toggled = true; break; @@ -142,16 +146,50 @@ ParameterDescriptor::ParameterDescriptor() , smallstep(0) , largestep(0) , integer_step(false) - , logarithmic(false) , sr_dependent(false) - , min_unbound(0) - , max_unbound(0) , enumeration(false) {} void ParameterDescriptor::update_steps() { + /* sanitize flags */ + if (toggled || enumeration) { + logarithmic = false; + } + if (logarithmic && sr_dependent && upper > lower && lower == 0) { + /* work-around for plugins with a log-scale control 0..SR; log (0) is not defined */ + lower = upper / 1000.f; + } + if (logarithmic && (upper <= lower || lower * upper <= 0)) { + /* log-scale params need upper > lower and both values need the same sign */ + logarithmic = false; + } + if (rangesteps < 2) { + rangesteps = 0; + } + if (enumeration) { + /* enums need scale-points. + * The GUI is more restrictive, a dropdown is displayed + * IIF scale_points.size() == (1 + upper - lower) + */ + if (!scale_points || scale_points->empty ()) { + enumeration = false; + } + } + if (integer_step) { + if (lower >= upper) { + integer_step = false; + } + } + + /* upper == lower does not make any sense */ + if (lower == upper) { + upper = lower + 0.01; // add some arbitrary value + } + + /* set steps */ + if (unit == ParameterDescriptor::MIDI_NOTE) { step = smallstep = 1; // semitone largestep = 12; // octave @@ -162,33 +200,39 @@ ParameterDescriptor::update_steps() largestep = position_to_gain (dB_coeff_step(upper)); step = position_to_gain (largestep / 10.0); smallstep = step; - } else { - /* note that LV2Plugin::get_parameter_descriptor () - * overrides this is lv2:rangeStep is set for a port. + } else if (logarithmic) { + /* ignore logscale rangesteps. {small|large}steps are used with the spinbox. + * gtk-spinbox shows the internal (not interface) value and up/down + * arrows linearly increase. + * The AutomationController uses internal_to_interface(): + * ui-step [0..1] -> log (1 + largestep / lower) / log (upper / lower) + * so we use a step that's a multiple of "lower" for the interface step: + * log (1 + x) / log (upper / lower) + */ + smallstep = step = lower / 11; + largestep = lower / 3; + /* NOTE: the actual value does use rangesteps via + * logscale_to_position_with_steps(), position_to_logscale_with_steps() + * when it is converted. */ + } else if (rangesteps > 1) { const float delta = upper - lower; - - /* 30 happens to be the total number of steps for a fader with default - max gain of 2.0 (6 dB), so we use 30 here too for consistency. */ - step = smallstep = (delta / 300.0f); - largestep = (delta / 30.0f); - - if (logarithmic) { - /* Steps are linear, but we map them with pow like values (in - internal_to_interface). Thus, they are applied exponentially, - which means too few steps. So, divide to get roughly the - desired number of steps (30). This is not mathematically - precise but seems to be about right for the controls I tried. - If you're reading this, you've probably found a case where that - isn't true, and somebody needs to sit down with a piece of paper - and actually do the math. */ - smallstep = smallstep / logf(30.0f); - step = step / logf(30.0f); - largestep = largestep / logf(30.0f); - } else if (integer_step) { - smallstep = 1.0; - step = std::max(1.f, rintf (step)); - largestep = std::max(1.f, rintf (largestep)); + if (integer_step) { + smallstep = step = 1.0; + largestep = std::max(1.f, rintf (delta / (rangesteps - 1.f))); + } else { + step = smallstep = delta / (rangesteps - 1.f); + largestep = std::min ((delta / 4.0f), 10.f * smallstep); + } + } else { + const float delta = upper - lower; + /* 30 steps between min/max (300 for fine-grained) */ + if (integer_step) { + smallstep = step = 1.0; + largestep = std::max(1.f, rintf (delta / 30.f)); + } else { + step = smallstep = (delta / 300.0f); + largestep = (delta / 30.0f); } } } @@ -259,4 +303,184 @@ ParameterDescriptor::midi_note_num (const std::string& name) return num; } +float +ParameterDescriptor::to_interface (float val) const +{ + val = std::min (upper, std::max (lower, val)); + switch(type) { + case GainAutomation: + case BusSendLevel: + case EnvelopeAutomation: + val = gain_to_slider_position_with_max (val, upper); + break; + case TrimAutomation: + { + const float lower_db = accurate_coefficient_to_dB (lower); + const float range_db = accurate_coefficient_to_dB (upper) - lower_db; + val = (accurate_coefficient_to_dB (val) - lower_db) / range_db; + } + break; + case PanAzimuthAutomation: + case PanElevationAutomation: + val = val; + break; + case PanWidthAutomation: + val = .5f + val * .5f; + break; + default: + if (logarithmic) { + if (rangesteps > 1) { + val = logscale_to_position_with_steps (val, lower, upper, rangesteps); + } else { + val = logscale_to_position (val, lower, upper); + } + } else if (toggled) { + return (val - lower) / (upper - lower) >= 0.5f ? 1.f : 0.f; + } else if (integer_step) { + /* evenly-divide steps. lower,upper inclusive + * e.g. 5 integers 0,1,2,3,4 are mapped to a fader + * [0.0 ... 0.2 | 0.2 ... 0.4 | 0.4 ... 0.6 | 0.6 ... 0.8 | 0.8 ... 1.0] + * 0 1 2 3 4 + * 0.1 0.3 0.5 0.7 0.9 + */ + val = (val + .5f - lower) / (1.f + upper - lower); + } else { + val = (val - lower) / (upper - lower); + } + break; + } + val = std::max (0.f, std::min (1.f, val)); + return val; +} + +float +ParameterDescriptor::from_interface (float val) const +{ + val = std::max (0.f, std::min (1.f, val)); + + switch(type) { + case GainAutomation: + case EnvelopeAutomation: + case BusSendLevel: + val = slider_position_to_gain_with_max (val, upper); + break; + case TrimAutomation: + { + const float lower_db = accurate_coefficient_to_dB (lower); + const float range_db = accurate_coefficient_to_dB (upper) - lower_db; + val = dB_to_coefficient (lower_db + val * range_db); + } + break; + case PanAzimuthAutomation: + case PanElevationAutomation: + val = val; + break; + case PanWidthAutomation: + val = 2.f * val - 1.f; + break; + default: + if (logarithmic) { + assert (!toggled && !integer_step); // update_steps() should prevent that. + if (rangesteps > 1) { + val = position_to_logscale_with_steps (val, lower, upper, rangesteps); + } else { + val = position_to_logscale (val, lower, upper); + } + } else if (toggled) { + val = val > 0 ? upper : lower; + } else if (integer_step) { + /* upper and lower are inclusive. use evenly-divided steps + * e.g. 5 integers 0,1,2,3,4 are mapped to a fader + * [0.0 .. 0.2 | 0.2 .. 0.4 | 0.4 .. 0.6 | 0.6 .. 0.8 | 0.8 .. 1.0] + */ + val = floor (lower + val * (1.f + upper - lower)); + } else if (rangesteps > 1) { + /* similar to above, but for float controls */ + val = round (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX + val = val * (upper - lower) + lower; + } else { + val = val * (upper - lower) + lower; + } + break; + } + val = std::min (upper, std::max (lower, val)); + return val; +} + +bool +ParameterDescriptor::is_linear () const +{ + if (logarithmic) { + return false; + } + switch(type) { + case GainAutomation: + case EnvelopeAutomation: + case BusSendLevel: + return false; + default: + break; + } + return true; +} + +float +ParameterDescriptor::compute_delta (float from, float to) const +{ + if (is_linear ()) { + return to - from; + } + if (from == 0) { + return 0; + } + return to / from; +} + +float +ParameterDescriptor::apply_delta (float val, float delta) const +{ + if (is_linear ()) { + return val + delta; + } else { + return val * delta; + } +} + +float +ParameterDescriptor::step_enum (float val, bool prev) const +{ + if (!enumeration) { + return val; + } + assert (scale_points && !scale_points->empty ()); + float rv = scale_points->begin()->second; + float delta = fabsf (val - rv); + std::vector avail; + + for (ScalePoints::const_iterator i = scale_points->begin (); i != scale_points->end (); ++i) { + float s = i->second; + avail.push_back (s); + if (fabsf (val - s) < delta) { + rv = s; + delta = fabsf (val - s); + } + } + /* ScalePoints map is sorted by text string */ + std::sort (avail.begin (), avail.end ()); + std::vector::const_iterator it = std::find (avail.begin (), avail.end (), rv); + assert (it != avail.end()); + + if (prev) { + if (it == avail.begin()) { + return rv; + } + return *(--it); + } else { + if (++it == avail.end()) { + return rv; + } + return *(it); + } +} + } // namespace ARDOUR