diff --git a/libs/evoral/Control.cc b/libs/evoral/Control.cc index 0fd6449aa3..5ca78b779c 100644 --- a/libs/evoral/Control.cc +++ b/libs/evoral/Control.cc @@ -41,10 +41,10 @@ Control::Control(const Parameter& parameter, /** Get the currently effective value (ie the one that corresponds to current output) */ double -Control::get_double (bool from_list, double frame) const +Control::get_double (bool from_list, Temporal::timepos_t when) const { if (from_list) { - return _list->eval(frame); + return _list->eval (when); } else { return _user_value; } @@ -52,7 +52,7 @@ Control::get_double (bool from_list, double frame) const void -Control::set_double (double value, double frame, bool to_list) +Control::set_double (double value, Temporal::timepos_t when, bool to_list) { _user_value = value; @@ -61,7 +61,7 @@ Control::set_double (double value, double frame, bool to_list) */ if (to_list && (!_list->in_write_pass() || _list->descriptor().toggled)) { - _list->add (frame, value, false); + _list->add (when, value, false); } } diff --git a/libs/evoral/ControlList.cc b/libs/evoral/ControlList.cc index b9aa3fd469..aa7e0e6fe8 100644 --- a/libs/evoral/ControlList.cc +++ b/libs/evoral/ControlList.cc @@ -33,7 +33,7 @@ #define isnan_local std::isnan #endif -#define GUARD_POINT_DELTA 64 +#define GUARD_POINT_DELTA Temporal::timecnt_t::from_samples (64) #include #include @@ -48,6 +48,8 @@ #include "pbd/control_math.h" #include "pbd/compose.h" +#include "pbd/error.h" +#include "pbd/i18n.h" #include "pbd/debug.h" using namespace std; @@ -60,10 +62,11 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b) return a->when < b->when; } -ControlList::ControlList (const Parameter& id, const ParameterDescriptor& desc) +ControlList::ControlList (const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain ts) : _parameter(id) , _desc(desc) , _interpolation (default_interpolation ()) + , _time_style (ts) , _curve(0) { _frozen = 0; @@ -85,6 +88,7 @@ ControlList::ControlList (const ControlList& other) : _parameter(other._parameter) , _desc(other._desc) , _interpolation(other._interpolation) + , _time_style (other._time_style) , _curve(0) { _frozen = 0; @@ -103,10 +107,11 @@ ControlList::ControlList (const ControlList& other) copy_events (other); } -ControlList::ControlList (const ControlList& other, double start, double end) +ControlList::ControlList (const ControlList& other, Temporal::timepos_t const & start, Temporal::timepos_t const & end) : _parameter(other._parameter) , _desc(other._desc) , _interpolation(other._interpolation) + , _time_style (other._time_style) , _curve(0) { _frozen = 0; @@ -145,9 +150,9 @@ ControlList::~ControlList() } boost::shared_ptr -ControlList::create(const Parameter& id, const ParameterDescriptor& desc) +ControlList::create(const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain time_style) { - return boost::shared_ptr(new ControlList(id, desc)); + return boost::shared_ptr(new ControlList(id, desc, time_style)); } bool @@ -251,21 +256,24 @@ ControlList::clear () } void -ControlList::x_scale (double factor) +ControlList::x_scale (Temporal::ratio_t const & factor) { Glib::Threads::RWLock::WriterLock lm (_lock); _x_scale (factor); } bool -ControlList::extend_to (double when) +ControlList::extend_to (Temporal::timepos_t const & end) { Glib::Threads::RWLock::WriterLock lm (_lock); - if (_events.empty() || _events.back()->when == when) { + + if (_events.empty() || _events.back()->when == end) { return false; } - double factor = when / _events.back()->when; + + Temporal::ratio_t factor (end.val(), _events.back()->when.val()); _x_scale (factor); + return true; } @@ -330,10 +338,10 @@ ControlList::list_merge (ControlList const& other, boost::functionwhen *= factor; + (*i)->when = (*i)->when.operator* (factor); } mark_dirty (); @@ -377,9 +385,13 @@ ControlList::thin (double thinning_factor) /* 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))); + const double ppw = prevprev->when.val(); + const double pw = prev->when.val(); + const double cw = cur->when.val(); + + double area = fabs ((ppw * (prev->value - cur->value)) + + (pw * (cur->value - prevprev->value)) + + (cw * (prevprev->value - prev->value))); if (area < thinning_factor) { iterator tmp = pprev; @@ -415,11 +427,11 @@ ControlList::thin (double thinning_factor) } void -ControlList::fast_simple_add (double when, double value) +ControlList::fast_simple_add (Temporal::timepos_t const & time, double value) { Glib::Threads::RWLock::WriterLock lm (_lock); /* to be used only for loading pre-sorted data from saved state */ - _events.insert (_events.end(), new ControlEvent (when, value)); + _events.insert (_events.end(), new ControlEvent (time, value)); mark_dirty (); if (_frozen) { @@ -459,9 +471,10 @@ ControlList::unlocked_remove_duplicates () } void -ControlList::start_write_pass (double when) +ControlList::start_write_pass (Temporal::timepos_t const & time) { Glib::Threads::RWLock::WriterLock lm (_lock); + Temporal::timepos_t when = time; DEBUG_TRACE (DEBUG::ControlList, string_compose ("%1: setup write pass @ %2\n", this, when)); @@ -481,7 +494,7 @@ ControlList::start_write_pass (double when) */ if (_in_write_pass && !new_write_pass) { #if 1 - add_guard_point (when, 0); // also sets most_recent_insert_iterator + add_guard_point (when, std::numeric_limits::min()); // also sets most_recent_insert_iterator #else const ControlEvent cp (when, 0.0); most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); @@ -490,7 +503,7 @@ ControlList::start_write_pass (double when) } void -ControlList::write_pass_finished (double /*when*/, double thinning_factor) +ControlList::write_pass_finished (Temporal::timepos_t const & /*when*/, double thinning_factor) { DEBUG_TRACE (DEBUG::ControlList, "write pass finished\n"); @@ -503,7 +516,7 @@ ControlList::write_pass_finished (double /*when*/, double thinning_factor) } void -ControlList::set_in_write_pass (bool yn, bool add_point, double when) +ControlList::set_in_write_pass (bool yn, bool add_point, Temporal::timepos_t when) { DEBUG_TRACE (DEBUG::ControlList, string_compose ("set_in_write_pass: in-write: %1 @ %2 add point? %3\n", yn, when, add_point)); @@ -511,20 +524,25 @@ ControlList::set_in_write_pass (bool yn, bool add_point, double when) if (yn && add_point) { Glib::Threads::RWLock::WriterLock lm (_lock); - add_guard_point (when, 0); + add_guard_point (when, std::numeric_limits::min()); } } void -ControlList::add_guard_point (double when, double offset) +ControlList::add_guard_point (Temporal::timepos_t const & time, Temporal::timecnt_t const & offset) { + Temporal::timepos_t when = time; + // caller needs to hold writer-lock - if (offset < 0 && when < offset) { + + if (offset.negative() && when < offset) { return; } - assert (offset <= 0); - if (offset != 0) { +#warning NUTEMPO FIXME this assertion should be possible to write + // assert (offset <= 0); + + if (!offset.zero()) { /* check if there are points between when + offset .. when */ ControlEvent cp (when + offset, 0.0); iterator s; @@ -533,7 +551,7 @@ ControlList::add_guard_point (double when, double offset) cp.when = when; e = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); if (s != e) { - DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add_guard_point, none added, found event between %2 and %3\n", this, when - offset, when)); + DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add_guard_point, none added, found event between %2 and %3\n", this, when.earlier (offset), when)); return; } } @@ -601,11 +619,12 @@ ControlList::in_write_pass () const } bool -ControlList::editor_add (double when, double value, bool with_guard) +ControlList::editor_add (Temporal::timepos_t const & time, double value, bool with_guard) { /* this is for making changes from a graphical line editor */ { Glib::Threads::RWLock::WriterLock lm (_lock); + Temporal::timepos_t when = time; ControlEvent cp (when, 0.0f); iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); @@ -624,7 +643,7 @@ ControlList::editor_add (double when, double value, bool with_guard) */ if (when >= 1) { - _events.insert (_events.end(), new ControlEvent (0, value)); + _events.insert (_events.end(), new ControlEvent (std::numeric_limits::min(), value)); DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added value %2 at zero\n", this, value)); } } @@ -652,11 +671,12 @@ ControlList::editor_add (double when, double value, bool with_guard) } void -ControlList::maybe_add_insert_guard (double when) +ControlList::maybe_add_insert_guard (Temporal::timepos_t const & time) { + Temporal::timepos_t when = time; // caller needs to hold writer-lock if (most_recent_insert_iterator != _events.end()) { - if ((*most_recent_insert_iterator)->when - when > GUARD_POINT_DELTA) { + if ((*most_recent_insert_iterator)->when.earlier (when) > GUARD_POINT_DELTA) { /* Next control point is some distance from where our new point is going to go, so add a new point to avoid changing the shape of the line too much. The insert iterator needs to point to the @@ -673,8 +693,10 @@ ControlList::maybe_add_insert_guard (double when) /** If we would just be adding to a straight line, move the previous point instead. */ bool -ControlList::maybe_insert_straight_line (double when, double value) +ControlList::maybe_insert_straight_line (Temporal::timepos_t const & time, double value) { + Temporal::timepos_t when = time; + // caller needs to hold writer-lock if (_events.empty()) { return false; @@ -702,8 +724,10 @@ ControlList::maybe_insert_straight_line (double when, double value) } ControlList::iterator -ControlList::erase_from_iterator_to (iterator iter, double when) +ControlList::erase_from_iterator_to (iterator iter, Temporal::timepos_t const & time) { + Temporal::timepos_t when = time; + // caller needs to hold writer-lock while (iter != _events.end()) { if ((*iter)->when < when) { @@ -723,8 +747,10 @@ ControlList::erase_from_iterator_to (iterator iter, double when) * control surface (GUI, MIDI, OSC etc) */ void -ControlList::add (double when, double value, bool with_guards, bool with_initial) +ControlList::add (Temporal::timepos_t const & time, double value, bool with_guards, bool with_initial) { + Temporal::timepos_t when = time; + /* clamp new value to allowed range */ value = std::min ((double)_desc.upper, std::max ((double)_desc.lower, value)); @@ -744,11 +770,11 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial if (when >= 1) { if (_desc.toggled) { const double opp_val = ((value >= 0.5) ? 1.0 : 0.0); - _events.insert (_events.end(), new ControlEvent (0, opp_val)); + _events.insert (_events.end(), new ControlEvent (std::numeric_limits::min(), 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)); + _events.insert (_events.end(), new ControlEvent (std::numeric_limits::min(), value)); DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _desc.normal)); } } @@ -759,7 +785,7 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial /* first write in a write pass: add guard point if requested */ if (with_guards) { - add_guard_point (insert_position, 0); + add_guard_point (insert_position, std::numeric_limits::min()); did_write_during_pass = true; } else { /* not adding a guard, but we need to set iterator appropriately */ @@ -905,10 +931,11 @@ ControlList::erase (iterator start, iterator end) /** Erase the first event which matches the given time and value */ void -ControlList::erase (double when, double value) +ControlList::erase (Temporal::timepos_t const & time, double value) { { Glib::Threads::RWLock::WriterLock lm (_lock); + Temporal::timepos_t when = time; iterator i = begin (); while (i != end() && ((*i)->when != when || (*i)->value != value)) { @@ -928,12 +955,13 @@ ControlList::erase (double when, double value) } void -ControlList::erase_range (double start, double endt) +ControlList::erase_range (Temporal::timepos_t const & start, Temporal::timepos_t const & endt) { bool erased = false; { Glib::Threads::RWLock::WriterLock lm (_lock); + erased = erase_range_internal (start, endt, _events); if (erased) { @@ -947,14 +975,32 @@ ControlList::erase_range (double start, double endt) } bool -ControlList::erase_range_internal (double start, double endt, EventList & events) +ControlList::erase_range_internal (Temporal::timepos_t const & start, Temporal::timepos_t const & endt, EventList & events) { bool erased = false; - ControlEvent cp (start, 0.0f); iterator s; iterator e; + /* This is where we have to pick the time domain to be used when + * defining the control points. + * + * start/endt retain their values no matter what the time domain is, + * but the location of the control point is specified as a single + * integer value that represents either samples or beats. The sample + * position and beat position, while representing the same position on + * the timeline, will be numerically different anywhere (except perhaps + * zero). + * + * eg. start = 1000000 samples == 12.34 beats + * cp.when = 100000 if ControlList uses AudioTime + * cp.when = 23074 if ControlList uses BeatTime (see Temporal::Beats::to_ticks()) + * + */ + + ControlEvent cp (start, 0.0f); + 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); @@ -968,7 +1014,7 @@ ControlList::erase_range_internal (double start, double endt, EventList & events } void -ControlList::slide (iterator before, double distance) +ControlList::slide (iterator before, Temporal::timecnt_t const & distance) { { Glib::Threads::RWLock::WriterLock lm (_lock); @@ -977,8 +1023,10 @@ ControlList::slide (iterator before, double distance) return; } + Temporal::timecnt_t wd = distance; + while (before != _events.end()) { - (*before)->when += distance; + (*before)->when += wd; ++before; } @@ -988,19 +1036,22 @@ ControlList::slide (iterator before, double distance) } void -ControlList::shift (double pos, double frames) +ControlList::shift (Temporal::timepos_t const & time, Temporal::timecnt_t const & distance) { + Temporal::timepos_t pos = time; + { Glib::Threads::RWLock::WriterLock lm (_lock); double v0, v1; - if (frames < 0) { + + if (distance.negative()) { /* Route::shift () with negative shift is used * for "remove time". The time [pos.. pos-frames] is removed. * and everyhing after, moved backwards. */ v0 = unlocked_eval (pos); - v1 = unlocked_eval (pos - frames); - erase_range_internal (pos, pos - frames, _events); + v1 = unlocked_eval (pos.earlier (distance)); + erase_range_internal (pos, pos.earlier (distance), _events); } else { v0 = v1 = unlocked_eval (pos); } @@ -1012,23 +1063,23 @@ ControlList::shift (double pos, double frames) dst_guard_exists = true; } if ((*i)->when >= pos) { - (*i)->when += frames; + (*i)->when += distance; } } /* add guard-points to retain shape, if needed */ - if (frames > 0) { + if (distance.positive()) { ControlEvent cp (pos, 0.0); iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); if (s != _events.end ()) { _events.insert (s, new ControlEvent (pos, v0)); } - pos += frames; - } else if (frames < 0 && pos > 0) { - ControlEvent cp (pos - 1, 0.0); + pos += distance; + } else if (distance.negative() && pos > 0) { + ControlEvent cp (pos.decrement(), 0.0); iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); if (s != _events.end ()) { - _events.insert (s, new ControlEvent (pos - 1, v0)); + _events.insert (s, new ControlEvent (pos.decrement(), v0)); } } if (!dst_guard_exists) { @@ -1044,7 +1095,7 @@ ControlList::shift (double pos, double frames) } void -ControlList::modify (iterator iter, double when, double val) +ControlList::modify (iterator iter, Temporal::timepos_t const & time, double val) { /* note: we assume higher level logic is in place to avoid this * reordering the time-order of control events in the list. ie. all @@ -1056,6 +1107,7 @@ ControlList::modify (iterator iter, double when, double val) { Glib::Threads::RWLock::WriterLock lm (_lock); + Temporal::timepos_t when = time; (*iter)->when = when; (*iter)->value = val; @@ -1077,10 +1129,11 @@ ControlList::modify (iterator iter, double when, double val) } std::pair -ControlList::control_points_adjacent (double xval) +ControlList::control_points_adjacent (Temporal::timepos_t const & xtime) { Glib::Threads::RWLock::ReaderLock lm (_lock); iterator i; + Temporal::timepos_t xval = xtime; ControlEvent cp (xval, 0.0f); std::pair ret; @@ -1140,10 +1193,10 @@ ControlList::thaw () void ControlList::mark_dirty () const { - _lookup_cache.left = -1; + _lookup_cache.left = std::numeric_limits::max(); _lookup_cache.range.first = _events.end(); _lookup_cache.range.second = _events.end(); - _search_cache.left = -1; + _search_cache.left = std::numeric_limits::max(); _search_cache.first = _events.end(); if (_curve) { @@ -1152,10 +1205,11 @@ ControlList::mark_dirty () const } void -ControlList::truncate_end (double last_coordinate) +ControlList::truncate_end (Temporal::timepos_t const & last_time) { { Glib::Threads::RWLock::WriterLock lm (_lock); + Temporal::timepos_t last_coordinate = last_time; ControlEvent cp (last_coordinate, 0); ControlList::reverse_iterator i; double last_val; @@ -1254,13 +1308,14 @@ ControlList::truncate_end (double last_coordinate) } void -ControlList::truncate_start (double overall_length) +ControlList::truncate_start (Temporal::timecnt_t const & overall) { { Glib::Threads::RWLock::WriterLock lm (_lock); iterator i; double first_legal_value; - double first_legal_coordinate; + Temporal::timepos_t first_legal_coordinate; + Temporal::timepos_t overall_length (overall); if (_events.empty()) { /* nothing to truncate */ @@ -1274,7 +1329,7 @@ ControlList::truncate_start (double overall_length) /* growing at front: duplicate first point. shift all others */ - double shift = overall_length - _events.back()->when; + Temporal::timepos_t shift (_events.back()->when.distance (overall_length)); uint32_t np; for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) { @@ -1284,7 +1339,7 @@ ControlList::truncate_start (double overall_length) if (np < 2) { /* less than 2 points: add a new point */ - _events.push_front (new ControlEvent (0, _events.front()->value)); + _events.push_front (new ControlEvent (std::numeric_limits::min(), _events.front()->value)); } else { @@ -1301,7 +1356,7 @@ ControlList::truncate_start (double overall_length) _events.front()->when = 0; } else { /* leave non-flat segment in place, add a new leading point. */ - _events.push_front (new ControlEvent (0, _events.front()->value)); + _events.push_front (new ControlEvent (std::numeric_limits::min(), _events.front()->value)); } } @@ -1309,7 +1364,7 @@ ControlList::truncate_start (double overall_length) /* shrinking at front */ - first_legal_coordinate = _events.back()->when - overall_length; + first_legal_coordinate = _events.back()->when.distance (overall_length); first_legal_value = unlocked_eval (first_legal_coordinate); first_legal_value = max ((double)_desc.lower, first_legal_value); first_legal_value = min ((double)_desc.upper, first_legal_value); @@ -1339,12 +1394,12 @@ ControlList::truncate_start (double overall_length) */ for (i = _events.begin(); i != _events.end(); ++i) { - (*i)->when -= first_legal_coordinate; + (*i)->when.shift_earlier (Temporal::timecnt_t (first_legal_coordinate, Temporal::timepos_t())); } /* add a new point for the interpolated new value */ - _events.push_front (new ControlEvent (0, first_legal_value)); + _events.push_front (new ControlEvent (std::numeric_limits::min(), first_legal_value)); } unlocked_invalidate_insert_iterator (); @@ -1354,14 +1409,16 @@ ControlList::truncate_start (double overall_length) } double -ControlList::unlocked_eval (double x) const +ControlList::unlocked_eval (Temporal::timepos_t const & xtime) const { pair range; int32_t npoints; - double lpos, upos; + Temporal::timepos_t lpos, upos; double lval, uval; double fraction; - + double xx; + double ll; + const_iterator length_check_iter = _events.begin(); for (npoints = 0; npoints < 4; ++npoints, ++length_check_iter) { if (length_check_iter == _events.end()) { @@ -1377,9 +1434,9 @@ ControlList::unlocked_eval (double x) const return _events.front()->value; case 2: - if (x >= _events.back()->when) { + if (xtime >= _events.back()->when) { return _events.back()->value; - } else if (x <= _events.front()->when) { + } else if (xtime <= _events.front()->when) { return _events.front()->value; } @@ -1388,7 +1445,10 @@ ControlList::unlocked_eval (double x) const upos = _events.back()->when; uval = _events.back()->value; - fraction = (double) (x - lpos) / (double) (upos - lpos); + xx = lpos.distance (xtime).distance().val(); + ll = lpos.distance (upos).distance().val(); + + fraction = xx / ll; switch (_interpolation) { case Discrete: @@ -1405,13 +1465,13 @@ ControlList::unlocked_eval (double x) const } default: - if (x >= _events.back()->when) { + if (xtime >= _events.back()->when) { return _events.back()->value; - } else if (x <= _events.front()->when) { + } else if (xtime <= _events.front()->when) { return _events.front()->value; } - return multipoint_eval (x); + return multipoint_eval (xtime); } abort(); /*NOTREACHED*/ /* stupid gcc */ @@ -1419,35 +1479,35 @@ ControlList::unlocked_eval (double x) const } double -ControlList::multipoint_eval (double x) const +ControlList::multipoint_eval (Temporal::timepos_t const & xtime) const { - double upos, lpos; + Temporal::timepos_t upos, lpos; double uval, lval; double fraction; /* "Stepped" lookup (no interpolation) */ /* FIXME: no cache. significant? */ if (_interpolation == Discrete) { - const ControlEvent cp (x, 0); + const ControlEvent cp (xtime, 0); 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()); - if (i == _events.begin() || (*i)->when == x) + if (i == _events.begin() || (*i)->when == xtime) return (*i)->value; else return (*(--i))->value; } - /* Only do the range lookup if x is in a different range than last time + /* Only do the range lookup if xtime is in a different range than last time * this was called (or if the lookup cache has been marked "dirty" (left<0) */ - if ((_lookup_cache.left < 0) || - ((_lookup_cache.left > x) || + if ((_lookup_cache.left == std::numeric_limits::max()) || + ((_lookup_cache.left > xtime) || (_lookup_cache.range.first == _events.end()) || - ((*_lookup_cache.range.second)->when < x))) { + ((*_lookup_cache.range.second)->when < xtime))) { - const ControlEvent cp (x, 0); + const ControlEvent cp (xtime, 0); _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator); } @@ -1458,7 +1518,7 @@ ControlList::multipoint_eval (double x) const /* x does not exist within the list as a control point */ - _lookup_cache.left = x; + _lookup_cache.left = xtime; if (range.first != _events.begin()) { --range.first; @@ -1478,7 +1538,7 @@ ControlList::multipoint_eval (double x) const upos = (*range.second)->when; uval = (*range.second)->value; - fraction = (double) (x - lpos) / (double) (upos - lpos); + fraction = (double) lpos.distance (xtime).distance().val() / (double) lpos.distance (upos).distance().val(); switch (_interpolation) { case Logarithmic: @@ -1499,20 +1559,22 @@ ControlList::multipoint_eval (double x) const } /* x is a control point in the data */ - _lookup_cache.left = -1; + _lookup_cache.left = std::numeric_limits::max(); return (*range.first)->value; } void -ControlList::build_search_cache_if_necessary (double start) const +ControlList::build_search_cache_if_necessary (Temporal::timepos_t const & start_time) const { + Temporal::timepos_t start = start_time; + if (_events.empty()) { /* Empty, nothing to cache, move to end. */ _search_cache.first = _events.end(); - _search_cache.left = 0; + _search_cache.left = Temporal::timepos_t(); return; - } else if ((_search_cache.left < 0) || (_search_cache.left > start)) { - /* Marked dirty (left < 0), or we're too far forward, re-search. */ + } else if ((_search_cache.left == std::numeric_limits::max()) || (_search_cache.left > start)) { + /* Marked dirty (left == max), or we're too far forward, re-search. */ const ControlEvent start_point (start, 0); @@ -1537,8 +1599,10 @@ ControlList::build_search_cache_if_necessary (double start) const * \return true if event is found (and \a x and \a y are valid). */ bool -ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x, double& y, bool inclusive) const +ControlList::rt_safe_earliest_event_discrete_unlocked (Temporal::timepos_t const & start_time, Temporal::timepos_t & x, double& y, bool inclusive) const { + Temporal::timepos_t start = start_time; + build_search_cache_if_necessary (start); if (_search_cache.first != _events.end()) { @@ -1554,7 +1618,7 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x, /* Move left of cache to this point * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; + _search_cache.left = first->when; ++_search_cache.first; assert(x >= start); @@ -1578,9 +1642,9 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x, * \return true if event is found (and \a x and \a y are valid). */ bool -ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, double& y, bool inclusive, double min_x_delta) const +ControlList::rt_safe_earliest_event_linear_unlocked (Temporal::timepos_t const & start_time, Temporal::timepos_t & x, double& y, bool inclusive, timepos_t const & min_x_delta) const { - Glib::Threads::RWLock::ReaderLock lm (_lock); // XXX + Temporal::timepos_t start = start_time; // cout << "earliest_event(start: " << start << ", x: " << x << ", y: " << y << ", inclusive: " << inclusive << ")" << endl; @@ -1591,7 +1655,6 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do return rt_safe_earliest_event_discrete_unlocked (start + min_x_delta, x, y, inclusive); } - if (min_x_delta > 0) { /* if there is an event between [start and start + min_x_delta], use it, * otherwise interpolate at start + min_x_delta @@ -1642,7 +1705,7 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do y = first->value; /* Move left of cache to this point * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; + _search_cache.left = first->when; return true; } else if (next->when < start || (!inclusive && next->when == start)) { /* "Next" is before the start, no points left. */ @@ -1655,34 +1718,44 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do y = next->value; /* Move left of cache to this point * (Optimize for immediate call this cycle within range) */ - _search_cache.left = x; + _search_cache.left = next->when; return true; } else { return false; } } - const double slope = (next->value - first->value) / (double)(next->when - first->when); + const double slope = (next->value - first->value) / (double) first->when.distance (next->when).distance().val(); //cerr << "start y: " << start_y << endl; //y = first->value + (slope * fabs(start - first->when)); y = first->value; - if (first->value < next->value) // ramping up + if (first->value < next->value) { // ramping up y = ceil(y); - else // ramping down + } else { // ramping down y = floor(y); + } - x = first->when + (y - first->value) / (double)slope; + if (_time_style == Temporal::AudioTime) { + x = first->when + Temporal::timepos_t::from_samples ((y - first->value) / (double)slope); + } else { + x = first->when + Temporal::timepos_t::from_ticks ((y - first->value) / (double)slope); + } - while ((inclusive && x < start) || (x <= start && y != next->value)) { + while ((inclusive && x < start_time) || (x <= start_time && y != next->value)) { - if (first->value < next->value) // ramping up + if (first->value < next->value) { // ramping up y += 1.0; - else // ramping down + } else { // ramping down y -= 1.0; + } - x = first->when + (y - first->value) / (double)slope; + if (_time_style == Temporal::AudioTime) { + x = first->when + Temporal::timepos_t::from_samples ((y - first->value) / (double)slope); + } else { + x = first->when + Temporal::timepos_t::from_ticks ((y - first->value) / (double)slope); + } } #if 0 @@ -1695,20 +1768,21 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do || (y <= first->value && y >= next->value) ); - const bool past_start = (inclusive ? x >= start : x > start); + const bool past_start = (inclusive ? x >= start_time : x > start_time); if (past_start) { /* 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); + assert(inclusive ? x >= start_time : x > start_time); return true; } else { if (inclusive) { x = next->when; + _search_cache.left = next->when; } else { x = start; + _search_cache.left = x; } - _search_cache.left = x; return true; } @@ -1724,10 +1798,12 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do * @param op 0 = cut, 1 = copy, 2 = clear. */ boost::shared_ptr -ControlList::cut_copy_clear (double start, double end, int op) +ControlList::cut_copy_clear (Temporal::timepos_t const & start_time, Temporal::timepos_t const & end_time, int op) { - boost::shared_ptr nal = create (_parameter, _desc); + boost::shared_ptr nal = create (_parameter, _desc, _time_style); iterator s, e; + Temporal::timepos_t start = start_time; + Temporal::timepos_t end = end_time; ControlEvent cp (start, 0.0); { @@ -1773,7 +1849,7 @@ ControlList::cut_copy_clear (double start, double end, int op) } if (op != 2) { // ! clear - nal->_events.push_back (new ControlEvent (0, val)); + nal->_events.push_back (new ControlEvent (std::numeric_limits::min(), val)); } } @@ -1784,7 +1860,7 @@ ControlList::cut_copy_clear (double start, double end, int op) */ if (op != 2) { - nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value)); + nal->_events.push_back (new ControlEvent (Temporal::timepos_t (start.distance ((*x)->when)), (*x)->value)); } if (op != 1) { @@ -1804,7 +1880,7 @@ ControlList::cut_copy_clear (double start, double end, int op) } if (op != 2 && (e != _events.end() && end < (*e)->when)) { // cut/copy - nal->_events.push_back (new ControlEvent (end - start, end_value)); + nal->_events.push_back (new ControlEvent (Temporal::timepos_t (start.distance (start)), end_value)); } } @@ -1821,26 +1897,26 @@ ControlList::cut_copy_clear (double start, double end, int op) boost::shared_ptr -ControlList::cut (double start, double end) +ControlList::cut (Temporal::timepos_t const & start, Temporal::timepos_t const & end) { return cut_copy_clear (start, end, 0); } boost::shared_ptr -ControlList::copy (double start, double end) +ControlList::copy (Temporal::timepos_t const & start, Temporal::timepos_t const & end) { return cut_copy_clear (start, end, 1); } void -ControlList::clear (double start, double end) +ControlList::clear (Temporal::timepos_t const & start, Temporal::timepos_t const & end) { cut_copy_clear (start, end, 2); } /** @param pos Position in model coordinates */ bool -ControlList::paste (const ControlList& alist, double pos) +ControlList::paste (const ControlList& alist, Temporal::timepos_t const & time) { if (alist._events.empty()) { return false; @@ -1850,7 +1926,8 @@ ControlList::paste (const ControlList& alist, double pos) Glib::Threads::RWLock::WriterLock lm (_lock); iterator where; iterator prev; - double end = 0; + Temporal::timepos_t end; + Temporal::timepos_t pos = time; ControlEvent cp (pos, 0.0); where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); @@ -1909,9 +1986,9 @@ ControlList::paste (const ControlList& alist, double pos) * @param return true if anything was changed, otherwise false (ie nothing needed changing) */ bool -ControlList::move_ranges (const list< RangeMove >& movements) +ControlList::move_ranges (const list< Temporal::RangeMove> & movements) { - typedef list< RangeMove > RangeMoveList; + typedef list RangeMoveList; { Glib::Threads::RWLock::WriterLock lm (_lock); @@ -1923,11 +2000,17 @@ ControlList::move_ranges (const list< RangeMove >& movements) bool things_erased = false; for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) { - if (erase_range_internal (i->from, i->from + i->length, _events)) { + Temporal::timepos_t start = i->from; + Temporal::timepos_t end = i->from + i->length; + + if (erase_range_internal (start, end, _events)) { things_erased = true; } - if (erase_range_internal (i->to, i->to + i->length, _events)) { + start = i->to; + end = i->to + i->length; + + if (erase_range_internal (start, end, _events)) { things_erased = true; } } @@ -1940,14 +2023,48 @@ ControlList::move_ranges (const list< RangeMove >& movements) /* 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) { + + const Temporal::timepos_t limit = i->from + i->length; + const Temporal::timecnt_t dx = i->from.distance (i->to); + + while (j != old_events.end ()) { + + Temporal::timepos_t jtime; + + switch (_time_style) { + case Temporal::AudioTime: + jtime = (*j)->when; + break; + case Temporal::BeatTime: + jtime = (*j)->when; + break; + default: + /*NOTREACHED*/ + return false; + } + + if (jtime > limit) { + break; + } + + if (jtime >= i->from) { ControlEvent* ev = new ControlEvent (**j); - ev->when += dx; + + switch (_time_style) { + case Temporal::AudioTime: + ev->when += dx.samples(); + break; + case Temporal::BeatTime: + ev->when += dx.beats().to_ticks(); + break; + default: + /*NOTREACHED*/ + return false; + } + _events.push_back (ev); } + ++j; } } @@ -2044,9 +2161,8 @@ ControlList::dump (ostream& o) /* NOT LOCKED ... for debugging only */ for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) { - o << (*x)->value << " @ " << (uint64_t) (*x)->when << endl; + o << (*x)->value << " @ " << (*x)->when << endl; } } } // namespace Evoral - diff --git a/libs/evoral/Curve.cc b/libs/evoral/Curve.cc index 37e6407537..cef9a5e2cf 100644 --- a/libs/evoral/Curve.cc +++ b/libs/evoral/Curve.cc @@ -62,20 +62,23 @@ Curve::solve () const (www.korf.co.uk/spline.pdf) for more details. */ - vector x(npoints); + vector x (npoints); vector y(npoints); uint32_t i; ControlList::EventList::const_iterator xx; for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) { - x[i] = (double) (*xx)->when; - y[i] = (double) (*xx)->value; + x[i] = (*xx)->when; + y[i] = (*xx)->value; } double lp0, lp1, fpone; + double xd; - lp0 = (x[1] - x[0])/(y[1] - y[0]); - lp1 = (x[2] - x[1])/(y[2] - y[1]); + xd = x[0].distance (x[1]).magnitude(); + lp0 = xd/(y[1] - y[0]); + xd = x[1].distance (x[2]).magnitude(); + lp1 = xd/(y[2] - y[1]); if (lp0*lp1 < 0) { fpone = 0; @@ -92,9 +95,12 @@ Curve::solve () const double ydelta; /* ditto */ double fppL, fppR; double fpi; + double xi = x[i].val(); + double xim1; if (i > 0) { - xdelta = x[i] - x[i-1]; + xim1 = x[i-1].val(); + xdelta = xi - xim1; xdelta2 = xdelta * xdelta; ydelta = y[i] - y[i-1]; } @@ -105,7 +111,7 @@ Curve::solve () const /* first segment */ - fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5)); + fplast = ((3 * (y[1] - y[0]) / (2 * (xi - xim1))) - (fpone * 0.5)); /* we don't store coefficients for i = 0 */ @@ -121,7 +127,9 @@ Curve::solve () const /* all other segments */ - double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i])); + double xip1 = x[i+1].val(); + + double slope_before = (xip1 - xi) / (y[i+1] - y[i]); double slope_after = (xdelta / ydelta); if (slope_after * slope_before < 0.0) { @@ -145,22 +153,22 @@ Curve::solve () const double b, c, d; d = (fppR - fppL) / (6 * xdelta); - c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta); + c = ((xi * fppL) - (xim1 * fppR))/(2 * xdelta); double xim12, xim13; double xi2, xi3; - xim12 = x[i-1] * x[i-1]; /* "x[i-1] squared" */ - xim13 = xim12 * x[i-1]; /* "x[i-1] cubed" */ - xi2 = x[i] * x[i]; /* "x[i] squared" */ - xi3 = xi2 * x[i]; /* "x[i] cubed" */ + xim12 = xim1 * xim1; /* "x[i-1] squared" */ + xim13 = xim12 * xim1; /* "x[i-1] cubed" */ + xi2 = xi * xi; /* "x[i] squared" */ + xi3 = xi2 * xi; /* "x[i] cubed" */ b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta; /* store */ (*xx)->create_coeffs(); - (*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13); + (*xx)->coeff[0] = y[i-1] - (b * xim1) - (c * xim12) - (d * xim13); (*xx)->coeff[1] = b; (*xx)->coeff[2] = c; (*xx)->coeff[3] = d; @@ -174,7 +182,7 @@ Curve::solve () const } bool -Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) const +Curve::rt_safe_get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const { Glib::Threads::RWLock::ReaderLock lm(_list.lock(), Glib::Threads::TRY_LOCK); @@ -187,16 +195,20 @@ Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) con } void -Curve::get_vector (double x0, double x1, float *vec, int32_t veclen) const +Curve::get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const { Glib::Threads::RWLock::ReaderLock lm(_list.lock()); _get_vector (x0, x1, vec, veclen); } void -Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const +Curve::_get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const { - double rx, lx, hx, max_x, min_x; + double rx, lx, hx; + const double start = x0.val(); + const double end = x1.val(); + double max_x; + double min_x; int32_t i; int32_t original_veclen; int32_t npoints; @@ -222,10 +234,10 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const /* events is now known not to be empty */ - max_x = _list.events().back()->when; - min_x = _list.events().front()->when; + max_x = _list.events().back()->when.val(); + min_x = _list.events().front()->when.val(); - if (x0 > max_x) { + if (start > max_x) { /* totally past the end - just fill the entire array with the final value */ for (int32_t i = 0; i < veclen; ++i) { vec[i] = _list.events().back()->value; @@ -233,7 +245,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const return; } - if (x1 < min_x) { + if (end < min_x) { /* totally before the first event - fill the entire array with * the initial value. */ @@ -245,13 +257,13 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const original_veclen = veclen; - if (x0 < min_x) { + if (start < min_x) { /* fill some beginning section of the array with the initial (used to be default) value */ - double frac = (min_x - x0) / (x1 - x0); + double frac = (min_x - start) / (end - start); int64_t fill_len = (int64_t) floor (veclen * frac); fill_len = min (fill_len, (int64_t)veclen); @@ -264,11 +276,11 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const vec += fill_len; } - if (veclen && x1 > max_x) { + if (veclen && end > max_x) { /* fill some end section of the array with the default or final value */ - double frac = (x1 - max_x) / (x1 - x0); + double frac = (end - max_x) / (end - start); int64_t fill_len = (int64_t) floor (original_veclen * frac); float val; @@ -282,14 +294,14 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const veclen -= fill_len; } - lx = max (min_x, x0); - hx = min (max_x, x1); + lx = max (min_x, start); + hx = min (max_x, end); if (npoints == 2) { - const double lpos = _list.events().front()->when; + const double lpos = _list.events().front()->when.val(); const double lval = _list.events().front()->value; - const double upos = _list.events().back()->when; + const double upos = _list.events().back()->when.val(); const double uval = _list.events().back()->value; /* dx that we are using */ @@ -360,18 +372,19 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const rx = lx; - double dx = 0; + double dx = 0.; + if (veclen > 1) { dx = (hx - lx) / (veclen - 1); } for (i = 0; i < veclen; ++i, rx += dx) { - vec[i] = multipoint_eval (rx); + vec[i] = multipoint_eval (x0.is_beats() ? Temporal::timepos_t::from_ticks (rx) : Temporal::timepos_t::from_superclock (rx)); } } double -Curve::multipoint_eval (double x) const +Curve::multipoint_eval (Temporal::timepos_t const & x) const { pair range; @@ -427,8 +440,11 @@ Curve::multipoint_eval (double x) const return before->value; } - double tdelta = x - before->when; - double trange = after->when - before->when; + double aw = after->when.val(); + double bw = before->when.val(); + + double tdelta = x.val() - bw; + double trange = aw - bw; switch (_list.interpolation()) { case ControlList::Discrete: @@ -440,8 +456,9 @@ Curve::multipoint_eval (double x) const case ControlList::Curved: if (after->coeff) { ControlEvent* ev = after; - double x2 = x * x; - return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x); +#warning NUTEMPO fixme possible overflow ... multiplyng two position types .. also, units? + double x2 = x.val() * x.val(); + return ev->coeff[0] + (ev->coeff[1] * x.val()) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x.val()); } /* fallthrough */ case ControlList::Linear: diff --git a/libs/evoral/Sequence.cc b/libs/evoral/Sequence.cc index 2ba0b0bb24..cb7ad0ba17 100644 --- a/libs/evoral/Sequence.cc +++ b/libs/evoral/Sequence.cc @@ -58,7 +58,7 @@ using namespace PBD; XXX: This is a hack. The time should probably be expressed in seconds rather than beats, and should be configurable etc. etc. */ -static double const time_between_interpolated_controller_outputs = 1.0 / 256; +static Temporal::Beats const time_between_interpolated_controller_outputs = Temporal::Beats::ticks (256); namespace Evoral { @@ -78,11 +78,11 @@ Sequence