changes to compile against libtemporal and use of timepos_t/timecnt_t

This commit is contained in:
Paul Davis 2020-08-24 14:40:03 -06:00
parent e111123b54
commit 6b09642406
13 changed files with 460 additions and 722 deletions

View file

@ -41,10 +41,10 @@ Control::Control(const Parameter& parameter,
/** Get the currently effective value (ie the one that corresponds to current output) /** Get the currently effective value (ie the one that corresponds to current output)
*/ */
double double
Control::get_double (bool from_list, double frame) const Control::get_double (bool from_list, Temporal::timepos_t when) const
{ {
if (from_list) { if (from_list) {
return _list->eval(frame); return _list->eval (when);
} else { } else {
return _user_value; return _user_value;
} }
@ -52,7 +52,7 @@ Control::get_double (bool from_list, double frame) const
void 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; _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)) { if (to_list && (!_list->in_write_pass() || _list->descriptor().toggled)) {
_list->add (frame, value, false); _list->add (when, value, false);
} }
} }

View file

@ -33,7 +33,7 @@
#define isnan_local std::isnan #define isnan_local std::isnan
#endif #endif
#define GUARD_POINT_DELTA 64 #define GUARD_POINT_DELTA Temporal::timecnt_t::from_samples (64)
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
@ -48,6 +48,8 @@
#include "pbd/control_math.h" #include "pbd/control_math.h"
#include "pbd/compose.h" #include "pbd/compose.h"
#include "pbd/error.h"
#include "pbd/i18n.h"
#include "pbd/debug.h" #include "pbd/debug.h"
using namespace std; using namespace std;
@ -60,10 +62,11 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b)
return a->when < b->when; 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) : _parameter(id)
, _desc(desc) , _desc(desc)
, _interpolation (default_interpolation ()) , _interpolation (default_interpolation ())
, _time_style (ts)
, _curve(0) , _curve(0)
{ {
_frozen = 0; _frozen = 0;
@ -85,6 +88,7 @@ ControlList::ControlList (const ControlList& other)
: _parameter(other._parameter) : _parameter(other._parameter)
, _desc(other._desc) , _desc(other._desc)
, _interpolation(other._interpolation) , _interpolation(other._interpolation)
, _time_style (other._time_style)
, _curve(0) , _curve(0)
{ {
_frozen = 0; _frozen = 0;
@ -103,10 +107,11 @@ ControlList::ControlList (const ControlList& other)
copy_events (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) : _parameter(other._parameter)
, _desc(other._desc) , _desc(other._desc)
, _interpolation(other._interpolation) , _interpolation(other._interpolation)
, _time_style (other._time_style)
, _curve(0) , _curve(0)
{ {
_frozen = 0; _frozen = 0;
@ -145,9 +150,9 @@ ControlList::~ControlList()
} }
boost::shared_ptr<ControlList> boost::shared_ptr<ControlList>
ControlList::create(const Parameter& id, const ParameterDescriptor& desc) ControlList::create(const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain time_style)
{ {
return boost::shared_ptr<ControlList>(new ControlList(id, desc)); return boost::shared_ptr<ControlList>(new ControlList(id, desc, time_style));
} }
bool bool
@ -251,21 +256,24 @@ ControlList::clear ()
} }
void void
ControlList::x_scale (double factor) ControlList::x_scale (Temporal::ratio_t const & factor)
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
_x_scale (factor); _x_scale (factor);
} }
bool bool
ControlList::extend_to (double when) ControlList::extend_to (Temporal::timepos_t const & end)
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
if (_events.empty() || _events.back()->when == when) {
if (_events.empty() || _events.back()->when == end) {
return false; return false;
} }
double factor = when / _events.back()->when;
Temporal::ratio_t factor (end.val(), _events.back()->when.val());
_x_scale (factor); _x_scale (factor);
return true; return true;
} }
@ -330,10 +338,10 @@ ControlList::list_merge (ControlList const& other, boost::function<double(double
} }
void void
ControlList::_x_scale (double factor) ControlList::_x_scale (Temporal::ratio_t const & factor)
{ {
for (iterator i = _events.begin(); i != _events.end(); ++i) { for (iterator i = _events.begin(); i != _events.end(); ++i) {
(*i)->when *= factor; (*i)->when = (*i)->when.operator* (factor);
} }
mark_dirty (); mark_dirty ();
@ -377,9 +385,13 @@ ControlList::thin (double thinning_factor)
/* compute the area of the triangle formed by 3 points /* compute the area of the triangle formed by 3 points
*/ */
double area = fabs ((prevprev->when * (prev->value - cur->value)) + const double ppw = prevprev->when.val();
(prev->when * (cur->value - prevprev->value)) + const double pw = prev->when.val();
(cur->when * (prevprev->value - prev->value))); 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) { if (area < thinning_factor) {
iterator tmp = pprev; iterator tmp = pprev;
@ -415,11 +427,11 @@ ControlList::thin (double thinning_factor)
} }
void 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); Glib::Threads::RWLock::WriterLock lm (_lock);
/* to be used only for loading pre-sorted data from saved state */ /* 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 (); mark_dirty ();
if (_frozen) { if (_frozen) {
@ -459,9 +471,10 @@ ControlList::unlocked_remove_duplicates ()
} }
void void
ControlList::start_write_pass (double when) ControlList::start_write_pass (Temporal::timepos_t const & time)
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); 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)); 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 (_in_write_pass && !new_write_pass) {
#if 1 #if 1
add_guard_point (when, 0); // also sets most_recent_insert_iterator add_guard_point (when, std::numeric_limits<Temporal::timecnt_t>::min()); // also sets most_recent_insert_iterator
#else #else
const ControlEvent cp (when, 0.0); const ControlEvent cp (when, 0.0);
most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
@ -490,7 +503,7 @@ ControlList::start_write_pass (double when)
} }
void 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"); DEBUG_TRACE (DEBUG::ControlList, "write pass finished\n");
@ -503,7 +516,7 @@ ControlList::write_pass_finished (double /*when*/, double thinning_factor)
} }
void 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)); 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) { if (yn && add_point) {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
add_guard_point (when, 0); add_guard_point (when, std::numeric_limits<Temporal::timecnt_t>::min());
} }
} }
void 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 // caller needs to hold writer-lock
if (offset < 0 && when < offset) {
if (offset.negative() && when < offset) {
return; 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 */ /* check if there are points between when + offset .. when */
ControlEvent cp (when + offset, 0.0); ControlEvent cp (when + offset, 0.0);
iterator s; iterator s;
@ -533,7 +551,7 @@ ControlList::add_guard_point (double when, double offset)
cp.when = when; cp.when = when;
e = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); e = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
if (s != e) { 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; return;
} }
} }
@ -601,11 +619,12 @@ ControlList::in_write_pass () const
} }
bool 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 */ /* this is for making changes from a graphical line editor */
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
Temporal::timepos_t when = time;
ControlEvent cp (when, 0.0f); ControlEvent cp (when, 0.0f);
iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); 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) { if (when >= 1) {
_events.insert (_events.end(), new ControlEvent (0, value)); _events.insert (_events.end(), new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), value));
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added value %2 at zero\n", this, 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 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 // caller needs to hold writer-lock
if (most_recent_insert_iterator != _events.end()) { 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 /* 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 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 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. */ /** If we would just be adding to a straight line, move the previous point instead. */
bool 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 // caller needs to hold writer-lock
if (_events.empty()) { if (_events.empty()) {
return false; return false;
@ -702,8 +724,10 @@ ControlList::maybe_insert_straight_line (double when, double value)
} }
ControlList::iterator 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 // caller needs to hold writer-lock
while (iter != _events.end()) { while (iter != _events.end()) {
if ((*iter)->when < when) { if ((*iter)->when < when) {
@ -723,8 +747,10 @@ ControlList::erase_from_iterator_to (iterator iter, double when)
* control surface (GUI, MIDI, OSC etc) * control surface (GUI, MIDI, OSC etc)
*/ */
void 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 */ /* clamp new value to allowed range */
value = std::min ((double)_desc.upper, std::max ((double)_desc.lower, value)); 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 (when >= 1) {
if (_desc.toggled) { if (_desc.toggled) {
const double opp_val = ((value >= 0.5) ? 1.0 : 0.0); 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<Temporal::timepos_t>::min(), opp_val));
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added toggled value %2 at zero\n", this, opp_val)); DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added toggled value %2 at zero\n", this, opp_val));
} else { } else {
_events.insert (_events.end(), new ControlEvent (0, value)); _events.insert (_events.end(), new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), value));
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _desc.normal)); 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 */ /* first write in a write pass: add guard point if requested */
if (with_guards) { if (with_guards) {
add_guard_point (insert_position, 0); add_guard_point (insert_position, std::numeric_limits<Temporal::timecnt_t>::min());
did_write_during_pass = true; did_write_during_pass = true;
} else { } else {
/* not adding a guard, but we need to set iterator appropriately */ /* 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 */ /** Erase the first event which matches the given time and value */
void void
ControlList::erase (double when, double value) ControlList::erase (Temporal::timepos_t const & time, double value)
{ {
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
Temporal::timepos_t when = time;
iterator i = begin (); iterator i = begin ();
while (i != end() && ((*i)->when != when || (*i)->value != value)) { while (i != end() && ((*i)->when != when || (*i)->value != value)) {
@ -928,12 +955,13 @@ ControlList::erase (double when, double value)
} }
void void
ControlList::erase_range (double start, double endt) ControlList::erase_range (Temporal::timepos_t const & start, Temporal::timepos_t const & endt)
{ {
bool erased = false; bool erased = false;
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
erased = erase_range_internal (start, endt, _events); erased = erase_range_internal (start, endt, _events);
if (erased) { if (erased) {
@ -947,14 +975,32 @@ ControlList::erase_range (double start, double endt)
} }
bool 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; bool erased = false;
ControlEvent cp (start, 0.0f);
iterator s; iterator s;
iterator e; 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()) { if ((s = lower_bound (events.begin(), events.end(), &cp, time_comparator)) != events.end()) {
cp.when = endt; cp.when = endt;
e = upper_bound (events.begin(), events.end(), &cp, time_comparator); e = upper_bound (events.begin(), events.end(), &cp, time_comparator);
events.erase (s, e); events.erase (s, e);
@ -968,7 +1014,7 @@ ControlList::erase_range_internal (double start, double endt, EventList & events
} }
void void
ControlList::slide (iterator before, double distance) ControlList::slide (iterator before, Temporal::timecnt_t const & distance)
{ {
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
@ -977,8 +1023,10 @@ ControlList::slide (iterator before, double distance)
return; return;
} }
Temporal::timecnt_t wd = distance;
while (before != _events.end()) { while (before != _events.end()) {
(*before)->when += distance; (*before)->when += wd;
++before; ++before;
} }
@ -988,19 +1036,22 @@ ControlList::slide (iterator before, double distance)
} }
void 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); Glib::Threads::RWLock::WriterLock lm (_lock);
double v0, v1; double v0, v1;
if (frames < 0) {
if (distance.negative()) {
/* Route::shift () with negative shift is used /* Route::shift () with negative shift is used
* for "remove time". The time [pos.. pos-frames] is removed. * for "remove time". The time [pos.. pos-frames] is removed.
* and everyhing after, moved backwards. * and everyhing after, moved backwards.
*/ */
v0 = unlocked_eval (pos); v0 = unlocked_eval (pos);
v1 = unlocked_eval (pos - frames); v1 = unlocked_eval (pos.earlier (distance));
erase_range_internal (pos, pos - frames, _events); erase_range_internal (pos, pos.earlier (distance), _events);
} else { } else {
v0 = v1 = unlocked_eval (pos); v0 = v1 = unlocked_eval (pos);
} }
@ -1012,23 +1063,23 @@ ControlList::shift (double pos, double frames)
dst_guard_exists = true; dst_guard_exists = true;
} }
if ((*i)->when >= pos) { if ((*i)->when >= pos) {
(*i)->when += frames; (*i)->when += distance;
} }
} }
/* add guard-points to retain shape, if needed */ /* add guard-points to retain shape, if needed */
if (frames > 0) { if (distance.positive()) {
ControlEvent cp (pos, 0.0); ControlEvent cp (pos, 0.0);
iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
if (s != _events.end ()) { if (s != _events.end ()) {
_events.insert (s, new ControlEvent (pos, v0)); _events.insert (s, new ControlEvent (pos, v0));
} }
pos += frames; pos += distance;
} else if (frames < 0 && pos > 0) { } else if (distance.negative() && pos > 0) {
ControlEvent cp (pos - 1, 0.0); ControlEvent cp (pos.decrement(), 0.0);
iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
if (s != _events.end ()) { if (s != _events.end ()) {
_events.insert (s, new ControlEvent (pos - 1, v0)); _events.insert (s, new ControlEvent (pos.decrement(), v0));
} }
} }
if (!dst_guard_exists) { if (!dst_guard_exists) {
@ -1044,7 +1095,7 @@ ControlList::shift (double pos, double frames)
} }
void 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 /* note: we assume higher level logic is in place to avoid this
* reordering the time-order of control events in the list. ie. all * 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); Glib::Threads::RWLock::WriterLock lm (_lock);
Temporal::timepos_t when = time;
(*iter)->when = when; (*iter)->when = when;
(*iter)->value = val; (*iter)->value = val;
@ -1077,10 +1129,11 @@ ControlList::modify (iterator iter, double when, double val)
} }
std::pair<ControlList::iterator,ControlList::iterator> std::pair<ControlList::iterator,ControlList::iterator>
ControlList::control_points_adjacent (double xval) ControlList::control_points_adjacent (Temporal::timepos_t const & xtime)
{ {
Glib::Threads::RWLock::ReaderLock lm (_lock); Glib::Threads::RWLock::ReaderLock lm (_lock);
iterator i; iterator i;
Temporal::timepos_t xval = xtime;
ControlEvent cp (xval, 0.0f); ControlEvent cp (xval, 0.0f);
std::pair<iterator,iterator> ret; std::pair<iterator,iterator> ret;
@ -1140,10 +1193,10 @@ ControlList::thaw ()
void void
ControlList::mark_dirty () const ControlList::mark_dirty () const
{ {
_lookup_cache.left = -1; _lookup_cache.left = std::numeric_limits<Temporal::timepos_t>::max();
_lookup_cache.range.first = _events.end(); _lookup_cache.range.first = _events.end();
_lookup_cache.range.second = _events.end(); _lookup_cache.range.second = _events.end();
_search_cache.left = -1; _search_cache.left = std::numeric_limits<Temporal::timepos_t>::max();
_search_cache.first = _events.end(); _search_cache.first = _events.end();
if (_curve) { if (_curve) {
@ -1152,10 +1205,11 @@ ControlList::mark_dirty () const
} }
void void
ControlList::truncate_end (double last_coordinate) ControlList::truncate_end (Temporal::timepos_t const & last_time)
{ {
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
Temporal::timepos_t last_coordinate = last_time;
ControlEvent cp (last_coordinate, 0); ControlEvent cp (last_coordinate, 0);
ControlList::reverse_iterator i; ControlList::reverse_iterator i;
double last_val; double last_val;
@ -1254,13 +1308,14 @@ ControlList::truncate_end (double last_coordinate)
} }
void void
ControlList::truncate_start (double overall_length) ControlList::truncate_start (Temporal::timecnt_t const & overall)
{ {
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
iterator i; iterator i;
double first_legal_value; double first_legal_value;
double first_legal_coordinate; Temporal::timepos_t first_legal_coordinate;
Temporal::timepos_t overall_length (overall);
if (_events.empty()) { if (_events.empty()) {
/* nothing to truncate */ /* nothing to truncate */
@ -1274,7 +1329,7 @@ ControlList::truncate_start (double overall_length)
/* growing at front: duplicate first point. shift all others */ /* 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; uint32_t np;
for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++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) { if (np < 2) {
/* less than 2 points: add a new point */ /* 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<Temporal::timepos_t>::min(), _events.front()->value));
} else { } else {
@ -1301,7 +1356,7 @@ ControlList::truncate_start (double overall_length)
_events.front()->when = 0; _events.front()->when = 0;
} else { } else {
/* leave non-flat segment in place, add a new leading point. */ /* 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<Temporal::timepos_t>::min(), _events.front()->value));
} }
} }
@ -1309,7 +1364,7 @@ ControlList::truncate_start (double overall_length)
/* shrinking at front */ /* 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 = unlocked_eval (first_legal_coordinate);
first_legal_value = max ((double)_desc.lower, first_legal_value); first_legal_value = max ((double)_desc.lower, first_legal_value);
first_legal_value = min ((double)_desc.upper, 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) { 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 */ /* 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<Temporal::timepos_t>::min(), first_legal_value));
} }
unlocked_invalidate_insert_iterator (); unlocked_invalidate_insert_iterator ();
@ -1354,13 +1409,15 @@ ControlList::truncate_start (double overall_length)
} }
double double
ControlList::unlocked_eval (double x) const ControlList::unlocked_eval (Temporal::timepos_t const & xtime) const
{ {
pair<EventList::iterator,EventList::iterator> range; pair<EventList::iterator,EventList::iterator> range;
int32_t npoints; int32_t npoints;
double lpos, upos; Temporal::timepos_t lpos, upos;
double lval, uval; double lval, uval;
double fraction; double fraction;
double xx;
double ll;
const_iterator length_check_iter = _events.begin(); const_iterator length_check_iter = _events.begin();
for (npoints = 0; npoints < 4; ++npoints, ++length_check_iter) { for (npoints = 0; npoints < 4; ++npoints, ++length_check_iter) {
@ -1377,9 +1434,9 @@ ControlList::unlocked_eval (double x) const
return _events.front()->value; return _events.front()->value;
case 2: case 2:
if (x >= _events.back()->when) { if (xtime >= _events.back()->when) {
return _events.back()->value; return _events.back()->value;
} else if (x <= _events.front()->when) { } else if (xtime <= _events.front()->when) {
return _events.front()->value; return _events.front()->value;
} }
@ -1388,7 +1445,10 @@ ControlList::unlocked_eval (double x) const
upos = _events.back()->when; upos = _events.back()->when;
uval = _events.back()->value; 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) { switch (_interpolation) {
case Discrete: case Discrete:
@ -1405,13 +1465,13 @@ ControlList::unlocked_eval (double x) const
} }
default: default:
if (x >= _events.back()->when) { if (xtime >= _events.back()->when) {
return _events.back()->value; return _events.back()->value;
} else if (x <= _events.front()->when) { } else if (xtime <= _events.front()->when) {
return _events.front()->value; return _events.front()->value;
} }
return multipoint_eval (x); return multipoint_eval (xtime);
} }
abort(); /*NOTREACHED*/ /* stupid gcc */ abort(); /*NOTREACHED*/ /* stupid gcc */
@ -1419,35 +1479,35 @@ ControlList::unlocked_eval (double x) const
} }
double 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 uval, lval;
double fraction; double fraction;
/* "Stepped" lookup (no interpolation) */ /* "Stepped" lookup (no interpolation) */
/* FIXME: no cache. significant? */ /* FIXME: no cache. significant? */
if (_interpolation == Discrete) { 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); EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
// shouldn't have made it to multipoint_eval // shouldn't have made it to multipoint_eval
assert(i != _events.end()); assert(i != _events.end());
if (i == _events.begin() || (*i)->when == x) if (i == _events.begin() || (*i)->when == xtime)
return (*i)->value; return (*i)->value;
else else
return (*(--i))->value; 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) */ * this was called (or if the lookup cache has been marked "dirty" (left<0) */
if ((_lookup_cache.left < 0) || if ((_lookup_cache.left == std::numeric_limits<Temporal::timepos_t>::max()) ||
((_lookup_cache.left > x) || ((_lookup_cache.left > xtime) ||
(_lookup_cache.range.first == _events.end()) || (_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); _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 */ /* x does not exist within the list as a control point */
_lookup_cache.left = x; _lookup_cache.left = xtime;
if (range.first != _events.begin()) { if (range.first != _events.begin()) {
--range.first; --range.first;
@ -1478,7 +1538,7 @@ ControlList::multipoint_eval (double x) const
upos = (*range.second)->when; upos = (*range.second)->when;
uval = (*range.second)->value; 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) { switch (_interpolation) {
case Logarithmic: case Logarithmic:
@ -1499,20 +1559,22 @@ ControlList::multipoint_eval (double x) const
} }
/* x is a control point in the data */ /* x is a control point in the data */
_lookup_cache.left = -1; _lookup_cache.left = std::numeric_limits<Temporal::timepos_t>::max();
return (*range.first)->value; return (*range.first)->value;
} }
void 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()) { if (_events.empty()) {
/* Empty, nothing to cache, move to end. */ /* Empty, nothing to cache, move to end. */
_search_cache.first = _events.end(); _search_cache.first = _events.end();
_search_cache.left = 0; _search_cache.left = Temporal::timepos_t();
return; return;
} else if ((_search_cache.left < 0) || (_search_cache.left > start)) { } else if ((_search_cache.left == std::numeric_limits<Temporal::timepos_t>::max()) || (_search_cache.left > start)) {
/* Marked dirty (left < 0), or we're too far forward, re-search. */ /* Marked dirty (left == max), or we're too far forward, re-search. */
const ControlEvent start_point (start, 0); 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). * \return true if event is found (and \a x and \a y are valid).
*/ */
bool 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); build_search_cache_if_necessary (start);
if (_search_cache.first != _events.end()) { 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 /* Move left of cache to this point
* (Optimize for immediate call this cycle within range) */ * (Optimize for immediate call this cycle within range) */
_search_cache.left = x; _search_cache.left = first->when;
++_search_cache.first; ++_search_cache.first;
assert(x >= start); 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). * \return true if event is found (and \a x and \a y are valid).
*/ */
bool 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; // 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); return rt_safe_earliest_event_discrete_unlocked (start + min_x_delta, x, y, inclusive);
} }
if (min_x_delta > 0) { if (min_x_delta > 0) {
/* if there is an event between [start and start + min_x_delta], use it, /* if there is an event between [start and start + min_x_delta], use it,
* otherwise interpolate at start + min_x_delta * 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; y = first->value;
/* Move left of cache to this point /* Move left of cache to this point
* (Optimize for immediate call this cycle within range) */ * (Optimize for immediate call this cycle within range) */
_search_cache.left = x; _search_cache.left = first->when;
return true; return true;
} else if (next->when < start || (!inclusive && next->when == start)) { } else if (next->when < start || (!inclusive && next->when == start)) {
/* "Next" is before the start, no points left. */ /* "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; y = next->value;
/* Move left of cache to this point /* Move left of cache to this point
* (Optimize for immediate call this cycle within range) */ * (Optimize for immediate call this cycle within range) */
_search_cache.left = x; _search_cache.left = next->when;
return true; return true;
} else { } else {
return false; 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; //cerr << "start y: " << start_y << endl;
//y = first->value + (slope * fabs(start - first->when)); //y = first->value + (slope * fabs(start - first->when));
y = first->value; y = first->value;
if (first->value < next->value) // ramping up if (first->value < next->value) { // ramping up
y = ceil(y); y = ceil(y);
else // ramping down } else { // ramping down
y = floor(y); 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; y += 1.0;
else // ramping down } else { // ramping down
y -= 1.0; 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 #if 0
@ -1695,20 +1768,21 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|| (y <= first->value && y >= next->value) ); || (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) { if (past_start) {
/* Move left of cache to this point /* Move left of cache to this point
* (Optimize for immediate call this cycle within range) */ * (Optimize for immediate call this cycle within range) */
_search_cache.left = x; _search_cache.left = x;
assert(inclusive ? x >= start : x > start); assert(inclusive ? x >= start_time : x > start_time);
return true; return true;
} else { } else {
if (inclusive) { if (inclusive) {
x = next->when; x = next->when;
_search_cache.left = next->when;
} else { } else {
x = start; x = start;
_search_cache.left = x;
} }
_search_cache.left = x;
return true; 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. * @param op 0 = cut, 1 = copy, 2 = clear.
*/ */
boost::shared_ptr<ControlList> boost::shared_ptr<ControlList>
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<ControlList> nal = create (_parameter, _desc); boost::shared_ptr<ControlList> nal = create (_parameter, _desc, _time_style);
iterator s, e; iterator s, e;
Temporal::timepos_t start = start_time;
Temporal::timepos_t end = end_time;
ControlEvent cp (start, 0.0); ControlEvent cp (start, 0.0);
{ {
@ -1773,7 +1849,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
} }
if (op != 2) { // ! clear if (op != 2) { // ! clear
nal->_events.push_back (new ControlEvent (0, val)); nal->_events.push_back (new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), val));
} }
} }
@ -1784,7 +1860,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
*/ */
if (op != 2) { 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) { 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 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> boost::shared_ptr<ControlList>
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); return cut_copy_clear (start, end, 0);
} }
boost::shared_ptr<ControlList> boost::shared_ptr<ControlList>
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); return cut_copy_clear (start, end, 1);
} }
void 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); cut_copy_clear (start, end, 2);
} }
/** @param pos Position in model coordinates */ /** @param pos Position in model coordinates */
bool bool
ControlList::paste (const ControlList& alist, double pos) ControlList::paste (const ControlList& alist, Temporal::timepos_t const & time)
{ {
if (alist._events.empty()) { if (alist._events.empty()) {
return false; return false;
@ -1850,7 +1926,8 @@ ControlList::paste (const ControlList& alist, double pos)
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
iterator where; iterator where;
iterator prev; iterator prev;
double end = 0; Temporal::timepos_t end;
Temporal::timepos_t pos = time;
ControlEvent cp (pos, 0.0); ControlEvent cp (pos, 0.0);
where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator); 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) * @param return true if anything was changed, otherwise false (ie nothing needed changing)
*/ */
bool bool
ControlList::move_ranges (const list< RangeMove<double> >& movements) ControlList::move_ranges (const list< Temporal::RangeMove> & movements)
{ {
typedef list< RangeMove<double> > RangeMoveList; typedef list<Temporal::RangeMove> RangeMoveList;
{ {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
@ -1923,11 +2000,17 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
bool things_erased = false; bool things_erased = false;
for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) { 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; 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; things_erased = true;
} }
} }
@ -1940,14 +2023,48 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
/* copy the events into the new list */ /* copy the events into the new list */
for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) { for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) {
iterator j = old_events.begin (); iterator j = old_events.begin ();
const double limit = i->from + i->length;
const double dx = i->to - i->from; const Temporal::timepos_t limit = i->from + i->length;
while (j != old_events.end () && (*j)->when <= limit) { const Temporal::timecnt_t dx = i->from.distance (i->to);
if ((*j)->when >= i->from) {
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); 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); _events.push_back (ev);
} }
++j; ++j;
} }
} }
@ -2044,9 +2161,8 @@ ControlList::dump (ostream& o)
/* NOT LOCKED ... for debugging only */ /* NOT LOCKED ... for debugging only */
for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) { 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 } // namespace Evoral

