Fix crash on iteration over an empty sequence and/or recording controllers only.
[ardour.git] / libs / evoral / src / ControlList.cpp
index 8f6ea1872fae6011f20ff669523b6424546e33d4..22517e68a06c81e500cf7b9cdb1881a2851e8bcb 100644 (file)
@@ -20,7 +20,7 @@
 #include <cassert>
 #include <utility>
 #include <iostream>
-#include <evoral/ControlList.hpp>
+#include "evoral/ControlList.hpp"
 
 using namespace std;
 
@@ -33,7 +33,7 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b)
 }
 
 
-ControlList::ControlList (Parameter id)
+ControlList::ControlList (const Parameter& id)
        : _parameter(id)
        , _interpolation(Linear)
        , _curve(new Curve(*this))
@@ -109,7 +109,6 @@ ControlList::~ControlList()
                delete (*x);
        }
 }
-       
 
 boost::shared_ptr<ControlList>
 ControlList::create(Parameter id)
@@ -117,7 +116,6 @@ ControlList::create(Parameter id)
        return boost::shared_ptr<ControlList>(new ControlList(id));
 }
 
-
 bool
 ControlList::operator== (const ControlList& other)
 {
@@ -205,7 +203,7 @@ ControlList::reposition_for_rt_add (double when)
 void
 ControlList::rt_add (double when, double value)
 {
-       // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
+       //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
 
        {
                Glib::Mutex::Lock lm (_lock);
@@ -392,16 +390,10 @@ ControlList::erase_range (double start, double endt)
 
        {
                Glib::Mutex::Lock lm (_lock);
-               ControlEvent cp (start, 0.0f);
-               iterator s;
-               iterator e;
+               erased = erase_range_internal (start, endt, _events);
 
-               if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
-                       cp.when = endt;
-                       e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
-                       _events.erase (s, e);
+               if (erased) {
                        reposition_for_rt_add (0);
-                       erased = true;
                        mark_dirty ();
                }
                
@@ -412,36 +404,22 @@ ControlList::erase_range (double start, double endt)
        }
 }
 
-void
-ControlList::move_range (iterator start, iterator end, double xdelta, double ydelta)
+bool
+ControlList::erase_range_internal (double start, double endt, EventList & events)
 {
-       /* note: we assume higher level logic is in place to avoid this
-          reordering the time-order of control events in the list. ie. all
-          points after end are later than (end)->when.
-       */
-
-       {
-               Glib::Mutex::Lock lm (_lock);
-
-               while (start != end) {
-                       (*start)->when += xdelta;
-                       (*start)->value += ydelta;
-                       if (isnan ((*start)->value)) {
-                               abort ();
-                       }
-                       ++start;
-               }
-
-               if (!_frozen) {
-                       _events.sort (event_time_less_than);
-               } else {
-                       _sort_pending = true;
-               }
-
-               mark_dirty ();
+       bool erased = false;
+       ControlEvent cp (start, 0.0f);
+       iterator s;
+       iterator e;
+
+       if ((s = lower_bound (events.begin(), events.end(), &cp, time_comparator)) != events.end()) {
+               cp.when = endt;
+               e = upper_bound (events.begin(), events.end(), &cp, time_comparator);
+               events.erase (s, e);
+               erased = true;
        }
 
-       maybe_signal_changed ();
+       return erased;
 }
 
 void
@@ -638,6 +616,8 @@ ControlList::truncate_end (double last_coordinate)
                           beyond the new last coordinate.
                        */
 
+                       // FIXME: SLOW! (size() == O(n))
+
                        uint32_t sz = _events.size();
                        
                        while (i != _events.rend() && sz > 2) {
@@ -777,27 +757,24 @@ ControlList::unlocked_eval (double x) const
        double lval, uval;
        double fraction;
 
-       npoints = _events.size();
+       const_iterator length_check_iter = _events.begin();
+       for (npoints = 0; npoints < 4; ++npoints, ++length_check_iter) {
+               if (length_check_iter == _events.end()) {
+                       break;
+               }
+       }
 
        switch (npoints) {
        case 0:
                return _default_value;
 
        case 1:
-               if (x >= _events.front()->when) {
-                       return _events.front()->value;
-               } else {
-                       // return _default_value;
-                       return _events.front()->value;
-               } 
+               return _events.front()->value;
                
        case 2:
                if (x >= _events.back()->when) {
                        return _events.back()->value;
-               } else if (x == _events.front()->when) {
-                       return _events.front()->value;
-               } else if (x < _events.front()->when) {
-                       // return _default_value;
+               } else if (x <= _events.front()->when) {
                        return _events.front()->value;
                }
 
@@ -806,32 +783,26 @@ ControlList::unlocked_eval (double x) const
                upos = _events.back()->when;
                uval = _events.back()->value;
                
-               if (_interpolation == Discrete)
+               if (_interpolation == Discrete) {
                        return lval;
+               }
 
-               /* linear interpolation betweeen the two points
-               */
-
+               /* linear interpolation betweeen the two points */
                fraction = (double) (x - lpos) / (double) (upos - lpos);
                return lval + (fraction * (uval - lval));
 
        default:
-
                if (x >= _events.back()->when) {
                        return _events.back()->value;
-               } else if (x == _events.front()->when) {
-                       return _events.front()->value;
-               } else if (x < _events.front()->when) {
-                       // return _default_value;
+               } else if (x <= _events.front()->when) {
                        return _events.front()->value;
                }
 
                return multipoint_eval (x);
-               break;
        }
 
        /*NOTREACHED*/ /* stupid gcc */
-       return 0.0;
+       return _default_value;
 }
 
 double
@@ -988,7 +959,7 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double end,
                const bool past_start = (inclusive ? first->when >= start : first->when > start);
 
                /* Earliest points is in range, return it */
-               if (past_start >= start && first->when < end) {
+               if (past_start && first->when < end) {
 
                        x = first->when;
                        y = first->value;
@@ -1022,11 +993,13 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double end,
 bool
 ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const
 {
-       //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
+       //cerr << "earliest_event(start: " << start << ", end: " << end
+       //<< ", x: " << x << ", y: " << y << ", inclusive: " << inclusive <<  ")" << endl;
 
-       if (_events.size() == 0)
+       const_iterator length_check_iter = _events.begin();
+       if (_events.empty()) // 0 events
                return false;
-       else if (_events.size() == 1)
+       else if (_events.end() == ++length_check_iter) // 1 event
                return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
 
        // Hack to avoid infinitely repeating the same event
@@ -1039,6 +1012,10 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                const ControlEvent* first = NULL;
                const ControlEvent* next = NULL;
 
+               /* No events past start (maybe?) */
+               if (next && next->when < start)
+                       return false;
+
                /* Step is after first */
                if (range.first == _events.begin() || (*range.first)->when == start) {
                        first = *range.first;
@@ -1060,17 +1037,19 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
                        //++_search_cache.range.first;
+                       assert(x >= start);
                        return true;
                }
                        
-               if (abs(first->value - next->value) <= 1) {
-                       if (next->when <= end && (!inclusive || next->when > start)) {
+               if (fabs(first->value - next->value) <= 1) {
+                       if (next->when <= end && (next->when > start)) {
                                x = next->when;
                                y = next->value;
                                /* Move left of cache to this point
                                 * (Optimize for immediate call this cycle within range) */
                                _search_cache.left = x;
                                //++_search_cache.range.first;
+                               assert(inclusive ? x >= start : x > start);
                                return true;
                        } else {
                                return false;
@@ -1100,9 +1079,9 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                        x = first->when + (y - first->value) / (double)slope;
                }
 
-               /*cerr << first->value << " @ " << first->when << " ... "
+               cerr << first->value << " @ " << first->when << " ... "
                                << next->value << " @ " << next->when
-                               << " = " << y << " @ " << x << endl;*/
+                               << " = " << y << " @ " << x << endl;
 
                assert(    (y >= first->value && y <= next->value)
                                || (y <= first->value && y >= next->value) );
@@ -1113,9 +1092,8 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double end, d
                        /* Move left of cache to this point
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
-
+                       assert(inclusive ? x >= start : x > start);
                        return true;
-
                } else {
                        return false;
                }
@@ -1306,5 +1284,53 @@ ControlList::paste (ControlList& alist, double pos, float times)
        return true;
 }
 
+/** Move automation around according to a list of region movements */
+void
+ControlList::move_ranges (const list< RangeMove<double> >& movements)
+{
+       typedef list< RangeMove<double> > RangeMoveList;
+
+       {
+               Glib::Mutex::Lock lm (_lock);
+
+               /* a copy of the events list before we started moving stuff around */
+               EventList old_events = _events;
+
+               /* clear the source and destination ranges in the new list */
+               for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) {
+
+                       erase_range_internal (i->from, i->from + i->length, _events);
+                       erase_range_internal (i->to, i->to + i->length, _events);
+
+               }
+
+               /* copy the events into the new list */
+               for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) {
+                       iterator j = old_events.begin ();
+                       const double limit = i->from + i->length;
+                       const double dx    = i->to - i->from;
+                       while (j != old_events.end () && (*j)->when <= limit) {
+                               if ((*j)->when >= i->from) {
+                                       ControlEvent* ev = new ControlEvent (**j);
+                                       ev->when += dx;
+                                       _events.push_back (ev);
+                               }
+                               ++j;
+                       }
+               }
+
+               if (!_frozen) {
+                       _events.sort (event_time_less_than);
+               } else {
+                       _sort_pending = true;
+               }
+
+               reposition_for_rt_add (0);
+               mark_dirty ();
+       }
+
+       maybe_signal_changed ();
+}
+
 } // namespace Evoral