add clamp for adding points to a ControlList from a (G)UI
[ardour.git] / libs / evoral / src / ControlList.cpp
index 0755bd27d229bc26520af40de60439c330a1df53..0b2184a97280f3181b8a754b21c1e017730a546c 100644 (file)
@@ -36,6 +36,7 @@
 #include "evoral/Curve.hpp"
 #include "evoral/ParameterDescriptor.hpp"
 #include "evoral/TypeMap.hpp"
+#include "evoral/types.hpp"
 
 #include "pbd/compose.h"
 #include "pbd/debug.h"
@@ -138,6 +139,7 @@ ControlList::~ControlList()
        for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
                delete (*x);
        }
+       _events.clear ();
 
        delete _curve;
 }
@@ -165,7 +167,7 @@ ControlList::operator= (const ControlList& other)
 
                _interpolation = other._interpolation;
                _default_value = other._default_value;
-               
+
                copy_events (other);
        }
 
@@ -177,6 +179,9 @@ ControlList::copy_events (const ControlList& other)
 {
        {
                Glib::Threads::RWLock::WriterLock lm (_lock);
+               for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
+                       delete (*x);
+               }
                _events.clear ();
                for (const_iterator i = other.begin(); i != other.end(); ++i) {
                        _events.push_back (new ControlEvent ((*i)->when, (*i)->value));
@@ -215,6 +220,9 @@ ControlList::clear ()
 {
        {
                Glib::Threads::RWLock::WriterLock lm (_lock);
+               for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
+                       delete (*x);
+               }
                _events.clear ();
                unlocked_invalidate_insert_iterator ();
                mark_dirty ();
@@ -269,49 +277,49 @@ ControlList::thin (double thinning_factor)
 
        {
                Glib::Threads::RWLock::WriterLock lm (_lock);
-               
+
                ControlEvent* prevprev = 0;
                ControlEvent* cur = 0;
                ControlEvent* prev = 0;
                iterator pprev;
                int counter = 0;
-               
+
                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 thin from %2 events\n", this, _events.size()));
-               
+
                for (iterator i = _events.begin(); i != _events.end(); ++i) {
-                       
+
                        cur = *i;
                        counter++;
-                       
+
                        if (counter > 2) {
-                               
+
                                /* compute the area of the triangle formed by 3 points
                                 */
-                               
+
                                double area = fabs ((prevprev->when * (prev->value - cur->value)) +
                                                    (prev->when * (cur->value - prevprev->value)) +
                                                    (cur->when * (prevprev->value - prev->value)));
-                               
+
                                if (area < thinning_factor) {
                                        iterator tmp = pprev;
-                                       
+
                                        /* pprev will change to current
                                           i is incremented to the next event
                                           as we loop.
                                        */
-                                       
+
                                        pprev = i;
                                        _events.erase (tmp);
                                        changed = true;
                                        continue;
                                }
                        }
-                       
+
                        prevprev = prev;
                        prev = cur;
                        pprev = i;
                }
-               
+
                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 thin => %2 events\n", this, _events.size()));
 
                if (changed) {
@@ -358,12 +366,12 @@ ControlList::start_write_pass (double when)
        new_write_pass = true;
        did_write_during_pass = false;
        insert_position = when;
-       
+
        /* leave the insert iterator invalid, so that we will do the lookup
           of where it should be in a "lazy" way - deferring it until
           we actually add the first point (which may never happen).
        */
-       
+
        unlocked_invalidate_insert_iterator ();
 }
 
@@ -382,9 +390,9 @@ ControlList::write_pass_finished (double /*when*/, double thinning_factor)
 
 void
 ControlList::set_in_write_pass (bool yn, bool add_point, double when)
-{      
+{
        DEBUG_TRACE (DEBUG::ControlList, string_compose ("now in write pass @ %1, add point ? %2\n", when, add_point));
-       
+
        _in_write_pass = yn;
 
        if (yn && add_point) {
@@ -401,19 +409,19 @@ ControlList::add_guard_point (double when)
        double eval_value = unlocked_eval (insert_position);
 
        if (most_recent_insert_iterator == _events.end()) {
-               
+
                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert iterator at end, adding eval-value there %2\n", this, eval_value));
                _events.push_back (new ControlEvent (when, eval_value));
                /* leave insert iterator at the end */
-               
+
        } else if ((*most_recent_insert_iterator)->when == when) {
 
                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert iterator at existing point, setting eval-value there %2\n", this, eval_value));
-               
+
                /* most_recent_insert_iterator points to a control event
                   already at the insert position, so there is
                   nothing to do.
-               
+
                   ... except ...
 
                   advance most_recent_insert_iterator so that the "real"
@@ -426,22 +434,22 @@ ControlList::add_guard_point (double when)
 
                /* insert a new control event at the right spot
                 */
-               
+
                DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 insert eval-value %2 just before iterator @ %3\n",
                                                                 this, eval_value, (*most_recent_insert_iterator)->when));
-               
+
                most_recent_insert_iterator = _events.insert (most_recent_insert_iterator, new ControlEvent (when, eval_value));
 
                /* advance most_recent_insert_iterator so that the "real"
                 * insert occurs in the right place, since it
                 * points to the control event just inserted.
                 */
-               
+
                ++most_recent_insert_iterator;
        }
-       
+
        /* don't do this again till the next write pass */
-       
+
        new_write_pass = false;
 }
 
@@ -451,14 +459,21 @@ ControlList::in_write_pass () const
        return _in_write_pass;
 }
 
-void
+bool
 ControlList::editor_add (double when, double value, bool with_guard)
 {
        /* this is for making changes from a graphical line editor
        */
 
+       ControlEvent cp (when, 0.0f);
+       iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+       if (i != _events.end () && (*i)->when == when) {
+               return false;
+       }
+
        if (_events.empty()) {
-               
+
                /* as long as the point we're adding is not at zero,
                 * add an "anchor" point there.
                 */
@@ -477,15 +492,23 @@ ControlList::editor_add (double when, double value, bool with_guard)
                maybe_add_insert_guard (when);
        }
 
-       ControlEvent cp (when, 0.0f);
-       iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+       /* clamp new value to allowed range */
+
+       value = max (_min_yval, value);
+       value = min (_max_yval, value);
+
+       iterator result;
        DEBUG_TRACE (DEBUG::ControlList, string_compose ("editor_add: actually add when= %1 value= %2\n", when, value));
-       _events.insert (i, new ControlEvent (when, value));
+       result = _events.insert (i, new ControlEvent (when, value));
 
-       mark_dirty ();
+       if (i == result) {
+               return false;
+       }
 
+       mark_dirty ();
        maybe_signal_changed ();
 
+       return true;
 }
 
 void
@@ -570,12 +593,19 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial
                iterator insertion_point;
 
                if (_events.empty() && with_initial) {
-                       
+
                        /* empty: add an "anchor" point if the point we're adding past time 0 */
 
                        if (when >= 1) {
-                               _events.insert (_events.end(), new ControlEvent (0, value));
-                               DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _default_value));
+                               if (_desc.toggled) {
+                                       const double opp_val = ((value < 0.5) ? 1.0 : 0.0);
+                                       _events.insert (_events.end(), new ControlEvent (0, opp_val));
+                                       DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added toggled value %2 at zero\n", this, opp_val));
+
+                               } else {
+                                       _events.insert (_events.end(), new ControlEvent (0, value));
+                                       DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _default_value));
+                               }
                        }
                }
 
