Use sys::path and SessionDirectory in Session::create_session_file_from_template...
[ardour.git] / libs / ardour / automation_event.cc
index d5b122cb531170a85110b1ab03370250df0abae8..3a80dddc76b59d3f7c769e67d5081a9a9051c6d8 100644 (file)
@@ -318,7 +318,6 @@ AutomationList::rt_add (double when, double value)
                Glib::Mutex::Lock lm (_lock);
 
                iterator where;
-               TimeComparator cmp;
                ControlEvent cp (when, 0.0);
                bool done = false;
 
@@ -370,7 +369,7 @@ AutomationList::rt_add (double when, double value)
                        
                } else {
 
-                       where = lower_bound (_events.begin(), _events.end(), &cp, cmp);
+                       where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
                        if (where != _events.end()) {
                                if ((*where)->when == when) {
@@ -379,7 +378,7 @@ AutomationList::rt_add (double when, double value)
                                }
                        }
                }
-               
+
                if (!done) {
                        _rt_insertion_point = _events.insert (where, new ControlEvent (when, value));
                }
@@ -405,12 +404,11 @@ AutomationList::add (double when, double value)
 
        {
                Glib::Mutex::Lock lm (_lock);
-               TimeComparator cmp;
                ControlEvent cp (when, 0.0f);
                bool insert = true;
                iterator insertion_point;
 
-               for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, cmp); insertion_point != _events.end(); ++insertion_point) {
+               for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) {
 
                        /* only one point allowed per time point */
 
@@ -469,15 +467,14 @@ AutomationList::reset_range (double start, double endt)
 
        {
         Glib::Mutex::Lock lm (_lock);
-               TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
                iterator e;
                
-               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
 
                        cp.when = endt;
-                       e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
+                       e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
                        for (iterator i = s; i != e; ++i) {
                                (*i)->value = _default_value;
@@ -501,14 +498,13 @@ AutomationList::erase_range (double start, double endt)
 
        {
                Glib::Mutex::Lock lm (_lock);
-               TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
                iterator e;
 
-               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) != _events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
                        cp.when = endt;
-                       e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
+                       e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
                        _events.erase (s, e);
                        reposition_for_rt_add (0);
                        erased = true;
@@ -608,14 +604,13 @@ AutomationList::control_points_adjacent (double xval)
 {
        Glib::Mutex::Lock lm (_lock);
        iterator i;
-       TimeComparator cmp;
        ControlEvent cp (xval, 0.0f);
        std::pair<iterator,iterator> ret;
 
        ret.first = _events.end();
        ret.second = _events.end();
 
-       for (i = lower_bound (_events.begin(), _events.end(), &cp, cmp); i != _events.end(); ++i) {
+       for (i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); i != _events.end(); ++i) {
                
                if (ret.first == _events.end()) {
                        if ((*i)->when >= xval) {
@@ -966,8 +961,7 @@ AutomationList::multipoint_eval (double x) const
        /* FIXME: no cache.  significant? */
        if (_interpolation == Discrete) {
                const ControlEvent cp (x, 0);
-               TimeComparator cmp;
-               EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, cmp);
+               EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
                // shouldn't have made it to multipoint_eval
                assert(i != _events.end());
@@ -986,9 +980,8 @@ AutomationList::multipoint_eval (double x) const
             ((*_lookup_cache.range.second)->when < x))) {
 
                const ControlEvent cp (x, 0);
-               TimeComparator cmp;
                
-               _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, cmp);
+               _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator);
        }
        
        pair<const_iterator,const_iterator> range = _lookup_cache.range;
@@ -1042,13 +1035,12 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
 
                const ControlEvent start_point (start, 0);
                const ControlEvent end_point (end, 0);
-               TimeComparator cmp;
 
-               //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") -> ("
+               //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := ("
                //      << start << ".." << end << ")" << endl;
 
-               _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, cmp);
-               _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, cmp);
+               _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator);
+               _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, time_comparator);
 
                _search_cache.left = start;
                _search_cache.right = end;
@@ -1058,40 +1050,61 @@ AutomationList::build_search_cache_if_necessary(double start, double end) const
 /** Get the earliest event between \a start and \a end, using the current interpolation style.
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const
+{
+       // FIXME: It would be nice if this was unnecessary..
+       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return false;
+       }
+
+       return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive);
+} 
+
+
+/** Get the earliest event between \a start and \a end, using the current interpolation style.
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
 {
        if (_interpolation == Discrete)
-               return rt_safe_earliest_event_discrete(start, end, x, y);
+               return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
        else
-               return rt_safe_earliest_event_linear(start, end, x, y);
+               return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive);
 } 
 
+
 /** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation)
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event_discrete (double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const
 {
-       // FIXME: It would be nice if this was unnecessary..
-       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
-       if (!lm.locked()) {
-               return false;
-       }
-       
        build_search_cache_if_necessary(start, end);
 
-       pair<const_iterator,const_iterator> range = _search_cache.range;
+       const pair<const_iterator,const_iterator>& range = _search_cache.range;
 
        if (range.first != _events.end()) {
                const ControlEvent* const first = *range.first;
 
+               const bool past_start = (inclusive ? first->when >= start : first->when > start);
+
                /* Earliest points is in range, return it */
