summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2017-06-19 15:45:47 +0200
committerRobin Gareus <robin@gareus.org>2017-06-21 18:12:26 +0200
commitd6c47def098ebf6c44e1b3e0ca2166d1c990e7d3 (patch)
treec51eaf36673f55cdc07381f05445c22fb0488d7d /libs
parent36e32e564126e4587e3c2bee829c58876e65d285 (diff)
Implement additional ControlList interpolation methods.
The Control and ControlList uses the raw value (eg. coefficient for gain, Hz for frequencies) and those Lists are stored in existing sessions. In the vast majority of cases interpolating automation values using exp/log scale for dB, freq makes more sense -- it's also what the fader does. Adding additional interpolation methods is future proof (we might at allow to even add different methods per automation point (to the next) like other DAWs do. Currently it's mainly used in preparation for consistent GUI automation- lanes. Between 2 points there's always a visual straight line.
Diffstat (limited to 'libs')
-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));
}
}