summaryrefslogtreecommitdiff
path: root/libs/evoral
diff options
context:
space:
mode:
Diffstat (limited to 'libs/evoral')
-rw-r--r--libs/evoral/evoral/ControlList.hpp1
-rw-r--r--libs/evoral/evoral/Range.hpp219
-rw-r--r--libs/evoral/evoral/types.hpp17
-rwxr-xr-xlibs/evoral/run-tests.sh4
-rw-r--r--libs/evoral/src/ControlList.cpp2
-rw-r--r--libs/evoral/src/Curve.cpp41
-rw-r--r--libs/evoral/test/RangeTest.cpp84
-rw-r--r--libs/evoral/test/RangeTest.hpp19
-rw-r--r--libs/evoral/wscript1
9 files changed, 352 insertions, 36 deletions
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
index 01e0f8c1fb..324d03cc28 100644
--- a/libs/evoral/evoral/ControlList.hpp
+++ b/libs/evoral/evoral/ControlList.hpp
@@ -26,6 +26,7 @@
#include <glibmm/thread.h>
#include "pbd/signals.h"
#include "evoral/types.hpp"
+#include "evoral/Range.hpp"
#include "evoral/Parameter.hpp"
namespace Evoral {
diff --git a/libs/evoral/evoral/Range.hpp b/libs/evoral/evoral/Range.hpp
new file mode 100644
index 0000000000..bc353a47d7
--- /dev/null
+++ b/libs/evoral/evoral/Range.hpp
@@ -0,0 +1,219 @@
+/* This file is part of Evoral.
+ * Copyright (C) 2008 David Robillard <http://drobilla.net>
+ * Copyright (C) 2000-2008 Paul Davis
+ *
+ * Evoral 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.
+ *
+ * Evoral 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 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef EVORAL_RANGE_HPP
+#define EVORAL_RANGE_HPP
+
+#include <list>
+
+namespace Evoral {
+
+enum OverlapType {
+ OverlapNone, // no overlap
+ OverlapInternal, // the overlap is 100% with 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>
+OverlapType coverage (T sa, T ea, T sb, T eb) {
+ /* OverlapType returned reflects how the second (B)
+ range overlaps the first (A).
+
+ The diagrams show various relative placements
+ of A and B for each OverlapType.
+
+ Notes:
+ Internal: the start points cannot coincide
+ External: the start and end points can coincide
+ Start: end points can coincide
+ End: start points can coincide
+
+ XXX Logically, Internal should disallow end
+ point equality.
+ */
+
+ /*
+ |--------------------| A
+ |------| B
+ |-----------------| B
+
+
+ "B is internal to A"
+
+ */
+
+ if ((sb > sa) && (eb <= ea)) {
+ return OverlapInternal;
+ }
+
+ /*
+ |--------------------| A
+ ----| B
+ -----------------------| B
+ --| B
+
+ "B overlaps the start of A"
+
+ */
+
+ if ((eb >= sa) && (eb <= ea)) {
+ return OverlapStart;
+ }
+ /*
+ |---------------------| A
+ |----------------- B
+ |----------------------- B
+ |- B
+
+ "B overlaps the end of A"
+
+ */
+ if ((sb > sa) && (sb <= ea)) {
+ return OverlapEnd;
+ }
+ /*
+ |--------------------| A
+ -------------------------- B
+ |----------------------- B
+ ----------------------| B
+ |--------------------| B
+
+
+ "B overlaps all of A"
+ */
+ if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
+ return OverlapExternal;
+ }
+
+ return OverlapNone;
+}
+
+/** Type to describe a time range */
+template<typename T>
+struct Range {
+ Range (T f, T t) : from (f), to (t) {}
+ T from; ///< start of the range
+ T to; ///< end of the range
+};
+
+template<typename T>
+bool operator== (Range<T> a, Range<T> b) {
+ return a.from == b.from && a.to == b.to;
+}
+
+template<typename T>
+class 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 ();
+ }
+
+private:
+ 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;
+ }
+
+ List _list;
+ bool _dirty;
+};
+
+/** Type to describe the movement of a time range */
+template<typename T>
+struct 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
+};
+
+template<typename T>
+RangeList<T> subtract (Range<T> range, RangeList<T> sub)
+{
+ RangeList<T> result;
+
+ if (sub.empty ()) {
+ result.add (range);
+ return result;
+ }
+
+ T x = range.from;
+
+ typename RangeList<T>::List s = sub.get ();
+
+ for (typename RangeList<T>::List::const_iterator i = s.begin(); i != s.end(); ++i) {
+
+ if (coverage (range.from, range.to, i->from, i->to) == OverlapNone) {
+ continue;
+ }
+
+ Range<T> clamped (std::max (range.from, i->from), std::min (range.to, i->to));
+
+ if (clamped.from != x) {
+ result.add (Range<T> (x, clamped.from - 1));
+ }
+
+ x = clamped.to;
+ }
+
+ if (s.back().to < range.to) {
+ result.add (Range<T> (x, range.to));
+ }
+
+ return result;
+}
+
+}
+
+#endif
diff --git a/libs/evoral/evoral/types.hpp b/libs/evoral/evoral/types.hpp
index c3cb6a9c21..35dec6de0b 100644
--- a/libs/evoral/evoral/types.hpp
+++ b/libs/evoral/evoral/types.hpp
@@ -46,23 +46,6 @@ static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
/** Type of an event (opaque, mapped by application) */
typedef uint32_t EventType;
-/** Type to describe a time range */
-template<typename T>
-struct Range {
- Range (T f, T t) : from (f), to (t) {}
- T from; ///< start of the range
- T to; ///< end of the range
-};
-
-/** Type to describe the movement of a time range */
-template<typename T>
-struct 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
-};
-
} // namespace Evoral
namespace PBD {
diff --git a/libs/evoral/run-tests.sh b/libs/evoral/run-tests.sh
index 35531a613e..8eb7ba8820 100755
--- a/libs/evoral/run-tests.sh
+++ b/libs/evoral/run-tests.sh
@@ -1,14 +1,14 @@
#!/bin/sh
srcdir=`pwd`
-export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/default/libs/evoral:$srcdir/../../build/default/libs/pbd
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$srcdir/../../build/libs/evoral:$srcdir/../../build/libs/pbd
if [ ! -f './test/testdata/TakeFive.mid' ]; then
echo "This script must be run from within the libs/evoral directory";
exit 1;
fi
# Make symlink to TakeFive.mid in build directory
-cd ../../build/default/libs/evoral
+cd ../../build/libs/evoral
mkdir -p ./test/testdata
ln -fs $srcdir/test/testdata/TakeFive.mid \
./test/testdata/TakeFive.mid
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index 75b591d6a3..758dc141e0 100644
--- a/libs/evoral/src/ControlList.cpp
+++ b/libs/evoral/src/ControlList.cpp
@@ -465,6 +465,8 @@ ControlList::fast_simple_add (double when, double value)
/* to be used only for loading pre-sorted data from saved state */
_events.insert (_events.end(), new ControlEvent (when, value));
assert(_events.back());
+
+ mark_dirty ();
}
void
diff --git a/libs/evoral/src/Curve.cpp b/libs/evoral/src/Curve.cpp
index dd327d488a..42f0eee49c 100644
--- a/libs/evoral/src/Curve.cpp
+++ b/libs/evoral/src/Curve.cpp
@@ -190,7 +190,7 @@ Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
void
Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
{
- double rx, dx, lx, hx, max_x, min_x;
+ double rx, lx, hx, max_x, min_x;
int32_t i;
int32_t original_veclen;
int32_t npoints;
@@ -276,26 +276,34 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
/* linear interpolation between 2 points */
- /* XXX I'm not sure that this is the right thing to
- do here. but its not a common case for the envisaged
- uses.
+ /* 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.
*/
- if (veclen > 1) {
- dx = (hx - lx) / (veclen - 1) ;
- } else {
- dx = 0; // not used
- }
+ /* 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;
- double slope = (_list.events().back()->value - _list.events().front()->value)/
- (_list.events().back()->when - _list.events().front()->when);
- double yfrac = dx*slope;
+ /* y intercept of the line */
+ double const c = double (_list.events().back()->value) - (m_num * _list.events().back()->when / m_den);
- vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
+ /* 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 (i = 1; i < veclen; ++i) {
- vec[i] = vec[i-1] + yfrac;
+ if (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;
+ }
+ } else {
+ vec[i] = lx;
}
+
return;
}
@@ -305,10 +313,9 @@ Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
rx = lx;
+ double dx = 0;
if (veclen > 1) {
dx = (hx - lx) / (veclen - 1);
- } else {
- dx = 0;
}
for (i = 0; i < veclen; ++i, rx += dx) {
diff --git a/libs/evoral/test/RangeTest.cpp b/libs/evoral/test/RangeTest.cpp
new file mode 100644
index 0000000000..ff9856a9b6
--- /dev/null
+++ b/libs/evoral/test/RangeTest.cpp
@@ -0,0 +1,84 @@
+#include "RangeTest.hpp"
+#include "evoral/Range.hpp"
+
+CPPUNIT_TEST_SUITE_REGISTRATION (RangeTest);
+
+using namespace Evoral;
+
+void
+RangeTest::coalesceTest ()
+{
+ RangeList<int> fred;
+ fred.add (Range<int> (2, 4));
+ fred.add (Range<int> (5, 6));
+ fred.add (Range<int> (6, 8));
+
+ RangeList<int>::List jim = fred.get ();
+
+ RangeList<int>::List::iterator i = jim.begin ();
+ CPPUNIT_ASSERT_EQUAL (2, i->from);
+ CPPUNIT_ASSERT_EQUAL (4, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (5, i->from);
+ CPPUNIT_ASSERT_EQUAL (8, i->to);
+}
+
+void
+RangeTest::subtractTest1 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (2, 4));
+ jim.add (Range<int> (7, 8));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (3), s.size ());
+
+ RangeList<int>::List::iterator i = s.begin ();
+ CPPUNIT_ASSERT_EQUAL (0, i->from);
+ CPPUNIT_ASSERT_EQUAL (1, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (4, i->from);
+ CPPUNIT_ASSERT_EQUAL (6, i->to);
+
+ ++i;
+ CPPUNIT_ASSERT_EQUAL (8, i->from);
+ CPPUNIT_ASSERT_EQUAL (10, i->to);
+}
+
+void
+RangeTest::subtractTest2 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (12, 19));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (1), s.size ());
+
+ RangeList<int>::List::iterator i = s.begin ();
+ CPPUNIT_ASSERT_EQUAL (0, i->from);
+ CPPUNIT_ASSERT_EQUAL (10, i->to);
+}
+
+void
+RangeTest::subtractTest3 ()
+{
+ Range<int> fred (0, 10);
+
+ RangeList<int> jim;
+ jim.add (Range<int> (0, 12));
+
+ RangeList<int> sheila = subtract (fred, jim);
+
+ RangeList<int>::List s = sheila.get ();
+ CPPUNIT_ASSERT_EQUAL (size_t (0), s.size ());
+}
diff --git a/libs/evoral/test/RangeTest.hpp b/libs/evoral/test/RangeTest.hpp
new file mode 100644
index 0000000000..6b65ad02d2
--- /dev/null
+++ b/libs/evoral/test/RangeTest.hpp
@@ -0,0 +1,19 @@
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class RangeTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (RangeTest);
+ CPPUNIT_TEST (coalesceTest);
+ CPPUNIT_TEST (subtractTest1);
+ CPPUNIT_TEST (subtractTest3);
+ CPPUNIT_TEST_SUITE_END ();
+
+public:
+ void coalesceTest ();
+ void subtractTest1 ();
+ void subtractTest2 ();
+ void subtractTest3 ();
+};
+
+
diff --git a/libs/evoral/wscript b/libs/evoral/wscript
index e8bf097db2..9cc0356c7f 100644
--- a/libs/evoral/wscript
+++ b/libs/evoral/wscript
@@ -124,6 +124,7 @@ def build(bld):
obj.source = '''
test/SequenceTest.cpp
test/SMFTest.cpp
+ test/RangeTest.cpp
test/testrunner.cpp
'''
obj.includes = ['.', './src']