View file

@ -62,20 +62,23 @@ Curve::solve () const
(www.korf.co.uk/spline.pdf) for more details. (www.korf.co.uk/spline.pdf) for more details.
*/ */
vector<double> x(npoints); vector<Temporal::timepos_t> x (npoints);
vector<double> y(npoints); vector<double> y(npoints);
uint32_t i; uint32_t i;
ControlList::EventList::const_iterator xx; ControlList::EventList::const_iterator xx;
for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) { for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
x[i] = (double) (*xx)->when; x[i] = (*xx)->when;
y[i] = (double) (*xx)->value; y[i] = (*xx)->value;
} }
double lp0, lp1, fpone; double lp0, lp1, fpone;
double xd;
lp0 = (x[1] - x[0])/(y[1] - y[0]); xd = x[0].distance (x[1]).magnitude();
lp1 = (x[2] - x[1])/(y[2] - y[1]); lp0 = xd/(y[1] - y[0]);
xd = x[1].distance (x[2]).magnitude();
lp1 = xd/(y[2] - y[1]);
if (lp0*lp1 < 0) { if (lp0*lp1 < 0) {
fpone = 0; fpone = 0;
@ -92,9 +95,12 @@ Curve::solve () const
double ydelta; /* ditto */ double ydelta; /* ditto */
double fppL, fppR; double fppL, fppR;
double fpi; double fpi;
double xi = x[i].val();
double xim1;
if (i > 0) { if (i > 0) {
xdelta = x[i] - x[i-1]; xim1 = x[i-1].val();
xdelta = xi - xim1;
xdelta2 = xdelta * xdelta; xdelta2 = xdelta * xdelta;
ydelta = y[i] - y[i-1]; ydelta = y[i] - y[i-1];
} }
@ -105,7 +111,7 @@ Curve::solve () const
/* first segment */ /* 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 */ /* we don't store coefficients for i = 0 */
@ -121,7 +127,9 @@ Curve::solve () const
/* all other segments */ /* 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); double slope_after = (xdelta / ydelta);
if (slope_after * slope_before < 0.0) { if (slope_after * slope_before < 0.0) {
@ -145,22 +153,22 @@ Curve::solve () const
double b, c, d; double b, c, d;
d = (fppR - fppL) / (6 * xdelta); 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 xim12, xim13;
double xi2, xi3; double xi2, xi3;
xim12 = x[i-1] * x[i-1]; /* "x[i-1] squared" */ xim12 = xim1 * xim1; /* "x[i-1] squared" */
xim13 = xim12 * x[i-1]; /* "x[i-1] cubed" */ xim13 = xim12 * xim1; /* "x[i-1] cubed" */
xi2 = x[i] * x[i]; /* "x[i] squared" */ xi2 = xi * xi; /* "x[i] squared" */
xi3 = xi2 * x[i]; /* "x[i] cubed" */ xi3 = xi2 * xi; /* "x[i] cubed" */
b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta; b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
/* store */ /* store */
(*xx)->create_coeffs(); (*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[1] = b;
(*xx)->coeff[2] = c; (*xx)->coeff[2] = c;
(*xx)->coeff[3] = d; (*xx)->coeff[3] = d;
@ -174,7 +182,7 @@ Curve::solve () const
} }
bool 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); 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 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()); Glib::Threads::RWLock::ReaderLock lm(_list.lock());
_get_vector (x0, x1, vec, veclen); _get_vector (x0, x1, vec, veclen);
} }
void 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 i;
int32_t original_veclen; int32_t original_veclen;
int32_t npoints; 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 */ /* events is now known not to be empty */
max_x = _list.events().back()->when; max_x = _list.events().back()->when.val();
min_x = _list.events().front()->when; 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 */ /* totally past the end - just fill the entire array with the final value */
for (int32_t i = 0; i < veclen; ++i) { for (int32_t i = 0; i < veclen; ++i) {
vec[i] = _list.events().back()->value; vec[i] = _list.events().back()->value;
@ -233,7 +245,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
return; return;
} }
if (x1 < min_x) { if (end < min_x) {
/* totally before the first event - fill the entire array with /* totally before the first event - fill the entire array with
* the initial value. * the initial value.
*/ */
@ -245,13 +257,13 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
original_veclen = veclen; original_veclen = veclen;
if (x0 < min_x) { if (start < min_x) {
/* fill some beginning section of the array with the /* fill some beginning section of the array with the
initial (used to be default) value 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); int64_t fill_len = (int64_t) floor (veclen * frac);
fill_len = min (fill_len, (int64_t)veclen); 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; 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 */ /* 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); int64_t fill_len = (int64_t) floor (original_veclen * frac);
float val; float val;
@ -282,14 +294,14 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
veclen -= fill_len; veclen -= fill_len;
} }
lx = max (min_x, x0); lx = max (min_x, start);
hx = min (max_x, x1); hx = min (max_x, end);
if (npoints == 2) { 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 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; const double uval = _list.events().back()->value;
/* dx that we are using */ /* dx that we are using */
@ -360,18 +372,19 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
rx = lx; rx = lx;
double dx = 0; double dx = 0.;
if (veclen > 1) { if (veclen > 1) {
dx = (hx - lx) / (veclen - 1); dx = (hx - lx) / (veclen - 1);
} }
for (i = 0; i < veclen; ++i, rx += dx) { 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 double
Curve::multipoint_eval (double x) const Curve::multipoint_eval (Temporal::timepos_t const & x) const
{ {
pair<ControlList::EventList::const_iterator,ControlList::EventList::const_iterator> range; pair<ControlList::EventList::const_iterator,ControlList::EventList::const_iterator> range;
@ -427,8 +440,11 @@ Curve::multipoint_eval (double x) const
return before->value; return before->value;
} }
double tdelta = x - before->when; double aw = after->when.val();
double trange = after->when - before->when; double bw = before->when.val();
double tdelta = x.val() - bw;
double trange = aw - bw;
switch (_list.interpolation()) { switch (_list.interpolation()) {
case ControlList::Discrete: case ControlList::Discrete:
@ -440,8 +456,9 @@ Curve::multipoint_eval (double x) const
case ControlList::Curved: case ControlList::Curved:
if (after->coeff) { if (after->coeff) {
ControlEvent* ev = after; ControlEvent* ev = after;
double x2 = x * x; #warning NUTEMPO fixme possible overflow ... multiplyng two position types .. also, units?
return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x); 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 */ /* fallthrough */
case ControlList::Linear: case ControlList::Linear:

View file

@ -58,7 +58,7 @@ using namespace PBD;
XXX: This is a hack. The time should probably be expressed in XXX: This is a hack. The time should probably be expressed in
seconds rather than beats, and should be configurable etc. etc. 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 { namespace Evoral {
@ -78,11 +78,11 @@ Sequence<Time>::const_iterator::const_iterator()
/** @param force_discrete true to force ControlLists to use discrete evaluation, otherwise false to get them to use their configured mode */ /** @param force_discrete true to force ControlLists to use discrete evaluation, otherwise false to get them to use their configured mode */
template<typename Time> template<typename Time>
Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq,
Time t, Time t,
bool force_discrete, bool force_discrete,
const std::set<Evoral::Parameter>& filtered, const std::set<Evoral::Parameter>& filtered,
const std::set<WeakNotePtr>* active_notes) std::set<WeakNotePtr> const * active_notes)
: _seq(&seq) : _seq(&seq)
, _active_patch_change_message (0) , _active_patch_change_message (0)
, _type(NIL) , _type(NIL)
@ -103,8 +103,7 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
// Add currently active notes, if given // Add currently active notes, if given
if (active_notes) { if (active_notes) {
for (typename std::set<WeakNotePtr>::const_iterator i = active_notes->begin(); for (typename std::set<WeakNotePtr>::const_iterator i = active_notes->begin(); i != active_notes->end(); ++i) {
i != active_notes->end(); ++i) {
NotePtr note = i->lock(); NotePtr note = i->lock();
if (note && note->time() <= t && note->end_time() > t) { if (note && note->time() <= t && note->end_time() > t) {
_active_notes.push(note); _active_notes.push(note);
@ -114,7 +113,6 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
// Find first note which begins at or after t // Find first note which begins at or after t
_note_iter = seq.note_lower_bound(t); _note_iter = seq.note_lower_bound(t);
// Find first sysex event at or after t // Find first sysex event at or after t
for (typename Sequence<Time>::SysExes::const_iterator i = seq.sysexes().begin(); for (typename Sequence<Time>::SysExes::const_iterator i = seq.sysexes().begin();
i != seq.sysexes().end(); ++i) { i != seq.sysexes().end(); ++i) {
@ -138,7 +136,8 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
_control_iters.reserve(seq._controls.size()); _control_iters.reserve(seq._controls.size());
bool found = false; bool found = false;
size_t earliest_control_index = 0; size_t earliest_control_index = 0;
double earliest_control_x = DBL_MAX; Temporal::timepos_t earliest_control_x = std::numeric_limits<Temporal::timepos_t>::max();
for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) { for (Controls::const_iterator i = seq._controls.begin(); i != seq._controls.end(); ++i) {
if (filtered.find (i->first) != filtered.end()) { if (filtered.find (i->first) != filtered.end()) {
@ -147,21 +146,21 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
} }
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: control: %1\n", seq._type_map.to_symbol(i->first))); DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: control: %1\n", seq._type_map.to_symbol(i->first)));
double x, y; Temporal::timepos_t xtime;
double y;
bool ret; bool ret;
if (_force_discrete || i->second->list()->interpolation() == ControlList::Discrete) { if (_force_discrete || i->second->list()->interpolation() == ControlList::Discrete) {
ret = i->second->list()->rt_safe_earliest_event_discrete_unlocked (t.to_double(), x, y, true); ret = i->second->list()->rt_safe_earliest_event_discrete_unlocked (Temporal::timepos_t (t), xtime, y, true);
} else { } else {
ret = i->second->list()->rt_safe_earliest_event_linear_unlocked(t.to_double(), x, y, true); ret = i->second->list()->rt_safe_earliest_event_linear_unlocked(Temporal::timepos_t (t), xtime, y, true);
} }
if (!ret) { if (!ret) {
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 (size %2) has no events past %3\n", DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 (size %2) has no events past %3\n",
i->first.id(), i->second->list()->size(), t)); i->first.id(), i->second->list()->size(), t));
continue; continue;
} }
assert(x >= 0);
const ParameterDescriptor& desc = seq.type_map().descriptor(i->first); const ParameterDescriptor& desc = seq.type_map().descriptor(i->first);
if (y < desc.lower || y > desc.upper) { if (y < desc.lower || y > desc.upper) {
cerr << "ERROR: Controller value " << y cerr << "ERROR: Controller value " << y
@ -170,14 +169,14 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
continue; continue;
} }
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 added (%2, %3)\n", i->first.id(), x, y)); DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: CC %1 added (%2, %3)\n", i->first.id(), xtime, y));
const ControlIterator new_iter(i->second->list(), x, y); const ControlIterator new_iter(i->second->list(), xtime, y);
_control_iters.push_back(new_iter); _control_iters.push_back(new_iter);
// Found a new earliest_control // Found a new earliest_control
if (x < earliest_control_x) { if (xtime < earliest_control_x) {
earliest_control_x = x; earliest_control_x = xtime;
earliest_control_index = _control_iters.size() - 1; earliest_control_index = _control_iters.size() - 1;
found = true; found = true;
} }
@ -215,13 +214,22 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
template<typename Time> template<typename Time>
void void
Sequence<Time>::const_iterator::invalidate(std::set< boost::weak_ptr< Note<Time> > >* notes) Sequence<Time>::const_iterator::get_active_notes (std::set<WeakNotePtr>& active_notes) const
{ {
while (!_active_notes.empty()) { /* can't iterate over a std::priority_queue<> such as ActiveNotes */
if (notes) { ActiveNotes copy (_active_notes);
notes->insert(_active_notes.top()); while (!copy.empty()) {
} active_notes.insert (copy.top());
_active_notes.pop(); copy.pop ();
}
}
template<typename Time>
void
Sequence<Time>::const_iterator::invalidate(bool preserve_active_notes)
{
if (!preserve_active_notes) {
_active_notes = ActiveNotes();
} }
_type = NIL; _type = NIL;
_is_end = true; _is_end = true;
@ -260,16 +268,16 @@ Sequence<Time>::const_iterator::choose_next(Time earliest_t)
/* Use the next earliest controller iff it's earlier or coincident with the note-on /* Use the next earliest controller iff it's earlier or coincident with the note-on
* or patch-change. Bank-select (CC0, CC32) needs to be sent before the PGM. */ * or patch-change. Bank-select (CC0, CC32) needs to be sent before the PGM. */
if (_control_iter != _control_iters.end() && if (_control_iter != _control_iters.end() &&
_control_iter->list && _control_iter->x != DBL_MAX) { _control_iter->list && _control_iter->x != std::numeric_limits<Temporal::timepos_t>::max()) {
if (_type == NIL || _control_iter->x <= earliest_t.to_double()) { if (_type == NIL || _control_iter->x <= earliest_t) {
_type = CONTROL; _type = CONTROL;
earliest_t = Time(_control_iter->x); earliest_t = _control_iter->x.beats();
} }
} }
/* .. but prefer to send any Note-off first */ /* .. but prefer to send any Note-off first */
if ((!_active_notes.empty())) { if ((!_active_notes.empty())) {
if (_type == NIL || _active_notes.top()->end_time().to_double() <= earliest_t.to_double()) { if (_type == NIL || _active_notes.top()->end_time() <= earliest_t) {
_type = NOTE_OFF; _type = NOTE_OFF;
earliest_t = _active_notes.top()->end_time(); earliest_t = _active_notes.top()->end_time();
} }
@ -351,9 +359,10 @@ Sequence<Time>::const_iterator::operator++()
<< int(ev.buffer()[0]) << int(ev.buffer()[1]) << int(ev.buffer()[2]) << endl; << int(ev.buffer()[0]) << int(ev.buffer()[1]) << int(ev.buffer()[2]) << endl;
} }
double x = 0.0; Temporal::timepos_t x;
double y = 0.0; Temporal::timepos_t xtime;
bool ret = false; double y = 0.0;
bool ret = false;
// Increment past current event // Increment past current event
switch (_type) { switch (_type) {
@ -366,19 +375,18 @@ Sequence<Time>::const_iterator::operator++()
case CONTROL: case CONTROL:
// Increment current controller iterator // Increment current controller iterator
if (_force_discrete || _control_iter->list->interpolation() == ControlList::Discrete) { if (_force_discrete || _control_iter->list->interpolation() == ControlList::Discrete) {
ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked ( ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked (_control_iter->x, xtime, y, false);
_control_iter->x, x, y, false);
} else { } else {
ret = _control_iter->list->rt_safe_earliest_event_linear_unlocked ( ret = _control_iter->list->rt_safe_earliest_event_linear_unlocked (
_control_iter->x, x, y, false, time_between_interpolated_controller_outputs); _control_iter->x, xtime, y, false, Temporal::timecnt_t::from_ticks (time_between_interpolated_controller_outputs));
} }
assert(!ret || x > _control_iter->x); assert(!ret || x > _control_iter->x);
if (ret) { if (ret) {
_control_iter->x = x; _control_iter->x = xtime;
_control_iter->y = y; _control_iter->y = y;
} else { } else {
_control_iter->list.reset(); _control_iter->list.reset();
_control_iter->x = DBL_MAX; _control_iter->x = std::numeric_limits<Time>::max();
_control_iter->y = DBL_MAX; _control_iter->y = DBL_MAX;
} }
@ -550,7 +558,7 @@ Sequence<Time>::control_to_midi_event(
assert(iter.list->parameter().id() <= INT8_MAX); assert(iter.list->parameter().id() <= INT8_MAX);
assert(iter.y <= INT8_MAX); assert(iter.y <= INT8_MAX);
ev->set_time(Time(iter.x)); ev->set_time(iter.x.beats());
ev->realloc(3); ev->realloc(3);
ev->buffer()[0] = MIDI_CMD_CONTROL + iter.list->parameter().channel(); ev->buffer()[0] = MIDI_CMD_CONTROL + iter.list->parameter().channel();
ev->buffer()[1] = (uint8_t)iter.list->parameter().id(); ev->buffer()[1] = (uint8_t)iter.list->parameter().id();
@ -562,7 +570,7 @@ Sequence<Time>::control_to_midi_event(
assert(iter.list->parameter().channel() < 16); assert(iter.list->parameter().channel() < 16);
assert(iter.y <= INT8_MAX); assert(iter.y <= INT8_MAX);
ev->set_time(Time(iter.x)); ev->set_time(iter.x.beats());
ev->realloc(2); ev->realloc(2);
ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.list->parameter().channel(); ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.list->parameter().channel();
ev->buffer()[1] = (uint8_t)iter.y; ev->buffer()[1] = (uint8_t)iter.y;
@ -573,7 +581,7 @@ Sequence<Time>::control_to_midi_event(
assert(iter.list->parameter().channel() < 16); assert(iter.list->parameter().channel() < 16);
assert(iter.y < (1<<14)); assert(iter.y < (1<<14));
ev->set_time(Time(iter.x)); ev->set_time(iter.x.beats());
ev->realloc(3); ev->realloc(3);
ev->buffer()[0] = MIDI_CMD_BENDER + iter.list->parameter().channel(); ev->buffer()[0] = MIDI_CMD_BENDER + iter.list->parameter().channel();
ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
@ -586,7 +594,7 @@ Sequence<Time>::control_to_midi_event(
assert(iter.list->parameter().id() <= INT8_MAX); assert(iter.list->parameter().id() <= INT8_MAX);
assert(iter.y <= INT8_MAX); assert(iter.y <= INT8_MAX);
ev->set_time(Time(iter.x)); ev->set_time(iter.x.beats());
ev->realloc(3); ev->realloc(3);
ev->buffer()[0] = MIDI_CMD_NOTE_PRESSURE + iter.list->parameter().channel(); ev->buffer()[0] = MIDI_CMD_NOTE_PRESSURE + iter.list->parameter().channel();
ev->buffer()[1] = (uint8_t)iter.list->parameter().id(); ev->buffer()[1] = (uint8_t)iter.list->parameter().id();
@ -598,7 +606,7 @@ Sequence<Time>::control_to_midi_event(
assert(iter.list->parameter().channel() < 16); assert(iter.list->parameter().channel() < 16);
assert(iter.y <= INT8_MAX); assert(iter.y <= INT8_MAX);
ev->set_time(Time(iter.x)); ev->set_time(iter.x.beats());
ev->realloc(2); ev->realloc(2);
ev->buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.list->parameter().channel(); ev->buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.list->parameter().channel();
ev->buffer()[1] = (uint8_t)iter.y; ev->buffer()[1] = (uint8_t)iter.y;
@ -1075,7 +1083,7 @@ Sequence<Time>::append_control_unlocked(const Parameter& param, Time time, doubl
DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 %2 @ %3 = %4 # controls: %5\n", DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 %2 @ %3 = %4 # controls: %5\n",
this, _type_map.to_symbol(param), time, value, _controls.size())); this, _type_map.to_symbol(param), time, value, _controls.size()));
boost::shared_ptr<Control> c = control(param, true); boost::shared_ptr<Control> c = control(param, true);
c->list()->add (time.to_double(), value, true, false); c->list()->add (Temporal::timepos_t (time), value, true, false);
/* XXX control events should use IDs */ /* XXX control events should use IDs */
} }
@ -1411,12 +1419,26 @@ Sequence<Time>::control_list_marked_dirty ()
template<typename Time> template<typename Time>
void void
Sequence<Time>::dump (ostream& str) const Sequence<Time>::dump (ostream& str, typename Sequence<Time>::const_iterator x, uint32_t limit) const
{ {
typename Sequence<Time>::const_iterator i; typename Sequence<Time>::const_iterator i = begin();
str << "+++ dump\n";
for (i = begin(); i != end(); ++i) { if (x != end()) {
i = x;
}
str << "+++ dump";
if (i != end()) {
str << " from " << i->time();
}
str << endl;
for (; i != end() && (limit >= 0); ++i) {
str << *i << endl; str << *i << endl;
if (limit) {
if (--limit == 0) {
break;
}
}
} }
str << "--- dump\n"; str << "--- dump\n";
} }

View file

@ -1,51 +0,0 @@
/*
* Copyright (C) 2014-2015 David Robillard <d@drobilla.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdint.h>
#include "temporal/beats.h"
#include "temporal/types.h"
#include "evoral/TimeConverter.h"
#include "evoral/types.h"
namespace Evoral {
template<typename A, typename B>
TimeConverter<A,B>::~TimeConverter()
{}
template<typename A, typename B>
B
IdentityConverter<A,B>::to(A a) const
{
return static_cast<B>(a);
}
template<typename A, typename B>
A
IdentityConverter<A,B>::from(B b) const
{
return static_cast<A>(b);
}
template class IdentityConverter<double, Temporal::samplepos_t>;
template class TimeConverter<double, Temporal::samplepos_t>;
template class TimeConverter<Temporal::Beats, Temporal::samplepos_t>;
} // namespace Evoral

View file

@ -26,6 +26,8 @@
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include "pbd/signals.h" #include "pbd/signals.h"
#include "temporal/timeline.h"
#include "evoral/visibility.h" #include "evoral/visibility.h"
#include "evoral/Parameter.h" #include "evoral/Parameter.h"
#include "evoral/ParameterDescriptor.h" #include "evoral/ParameterDescriptor.h"
@ -52,8 +54,8 @@ public:
virtual ~Control() {} virtual ~Control() {}
virtual void set_double (double val, double frame=0, bool to_list=false); virtual void set_double (double val, Temporal::timepos_t when = std::numeric_limits<Temporal::timepos_t>::min(), bool to_list=false);
virtual double get_double (bool from_list=false, double frame=0) const; virtual double get_double (bool from_list=false, Temporal::timepos_t frame = std::numeric_limits<Temporal::timepos_t>::min()) const;
/** Get the latest user-set value /** Get the latest user-set value
* (which may not equal get_value() when automation is playing back). * (which may not equal get_value() when automation is playing back).

View file

@ -34,8 +34,12 @@
#include "pbd/signals.h" #include "pbd/signals.h"
#include "temporal/timeline.h"
#include "temporal/types.h"
#include "temporal/range.h"
#include "evoral/visibility.h" #include "evoral/visibility.h"
#include "evoral/Range.h"
#include "evoral/Parameter.h" #include "evoral/Parameter.h"
#include "evoral/ParameterDescriptor.h" #include "evoral/ParameterDescriptor.h"
@ -48,7 +52,8 @@ class TypeMap;
*/ */
class LIBEVORAL_API ControlEvent { class LIBEVORAL_API ControlEvent {
public: public:
ControlEvent (double w, double v)
ControlEvent (Temporal::timepos_t const & w, double v)
: when (w), value (v), coeff (0) : when (w), value (v), coeff (0)
{} {}
@ -71,11 +76,12 @@ public:
coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0; coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
} }
double when; Temporal::timepos_t when;
double value; double value;
double* coeff; ///< double[4] allocated by Curve as needed double* coeff; ///< double[4] allocated by Curve as needed
}; };
/** A list (sequence) of time-stamped values for a control /** A list (sequence) of time-stamped values for a control
*/ */
class LIBEVORAL_API ControlList class LIBEVORAL_API ControlList
@ -87,12 +93,14 @@ public:
typedef EventList::const_iterator const_iterator; typedef EventList::const_iterator const_iterator;
typedef EventList::const_reverse_iterator const_reverse_iterator; typedef EventList::const_reverse_iterator const_reverse_iterator;
ControlList (const Parameter& id, const ParameterDescriptor& desc); ControlList (const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain);
ControlList (const ControlList&, Temporal::timepos_t const & start, Temporal::timepos_t const & end);
ControlList (const ControlList&); ControlList (const ControlList&);
ControlList (const ControlList&, double start, double end);
virtual ~ControlList(); virtual ~ControlList();
virtual boost::shared_ptr<ControlList> create(const Parameter& id, const ParameterDescriptor& desc); Temporal::TimeDomain time_style() const { return _time_style; }
virtual boost::shared_ptr<ControlList> create(const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain);
void dump (std::ostream&); void dump (std::ostream&);
@ -113,26 +121,26 @@ public:
EventList::size_type size() const { return _events.size(); } EventList::size_type size() const { return _events.size(); }
/** @return time-stamp of first or last event in the list */ /** @return time-stamp of first or last event in the list */
double when (bool at_start) const { Temporal::timepos_t when (bool at_start) const {
Glib::Threads::RWLock::ReaderLock lm (_lock); Glib::Threads::RWLock::ReaderLock lm (_lock);
if (_events.empty()) { if (_events.empty()) {
return 0.0; return std::numeric_limits<Temporal::timepos_t>::min();
} }
return at_start ? _events.front()->when : _events.back()->when; return at_start ? _events.front()->when : _events.back()->when;
} }
double length() const { Temporal::timecnt_t length() const {
Glib::Threads::RWLock::ReaderLock lm (_lock); Glib::Threads::RWLock::ReaderLock lm (_lock);
return _events.empty() ? 0.0 : _events.back()->when; return _events.empty() ? std::numeric_limits<Temporal::timecnt_t>::min() : Temporal::timecnt_t (_events.back()->when, Temporal::timepos_t());
} }
bool empty() const { return _events.empty(); } bool empty() const { return _events.empty(); }
/** Remove all events from this list. */ /** Remove all events from this list. */
void clear (); void clear ();
void x_scale (double factor); void x_scale (Temporal::ratio_t const &);
bool extend_to (double); bool extend_to (Temporal::timepos_t const & );
void slide (iterator before, double distance); void slide (iterator before, Temporal::timecnt_t const & distance);
void shift (double before, double distance); void shift (Temporal::timepos_t const & before, Temporal::timecnt_t const & distance);
void y_transform (boost::function<double(double)> callback); void y_transform (boost::function<double(double)> callback);
void list_merge (ControlList const& other, boost::function<double(double, double)> callback); void list_merge (ControlList const& other, boost::function<double(double, double)> callback);
@ -147,7 +155,7 @@ public:
* @param with_guards if true, add guard-points * @param with_guards if true, add guard-points
* @param with_initial if true, add an initial point if the list is empty * @param with_initial if true, add an initial point if the list is empty
*/ */
virtual void add (double when, double value, bool with_guards=true, bool with_initial=true); virtual void add (Temporal::timepos_t const & when, double value, bool with_guards=true, bool with_initial=true);
/** Add an event to this list. /** Add an event to this list.
* *
@ -160,17 +168,17 @@ public:
* *
* @return true if an event was added. * @return true if an event was added.
*/ */
virtual bool editor_add (double when, double value, bool with_guard); virtual bool editor_add (Temporal::timepos_t const & when, double value, bool with_guard);
/* to be used only for loading pre-sorted data from saved state */ /* to be used only for loading pre-sorted data from saved state */
void fast_simple_add (double when, double value); void fast_simple_add (Temporal::timepos_t const & when, double value);
void erase_range (double start, double end); void erase_range (Temporal::timepos_t const & start , Temporal::timepos_t const & end);
void erase (iterator); void erase (iterator);
void erase (iterator, iterator); void erase (iterator, iterator);
void erase (double, double); void erase (Temporal::timepos_t const &, double);
bool move_ranges (std::list< RangeMove<double> > const &); bool move_ranges (std::list<Temporal::RangeMove> const &);
void modify (iterator, double, double); void modify (iterator, Temporal::timepos_t const &, double);
/** Thin the number of events in this list. /** Thin the number of events in this list.
* *
@ -192,29 +200,29 @@ public:
*/ */
void thin (double thinning_factor); void thin (double thinning_factor);
boost::shared_ptr<ControlList> cut (double, double); boost::shared_ptr<ControlList> cut (Temporal::timepos_t const &, Temporal::timepos_t const &);
boost::shared_ptr<ControlList> copy (double, double); boost::shared_ptr<ControlList> copy (Temporal::timepos_t const &, Temporal::timepos_t const &);
/** Remove all events in the given time range from this list. /** Remove all events in the given time range from this list.
* *
* @param start start of range (inclusive) in audio samples * @param start start of range (inclusive) in audio samples
* @param end end of range (inclusive) in audio samples * @param end end of range (inclusive) in audio samples
*/ */
void clear (double start, double end); void clear (Temporal::timepos_t const &, Temporal::timepos_t const &);
bool paste (const ControlList&, double position); bool paste (const ControlList&, Temporal::timepos_t const &);
/** Remove all events after the given time from this list. /** Remove all events after the given time from this list.
* *
* @param last_coordinate time in audio samples of the last event to keep * @param last_coordinate time in audio samples of the last event to keep
*/ */
void truncate_end (double last_coordinate); void truncate_end (Temporal::timepos_t const & last_coordinate);
/** Remove all events up to to the given time from this list. /** Remove all events up to to the given time from this list.
* *
* @param overall_length overall length in audio samples * @param overall_length overall length
*/ */
void truncate_start (double overall_length); void truncate_start (Temporal::timecnt_t const & overall_length);
iterator begin() { return _events.begin(); } iterator begin() { return _events.begin(); }
const_iterator begin() const { return _events.begin(); } const_iterator begin() const { return _events.begin(); }
@ -229,7 +237,7 @@ public:
ControlEvent* front() { return _events.front(); } ControlEvent* front() { return _events.front(); }
const ControlEvent* front() const { return _events.front(); } const ControlEvent* front() const { return _events.front(); }
std::pair<ControlList::iterator,ControlList::iterator> control_points_adjacent (double when); std::pair<ControlList::iterator,ControlList::iterator> control_points_adjacent (Temporal::timepos_t const & when);
template<class T> void apply_to_points (T& obj, void (T::*method)(const ControlList&)) { template<class T> void apply_to_points (T& obj, void (T::*method)(const ControlList&)) {
Glib::Threads::RWLock::WriterLock lm (_lock); Glib::Threads::RWLock::WriterLock lm (_lock);
@ -242,7 +250,7 @@ public:
* @param where absolute time in samples * @param where absolute time in samples
* @returns parameter value * @returns parameter value
*/ */
double eval (double where) const { double eval (Temporal::timepos_t const & where) const {
Glib::Threads::RWLock::ReaderLock lm (_lock); Glib::Threads::RWLock::ReaderLock lm (_lock);
return unlocked_eval (where); return unlocked_eval (where);
} }
@ -254,7 +262,7 @@ public:
* @param ok boolean reference if returned value is valid * @param ok boolean reference if returned value is valid
* @returns parameter value * @returns parameter value
*/ */
double rt_safe_eval (double where, bool& ok) const { double rt_safe_eval (Temporal::timepos_t const & where, bool& ok) const {
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK); Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);
@ -271,15 +279,15 @@ public:
/** Lookup cache for eval functions, range contains equivalent values */ /** Lookup cache for eval functions, range contains equivalent values */
struct LookupCache { struct LookupCache {
LookupCache() : left(-1) {} LookupCache() : left (std::numeric_limits<Temporal::timepos_t>::max()) {}
double left; /* leftmost x coordinate used when finding "range" */ Temporal::timepos_t left; /* leftmost x coordinate used when finding "range" */
std::pair<ControlList::const_iterator,ControlList::const_iterator> range; std::pair<ControlList::const_iterator,ControlList::const_iterator> range;
}; };
/** Lookup cache for point finding, range contains points after left */ /** Lookup cache for point finding, range contains points after left */
struct SearchCache { struct SearchCache {
SearchCache () : left(-1) {} SearchCache () : left (std::numeric_limits<Temporal::timepos_t>::max()) {}
double left; /* leftmost x coordinate used when finding "first" */ Temporal::timepos_t left; /* leftmost x coordinate used when finding "first" */
ControlList::const_iterator first; ControlList::const_iterator first;
}; };
@ -296,10 +304,10 @@ public:
* *
* FIXME: Should this be private? Curve needs it.. * FIXME: Should this be private? Curve needs it..
*/ */
double unlocked_eval (double x) const; double unlocked_eval (Temporal::timepos_t const & x) const;
bool rt_safe_earliest_event_discrete_unlocked (double start, double& x, double& y, bool inclusive) const; bool rt_safe_earliest_event_discrete_unlocked (Temporal::timepos_t const & start, Temporal::timepos_t & x, double& y, bool inclusive) const;
bool rt_safe_earliest_event_linear_unlocked (double start, double& x, double& y, bool inclusive, double min_d_delta = 0) const; bool rt_safe_earliest_event_linear_unlocked (Temporal::timepos_t const & start, Temporal::timepos_t & x, double& y, bool inclusive, Temporal::timepos_t min_x_delta = Temporal::timepos_t ()) const;
void create_curve(); void create_curve();
void destroy_curve(); void destroy_curve();
@ -338,9 +346,9 @@ public:
virtual bool touching() const { return false; } virtual bool touching() const { return false; }
virtual bool writing() const { return false; } virtual bool writing() const { return false; }
virtual bool touch_enabled() const { return false; } virtual bool touch_enabled() const { return false; }
void start_write_pass (double when); void start_write_pass (Temporal::timepos_t const &);
void write_pass_finished (double when, double thinning_factor=0.0); void write_pass_finished (Temporal::timepos_t const &, double thinning_factor=0.0);
void set_in_write_pass (bool, bool add_point = false, double when = 0.0); void set_in_write_pass (bool, bool add_point = false, Temporal::timepos_t = std::numeric_limits<Temporal::timepos_t>::min());
/** @return true if transport is running and this list is in write mode */ /** @return true if transport is running and this list is in write mode */
bool in_write_pass () const; bool in_write_pass () const;
bool in_new_write_pass () { return new_write_pass; } bool in_new_write_pass () { return new_write_pass; }
@ -355,23 +363,23 @@ public:
void invalidate_insert_iterator (); void invalidate_insert_iterator ();
protected: protected:
/** Called by unlocked_eval() to handle cases of 3 or more control points. */ /** Called by unlocked_eval() to handle cases of 3 or more control points. */
double multipoint_eval (double x) const; double multipoint_eval (Temporal::timepos_t const & x) const;
void build_search_cache_if_necessary (double start) const; void build_search_cache_if_necessary (Temporal::timepos_t const & start) const;
boost::shared_ptr<ControlList> cut_copy_clear (double, double, int op); boost::shared_ptr<ControlList> cut_copy_clear (Temporal::timepos_t const &, Temporal::timepos_t const &, int op);
bool erase_range_internal (double start, double end, EventList &); bool erase_range_internal (Temporal::timepos_t const & start, Temporal::timepos_t const & end, EventList &);
void maybe_add_insert_guard (double when); void maybe_add_insert_guard (Temporal::timepos_t const & when);
iterator erase_from_iterator_to (iterator iter, double when); iterator erase_from_iterator_to (iterator iter, Temporal::timepos_t const & when);
bool maybe_insert_straight_line (double when, double value); bool maybe_insert_straight_line (Temporal::timepos_t const & when, double value);
virtual void maybe_signal_changed (); virtual void maybe_signal_changed ();
void _x_scale (double factor); void _x_scale (Temporal::ratio_t const &);
mutable LookupCache _lookup_cache; mutable LookupCache _lookup_cache;
mutable SearchCache _search_cache; mutable SearchCache _search_cache;
@ -385,24 +393,25 @@ protected:
int8_t _frozen; int8_t _frozen;
bool _changed_when_thawed; bool _changed_when_thawed;
bool _sort_pending; bool _sort_pending;
Temporal::TimeDomain _time_style;
Curve* _curve; Curve* _curve;
private: private:
iterator most_recent_insert_iterator; iterator most_recent_insert_iterator;
double insert_position; Temporal::timepos_t insert_position;
bool new_write_pass; bool new_write_pass;
bool did_write_during_pass; bool did_write_during_pass;
bool _in_write_pass; bool _in_write_pass;
void unlocked_remove_duplicates (); void unlocked_remove_duplicates ();
void unlocked_invalidate_insert_iterator (); void unlocked_invalidate_insert_iterator ();
void add_guard_point (double when, double offset); void add_guard_point (Temporal::timepos_t const & when, Temporal::timecnt_t const & offset);
bool is_sorted () const; bool is_sorted () const;
}; };
} // namespace Evoral } // namespace Evoral
#endif // EVORAL_CONTROL_LIST_HPP #endif // EVORAL_CONTROL_LIST_HPP

View file

@ -22,6 +22,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <boost/utility.hpp> #include <boost/utility.hpp>
#include "temporal/timeline.h"
#include "evoral/visibility.h" #include "evoral/visibility.h"
namespace Evoral { namespace Evoral {
@ -33,17 +35,17 @@ class LIBEVORAL_API Curve : public boost::noncopyable
public: public:
Curve (const ControlList& cl); Curve (const ControlList& cl);
bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen) const; bool rt_safe_get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *arg, int32_t veclen) const;
void get_vector (double x0, double x1, float *arg, int32_t veclen) const; void get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *arg, int32_t veclen) const;
void solve () const; void solve () const;
void mark_dirty() const { _dirty = true; } void mark_dirty() const { _dirty = true; }
private: private:
double multipoint_eval (double x) const; double multipoint_eval (Temporal::timepos_t const & x) const;
void _get_vector (double x0, double x1, float *arg, int32_t veclen) const; void _get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *arg, int32_t veclen) const;
mutable bool _dirty; mutable bool _dirty;
const ControlList& _list; const ControlList& _list;

View file

@ -1,298 +0,0 @@
/*
* Copyright (C) 2012 Carl Hetherington <carl@carlh.net>
* Copyright (C) 2013-2016 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef EVORAL_RANGE_HPP
#define EVORAL_RANGE_HPP
#include <list>
#include <assert.h>
#include <iostream>
#include "evoral/visibility.h"
namespace Evoral {
enum /*LIBEVORAL_API*/ OverlapType {
OverlapNone, // no overlap
OverlapInternal, // the overlap is 100% within the object
OverlapStart, // overlap covers start, but ends within
OverlapEnd, // overlap begins within and covers end
OverlapExternal // overlap extends to (at least) begin+end
};
template<typename T>
/*LIBEVORAL_API*/ OverlapType coverage (T sa, T ea, T sb, T eb) {
/* OverlapType returned reflects how the second (B)
* range overlaps the first (A).
*
* The diagram shows the OverlapType of each possible relative
* placement of A and B.
*
* Notes:
* Internal: the start and end points cannot coincide
* External: the start and end points can coincide
* Start: end points can coincide
* End: start points can coincide
*
* Internal disallows start and end point equality, and thus implies
* that there are two disjoint portions of A which do not overlap B.
*
* A: |---|
* B starts before A
* B: |-| None
* B: |--| Start
* B: |----| Start
* B: |------| External
* B: |--------| External
* B starts equal to A
* B: |-| Start
* B: |---| External
* B: |----| External
* B starts inside A
* B: |-| Internal
* B: |--| End
* B: |---| End
* B starts at end of A
* B: |--| End
* B starts after A
* B: |-| None
* A: |---|
*/
if (sa > ea) {
// seems we are sometimes called with negative length ranges
return OverlapNone;
}
if (sb > eb) {
// seems we are sometimes called with negative length ranges
return OverlapNone;
}
if (sb < sa) { // B starts before A
if (eb < sa) {
return OverlapNone;
} else if (eb == sa) {
return OverlapStart;
} else { // eb > sa
if (eb < ea) {
return OverlapStart;
} else if (eb == ea) {
return OverlapExternal;
} else {
return OverlapExternal;
}
}
} else if (sb == sa) { // B starts equal to A
if (eb < ea) {
return OverlapStart;
} else if (eb == ea) {
return OverlapExternal;
} else { // eb > ea
return OverlapExternal;
}
} else { // sb > sa
if (eb < ea) {
return OverlapInternal;
} else if (eb == ea) {
return OverlapEnd;
} else { // eb > ea
if (sb < ea) { // B starts inside A
return OverlapEnd;
} else if (sb == ea) { // B starts at end of A
return OverlapEnd;
} else { // sb > ea, B starts after A
return OverlapNone;
}
}
}
std::cerr << "unknown overlap type!" << sa << ", " << ea << "; " << sb << ", " << eb << std::endl;
assert(!"unknown overlap type!");
return OverlapNone;
}
/** Type to describe a time range */
template<typename T>
struct /*LIBEVORAL_API*/ Range {
Range (T f, T t) : from (f), to (t) {}
T from; ///< start of the range
T to; ///< end of the range (inclusive: to lies inside the range)
bool empty() const { return from == to; }
T length() const { return to - from + 1; }
/** for a T, return a mapping of it into the range (used for
* looping). If the argument is earlier than or equal to the end of
* this range, do nothing.
*/
T squish (T t) const {
if (t > to) {
t = (from + ((t - from) % length()));
}
return t;
}
};
template<typename T>
bool operator== (Range<T> a, Range<T> b) {
return a.from == b.from && a.to == b.to;
}
template<typename T>
class /*LIBEVORAL_API*/ RangeList {
public:
RangeList () : _dirty (false) {}
typedef std::list<Range<T> > List;
List const & get () {
coalesce ();
return _list;
}
void add (Range<T> const & range) {
_dirty = true;
_list.push_back (range);
}
bool empty () const {
return _list.empty ();
}
void coalesce () {
if (!_dirty) {
return;
}
restart:
for (typename List::iterator i = _list.begin(); i != _list.end(); ++i) {
for (typename List::iterator j = _list.begin(); j != _list.end(); ++j) {
if (i == j) {
continue;
}
if (coverage (i->from, i->to, j->from, j->to) != OverlapNone) {
i->from = std::min (i->from, j->from);
i->to = std::max (i->to, j->to);
_list.erase (j);
goto restart;
}
}
}
_dirty = false;
}
private:
List _list;
bool _dirty;
};
/** Type to describe the movement of a time range */
template<typename T>
struct /*LIBEVORAL_API*/ RangeMove {
RangeMove (T f, double l, T t) : from (f), length (l), to (t) {}
T from; ///< start of the range
double length; ///< length of the range
T to; ///< new start of the range
};
/** Subtract the ranges in `sub' from that in `range',
* returning the result.
*/
template<typename T>
RangeList<T> subtract (Range<T> range, RangeList<T> sub)
{
/* Start with the input range */
RangeList<T> result;
result.add (range);
if (sub.empty () || range.empty()) {
return result;
}
typename RangeList<T>::List s = sub.get ();
/* The basic idea here is to keep a list of the result ranges, and subtract
the bits of `sub' from them one by one.
*/
for (typename RangeList<T>::List::const_iterator i = s.begin(); i != s.end(); ++i) {
/* Here's where we'll put the new current result after subtracting *i from it */
RangeList<T> new_result;
typename RangeList<T>::List r = result.get ();
/* Work on all parts of the current result using this range *i */
for (typename RangeList<T>::List::const_iterator j = r.begin(); j != r.end(); ++j) {
switch (coverage (j->from, j->to, i->from, i->to)) {
case OverlapNone:
/* The thing we're subtracting (*i) does not overlap this bit of the result (*j),
so pass it through.
*/
new_result.add (*j);
break;
case OverlapInternal:
/* Internal overlap of the thing we're subtracting (*i) from this bit of the result,
so we should end up with two bits of (*j) left over, from the start of (*j) to
the start of (*i), and from the end of (*i) to the end of (*j).
*/
assert (j->from < i->from);
assert (j->to > i->to);
new_result.add (Range<T> (j->from, i->from - 1));
new_result.add (Range<T> (i->to + 1, j->to));
break;
case OverlapStart:
/* The bit we're subtracting (*i) overlaps the start of the bit of the result (*j),
* so we keep only the part of of (*j) from after the end of (*i)
*/
assert (i->to < j->to);
new_result.add (Range<T> (i->to + 1, j->to));
break;
case OverlapEnd:
/* The bit we're subtracting (*i) overlaps the end of the bit of the result (*j),
* so we keep only the part of of (*j) from before the start of (*i)
*/
assert (j->from < i->from);
new_result.add (Range<T> (j->from, i->from - 1));
break;
case OverlapExternal:
/* total overlap of the bit we're subtracting with the result bit, so the
result bit is completely removed; do nothing */
break;
}
}
new_result.coalesce ();
result = new_result;
}
return result;
}
}
#endif

View file

@ -49,14 +49,14 @@ template<typename Time> class Event;
*/ */
class /*LIBEVORAL_API*/ ControlIterator { class /*LIBEVORAL_API*/ ControlIterator {
public: public:
ControlIterator(boost::shared_ptr<const ControlList> al, double ax, double ay) ControlIterator(boost::shared_ptr<const ControlList> al, Temporal::timepos_t const & ax, double ay)
: list(al) : list(al)
, x(ax) , x(ax)
, y(ay) , y(ay)
{} {}
boost::shared_ptr<const ControlList> list; boost::shared_ptr<const ControlList> list;
double x; Temporal::timepos_t x;
double y; double y;
}; };
@ -151,7 +151,7 @@ public:
typedef const Note<Time>* value_type; typedef const Note<Time>* value_type;
inline bool operator()(const boost::shared_ptr< const Note<Time> > a, inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
const boost::shared_ptr< const Note<Time> > b) const { const boost::shared_ptr< const Note<Time> > b) const {
return a->end_time().to_double() > b->end_time().to_double(); return a->end_time() > b->end_time();
} }
}; };
@ -216,8 +216,6 @@ public:
inline PatchChanges& patch_changes () { return _patch_changes; } inline PatchChanges& patch_changes () { return _patch_changes; }
inline const PatchChanges& patch_changes () const { return _patch_changes; } inline const PatchChanges& patch_changes () const { return _patch_changes; }
void dump (std::ostream&) const;
private: private:
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes; typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
public: public:
@ -226,15 +224,15 @@ public:
class LIBEVORAL_API const_iterator { class LIBEVORAL_API const_iterator {
public: public:
const_iterator(); const_iterator();
const_iterator(const Sequence<Time>& seq, const_iterator(const Sequence<Time>& seq,
Time t, Time t,
bool force_discrete, bool force_discrete,
const std::set<Evoral::Parameter>& filtered, std::set<Evoral::Parameter> const & filtered,
const std::set<WeakNotePtr>* active_notes=NULL); std::set<WeakNotePtr> const* active_notes = 0);
inline bool valid() const { return !_is_end && _event; } inline bool valid() const { return !_is_end && _event; }
void invalidate(std::set<WeakNotePtr>* notes); void invalidate (bool preserve_notes);
const Event<Time>& operator*() const { return *_event; } const Event<Time>& operator*() const { return *_event; }
const boost::shared_ptr< const Event<Time> > operator->() const { return _event; } const boost::shared_ptr< const Event<Time> > operator->() const { return _event; }
@ -246,6 +244,8 @@ public:
const_iterator& operator=(const const_iterator& other); const_iterator& operator=(const const_iterator& other);
void get_active_notes (std::set<WeakNotePtr>&) const;
private: private:
friend class Sequence<Time>; friend class Sequence<Time>;
@ -277,12 +277,14 @@ public:
Time t = Time(), Time t = Time(),
bool force_discrete = false, bool force_discrete = false,
const std::set<Evoral::Parameter>& f = std::set<Evoral::Parameter>(), const std::set<Evoral::Parameter>& f = std::set<Evoral::Parameter>(),
const std::set<WeakNotePtr>* active_notes = NULL) const { std::set<WeakNotePtr> const * active_notes = 0) const {
return const_iterator (*this, t, force_discrete, f, active_notes); return const_iterator (*this, t, force_discrete, f, active_notes);
} }
const const_iterator& end() const { return _end_iter; } const const_iterator& end() const { return _end_iter; }
void dump (std::ostream&, const_iterator x, uint32_t limit = 0) const;
// CONST iterator implementations (x3) // CONST iterator implementations (x3)
typename Notes::const_iterator note_lower_bound (Time t) const; typename Notes::const_iterator note_lower_bound (Time t) const;
typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const; typename PatchChanges::const_iterator patch_change_lower_bound (Time t) const;
@ -378,4 +380,3 @@ template<typename Time> /*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream&
#endif // EVORAL_SEQUENCE_HPP #endif // EVORAL_SEQUENCE_HPP

View file

@ -1,81 +0,0 @@
/*
* Copyright (C) 2009-2014 David Robillard <d@drobilla.net>
* Copyright (C) 2010-2012 Carl Hetherington <carl@carlh.net>
* Copyright (C) 2011-2014 Paul Davis <paul@linuxaudiosystems.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef EVORAL_TIME_CONVERTER_HPP
#define EVORAL_TIME_CONVERTER_HPP
#include "evoral/visibility.h"
namespace Evoral {
/** A bidirectional converter between two different time units.
*
* Think of the conversion method names as if they are written in-between
* the two template parameters (i.e. "A <name> B").
*
* _origin_b should be the origin for conversion in the units of B.
* That is, there is some point in time _origin_b, such that:
*
* to() converts a time _origin_b + a into an offset from _origin_b in units of B.
* from() converts a time _origin_b + b into an offset from _origin_b in units of A.
*/
template<typename A, typename B>
class LIBEVORAL_TEMPLATE_API TimeConverter {
public:
TimeConverter () : _origin_b (0) {}
TimeConverter (B ob) : _origin_b (ob) {}
virtual ~TimeConverter();
/** Convert A time to B time (A to B) */
virtual B to(A a) const = 0;
/** Convert B time to A time (A from B) */
virtual A from(B b) const = 0;
B origin_b () const {
return _origin_b;
}
void set_origin_b (B o) {
_origin_b = o;
}
protected:
B _origin_b;
};
/** A stub TimeConverter that simple statically casts between types.
* _origin_b has no bearing here, as there is no time conversion
* going on.
*/
template<typename A, typename B>
class LIBEVORAL_TEMPLATE_API IdentityConverter : public TimeConverter<A,B> {
public:
IdentityConverter() {}
B to(A a) const;
A from(B b) const;
};
} // namespace Evoral
#endif // EVORAL_TIME_CONVERTER_HPP

View file

@ -1,5 +1,5 @@
#include "RangeTest.h" #include "RangeTest.h"
#include "evoral/Range.h" #include "temporal/range.h"
#include <stdlib.h> #include <stdlib.h>
CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest); CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest);

View file

@ -95,7 +95,6 @@ def build(bld):
Note.cc Note.cc
SMF.cc SMF.cc
Sequence.cc Sequence.cc
TimeConverter.cc
debug.cc debug.cc
''' '''