summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/enums.cc2
-rw-r--r--libs/evoral/evoral/ControlList.hpp33
-rw-r--r--libs/evoral/src/ControlList.cpp90
-rw-r--r--libs/evoral/src/Curve.cpp99
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));
}
}