-               if (first->when >= start && first->when < end) {
+               if (past_start >= start && first->when < end) {
 
                        x = first->when;
                        y = first->value;
@@ -1106,7 +1119,6 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
                        return true;
 
                } else {
-                               
                        return false;
                }
        
@@ -1119,20 +1131,19 @@ AutomationList::rt_safe_earliest_event_discrete (double start, double end, doubl
 /** Get the earliest time the line crosses an integer (Linear interpolation).
  *
  * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
  * \return true if event is found (and \a x and \a y are valid).
  */
 bool
-AutomationList::rt_safe_earliest_event_linear (double start, double end, double& x, double& y) const
+AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const
 {
-       // FIXME: It would be nice if this was unnecessary..
-       Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
-       if (!lm.locked()) {
-               return false;
-       }
+       //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
 
        if (_events.size() < 2)
-               return false;
+               return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
 
+       // Hack to avoid infinitely repeating the same event
        build_search_cache_if_necessary(start, end);
        
        pair<const_iterator,const_iterator> range = _search_cache.range;
@@ -1146,6 +1157,7 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
                if (range.first == _events.begin() || (*range.first)->when == start) {
                        first = *range.first;
                        next = *(++range.first);
+                       ++_search_cache.range.first;
 
                /* Step is before first */
                } else {
@@ -1155,48 +1167,70 @@ AutomationList::rt_safe_earliest_event_linear (double start, double end, double&
                        next = *range.first;
                }
                
-               if (first->when == start) {
-                       x = start;
+               if (inclusive && first->when == start) {
+                       x = first->when;
                        y = first->value;
                        /* Move left of cache to this point
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
-                       ++_search_cache.range.first;
+                       //++_search_cache.range.first;
                        return true;
                }
-
-               /*cerr << first->value << " @ " << first->when << " ---> "
-                               << next->value << " @ " << next->when << endl;*/
+                       
+               if (abs(first->value - next->value) <= 1) {
+                       if (next->when <= end && (!inclusive || 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;
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
 
                const double slope = (next->value - first->value) / (double)(next->when - first->when);
-
-               const double start_y = first->value + (slope * (start - first->when));
                //cerr << "start y: " << start_y << endl;
 
-               if (start_y >= first->value) {
-                       x = first->when + ((ceil(start_y) - first->value) / (double)slope);
-                       y = ceil(start_y);
-               } else {
-                       x = first->when + ((floor(start_y) - first->value) / (double)slope);
-                       y = floor(start_y);
+               //y = first->value + (slope * fabs(start - first->when));
+               y = first->value;
+
+               if (first->value < next->value) // ramping up
+                       y = ceil(y);
+               else // ramping down
+                       y = floor(y);
+
+               x = first->when + (y - first->value) / (double)slope;
+               
+               while ((inclusive && x < start) || x <= start && y != next->value) {
+                       
+                       if (first->value < next->value) // ramping up
+                               y += 1.0;
+                       else // ramping down
+                               y -= 1.0;
+
+                       x = first->when + (y - first->value) / (double)slope;
                }
 
-               if (x >= start && x < end) {
-                       //cerr << y << " @ " << x << endl;
+               /*cerr << first->value << " @ " << first->when << " ... "
+                               << next->value << " @ " << next->when
+                               << " = " << y << " @ " << x << endl;*/
 
-                       x = floor(x);
+               assert(    (y >= first->value && y <= next->value)
+                               || (y <= first->value && y >= next->value) );
 
+               
+               const bool past_start = (inclusive ? x >= start : x > start);
+               if (past_start && x < end) {
                        /* Move left of cache to this point
                         * (Optimize for immediate call this cycle within range) */
                        _search_cache.left = x;
-                       
-                       if (x >= next->when)
-                               ++_search_cache.range.first;
 
                        return true;
 
                } else {
-                       //cerr << "\tNo: " << start_y << ", " << x << endl;
                        return false;
                }
        
@@ -1242,18 +1276,17 @@ AutomationList::cut_copy_clear (double start, double end, int op)
        AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value);
        iterator s, e;
        ControlEvent cp (start, 0.0);
-       TimeComparator cmp;
        bool changed = false;
        
        {
                Glib::Mutex::Lock lm (_lock);
 
-               if ((s = lower_bound (_events.begin(), _events.end(), &cp, cmp)) == _events.end()) {
+               if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) == _events.end()) {
                        return nal;
                }
 
                cp.when = end;
-               e = upper_bound (_events.begin(), _events.end(), &cp, cmp);
+               e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
                if (op != 2 && (*s)->when != start) {
                        nal->_events.push_back (new ControlEvent (0, unlocked_eval (start)));
@@ -1353,9 +1386,8 @@ AutomationList::paste (AutomationList& alist, double pos, float times)
                iterator prev;
                double end = 0;
                ControlEvent cp (pos, 0.0);
-               TimeComparator cmp;
 
-               where = upper_bound (_events.begin(), _events.end(), &cp, cmp);
+               where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
                for (iterator i = alist.begin();i != alist.end(); ++i) {
                        _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value));