diff options
-rw-r--r-- | libs/ardour/enums.cc | 2 | ||||
-rw-r--r-- | libs/evoral/evoral/ControlList.hpp | 33 | ||||
-rw-r--r-- | libs/evoral/src/ControlList.cpp | 90 | ||||
-rw-r--r-- | libs/evoral/src/Curve.cpp | 99 |
4 files changed, 171 insertions, 53 deletions
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 61a7e9a39f..748d725c09 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -545,6 +545,8 @@ setup_enum_writer () REGISTER_CLASS_ENUM (AutomationList, Discrete); REGISTER_CLASS_ENUM (AutomationList, Linear); REGISTER_CLASS_ENUM (AutomationList, Curved); + REGISTER_CLASS_ENUM (AutomationList, Logarithmic); + REGISTER_CLASS_ENUM (AutomationList, Exponential); REGISTER (_AutomationList_InterpolationStyle); REGISTER_CLASS_ENUM (AnyTime, Timecode); diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp index 99958d79fb..c4051193b8 100644 --- a/libs/evoral/evoral/ControlList.hpp +++ b/libs/evoral/evoral/ControlList.hpp @@ -275,7 +275,9 @@ public: enum InterpolationStyle { Discrete, Linear, - Curved + Curved, // spline, used for x-fades + Logarithmic, + Exponential // fader, gain }; /** query interpolation style of the automation data @@ -283,10 +285,18 @@ public: */ InterpolationStyle interpolation() const { return _interpolation; } - /** set the interpolation style of the automation data + /** query default interpolation for parameter-descriptor */ + virtual InterpolationStyle default_interpolation() const; + + /** set the interpolation style of the automation data. + * + * This will fail when asking for Logarithmic scale and min,max crosses 0 + * or Exponential scale with min != 0. + * * @param is interpolation style + * @returns true if style change was successful */ - void set_interpolation (InterpolationStyle is); + bool set_interpolation (InterpolationStyle is); virtual bool touching() const { return false; } virtual bool writing() const { return false; } @@ -339,14 +349,15 @@ protected: Curve* _curve; - private: - iterator most_recent_insert_iterator; - double insert_position; - bool new_write_pass; - bool did_write_during_pass; - bool _in_write_pass; - void unlocked_invalidate_insert_iterator (); - void add_guard_point (double when); +private: + iterator most_recent_insert_iterator; + double insert_position; + bool new_write_pass; + bool did_write_during_pass; + bool _in_write_pass; + + void unlocked_invalidate_insert_iterator (); + void add_guard_point (double when); }; } // namespace Evoral diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp index 3a3737004b..516ffc56c2 100644 --- a/libs/evoral/src/ControlList.cpp +++ b/libs/evoral/src/ControlList.cpp @@ -38,6 +38,7 @@ #include "evoral/TypeMap.hpp" #include "evoral/types.hpp" +#include "pbd/control_math.h" #include "pbd/compose.h" #include "pbd/debug.h" @@ -54,9 +55,9 @@ inline bool event_time_less_than (ControlEvent* a, ControlEvent* b) ControlList::ControlList (const Parameter& id, const ParameterDescriptor& desc) : _parameter(id) , _desc(desc) + , _interpolation (default_interpolation ()) , _curve(0) { - _interpolation = desc.toggled ? Discrete : Linear; _frozen = 0; _changed_when_thawed = false; _lookup_cache.left = -1; @@ -90,9 +91,8 @@ ControlList::ControlList (const ControlList& other) insert_position = -1; most_recent_insert_iterator = _events.end(); + // XXX copy_events() emits Dirty, but this is just assignment copy/construction copy_events (other); - - mark_dirty (); } ControlList::ControlList (const ControlList& other, double start, double end) @@ -113,6 +113,7 @@ ControlList::ControlList (const ControlList& other, double start, double end) boost::shared_ptr<ControlList> section = const_cast<ControlList*>(&other)->copy (start, end); if (!section->empty()) { + // XXX copy_events() emits Dirty, but this is just assignment copy/construction copy_events (*(section.get())); } @@ -151,10 +152,21 @@ ControlList& ControlList::operator= (const ControlList& other) { if (this != &other) { + _frozen = 0; + _changed_when_thawed = false; + _sort_pending = false; + insert_position = other.insert_position; + new_write_pass = true; + _in_write_pass = false; + did_write_during_pass = false; + insert_position = -1; + _parameter = other._parameter; + _desc = other._desc; _interpolation = other._interpolation; + // XXX copy_events() emits Dirty, but this is just assignment copy/construction copy_events (other); } @@ -170,6 +182,7 @@ ControlList::copy_events (const ControlList& other) delete (*x); } _events.clear (); + Glib::Threads::RWLock::ReaderLock olm (other._lock); for (const_iterator i = other.begin(); i != other.end(); ++i) { _events.push_back (new ControlEvent ((*i)->when, (*i)->value)); } @@ -192,6 +205,17 @@ ControlList::destroy_curve() _curve = NULL; } +ControlList::InterpolationStyle +ControlList::default_interpolation () const +{ + if (_desc.toggled) { + return Discrete; + } else if (_desc.logarithmic) { + return Logarithmic; + } + return Linear; +} + void ControlList::maybe_signal_changed () { @@ -1252,13 +1276,21 @@ ControlList::unlocked_eval (double x) const upos = _events.back()->when; uval = _events.back()->value; - if (_interpolation == Discrete) { - return lval; - } - - /* linear interpolation between the two points */ fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); + + switch (_interpolation) { + case Discrete: + return lval; + case Logarithmic: + return interpolate_logarithmic (lval, uval, fraction, _desc.lower, _desc.upper); + case Exponential: + return interpolate_gain (lval, uval, fraction, _desc.upper); + case Curved: + /* only used x-fade curves, never direct eval */ + assert (0); + default: // Linear + return interpolate_linear (lval, uval, fraction); + } default: if (x >= _events.back()->when) { @@ -1334,13 +1366,24 @@ ControlList::multipoint_eval (double x) const upos = (*range.second)->when; uval = (*range.second)->value; - /* linear interpolation betweeen the two points - on either side of x - */ - fraction = (double) (x - lpos) / (double) (upos - lpos); - return lval + (fraction * (uval - lval)); + switch (_interpolation) { + case Logarithmic: + return interpolate_logarithmic (lval, uval, fraction, _desc.lower, _desc.upper); + case Exponential: + return interpolate_gain (lval, uval, fraction, _desc.upper); + case Discrete: + /* should not reach here */ + assert (0); + case Curved: + /* only used x-fade curves, never direct eval */ + assert (0); + default: // Linear + return interpolate_linear (lval, uval, fraction); + break; + } + assert (0); } /* x is a control point in the data */ @@ -1823,15 +1866,30 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements) return true; } -void +bool ControlList::set_interpolation (InterpolationStyle s) { if (_interpolation == s) { - return; + return true; + } + + switch (s) { + case Logarithmic: + if (_desc.lower * _desc.upper <= 0 || _desc.upper <= _desc.lower) { + return false; + } + break; + case Exponential: + if (_desc.lower != 0 || _desc.upper <= _desc.lower) { + return false; + } + default: + break; } _interpolation = s; InterpolationChanged (s); /* EMIT SIGNAL */ + return true; } bool diff --git a/libs/evoral/src/Curve.cpp b/libs/evoral/src/Curve.cpp index 07dd158079..e7eeeccb9f 100644 --- a/libs/evoral/src/Curve.cpp +++ b/libs/evoral/src/Curve.cpp @@ -26,6 +26,8 @@ #include <glibmm/threads.h> +#include "pbd/control_math.h" + #include "evoral/Curve.hpp" #include "evoral/ControlList.hpp" @@ -282,31 +284,66 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen) const if (npoints == 2) { - /* linear interpolation between 2 points */ - - /* XXX: this numerator / denominator stuff is pretty grim, but it's the only - way I could get the maths to be accurate; doing everything with pure doubles - gives ~1e-17 errors in the vec[i] computation. - */ - - /* gradient of the line */ - double const m_num = _list.events().back()->value - _list.events().front()->value; - double const m_den = _list.events().back()->when - _list.events().front()->when; - - /* y intercept of the line */ - double const c = double (_list.events().back()->value) - (m_num * _list.events().back()->when / m_den); + const double lpos = _list.events().front()->when; + const double lval = _list.events().front()->value; + const double upos = _list.events().back()->when; + const double uval = _list.events().back()->value; /* dx that we are using */ - double dx_num = 0; - double dx_den = 1; if (veclen > 1) { - dx_num = hx - lx; - dx_den = veclen - 1; - for (int i = 0; i < veclen; ++i) { - vec[i] = (lx * (m_num / m_den) + m_num * i * dx_num / (m_den * dx_den)) + c; + const double dx_num = hx - lx; + const double dx_den = veclen - 1; + const double lower = _list.descriptor().lower; + const double upper = _list.descriptor().upper; + + /* gradient of the line */ + const double m_num = uval - lval; + const double m_den = upos - lpos; + /* y intercept of the line */ + const double c = uval - (m_num * upos / m_den); + + switch (_list.interpolation()) { + case ControlList::Logarithmic: + for (int i = 0; i < veclen; ++i) { + const double fraction = (lx - lpos + i * dx_num / dx_den) / m_den; + vec[i] = interpolate_logarithmic (lval, uval, fraction, lower, upper); + } + break; + case ControlList::Exponential: + for (int i = 0; i < veclen; ++i) { + const double fraction = (lx - lpos + i * dx_num / dx_den) / m_den; + vec[i] = interpolate_gain (lval, uval, fraction, upper); + } + break; + case ControlList::Discrete: + // any discrete vector curves somewhere? + assert (0); + case ControlList::Curved: + // fallthrough, no 2 point spline + default: // Linear: + for (int i = 0; i < veclen; ++i) { + vec[i] = (lx * (m_num / m_den) + m_num * i * dx_num / (m_den * dx_den)) + c; + } + break; } } else { - vec[0] = lx * (m_num / m_den) + c; + double fraction = (lx - lpos) / (upos - lpos); + switch (_list.interpolation()) { + case ControlList::Logarithmic: + vec[0] = interpolate_logarithmic (lval, uval, fraction, _list.descriptor().lower, _list.descriptor().upper); + break; + case ControlList::Exponential: + vec[0] = interpolate_gain (lval, uval, fraction, _list.descriptor().upper); + break; + case ControlList::Discrete: + // any discrete vector curves somewhere? + assert (0); + case ControlList::Curved: + // fallthrough, no 2 point spline + default: // Linear: + vec[0] = interpolate_linear (lval, uval, fraction); + break; + } } return; @@ -388,12 +425,22 @@ Curve::multipoint_eval (double x) const double tdelta = x - before->when; double trange = after->when - before->when; - if (_list.interpolation() == ControlList::Curved && 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); - } else { - return before->value + (vdelta * (tdelta / trange)); + switch (_list.interpolation()) { + case ControlList::Discrete: + return before->value; + case ControlList::Logarithmic: + return interpolate_logarithmic (before->value, after->value, tdelta / trange, _list.descriptor().lower, _list.descriptor().upper); + case ControlList::Exponential: + return interpolate_gain (before->value, after->value, tdelta / trange, _list.descriptor().upper); + 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); + } + // no break, fallthru + default: // Linear + return before->value + (vdelta * (tdelta / trange)); } } |