Prepare export-format codec-quality setting
[ardour.git] / libs / ardour / parameter_descriptor.cc
index 7ed0c785126d6a9808182a37aa3123e1aaaa48ae..4f26af997d843087dca66a5c4d4ea6f5a7620ee6 100644 (file)
@@ -17,6 +17,7 @@
     675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <algorithm>
 #include <boost/algorithm/string.hpp>
 
 #include "pbd/control_math.h"
@@ -100,20 +101,29 @@ 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<ScalePoints>(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<ScalePoints>(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:
        case SoloSafeAutomation:
@@ -143,6 +153,43 @@ ParameterDescriptor::ParameterDescriptor()
 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
@@ -153,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);
                }
        }
 }
@@ -269,7 +322,7 @@ ParameterDescriptor::to_interface (float val) const
                        break;
                case PanAzimuthAutomation:
                case PanElevationAutomation:
-                       val = 1.f - val;
+                       val = val;
                        break;
                case PanWidthAutomation:
                        val = .5f + val * .5f;
@@ -320,7 +373,7 @@ ParameterDescriptor::from_interface (float val) const
                        break;
                case PanAzimuthAutomation:
                case PanElevationAutomation:
-                        val = 1.f - val;
+                        val = val;
                        break;
                case PanWidthAutomation:
                        val = 2.f * val - 1.f;
@@ -340,10 +393,10 @@ ParameterDescriptor::from_interface (float val) const
                                 * 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 =  round (lower + val * (1.f + upper - lower) - .5f);
+                               val = floor (lower + val * (1.f + upper - lower));
                        } else if (rangesteps > 1) {
                                /* similar to above, but for float controls */
-                               val = floor (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
+                               val = round (val * (rangesteps - 1.f)) / (rangesteps - 1.f); // XXX
                                val = val * (upper - lower) + lower;
                        } else {
                                val = val * (upper - lower) + lower;
@@ -354,4 +407,80 @@ ParameterDescriptor::from_interface (float val) const
        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<float> 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<float>::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