@@ -609,7 +639,7 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial
                        }
 
                } else if (!_in_write_pass) {
-                               
+
                        /* not in a write pass: figure out the iterator we should insert in front of */
 
                        DEBUG_TRACE (DEBUG::ControlList, string_compose ("compute(b) MRI for position %1\n", when));
@@ -621,7 +651,7 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial
 
                if (most_recent_insert_iterator == _events.end()) {
                        DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 appending new point at end\n", this));
-                       
+
                        const bool done = maybe_insert_straight_line (when, value);
                        if (!done) {
                                _events.push_back (new ControlEvent (when, value));
@@ -1032,7 +1062,7 @@ ControlList::truncate_end (double last_coordinate)
                        _events.back()->when = last_coordinate;
                        _events.back()->value = last_val;
                }
-               
+
                unlocked_invalidate_insert_iterator ();
                mark_dirty();
        }
@@ -1180,7 +1210,7 @@ ControlList::unlocked_eval (double x) const
                        return lval;
                }
 
-               /* linear interpolation betweeen the two points */
+               /* linear interpolation between the two points */
                fraction = (double) (x - lpos) / (double) (upos - lpos);
                return lval + (fraction * (uval - lval));
 
@@ -1642,10 +1672,19 @@ ControlList::paste (const ControlList& alist, double pos, float /*times*/)
                        if (alist.parameter() != parameter()) {
                                const ParameterDescriptor& src_desc = alist.descriptor();
 
+                               // This does not work for logscale and will probably also not do
+                               // the right thing for integer_step and sr_dependent parameters.
+                               //
+                               // TODO various flags from from ARDOUR::ParameterDescriptor
+                               // to Evoral::ParameterDescriptor
+
                                value -= src_desc.lower;  // translate to 0-relative
                                value /= (src_desc.upper - src_desc.lower);  // normalize range
                                value *= (_desc.upper - _desc.lower);  // scale to our range
                                value += _desc.lower;  // translate to our offset
+                               if (_desc.toggled) {
+                                       value = (value < 0.5) ? 0.0 : 1.0;
+                               }
                        }
                        _events.insert (where, new ControlEvent((*i)->when + pos, value));
                        end = (*i)->when + pos;
@@ -1767,7 +1806,7 @@ ControlList::operator!= (ControlList const & other) const
        if (i != _events.end ()) {
                return true;
        }
-       
+
        return (
                _parameter != other._parameter ||
                _interpolation != other._interpolation ||