mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +01:00
changes to compile against libtemporal and use of timepos_t/timecnt_t
This commit is contained in:
parent
e111123b54
commit
6b09642406
13 changed files with 460 additions and 722 deletions
|
|
@ -41,10 +41,10 @@ Control::Control(const Parameter& parameter,
|
|||
/** Get the currently effective value (ie the one that corresponds to current output)
|
||||
*/
|
||||
double
|
||||
Control::get_double (bool from_list, double frame) const
|
||||
Control::get_double (bool from_list, Temporal::timepos_t when) const
|
||||
{
|
||||
if (from_list) {
|
||||
return _list->eval(frame);
|
||||
return _list->eval (when);
|
||||
} else {
|
||||
return _user_value;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ Control::get_double (bool from_list, double frame) const
|
|||
|
||||
|
||||
void
|
||||
Control::set_double (double value, double frame, bool to_list)
|
||||
Control::set_double (double value, Temporal::timepos_t when, bool to_list)
|
||||
{
|
||||
_user_value = value;
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ Control::set_double (double value, double frame, bool to_list)
|
|||
*/
|
||||
|
||||
if (to_list && (!_list->in_write_pass() || _list->descriptor().toggled)) {
|
||||
_list->add (frame, value, false);
|
||||
_list->add (when, value, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#define isnan_local std::isnan
|
||||
#endif
|
||||
|
||||
#define GUARD_POINT_DELTA 64
|
||||
#define GUARD_POINT_DELTA Temporal::timecnt_t::from_samples (64)
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
|
@ -48,6 +48,8 @@
|
|||
|
||||
#include "pbd/control_math.h"
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/error.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/debug.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
@ -60,10 +62,11 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b)
|
|||
return a->when < b->when;
|
||||
}
|
||||
|
||||
ControlList::ControlList (const Parameter& id, const ParameterDescriptor& desc)
|
||||
ControlList::ControlList (const Parameter& id, const ParameterDescriptor& desc, Temporal::TimeDomain ts)
|
||||
: _parameter(id)
|
||||
, _desc(desc)
|
||||
, _interpolation (default_interpolation ())
|
||||
, _time_style (ts)
|
||||
, _curve(0)
|
||||
{
|
||||
_frozen = 0;
|
||||
|
|
@ -85,6 +88,7 @@ ControlList::ControlList (const ControlList& other)
|
|||
: _parameter(other._parameter)
|
||||
, _desc(other._desc)
|
||||
, _interpolation(other._interpolation)
|
||||
, _time_style (other._time_style)
|
||||
, _curve(0)
|
||||
{
|
||||
_frozen = 0;
|
||||
|
|
@ -103,10 +107,11 @@ ControlList::ControlList (const ControlList& other)
|
|||
copy_events (other);
|
||||
}
|
||||
|
||||
ControlList::ControlList (const ControlList& other, double start, double end)
|
||||
ControlList::ControlList (const ControlList& other, Temporal::timepos_t const & start, Temporal::timepos_t const & end)
|
||||
: _parameter(other._parameter)
|
||||
, _desc(other._desc)
|
||||
, _interpolation(other._interpolation)
|
||||
, _time_style (other._time_style)
|
||||
, _curve(0)
|
||||
{
|
||||
_frozen = 0;
|
||||
|
|
@ -145,9 +150,9 @@ ControlList::~ControlList()
|
|||
}
|
||||
|
||||
boost::shared_ptr<ControlList>
|
||||
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
|
||||
|
|
@ -251,21 +256,24 @@ ControlList::clear ()
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::x_scale (double factor)
|
||||
ControlList::x_scale (Temporal::ratio_t const & factor)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
_x_scale (factor);
|
||||
}
|
||||
|
||||
bool
|
||||
ControlList::extend_to (double when)
|
||||
ControlList::extend_to (Temporal::timepos_t const & end)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
if (_events.empty() || _events.back()->when == when) {
|
||||
|
||||
if (_events.empty() || _events.back()->when == end) {
|
||||
return false;
|
||||
}
|
||||
double factor = when / _events.back()->when;
|
||||
|
||||
Temporal::ratio_t factor (end.val(), _events.back()->when.val());
|
||||
_x_scale (factor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -330,10 +338,10 @@ ControlList::list_merge (ControlList const& other, boost::function<double(double
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::_x_scale (double factor)
|
||||
ControlList::_x_scale (Temporal::ratio_t const & factor)
|
||||
{
|
||||
for (iterator i = _events.begin(); i != _events.end(); ++i) {
|
||||
(*i)->when *= factor;
|
||||
(*i)->when = (*i)->when.operator* (factor);
|
||||
}
|
||||
|
||||
mark_dirty ();
|
||||
|
|
@ -377,9 +385,13 @@ ControlList::thin (double thinning_factor)
|
|||
/* compute the area of the triangle formed by 3 points
|
||||
*/
|
||||
|
||||
double area = fabs ((prevprev->when * (prev->value - cur->value)) +
|
||||
(prev->when * (cur->value - prevprev->value)) +
|
||||
(cur->when * (prevprev->value - prev->value)));
|
||||
const double ppw = prevprev->when.val();
|
||||
const double pw = prev->when.val();
|
||||
const double cw = cur->when.val();
|
||||
|
||||
double area = fabs ((ppw * (prev->value - cur->value)) +
|
||||
(pw * (cur->value - prevprev->value)) +
|
||||
(cw * (prevprev->value - prev->value)));
|
||||
|
||||
if (area < thinning_factor) {
|
||||
iterator tmp = pprev;
|
||||
|
|
@ -415,11 +427,11 @@ ControlList::thin (double thinning_factor)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::fast_simple_add (double when, double value)
|
||||
ControlList::fast_simple_add (Temporal::timepos_t const & time, double value)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
/* to be used only for loading pre-sorted data from saved state */
|
||||
_events.insert (_events.end(), new ControlEvent (when, value));
|
||||
_events.insert (_events.end(), new ControlEvent (time, value));
|
||||
|
||||
mark_dirty ();
|
||||
if (_frozen) {
|
||||
|
|
@ -459,9 +471,10 @@ ControlList::unlocked_remove_duplicates ()
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::start_write_pass (double when)
|
||||
ControlList::start_write_pass (Temporal::timepos_t const & time)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("%1: setup write pass @ %2\n", this, when));
|
||||
|
||||
|
|
@ -481,7 +494,7 @@ ControlList::start_write_pass (double when)
|
|||
*/
|
||||
if (_in_write_pass && !new_write_pass) {
|
||||
#if 1
|
||||
add_guard_point (when, 0); // also sets most_recent_insert_iterator
|
||||
add_guard_point (when, std::numeric_limits<Temporal::timecnt_t>::min()); // also sets most_recent_insert_iterator
|
||||
#else
|
||||
const ControlEvent cp (when, 0.0);
|
||||
most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
|
|
@ -490,7 +503,7 @@ ControlList::start_write_pass (double when)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::write_pass_finished (double /*when*/, double thinning_factor)
|
||||
ControlList::write_pass_finished (Temporal::timepos_t const & /*when*/, double thinning_factor)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ControlList, "write pass finished\n");
|
||||
|
||||
|
|
@ -503,7 +516,7 @@ ControlList::write_pass_finished (double /*when*/, double thinning_factor)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::set_in_write_pass (bool yn, bool add_point, double when)
|
||||
ControlList::set_in_write_pass (bool yn, bool add_point, Temporal::timepos_t when)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("set_in_write_pass: in-write: %1 @ %2 add point? %3\n", yn, when, add_point));
|
||||
|
||||
|
|
@ -511,20 +524,25 @@ ControlList::set_in_write_pass (bool yn, bool add_point, double when)
|
|||
|
||||
if (yn && add_point) {
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
add_guard_point (when, 0);
|
||||
add_guard_point (when, std::numeric_limits<Temporal::timecnt_t>::min());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ControlList::add_guard_point (double when, double offset)
|
||||
ControlList::add_guard_point (Temporal::timepos_t const & time, Temporal::timecnt_t const & offset)
|
||||
{
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
// caller needs to hold writer-lock
|
||||
if (offset < 0 && when < offset) {
|
||||
|
||||
if (offset.negative() && when < offset) {
|
||||
return;
|
||||
}
|
||||
assert (offset <= 0);
|
||||
|
||||
if (offset != 0) {
|
||||
#warning NUTEMPO FIXME this assertion should be possible to write
|
||||
// assert (offset <= 0);
|
||||
|
||||
if (!offset.zero()) {
|
||||
/* check if there are points between when + offset .. when */
|
||||
ControlEvent cp (when + offset, 0.0);
|
||||
iterator s;
|
||||
|
|
@ -533,7 +551,7 @@ ControlList::add_guard_point (double when, double offset)
|
|||
cp.when = when;
|
||||
e = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
if (s != e) {
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add_guard_point, none added, found event between %2 and %3\n", this, when - offset, when));
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 add_guard_point, none added, found event between %2 and %3\n", this, when.earlier (offset), when));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -601,11 +619,12 @@ ControlList::in_write_pass () const
|
|||
}
|
||||
|
||||
bool
|
||||
ControlList::editor_add (double when, double value, bool with_guard)
|
||||
ControlList::editor_add (Temporal::timepos_t const & time, double value, bool with_guard)
|
||||
{
|
||||
/* this is for making changes from a graphical line editor */
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
ControlEvent cp (when, 0.0f);
|
||||
iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
|
|
@ -624,7 +643,7 @@ ControlList::editor_add (double when, double value, bool with_guard)
|
|||
*/
|
||||
|
||||
if (when >= 1) {
|
||||
_events.insert (_events.end(), new ControlEvent (0, value));
|
||||
_events.insert (_events.end(), new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), value));
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added value %2 at zero\n", this, value));
|
||||
}
|
||||
}
|
||||
|
|
@ -652,11 +671,12 @@ ControlList::editor_add (double when, double value, bool with_guard)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::maybe_add_insert_guard (double when)
|
||||
ControlList::maybe_add_insert_guard (Temporal::timepos_t const & time)
|
||||
{
|
||||
Temporal::timepos_t when = time;
|
||||
// caller needs to hold writer-lock
|
||||
if (most_recent_insert_iterator != _events.end()) {
|
||||
if ((*most_recent_insert_iterator)->when - when > GUARD_POINT_DELTA) {
|
||||
if ((*most_recent_insert_iterator)->when.earlier (when) > GUARD_POINT_DELTA) {
|
||||
/* Next control point is some distance from where our new point is
|
||||
going to go, so add a new point to avoid changing the shape of
|
||||
the line too much. The insert iterator needs to point to the
|
||||
|
|
@ -673,8 +693,10 @@ ControlList::maybe_add_insert_guard (double when)
|
|||
|
||||
/** If we would just be adding to a straight line, move the previous point instead. */
|
||||
bool
|
||||
ControlList::maybe_insert_straight_line (double when, double value)
|
||||
ControlList::maybe_insert_straight_line (Temporal::timepos_t const & time, double value)
|
||||
{
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
// caller needs to hold writer-lock
|
||||
if (_events.empty()) {
|
||||
return false;
|
||||
|
|
@ -702,8 +724,10 @@ ControlList::maybe_insert_straight_line (double when, double value)
|
|||
}
|
||||
|
||||
ControlList::iterator
|
||||
ControlList::erase_from_iterator_to (iterator iter, double when)
|
||||
ControlList::erase_from_iterator_to (iterator iter, Temporal::timepos_t const & time)
|
||||
{
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
// caller needs to hold writer-lock
|
||||
while (iter != _events.end()) {
|
||||
if ((*iter)->when < when) {
|
||||
|
|
@ -723,8 +747,10 @@ ControlList::erase_from_iterator_to (iterator iter, double when)
|
|||
* control surface (GUI, MIDI, OSC etc)
|
||||
*/
|
||||
void
|
||||
ControlList::add (double when, double value, bool with_guards, bool with_initial)
|
||||
ControlList::add (Temporal::timepos_t const & time, double value, bool with_guards, bool with_initial)
|
||||
{
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
/* clamp new value to allowed range */
|
||||
value = std::min ((double)_desc.upper, std::max ((double)_desc.lower, value));
|
||||
|
||||
|
|
@ -744,11 +770,11 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial
|
|||
if (when >= 1) {
|
||||
if (_desc.toggled) {
|
||||
const double opp_val = ((value >= 0.5) ? 1.0 : 0.0);
|
||||
_events.insert (_events.end(), new ControlEvent (0, opp_val));
|
||||
_events.insert (_events.end(), new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), opp_val));
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added toggled value %2 at zero\n", this, opp_val));
|
||||
|
||||
} else {
|
||||
_events.insert (_events.end(), new ControlEvent (0, value));
|
||||
_events.insert (_events.end(), new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), value));
|
||||
DEBUG_TRACE (DEBUG::ControlList, string_compose ("@%1 added default value %2 at zero\n", this, _desc.normal));
|
||||
}
|
||||
}
|
||||
|
|
@ -759,7 +785,7 @@ ControlList::add (double when, double value, bool with_guards, bool with_initial
|
|||
/* first write in a write pass: add guard point if requested */
|
||||
|
||||
if (with_guards) {
|
||||
add_guard_point (insert_position, 0);
|
||||
add_guard_point (insert_position, std::numeric_limits<Temporal::timecnt_t>::min());
|
||||
did_write_during_pass = true;
|
||||
} else {
|
||||
/* not adding a guard, but we need to set iterator appropriately */
|
||||
|
|
@ -905,10 +931,11 @@ ControlList::erase (iterator start, iterator end)
|
|||
|
||||
/** Erase the first event which matches the given time and value */
|
||||
void
|
||||
ControlList::erase (double when, double value)
|
||||
ControlList::erase (Temporal::timepos_t const & time, double value)
|
||||
{
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
iterator i = begin ();
|
||||
while (i != end() && ((*i)->when != when || (*i)->value != value)) {
|
||||
|
|
@ -928,12 +955,13 @@ ControlList::erase (double when, double value)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::erase_range (double start, double endt)
|
||||
ControlList::erase_range (Temporal::timepos_t const & start, Temporal::timepos_t const & endt)
|
||||
{
|
||||
bool erased = false;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
|
||||
erased = erase_range_internal (start, endt, _events);
|
||||
|
||||
if (erased) {
|
||||
|
|
@ -947,14 +975,32 @@ ControlList::erase_range (double start, double endt)
|
|||
}
|
||||
|
||||
bool
|
||||
ControlList::erase_range_internal (double start, double endt, EventList & events)
|
||||
ControlList::erase_range_internal (Temporal::timepos_t const & start, Temporal::timepos_t const & endt, EventList & events)
|
||||
{
|
||||
bool erased = false;
|
||||
ControlEvent cp (start, 0.0f);
|
||||
iterator s;
|
||||
iterator e;
|
||||
|
||||
/* This is where we have to pick the time domain to be used when
|
||||
* defining the control points.
|
||||
*
|
||||
* start/endt retain their values no matter what the time domain is,
|
||||
* but the location of the control point is specified as a single
|
||||
* integer value that represents either samples or beats. The sample
|
||||
* position and beat position, while representing the same position on
|
||||
* the timeline, will be numerically different anywhere (except perhaps
|
||||
* zero).
|
||||
*
|
||||
* eg. start = 1000000 samples == 12.34 beats
|
||||
* cp.when = 100000 if ControlList uses AudioTime
|
||||
* cp.when = 23074 if ControlList uses BeatTime (see Temporal::Beats::to_ticks())
|
||||
*
|
||||
*/
|
||||
|
||||
ControlEvent cp (start, 0.0f);
|
||||
|
||||
if ((s = lower_bound (events.begin(), events.end(), &cp, time_comparator)) != events.end()) {
|
||||
|
||||
cp.when = endt;
|
||||
e = upper_bound (events.begin(), events.end(), &cp, time_comparator);
|
||||
events.erase (s, e);
|
||||
|
|
@ -968,7 +1014,7 @@ ControlList::erase_range_internal (double start, double endt, EventList & events
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::slide (iterator before, double distance)
|
||||
ControlList::slide (iterator before, Temporal::timecnt_t const & distance)
|
||||
{
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
|
|
@ -977,8 +1023,10 @@ ControlList::slide (iterator before, double distance)
|
|||
return;
|
||||
}
|
||||
|
||||
Temporal::timecnt_t wd = distance;
|
||||
|
||||
while (before != _events.end()) {
|
||||
(*before)->when += distance;
|
||||
(*before)->when += wd;
|
||||
++before;
|
||||
}
|
||||
|
||||
|
|
@ -988,19 +1036,22 @@ ControlList::slide (iterator before, double distance)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::shift (double pos, double frames)
|
||||
ControlList::shift (Temporal::timepos_t const & time, Temporal::timecnt_t const & distance)
|
||||
{
|
||||
Temporal::timepos_t pos = time;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
double v0, v1;
|
||||
if (frames < 0) {
|
||||
|
||||
if (distance.negative()) {
|
||||
/* Route::shift () with negative shift is used
|
||||
* for "remove time". The time [pos.. pos-frames] is removed.
|
||||
* and everyhing after, moved backwards.
|
||||
*/
|
||||
v0 = unlocked_eval (pos);
|
||||
v1 = unlocked_eval (pos - frames);
|
||||
erase_range_internal (pos, pos - frames, _events);
|
||||
v1 = unlocked_eval (pos.earlier (distance));
|
||||
erase_range_internal (pos, pos.earlier (distance), _events);
|
||||
} else {
|
||||
v0 = v1 = unlocked_eval (pos);
|
||||
}
|
||||
|
|
@ -1012,23 +1063,23 @@ ControlList::shift (double pos, double frames)
|
|||
dst_guard_exists = true;
|
||||
}
|
||||
if ((*i)->when >= pos) {
|
||||
(*i)->when += frames;
|
||||
(*i)->when += distance;
|
||||
}
|
||||
}
|
||||
|
||||
/* add guard-points to retain shape, if needed */
|
||||
if (frames > 0) {
|
||||
if (distance.positive()) {
|
||||
ControlEvent cp (pos, 0.0);
|
||||
iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
if (s != _events.end ()) {
|
||||
_events.insert (s, new ControlEvent (pos, v0));
|
||||
}
|
||||
pos += frames;
|
||||
} else if (frames < 0 && pos > 0) {
|
||||
ControlEvent cp (pos - 1, 0.0);
|
||||
pos += distance;
|
||||
} else if (distance.negative() && pos > 0) {
|
||||
ControlEvent cp (pos.decrement(), 0.0);
|
||||
iterator s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
if (s != _events.end ()) {
|
||||
_events.insert (s, new ControlEvent (pos - 1, v0));
|
||||
_events.insert (s, new ControlEvent (pos.decrement(), v0));
|
||||
}
|
||||
}
|
||||
if (!dst_guard_exists) {
|
||||
|
|
@ -1044,7 +1095,7 @@ ControlList::shift (double pos, double frames)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::modify (iterator iter, double when, double val)
|
||||
ControlList::modify (iterator iter, Temporal::timepos_t const & time, double val)
|
||||
{
|
||||
/* note: we assume higher level logic is in place to avoid this
|
||||
* reordering the time-order of control events in the list. ie. all
|
||||
|
|
@ -1056,6 +1107,7 @@ ControlList::modify (iterator iter, double when, double val)
|
|||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
Temporal::timepos_t when = time;
|
||||
|
||||
(*iter)->when = when;
|
||||
(*iter)->value = val;
|
||||
|
|
@ -1077,10 +1129,11 @@ ControlList::modify (iterator iter, double when, double val)
|
|||
}
|
||||
|
||||
std::pair<ControlList::iterator,ControlList::iterator>
|
||||
ControlList::control_points_adjacent (double xval)
|
||||
ControlList::control_points_adjacent (Temporal::timepos_t const & xtime)
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (_lock);
|
||||
iterator i;
|
||||
Temporal::timepos_t xval = xtime;
|
||||
ControlEvent cp (xval, 0.0f);
|
||||
std::pair<iterator,iterator> ret;
|
||||
|
||||
|
|
@ -1140,10 +1193,10 @@ ControlList::thaw ()
|
|||
void
|
||||
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.second = _events.end();
|
||||
_search_cache.left = -1;
|
||||
_search_cache.left = std::numeric_limits<Temporal::timepos_t>::max();
|
||||
_search_cache.first = _events.end();
|
||||
|
||||
if (_curve) {
|
||||
|
|
@ -1152,10 +1205,11 @@ ControlList::mark_dirty () const
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::truncate_end (double last_coordinate)
|
||||
ControlList::truncate_end (Temporal::timepos_t const & last_time)
|
||||
{
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
Temporal::timepos_t last_coordinate = last_time;
|
||||
ControlEvent cp (last_coordinate, 0);
|
||||
ControlList::reverse_iterator i;
|
||||
double last_val;
|
||||
|
|
@ -1254,13 +1308,14 @@ ControlList::truncate_end (double last_coordinate)
|
|||
}
|
||||
|
||||
void
|
||||
ControlList::truncate_start (double overall_length)
|
||||
ControlList::truncate_start (Temporal::timecnt_t const & overall)
|
||||
{
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
iterator i;
|
||||
double first_legal_value;
|
||||
double first_legal_coordinate;
|
||||
Temporal::timepos_t first_legal_coordinate;
|
||||
Temporal::timepos_t overall_length (overall);
|
||||
|
||||
if (_events.empty()) {
|
||||
/* nothing to truncate */
|
||||
|
|
@ -1274,7 +1329,7 @@ ControlList::truncate_start (double overall_length)
|
|||
|
||||
/* growing at front: duplicate first point. shift all others */
|
||||
|
||||
double shift = overall_length - _events.back()->when;
|
||||
Temporal::timepos_t shift (_events.back()->when.distance (overall_length));
|
||||
uint32_t np;
|
||||
|
||||
for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) {
|
||||
|
|
@ -1284,7 +1339,7 @@ ControlList::truncate_start (double overall_length)
|
|||
if (np < 2) {
|
||||
|
||||
/* less than 2 points: add a new point */
|
||||
_events.push_front (new ControlEvent (0, _events.front()->value));
|
||||
_events.push_front (new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), _events.front()->value));
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -1301,7 +1356,7 @@ ControlList::truncate_start (double overall_length)
|
|||
_events.front()->when = 0;
|
||||
} else {
|
||||
/* leave non-flat segment in place, add a new leading point. */
|
||||
_events.push_front (new ControlEvent (0, _events.front()->value));
|
||||
_events.push_front (new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), _events.front()->value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1309,7 +1364,7 @@ ControlList::truncate_start (double overall_length)
|
|||
|
||||
/* shrinking at front */
|
||||
|
||||
first_legal_coordinate = _events.back()->when - overall_length;
|
||||
first_legal_coordinate = _events.back()->when.distance (overall_length);
|
||||
first_legal_value = unlocked_eval (first_legal_coordinate);
|
||||
first_legal_value = max ((double)_desc.lower, first_legal_value);
|
||||
first_legal_value = min ((double)_desc.upper, first_legal_value);
|
||||
|
|
@ -1339,12 +1394,12 @@ ControlList::truncate_start (double overall_length)
|
|||
*/
|
||||
|
||||
for (i = _events.begin(); i != _events.end(); ++i) {
|
||||
(*i)->when -= first_legal_coordinate;
|
||||
(*i)->when.shift_earlier (Temporal::timecnt_t (first_legal_coordinate, Temporal::timepos_t()));
|
||||
}
|
||||
|
||||
/* add a new point for the interpolated new value */
|
||||
|
||||
_events.push_front (new ControlEvent (0, first_legal_value));
|
||||
_events.push_front (new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), first_legal_value));
|
||||
}
|
||||
|
||||
unlocked_invalidate_insert_iterator ();
|
||||
|
|
@ -1354,14 +1409,16 @@ ControlList::truncate_start (double overall_length)
|
|||
}
|
||||
|
||||
double
|
||||
ControlList::unlocked_eval (double x) const
|
||||
ControlList::unlocked_eval (Temporal::timepos_t const & xtime) const
|
||||
{
|
||||
pair<EventList::iterator,EventList::iterator> range;
|
||||
int32_t npoints;
|
||||
double lpos, upos;
|
||||
Temporal::timepos_t lpos, upos;
|
||||
double lval, uval;
|
||||
double fraction;
|
||||
|
||||
double xx;
|
||||
double ll;
|
||||
|
||||
const_iterator length_check_iter = _events.begin();
|
||||
for (npoints = 0; npoints < 4; ++npoints, ++length_check_iter) {
|
||||
if (length_check_iter == _events.end()) {
|
||||
|
|
@ -1377,9 +1434,9 @@ ControlList::unlocked_eval (double x) const
|
|||
return _events.front()->value;
|
||||
|
||||
case 2:
|
||||
if (x >= _events.back()->when) {
|
||||
if (xtime >= _events.back()->when) {
|
||||
return _events.back()->value;
|
||||
} else if (x <= _events.front()->when) {
|
||||
} else if (xtime <= _events.front()->when) {
|
||||
return _events.front()->value;
|
||||
}
|
||||
|
||||
|
|
@ -1388,7 +1445,10 @@ ControlList::unlocked_eval (double x) const
|
|||
upos = _events.back()->when;
|
||||
uval = _events.back()->value;
|
||||
|
||||
fraction = (double) (x - lpos) / (double) (upos - lpos);
|
||||
xx = lpos.distance (xtime).distance().val();
|
||||
ll = lpos.distance (upos).distance().val();
|
||||
|
||||
fraction = xx / ll;
|
||||
|
||||
switch (_interpolation) {
|
||||
case Discrete:
|
||||
|
|
@ -1405,13 +1465,13 @@ ControlList::unlocked_eval (double x) const
|
|||
}
|
||||
|
||||
default:
|
||||
if (x >= _events.back()->when) {
|
||||
if (xtime >= _events.back()->when) {
|
||||
return _events.back()->value;
|
||||
} else if (x <= _events.front()->when) {
|
||||
} else if (xtime <= _events.front()->when) {
|
||||
return _events.front()->value;
|
||||
}
|
||||
|
||||
return multipoint_eval (x);
|
||||
return multipoint_eval (xtime);
|
||||
}
|
||||
|
||||
abort(); /*NOTREACHED*/ /* stupid gcc */
|
||||
|
|
@ -1419,35 +1479,35 @@ ControlList::unlocked_eval (double x) const
|
|||
}
|
||||
|
||||
double
|
||||
ControlList::multipoint_eval (double x) const
|
||||
ControlList::multipoint_eval (Temporal::timepos_t const & xtime) const
|
||||
{
|
||||
double upos, lpos;
|
||||
Temporal::timepos_t upos, lpos;
|
||||
double uval, lval;
|
||||
double fraction;
|
||||
|
||||
/* "Stepped" lookup (no interpolation) */
|
||||
/* FIXME: no cache. significant? */
|
||||
if (_interpolation == Discrete) {
|
||||
const ControlEvent cp (x, 0);
|
||||
const ControlEvent cp (xtime, 0);
|
||||
EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
|
||||
// shouldn't have made it to multipoint_eval
|
||||
assert(i != _events.end());
|
||||
|
||||
if (i == _events.begin() || (*i)->when == x)
|
||||
if (i == _events.begin() || (*i)->when == xtime)
|
||||
return (*i)->value;
|
||||
else
|
||||
return (*(--i))->value;
|
||||
}
|
||||
|
||||
/* Only do the range lookup if x is in a different range than last time
|
||||
/* Only do the range lookup if xtime is in a different range than last time
|
||||
* this was called (or if the lookup cache has been marked "dirty" (left<0) */
|
||||
if ((_lookup_cache.left < 0) ||
|
||||
((_lookup_cache.left > x) ||
|
||||
if ((_lookup_cache.left == std::numeric_limits<Temporal::timepos_t>::max()) ||
|
||||
((_lookup_cache.left > xtime) ||
|
||||
(_lookup_cache.range.first == _events.end()) ||
|
||||
((*_lookup_cache.range.second)->when < x))) {
|
||||
((*_lookup_cache.range.second)->when < xtime))) {
|
||||
|
||||
const ControlEvent cp (x, 0);
|
||||
const ControlEvent cp (xtime, 0);
|
||||
|
||||
_lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
}
|
||||
|
|
@ -1458,7 +1518,7 @@ ControlList::multipoint_eval (double x) const
|
|||
|
||||
/* x does not exist within the list as a control point */
|
||||
|
||||
_lookup_cache.left = x;
|
||||
_lookup_cache.left = xtime;
|
||||
|
||||
if (range.first != _events.begin()) {
|
||||
--range.first;
|
||||
|
|
@ -1478,7 +1538,7 @@ ControlList::multipoint_eval (double x) const
|
|||
upos = (*range.second)->when;
|
||||
uval = (*range.second)->value;
|
||||
|
||||
fraction = (double) (x - lpos) / (double) (upos - lpos);
|
||||
fraction = (double) lpos.distance (xtime).distance().val() / (double) lpos.distance (upos).distance().val();
|
||||
|
||||
switch (_interpolation) {
|
||||
case Logarithmic:
|
||||
|
|
@ -1499,20 +1559,22 @@ ControlList::multipoint_eval (double x) const
|
|||
}
|
||||
|
||||
/* x is a control point in the data */
|
||||
_lookup_cache.left = -1;
|
||||
_lookup_cache.left = std::numeric_limits<Temporal::timepos_t>::max();
|
||||
return (*range.first)->value;
|
||||
}
|
||||
|
||||
void
|
||||
ControlList::build_search_cache_if_necessary (double start) const
|
||||
ControlList::build_search_cache_if_necessary (Temporal::timepos_t const & start_time) const
|
||||
{
|
||||
Temporal::timepos_t start = start_time;
|
||||
|
||||
if (_events.empty()) {
|
||||
/* Empty, nothing to cache, move to end. */
|
||||
_search_cache.first = _events.end();
|
||||
_search_cache.left = 0;
|
||||
_search_cache.left = Temporal::timepos_t();
|
||||
return;
|
||||
} else if ((_search_cache.left < 0) || (_search_cache.left > start)) {
|
||||
/* Marked dirty (left < 0), or we're too far forward, re-search. */
|
||||
} else if ((_search_cache.left == std::numeric_limits<Temporal::timepos_t>::max()) || (_search_cache.left > start)) {
|
||||
/* Marked dirty (left == max), or we're too far forward, re-search. */
|
||||
|
||||
const ControlEvent start_point (start, 0);
|
||||
|
||||
|
|
@ -1537,8 +1599,10 @@ ControlList::build_search_cache_if_necessary (double start) const
|
|||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x, double& y, bool inclusive) const
|
||||
ControlList::rt_safe_earliest_event_discrete_unlocked (Temporal::timepos_t const & start_time, Temporal::timepos_t & x, double& y, bool inclusive) const
|
||||
{
|
||||
Temporal::timepos_t start = start_time;
|
||||
|
||||
build_search_cache_if_necessary (start);
|
||||
|
||||
if (_search_cache.first != _events.end()) {
|
||||
|
|
@ -1554,7 +1618,7 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x,
|
|||
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
_search_cache.left = first->when;
|
||||
++_search_cache.first;
|
||||
|
||||
assert(x >= start);
|
||||
|
|
@ -1578,9 +1642,9 @@ ControlList::rt_safe_earliest_event_discrete_unlocked (double start, double& x,
|
|||
* \return true if event is found (and \a x and \a y are valid).
|
||||
*/
|
||||
bool
|
||||
ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, double& y, bool inclusive, double min_x_delta) const
|
||||
ControlList::rt_safe_earliest_event_linear_unlocked (Temporal::timepos_t const & start_time, Temporal::timepos_t & x, double& y, bool inclusive, timepos_t const & min_x_delta) const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (_lock); // XXX
|
||||
Temporal::timepos_t start = start_time;
|
||||
|
||||
// cout << "earliest_event(start: " << start << ", x: " << x << ", y: " << y << ", inclusive: " << inclusive << ")" << endl;
|
||||
|
||||
|
|
@ -1591,7 +1655,6 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|
|||
return rt_safe_earliest_event_discrete_unlocked (start + min_x_delta, x, y, inclusive);
|
||||
}
|
||||
|
||||
|
||||
if (min_x_delta > 0) {
|
||||
/* if there is an event between [start and start + min_x_delta], use it,
|
||||
* otherwise interpolate at start + min_x_delta
|
||||
|
|
@ -1642,7 +1705,7 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|
|||
y = first->value;
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
_search_cache.left = first->when;
|
||||
return true;
|
||||
} else if (next->when < start || (!inclusive && next->when == start)) {
|
||||
/* "Next" is before the start, no points left. */
|
||||
|
|
@ -1655,34 +1718,44 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|
|||
y = next->value;
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
_search_cache.left = next->when;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const double slope = (next->value - first->value) / (double)(next->when - first->when);
|
||||
const double slope = (next->value - first->value) / (double) first->when.distance (next->when).distance().val();
|
||||
//cerr << "start y: " << start_y << endl;
|
||||
|
||||
//y = first->value + (slope * fabs(start - first->when));
|
||||
y = first->value;
|
||||
|
||||
if (first->value < next->value) // ramping up
|
||||
if (first->value < next->value) { // ramping up
|
||||
y = ceil(y);
|
||||
else // ramping down
|
||||
} else { // ramping down
|
||||
y = floor(y);
|
||||
}
|
||||
|
||||
x = first->when + (y - first->value) / (double)slope;
|
||||
if (_time_style == Temporal::AudioTime) {
|
||||
x = first->when + Temporal::timepos_t::from_samples ((y - first->value) / (double)slope);
|
||||
} else {
|
||||
x = first->when + Temporal::timepos_t::from_ticks ((y - first->value) / (double)slope);
|
||||
}
|
||||
|
||||
while ((inclusive && x < start) || (x <= start && y != next->value)) {
|
||||
while ((inclusive && x < start_time) || (x <= start_time && y != next->value)) {
|
||||
|
||||
if (first->value < next->value) // ramping up
|
||||
if (first->value < next->value) { // ramping up
|
||||
y += 1.0;
|
||||
else // ramping down
|
||||
} else { // ramping down
|
||||
y -= 1.0;
|
||||
}
|
||||
|
||||
x = first->when + (y - first->value) / (double)slope;
|
||||
if (_time_style == Temporal::AudioTime) {
|
||||
x = first->when + Temporal::timepos_t::from_samples ((y - first->value) / (double)slope);
|
||||
} else {
|
||||
x = first->when + Temporal::timepos_t::from_ticks ((y - first->value) / (double)slope);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
@ -1695,20 +1768,21 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|
|||
|| (y <= first->value && y >= next->value) );
|
||||
|
||||
|
||||
const bool past_start = (inclusive ? x >= start : x > start);
|
||||
const bool past_start = (inclusive ? x >= start_time : x > start_time);
|
||||
if (past_start) {
|
||||
/* Move left of cache to this point
|
||||
* (Optimize for immediate call this cycle within range) */
|
||||
_search_cache.left = x;
|
||||
assert(inclusive ? x >= start : x > start);
|
||||
assert(inclusive ? x >= start_time : x > start_time);
|
||||
return true;
|
||||
} else {
|
||||
if (inclusive) {
|
||||
x = next->when;
|
||||
_search_cache.left = next->when;
|
||||
} else {
|
||||
x = start;
|
||||
_search_cache.left = x;
|
||||
}
|
||||
_search_cache.left = x;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1724,10 +1798,12 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
|
|||
* @param op 0 = cut, 1 = copy, 2 = clear.
|
||||
*/
|
||||
boost::shared_ptr<ControlList>
|
||||
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;
|
||||
Temporal::timepos_t start = start_time;
|
||||
Temporal::timepos_t end = end_time;
|
||||
ControlEvent cp (start, 0.0);
|
||||
|
||||
{
|
||||
|
|
@ -1773,7 +1849,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
|
|||
}
|
||||
|
||||
if (op != 2) { // ! clear
|
||||
nal->_events.push_back (new ControlEvent (0, val));
|
||||
nal->_events.push_back (new ControlEvent (std::numeric_limits<Temporal::timepos_t>::min(), val));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1784,7 +1860,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
|
|||
*/
|
||||
|
||||
if (op != 2) {
|
||||
nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value));
|
||||
nal->_events.push_back (new ControlEvent (Temporal::timepos_t (start.distance ((*x)->when)), (*x)->value));
|
||||
}
|
||||
|
||||
if (op != 1) {
|
||||
|
|
@ -1804,7 +1880,7 @@ ControlList::cut_copy_clear (double start, double end, int op)
|
|||
}
|
||||
|
||||
if (op != 2 && (e != _events.end() && end < (*e)->when)) { // cut/copy
|
||||
nal->_events.push_back (new ControlEvent (end - start, end_value));
|
||||
nal->_events.push_back (new ControlEvent (Temporal::timepos_t (start.distance (start)), end_value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1821,26 +1897,26 @@ ControlList::cut_copy_clear (double start, double end, int op)
|
|||
|
||||
|
||||
boost::shared_ptr<ControlList>
|
||||
ControlList::cut (double start, double end)
|
||||
ControlList::cut (Temporal::timepos_t const & start, Temporal::timepos_t const & end)
|
||||
{
|
||||
return cut_copy_clear (start, end, 0);
|
||||
}
|
||||
|
||||
boost::shared_ptr<ControlList>
|
||||
ControlList::copy (double start, double end)
|
||||
ControlList::copy (Temporal::timepos_t const & start, Temporal::timepos_t const & end)
|
||||
{
|
||||
return cut_copy_clear (start, end, 1);
|
||||
}
|
||||
|
||||
void
|
||||
ControlList::clear (double start, double end)
|
||||
ControlList::clear (Temporal::timepos_t const & start, Temporal::timepos_t const & end)
|
||||
{
|
||||
cut_copy_clear (start, end, 2);
|
||||
}
|
||||
|
||||
/** @param pos Position in model coordinates */
|
||||
bool
|
||||
ControlList::paste (const ControlList& alist, double pos)
|
||||
ControlList::paste (const ControlList& alist, Temporal::timepos_t const & time)
|
||||
{
|
||||
if (alist._events.empty()) {
|
||||
return false;
|
||||
|
|
@ -1850,7 +1926,8 @@ ControlList::paste (const ControlList& alist, double pos)
|
|||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
iterator where;
|
||||
iterator prev;
|
||||
double end = 0;
|
||||
Temporal::timepos_t end;
|
||||
Temporal::timepos_t pos = time;
|
||||
ControlEvent cp (pos, 0.0);
|
||||
|
||||
where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
|
||||
|
|
@ -1909,9 +1986,9 @@ ControlList::paste (const ControlList& alist, double pos)
|
|||
* @param return true if anything was changed, otherwise false (ie nothing needed changing)
|
||||
*/
|
||||
bool
|
||||
ControlList::move_ranges (const list< RangeMove<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);
|
||||
|
|
@ -1923,11 +2000,17 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
|
|||
bool things_erased = false;
|
||||
for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) {
|
||||
|
||||
if (erase_range_internal (i->from, i->from + i->length, _events)) {
|
||||
Temporal::timepos_t start = i->from;
|
||||
Temporal::timepos_t end = i->from + i->length;
|
||||
|
||||
if (erase_range_internal (start, end, _events)) {
|
||||
things_erased = true;
|
||||
}
|
||||
|
||||
if (erase_range_internal (i->to, i->to + i->length, _events)) {
|
||||
start = i->to;
|
||||
end = i->to + i->length;
|
||||
|
||||
if (erase_range_internal (start, end, _events)) {
|
||||
things_erased = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1940,14 +2023,48 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
|
|||
/* copy the events into the new list */
|
||||
for (RangeMoveList::const_iterator i = movements.begin (); i != movements.end (); ++i) {
|
||||
iterator j = old_events.begin ();
|
||||
const double limit = i->from + i->length;
|
||||
const double dx = i->to - i->from;
|
||||
while (j != old_events.end () && (*j)->when <= limit) {
|
||||
if ((*j)->when >= i->from) {
|
||||
|
||||
const Temporal::timepos_t limit = i->from + i->length;
|
||||
const Temporal::timecnt_t dx = i->from.distance (i->to);
|
||||
|
||||
while (j != old_events.end ()) {
|
||||
|
||||
Temporal::timepos_t jtime;
|
||||
|
||||
switch (_time_style) {
|
||||
case Temporal::AudioTime:
|
||||
jtime = (*j)->when;
|
||||
break;
|
||||
case Temporal::BeatTime:
|
||||
jtime = (*j)->when;
|
||||
break;
|
||||
default:
|
||||
/*NOTREACHED*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jtime > limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (jtime >= i->from) {
|
||||
ControlEvent* ev = new ControlEvent (**j);
|
||||
ev->when += dx;
|
||||
|
||||
switch (_time_style) {
|
||||
case Temporal::AudioTime:
|
||||
ev->when += dx.samples();
|
||||
break;
|
||||
case Temporal::BeatTime:
|
||||
ev->when += dx.beats().to_ticks();
|
||||
break;
|
||||
default:
|
||||
/*NOTREACHED*/
|
||||
return false;
|
||||
}
|
||||
|
||||
_events.push_back (ev);
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
|
@ -2044,9 +2161,8 @@ ControlList::dump (ostream& o)
|
|||
/* NOT LOCKED ... for debugging only */
|
||||
|
||||
for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
|
||||
o << (*x)->value << " @ " << (uint64_t) (*x)->when << endl;
|
||||
o << (*x)->value << " @ " << (*x)->when << endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
|
|
|
|||
|
|
@ -62,20 +62,23 @@ Curve::solve () const
|
|||
(www.korf.co.uk/spline.pdf) for more details.
|
||||
*/
|
||||
|
||||
vector<double> x(npoints);
|
||||
vector<Temporal::timepos_t> x (npoints);
|
||||
vector<double> y(npoints);
|
||||
uint32_t i;
|
||||
ControlList::EventList::const_iterator xx;
|
||||
|
||||
for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
|
||||
x[i] = (double) (*xx)->when;
|
||||
y[i] = (double) (*xx)->value;
|
||||
x[i] = (*xx)->when;
|
||||
y[i] = (*xx)->value;
|
||||
}
|
||||
|
||||
double lp0, lp1, fpone;
|
||||
double xd;
|
||||
|
||||
lp0 = (x[1] - x[0])/(y[1] - y[0]);
|
||||
lp1 = (x[2] - x[1])/(y[2] - y[1]);
|
||||
xd = x[0].distance (x[1]).magnitude();
|
||||
lp0 = xd/(y[1] - y[0]);
|
||||
xd = x[1].distance (x[2]).magnitude();
|
||||
lp1 = xd/(y[2] - y[1]);
|
||||
|
||||
if (lp0*lp1 < 0) {
|
||||
fpone = 0;
|
||||
|
|
@ -92,9 +95,12 @@ Curve::solve () const
|
|||
double ydelta; /* ditto */
|
||||
double fppL, fppR;
|
||||
double fpi;
|
||||
double xi = x[i].val();
|
||||
double xim1;
|
||||
|
||||
if (i > 0) {
|
||||
xdelta = x[i] - x[i-1];
|
||||
xim1 = x[i-1].val();
|
||||
xdelta = xi - xim1;
|
||||
xdelta2 = xdelta * xdelta;
|
||||
ydelta = y[i] - y[i-1];
|
||||
}
|
||||
|
|
@ -105,7 +111,7 @@ Curve::solve () const
|
|||
|
||||
/* first segment */
|
||||
|
||||
fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5));
|
||||
fplast = ((3 * (y[1] - y[0]) / (2 * (xi - xim1))) - (fpone * 0.5));
|
||||
|
||||
/* we don't store coefficients for i = 0 */
|
||||
|
||||
|
|
@ -121,7 +127,9 @@ Curve::solve () const
|
|||
|
||||
/* all other segments */
|
||||
|
||||
double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i]));
|
||||
double xip1 = x[i+1].val();
|
||||
|
||||
double slope_before = (xip1 - xi) / (y[i+1] - y[i]);
|
||||
double slope_after = (xdelta / ydelta);
|
||||
|
||||
if (slope_after * slope_before < 0.0) {
|
||||
|
|
@ -145,22 +153,22 @@ Curve::solve () const
|
|||
double b, c, d;
|
||||
|
||||
d = (fppR - fppL) / (6 * xdelta);
|
||||
c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta);
|
||||
c = ((xi * fppL) - (xim1 * fppR))/(2 * xdelta);
|
||||
|
||||
double xim12, xim13;
|
||||
double xi2, xi3;
|
||||
|
||||
xim12 = x[i-1] * x[i-1]; /* "x[i-1] squared" */
|
||||
xim13 = xim12 * x[i-1]; /* "x[i-1] cubed" */
|
||||
xi2 = x[i] * x[i]; /* "x[i] squared" */
|
||||
xi3 = xi2 * x[i]; /* "x[i] cubed" */
|
||||
xim12 = xim1 * xim1; /* "x[i-1] squared" */
|
||||
xim13 = xim12 * xim1; /* "x[i-1] cubed" */
|
||||
xi2 = xi * xi; /* "x[i] squared" */
|
||||
xi3 = xi2 * xi; /* "x[i] cubed" */
|
||||
|
||||
b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
|
||||
|
||||
/* store */
|
||||
|
||||
(*xx)->create_coeffs();
|
||||
(*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
|
||||
(*xx)->coeff[0] = y[i-1] - (b * xim1) - (c * xim12) - (d * xim13);
|
||||
(*xx)->coeff[1] = b;
|
||||
(*xx)->coeff[2] = c;
|
||||
(*xx)->coeff[3] = d;
|
||||
|
|
@ -174,7 +182,7 @@ Curve::solve () const
|
|||
}
|
||||
|
||||
bool
|
||||
Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
||||
Curve::rt_safe_get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm(_list.lock(), Glib::Threads::TRY_LOCK);
|
||||
|
||||
|
|
@ -187,16 +195,20 @@ Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen) con
|
|||
}
|
||||
|
||||
void
|
||||
Curve::get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
||||
Curve::get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm(_list.lock());
|
||||
_get_vector (x0, x1, vec, veclen);
|
||||
}
|
||||
|
||||
void
|
||||
Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
||||
Curve::_get_vector (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *vec, int32_t veclen) const
|
||||
{
|
||||
double rx, lx, hx, max_x, min_x;
|
||||
double rx, lx, hx;
|
||||
const double start = x0.val();
|
||||
const double end = x1.val();
|
||||
double max_x;
|
||||
double min_x;
|
||||
int32_t i;
|
||||
int32_t original_veclen;
|
||||
int32_t npoints;
|
||||
|
|
@ -222,10 +234,10 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
|
||||
/* events is now known not to be empty */
|
||||
|
||||
max_x = _list.events().back()->when;
|
||||
min_x = _list.events().front()->when;
|
||||
max_x = _list.events().back()->when.val();
|
||||
min_x = _list.events().front()->when.val();
|
||||
|
||||
if (x0 > max_x) {
|
||||
if (start > max_x) {
|
||||
/* totally past the end - just fill the entire array with the final value */
|
||||
for (int32_t i = 0; i < veclen; ++i) {
|
||||
vec[i] = _list.events().back()->value;
|
||||
|
|
@ -233,7 +245,7 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
return;
|
||||
}
|
||||
|
||||
if (x1 < min_x) {
|
||||
if (end < min_x) {
|
||||
/* totally before the first event - fill the entire array with
|
||||
* the initial value.
|
||||
*/
|
||||
|
|
@ -245,13 +257,13 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
|
||||
original_veclen = veclen;
|
||||
|
||||
if (x0 < min_x) {
|
||||
if (start < min_x) {
|
||||
|
||||
/* fill some beginning section of the array with the
|
||||
initial (used to be default) value
|
||||
*/
|
||||
|
||||
double frac = (min_x - x0) / (x1 - x0);
|
||||
double frac = (min_x - start) / (end - start);
|
||||
int64_t fill_len = (int64_t) floor (veclen * frac);
|
||||
|
||||
fill_len = min (fill_len, (int64_t)veclen);
|
||||
|
|
@ -264,11 +276,11 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
vec += fill_len;
|
||||
}
|
||||
|
||||
if (veclen && x1 > max_x) {
|
||||
if (veclen && end > max_x) {
|
||||
|
||||
/* fill some end section of the array with the default or final value */
|
||||
|
||||
double frac = (x1 - max_x) / (x1 - x0);
|
||||
double frac = (end - max_x) / (end - start);
|
||||
int64_t fill_len = (int64_t) floor (original_veclen * frac);
|
||||
float val;
|
||||
|
||||
|
|
@ -282,14 +294,14 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
veclen -= fill_len;
|
||||
}
|
||||
|
||||
lx = max (min_x, x0);
|
||||
hx = min (max_x, x1);
|
||||
lx = max (min_x, start);
|
||||
hx = min (max_x, end);
|
||||
|
||||
if (npoints == 2) {
|
||||
|
||||
const double lpos = _list.events().front()->when;
|
||||
const double lpos = _list.events().front()->when.val();
|
||||
const double lval = _list.events().front()->value;
|
||||
const double upos = _list.events().back()->when;
|
||||
const double upos = _list.events().back()->when.val();
|
||||
const double uval = _list.events().back()->value;
|
||||
|
||||
/* dx that we are using */
|
||||
|
|
@ -360,18 +372,19 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const
|
|||
|
||||
rx = lx;
|
||||
|
||||
double dx = 0;
|
||||
double dx = 0.;
|
||||
|
||||
if (veclen > 1) {
|
||||
dx = (hx - lx) / (veclen - 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < veclen; ++i, rx += dx) {
|
||||
vec[i] = multipoint_eval (rx);
|
||||
vec[i] = multipoint_eval (x0.is_beats() ? Temporal::timepos_t::from_ticks (rx) : Temporal::timepos_t::from_superclock (rx));
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
Curve::multipoint_eval (double x) const
|
||||
Curve::multipoint_eval (Temporal::timepos_t const & x) const
|
||||
{
|
||||
pair<ControlList::EventList::const_iterator,ControlList::EventList::const_iterator> range;
|
||||
|
||||
|
|
@ -427,8 +440,11 @@ Curve::multipoint_eval (double x) const
|
|||
return before->value;
|
||||
}
|
||||
|
||||
double tdelta = x - before->when;
|
||||
double trange = after->when - before->when;
|
||||
double aw = after->when.val();
|
||||
double bw = before->when.val();
|
||||
|
||||
double tdelta = x.val() - bw;
|
||||
double trange = aw - bw;
|
||||
|
||||
switch (_list.interpolation()) {
|
||||
case ControlList::Discrete:
|
||||
|
|
@ -440,8 +456,9 @@ Curve::multipoint_eval (double x) const
|
|||
case ControlList::Curved:
|
||||
if (after->coeff) {
|
||||
ControlEvent* ev = after;
|
||||
double x2 = x * x;
|
||||
return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
|
||||
#warning NUTEMPO fixme possible overflow ... multiplyng two position types .. also, units?
|
||||
double x2 = x.val() * x.val();
|
||||
return ev->coeff[0] + (ev->coeff[1] * x.val()) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x.val());
|
||||
}
|
||||
/* fallthrough */
|
||||
case ControlList::Linear:
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ using namespace PBD;
|
|||
XXX: This is a hack. The time should probably be expressed in
|
||||
seconds rather than beats, and should be configurable etc. etc.
|
||||
*/
|
||||
static double const time_between_interpolated_controller_outputs = 1.0 / 256;
|
||||
static Temporal::Beats const time_between_interpolated_controller_outputs = Temporal::Beats::ticks (256);
|
||||
|
||||
namespace Evoral {
|
||||
|
||||
|
|
@ -78,11 +78,11 @@ Sequence<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 */
|
||||
template<typename Time>
|
||||
Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq,
|
||||
Time t,
|
||||
bool force_discrete,
|
||||
const std::set<Evoral::Parameter>& filtered,
|
||||
const std::set<WeakNotePtr>* active_notes)
|
||||
Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq,
|
||||
Time t,
|
||||
bool force_discrete,
|
||||
const std::set<Evoral::Parameter>& filtered,
|
||||
std::set<WeakNotePtr> const * active_notes)
|
||||
: _seq(&seq)
|
||||
, _active_patch_change_message (0)
|
||||
, _type(NIL)
|
||||
|
|
@ -103,8 +103,7 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
|
|||
|
||||
// Add currently active notes, if given
|
||||
if (active_notes) {
|
||||
for (typename std::set<WeakNotePtr>::const_iterator i = active_notes->begin();
|
||||
i != active_notes->end(); ++i) {
|
||||
for (typename std::set<WeakNotePtr>::const_iterator i = active_notes->begin(); i != active_notes->end(); ++i) {
|
||||
NotePtr note = i->lock();
|
||||
if (note && note->time() <= t && note->end_time() > t) {
|
||||
_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
|
||||
_note_iter = seq.note_lower_bound(t);
|
||||
|
||||
// Find first sysex event at or after t
|
||||
for (typename Sequence<Time>::SysExes::const_iterator i = seq.sysexes().begin();
|
||||
i != seq.sysexes().end(); ++i) {
|
||||
|
|
@ -138,7 +136,8 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
|
|||
_control_iters.reserve(seq._controls.size());
|
||||
bool found = false;
|
||||
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) {
|
||||
|
||||
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)));
|
||||
double x, y;
|
||||
Temporal::timepos_t xtime;
|
||||
double y;
|
||||
bool ret;
|
||||
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 {
|
||||
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) {
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(x >= 0);
|
||||
|
||||
const ParameterDescriptor& desc = seq.type_map().descriptor(i->first);
|
||||
if (y < desc.lower || y > desc.upper) {
|
||||
cerr << "ERROR: Controller value " << y
|
||||
|
|
@ -170,14 +169,14 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
|
|||
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);
|
||||
|
||||
// Found a new earliest_control
|
||||
if (x < earliest_control_x) {
|
||||
earliest_control_x = x;
|
||||
if (xtime < earliest_control_x) {
|
||||
earliest_control_x = xtime;
|
||||
earliest_control_index = _control_iters.size() - 1;
|
||||
found = true;
|
||||
}
|
||||
|
|
@ -215,13 +214,22 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>&
|
|||
|
||||
template<typename Time>
|
||||
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()) {
|
||||
if (notes) {
|
||||
notes->insert(_active_notes.top());
|
||||
}
|
||||
_active_notes.pop();
|
||||
/* can't iterate over a std::priority_queue<> such as ActiveNotes */
|
||||
ActiveNotes copy (_active_notes);
|
||||
while (!copy.empty()) {
|
||||
active_notes.insert (copy.top());
|
||||
copy.pop ();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Time>
|
||||
void
|
||||
Sequence<Time>::const_iterator::invalidate(bool preserve_active_notes)
|
||||
{
|
||||
if (!preserve_active_notes) {
|
||||
_active_notes = ActiveNotes();
|
||||
}
|
||||
_type = NIL;
|
||||
_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
|
||||
* or patch-change. Bank-select (CC0, CC32) needs to be sent before the PGM. */
|
||||
if (_control_iter != _control_iters.end() &&
|
||||
_control_iter->list && _control_iter->x != DBL_MAX) {
|
||||
if (_type == NIL || _control_iter->x <= earliest_t.to_double()) {
|
||||
_control_iter->list && _control_iter->x != std::numeric_limits<Temporal::timepos_t>::max()) {
|
||||
if (_type == NIL || _control_iter->x <= earliest_t) {
|
||||
_type = CONTROL;
|
||||
earliest_t = Time(_control_iter->x);
|
||||
earliest_t = _control_iter->x.beats();
|
||||
}
|
||||
}
|
||||
|
||||
/* .. but prefer to send any Note-off first */
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
double x = 0.0;
|
||||
double y = 0.0;
|
||||
bool ret = false;
|
||||
Temporal::timepos_t x;
|
||||
Temporal::timepos_t xtime;
|
||||
double y = 0.0;
|
||||
bool ret = false;
|
||||
|
||||
// Increment past current event
|
||||
switch (_type) {
|
||||
|
|
@ -366,19 +375,18 @@ Sequence<Time>::const_iterator::operator++()
|
|||
case CONTROL:
|
||||
// Increment current controller iterator
|
||||
if (_force_discrete || _control_iter->list->interpolation() == ControlList::Discrete) {
|
||||
ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked (
|
||||
_control_iter->x, x, y, false);
|
||||
ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked (_control_iter->x, xtime, y, false);
|
||||
} else {
|
||||
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);
|
||||
if (ret) {
|
||||
_control_iter->x = x;
|
||||
_control_iter->x = xtime;
|
||||
_control_iter->y = y;
|
||||
} else {
|
||||
_control_iter->list.reset();
|
||||
_control_iter->x = DBL_MAX;
|
||||
_control_iter->x = std::numeric_limits<Time>::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.y <= INT8_MAX);
|
||||
|
||||
ev->set_time(Time(iter.x));
|
||||
ev->set_time(iter.x.beats());
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_CONTROL + iter.list->parameter().channel();
|
||||
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.y <= INT8_MAX);
|
||||
|
||||
ev->set_time(Time(iter.x));
|
||||
ev->set_time(iter.x.beats());
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.list->parameter().channel();
|
||||
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.y < (1<<14));
|
||||
|
||||
ev->set_time(Time(iter.x));
|
||||
ev->set_time(iter.x.beats());
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_BENDER + iter.list->parameter().channel();
|
||||
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.y <= INT8_MAX);
|
||||
|
||||
ev->set_time(Time(iter.x));
|
||||
ev->set_time(iter.x.beats());
|
||||
ev->realloc(3);
|
||||
ev->buffer()[0] = MIDI_CMD_NOTE_PRESSURE + iter.list->parameter().channel();
|
||||
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.y <= INT8_MAX);
|
||||
|
||||
ev->set_time(Time(iter.x));
|
||||
ev->set_time(iter.x.beats());
|
||||
ev->realloc(2);
|
||||
ev->buffer()[0] = MIDI_CMD_CHANNEL_PRESSURE + iter.list->parameter().channel();
|
||||
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",
|
||||
this, _type_map.to_symbol(param), time, value, _controls.size()));
|
||||
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 */
|
||||
}
|
||||
|
||||
|
|
@ -1411,12 +1419,26 @@ Sequence<Time>::control_list_marked_dirty ()
|
|||
|
||||
template<typename Time>
|
||||
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;
|
||||
str << "+++ dump\n";
|
||||
for (i = begin(); i != end(); ++i) {
|
||||
typename Sequence<Time>::const_iterator i = begin();
|
||||
|
||||
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;
|
||||
if (limit) {
|
||||
if (--limit == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
str << "--- dump\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
#include <boost/shared_ptr.hpp>
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "temporal/timeline.h"
|
||||
|
||||
#include "evoral/visibility.h"
|
||||
#include "evoral/Parameter.h"
|
||||
#include "evoral/ParameterDescriptor.h"
|
||||
|
|
@ -52,8 +54,8 @@ public:
|
|||
|
||||
virtual ~Control() {}
|
||||
|
||||
virtual void set_double (double val, double frame=0, bool to_list=false);
|
||||
virtual double get_double (bool from_list=false, double frame=0) const;
|
||||
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, Temporal::timepos_t frame = std::numeric_limits<Temporal::timepos_t>::min()) const;
|
||||
|
||||
/** Get the latest user-set value
|
||||
* (which may not equal get_value() when automation is playing back).
|
||||
|
|
|
|||
|
|
@ -34,8 +34,12 @@
|
|||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "temporal/timeline.h"
|
||||
#include "temporal/types.h"
|
||||
#include "temporal/range.h"
|
||||
|
||||
#include "evoral/visibility.h"
|
||||
#include "evoral/Range.h"
|
||||
|
||||
#include "evoral/Parameter.h"
|
||||
#include "evoral/ParameterDescriptor.h"
|
||||
|
||||
|
|
@ -48,7 +52,8 @@ class TypeMap;
|
|||
*/
|
||||
class LIBEVORAL_API ControlEvent {
|
||||
public:
|
||||
ControlEvent (double w, double v)
|
||||
|
||||
ControlEvent (Temporal::timepos_t const & w, double v)
|
||||
: when (w), value (v), coeff (0)
|
||||
{}
|
||||
|
||||
|
|
@ -71,11 +76,12 @@ public:
|
|||
coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
|
||||
}
|
||||
|
||||
double when;
|
||||
Temporal::timepos_t when;
|
||||
double value;
|
||||
double* coeff; ///< double[4] allocated by Curve as needed
|
||||
};
|
||||
|
||||
|
||||
/** A list (sequence) of time-stamped values for a control
|
||||
*/
|
||||
class LIBEVORAL_API ControlList
|
||||
|
|
@ -87,12 +93,14 @@ public:
|
|||
typedef EventList::const_iterator const_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&, double start, double end);
|
||||
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&);
|
||||
|
||||
|
|
@ -113,26 +121,26 @@ public:
|
|||
EventList::size_type size() const { return _events.size(); }
|
||||
|
||||
/** @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);
|
||||
if (_events.empty()) {
|
||||
return 0.0;
|
||||
return std::numeric_limits<Temporal::timepos_t>::min();
|
||||
}
|
||||
return at_start ? _events.front()->when : _events.back()->when;
|
||||
}
|
||||
|
||||
double length() const {
|
||||
Temporal::timecnt_t length() const {
|
||||
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(); }
|
||||
|
||||
/** Remove all events from this list. */
|
||||
void clear ();
|
||||
void x_scale (double factor);
|
||||
bool extend_to (double);
|
||||
void slide (iterator before, double distance);
|
||||
void shift (double before, double distance);
|
||||
void x_scale (Temporal::ratio_t const &);
|
||||
bool extend_to (Temporal::timepos_t const & );
|
||||
void slide (iterator before, Temporal::timecnt_t const & distance);
|
||||
void shift (Temporal::timepos_t const & before, Temporal::timecnt_t const & distance);
|
||||
|
||||
void y_transform (boost::function<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_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.
|
||||
*
|
||||
|
|
@ -160,17 +168,17 @@ public:
|
|||
*
|
||||
* @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 */
|
||||
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, iterator);
|
||||
void erase (double, double);
|
||||
bool move_ranges (std::list< RangeMove<double> > const &);
|
||||
void modify (iterator, double, double);
|
||||
void erase (Temporal::timepos_t const &, double);
|
||||
bool move_ranges (std::list<Temporal::RangeMove> const &);
|
||||
void modify (iterator, Temporal::timepos_t const &, double);
|
||||
|
||||
/** Thin the number of events in this list.
|
||||
*
|
||||
|
|
@ -192,29 +200,29 @@ public:
|
|||
*/
|
||||
void thin (double thinning_factor);
|
||||
|
||||
boost::shared_ptr<ControlList> cut (double, double);
|
||||
boost::shared_ptr<ControlList> copy (double, double);
|
||||
boost::shared_ptr<ControlList> cut (Temporal::timepos_t const &, Temporal::timepos_t const &);
|
||||
boost::shared_ptr<ControlList> copy (Temporal::timepos_t const &, Temporal::timepos_t const &);
|
||||
|
||||
/** Remove all events in the given time range from this list.
|
||||
*
|
||||
* @param start start 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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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(); }
|
||||
const_iterator begin() const { return _events.begin(); }
|
||||
|
|
@ -229,7 +237,7 @@ public:
|
|||
ControlEvent* front() { 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&)) {
|
||||
Glib::Threads::RWLock::WriterLock lm (_lock);
|
||||
|
|
@ -242,7 +250,7 @@ public:
|
|||
* @param where absolute time in samples
|
||||
* @returns parameter value
|
||||
*/
|
||||
double eval (double where) const {
|
||||
double eval (Temporal::timepos_t const & where) const {
|
||||
Glib::Threads::RWLock::ReaderLock lm (_lock);
|
||||
return unlocked_eval (where);
|
||||
}
|
||||
|
|
@ -254,7 +262,7 @@ public:
|
|||
* @param ok boolean reference if returned value is valid
|
||||
* @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);
|
||||
|
||||
|
|
@ -271,15 +279,15 @@ public:
|
|||
|
||||
/** Lookup cache for eval functions, range contains equivalent values */
|
||||
struct LookupCache {
|
||||
LookupCache() : left(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "range" */
|
||||
LookupCache() : left (std::numeric_limits<Temporal::timepos_t>::max()) {}
|
||||
Temporal::timepos_t left; /* leftmost x coordinate used when finding "range" */
|
||||
std::pair<ControlList::const_iterator,ControlList::const_iterator> range;
|
||||
};
|
||||
|
||||
/** Lookup cache for point finding, range contains points after left */
|
||||
struct SearchCache {
|
||||
SearchCache () : left(-1) {}
|
||||
double left; /* leftmost x coordinate used when finding "first" */
|
||||
SearchCache () : left (std::numeric_limits<Temporal::timepos_t>::max()) {}
|
||||
Temporal::timepos_t left; /* leftmost x coordinate used when finding "first" */
|
||||
ControlList::const_iterator first;
|
||||
};
|
||||
|
||||
|
|
@ -296,10 +304,10 @@ public:
|
|||
*
|
||||
* 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_linear_unlocked (double start, double& x, double& y, bool inclusive, double min_d_delta = 0) 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 (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 destroy_curve();
|
||||
|
|
@ -338,9 +346,9 @@ public:
|
|||
virtual bool touching() const { return false; }
|
||||
virtual bool writing() const { return false; }
|
||||
virtual bool touch_enabled() const { return false; }
|
||||
void start_write_pass (double when);
|
||||
void write_pass_finished (double when, double thinning_factor=0.0);
|
||||
void set_in_write_pass (bool, bool add_point = false, double when = 0.0);
|
||||
void start_write_pass (Temporal::timepos_t const &);
|
||||
void write_pass_finished (Temporal::timepos_t const &, double thinning_factor=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 */
|
||||
bool in_write_pass () const;
|
||||
bool in_new_write_pass () { return new_write_pass; }
|
||||
|
|
@ -355,23 +363,23 @@ public:
|
|||
|
||||
void invalidate_insert_iterator ();
|
||||
|
||||
protected:
|
||||
protected:
|
||||
|
||||
/** 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);
|
||||
bool erase_range_internal (double start, double end, EventList &);
|
||||
boost::shared_ptr<ControlList> cut_copy_clear (Temporal::timepos_t const &, Temporal::timepos_t const &, int op);
|
||||
bool erase_range_internal (Temporal::timepos_t const & start, Temporal::timepos_t const & end, EventList &);
|
||||
|
||||
void maybe_add_insert_guard (double when);
|
||||
iterator erase_from_iterator_to (iterator iter, double when);
|
||||
bool maybe_insert_straight_line (double when, double value);
|
||||
void maybe_add_insert_guard (Temporal::timepos_t const & when);
|
||||
iterator erase_from_iterator_to (iterator iter, Temporal::timepos_t const & when);
|
||||
bool maybe_insert_straight_line (Temporal::timepos_t const & when, double value);
|
||||
|
||||
virtual void maybe_signal_changed ();
|
||||
|
||||
void _x_scale (double factor);
|
||||
void _x_scale (Temporal::ratio_t const &);
|
||||
|
||||
mutable LookupCache _lookup_cache;
|
||||
mutable SearchCache _search_cache;
|
||||
|
|
@ -385,24 +393,25 @@ protected:
|
|||
int8_t _frozen;
|
||||
bool _changed_when_thawed;
|
||||
bool _sort_pending;
|
||||
Temporal::TimeDomain _time_style;
|
||||
|
||||
Curve* _curve;
|
||||
|
||||
private:
|
||||
private:
|
||||
iterator most_recent_insert_iterator;
|
||||
double insert_position;
|
||||
Temporal::timepos_t insert_position;
|
||||
bool new_write_pass;
|
||||
bool did_write_during_pass;
|
||||
bool _in_write_pass;
|
||||
|
||||
void unlocked_remove_duplicates ();
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Evoral
|
||||
|
||||
#endif // EVORAL_CONTROL_LIST_HPP
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
#include <inttypes.h>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include "temporal/timeline.h"
|
||||
|
||||
#include "evoral/visibility.h"
|
||||
|
||||
namespace Evoral {
|
||||
|
|
@ -33,17 +35,17 @@ class LIBEVORAL_API Curve : public boost::noncopyable
|
|||
public:
|
||||
Curve (const ControlList& cl);
|
||||
|
||||
bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen) const;
|
||||
void 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 (Temporal::timepos_t const & x0, Temporal::timepos_t const & x1, float *arg, int32_t veclen) const;
|
||||
|
||||
void solve () const;
|
||||
|
||||
void mark_dirty() const { _dirty = true; }
|
||||
|
||||
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;
|
||||
const ControlList& _list;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -49,14 +49,14 @@ template<typename Time> class Event;
|
|||
*/
|
||||
class /*LIBEVORAL_API*/ ControlIterator {
|
||||
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)
|
||||
, x(ax)
|
||||
, y(ay)
|
||||
{}
|
||||
|
||||
boost::shared_ptr<const ControlList> list;
|
||||
double x;
|
||||
Temporal::timepos_t x;
|
||||
double y;
|
||||
};
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ public:
|
|||
typedef const Note<Time>* value_type;
|
||||
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
|
||||
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 const PatchChanges& patch_changes () const { return _patch_changes; }
|
||||
|
||||
void dump (std::ostream&) const;
|
||||
|
||||
private:
|
||||
typedef std::priority_queue<NotePtr, std::deque<NotePtr>, LaterNoteEndComparator> ActiveNotes;
|
||||
public:
|
||||
|
|
@ -226,15 +224,15 @@ public:
|
|||
class LIBEVORAL_API const_iterator {
|
||||
public:
|
||||
const_iterator();
|
||||
const_iterator(const Sequence<Time>& seq,
|
||||
Time t,
|
||||
bool force_discrete,
|
||||
const std::set<Evoral::Parameter>& filtered,
|
||||
const std::set<WeakNotePtr>* active_notes=NULL);
|
||||
const_iterator(const Sequence<Time>& seq,
|
||||
Time t,
|
||||
bool force_discrete,
|
||||
std::set<Evoral::Parameter> const & filtered,
|
||||
std::set<WeakNotePtr> const* active_notes = 0);
|
||||
|
||||
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 boost::shared_ptr< const Event<Time> > operator->() const { return _event; }
|
||||
|
|
@ -246,6 +244,8 @@ public:
|
|||
|
||||
const_iterator& operator=(const const_iterator& other);
|
||||
|
||||
void get_active_notes (std::set<WeakNotePtr>&) const;
|
||||
|
||||
private:
|
||||
friend class Sequence<Time>;
|
||||
|
||||
|
|
@ -277,12 +277,14 @@ public:
|
|||
Time t = Time(),
|
||||
bool force_discrete = false,
|
||||
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);
|
||||
}
|
||||
|
||||
const const_iterator& end() const { return _end_iter; }
|
||||
|
||||
void dump (std::ostream&, const_iterator x, uint32_t limit = 0) const;
|
||||
|
||||
// CONST iterator implementations (x3)
|
||||
typename Notes::const_iterator note_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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#include "RangeTest.h"
|
||||
#include "evoral/Range.h"
|
||||
#include "temporal/range.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest);
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ def build(bld):
|
|||
Note.cc
|
||||
SMF.cc
|
||||
Sequence.cc
|
||||
TimeConverter.cc
|
||||
debug.cc
|
||||
'''
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue