diff options
Diffstat (limited to 'libs/evoral')
-rw-r--r-- | libs/evoral/evoral/ControlList.hpp | 1 | ||||
-rw-r--r-- | libs/evoral/evoral/Range.hpp | 219 | ||||
-rw-r--r-- | libs/evoral/evoral/types.hpp | 17 | ||||
-rwxr-xr-x | libs/evoral/run-tests.sh | 4 | ||||
-rw-r--r-- | libs/evoral/src/ControlList.cpp | 2 | ||||
-rw-r--r-- | libs/evoral/src/Curve.cpp | 41 | ||||
-rw-r--r-- | libs/evoral/test/RangeTest.cpp | 84 | ||||
-rw-r--r-- | libs/evoral/test/RangeTest.hpp | 19 | ||||
-rw-r--r-- | libs/evoral/wscript | 1 |
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'] |