summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorHans Baier <hansfbaier@googlemail.com>2009-06-10 00:03:28 +0000
committerHans Baier <hansfbaier@googlemail.com>2009-06-10 00:03:28 +0000
commitf284d28d5306114e9badc9077835683e541420e0 (patch)
tree500bbe83900479c1a0780e780242187cd51bc9ef /libs/ardour
parentd039e2e80ff2179aef7430c53ca41e43297d9065 (diff)
libardour: * Add basic classes for later support of multiple interpolation algorithms for varispeed
* Add unit tests: Test which shows how the varispeed implementation in diskstream is broken. git-svn-id: svn://localhost/ardour2/branches/3.0@5144 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/interpolation.h49
-rw-r--r--libs/ardour/interpolation.cc43
-rw-r--r--libs/ardour/tests/interpolation-test.cc46
-rw-r--r--libs/ardour/tests/interpolation-test.h58
-rw-r--r--libs/ardour/tests/testrunner.cpp27
-rw-r--r--libs/ardour/wscript14
6 files changed, 237 insertions, 0 deletions
diff --git a/libs/ardour/ardour/interpolation.h b/libs/ardour/ardour/interpolation.h
new file mode 100644
index 0000000000..09ae242490
--- /dev/null
+++ b/libs/ardour/ardour/interpolation.h
@@ -0,0 +1,49 @@
+#include <math.h>
+//#include "ardour/types.h"
+
+typedef float Sample;
+#define nframes_t uint32_t
+
+// 40.24 fixpoint math
+#define FIXPOINT_ONE 0x1000000
+
+class Interpolation {
+ protected:
+ /// speed in fixed point math
+ uint64_t phi;
+
+ /// target speed in fixed point math
+ uint64_t target_phi;
+
+ uint64_t last_phase;
+
+ // Fixed point is just an integer with an implied scaling factor.
+ // In 40.24 the scaling factor is 2^24 = 16777216,
+ // so a value of 10*2^24 (in integer space) is equivalent to 10.0.
+ //
+ // The advantage is that addition and modulus [like x = (x + y) % 2^40]
+ // have no rounding errors and no drift, and just require a single integer add.
+ // (swh)
+
+ static const int64_t fractional_part_mask = 0xFFFFFF;
+ static const Sample binary_scaling_factor = 16777216.0f;
+
+ public:
+ Interpolation () : phi (FIXPOINT_ONE), target_phi (FIXPOINT_ONE), last_phase (0) {}
+
+ void set_speed (double new_speed) {
+ target_phi = (uint64_t) (FIXPOINT_ONE * fabs(new_speed));
+ phi = target_phi;
+ }
+
+ uint64_t get_phi () const { return phi; }
+ uint64_t get_target_phi () const { return target_phi; }
+ uint64_t get_last_phase () const { return last_phase; }
+
+ virtual nframes_t interpolate (nframes_t nframes, Sample* input, Sample* output) = 0;
+};
+
+class LinearInterpolation : public Interpolation {
+ public:
+ nframes_t interpolate (nframes_t nframes, Sample* input, Sample* output);
+};
diff --git a/libs/ardour/interpolation.cc b/libs/ardour/interpolation.cc
new file mode 100644
index 0000000000..7aece6453c
--- /dev/null
+++ b/libs/ardour/interpolation.cc
@@ -0,0 +1,43 @@
+#include <stdint.h>
+#include "ardour/interpolation.h"
+
+nframes_t
+LinearInterpolation::interpolate (nframes_t nframes, Sample *input, Sample *output)
+{
+ // the idea behind phase is that when the speed is not 1.0, we have to
+ // interpolate between samples and then we have to store where we thought we were.
+ // rather than being at sample N or N+1, we were at N+0.8792922
+ // so the "phase" element, if you want to think about this way,
+ // varies from 0 to 1, representing the "offset" between samples
+ uint64_t phase = last_phase;
+
+ // acceleration
+ int64_t phi_delta;
+
+ // phi = fixed point speed
+ if (phi != target_phi) {
+ phi_delta = ((int64_t)(target_phi - phi)) / nframes;
+ } else {
+ phi_delta = 0;
+ }
+
+ // index in the input buffers
+ nframes_t i = 0;
+
+ for (nframes_t outsample = 0; outsample < nframes; ++outsample) {
+ i = phase >> 24;
+ Sample fractional_phase_part = (phase & fractional_part_mask) / binary_scaling_factor;
+
+ // Linearly interpolate into the output buffer
+ // using fixed point math
+ output[outsample] =
+ input[i] * (1.0f - fractional_phase_part) +
+ input[i+1] * fractional_phase_part;
+ phase += phi + phi_delta;
+ }
+
+ last_phase = (phase & fractional_part_mask);
+
+ // playback distance
+ return i;
+}
diff --git a/libs/ardour/tests/interpolation-test.cc b/libs/ardour/tests/interpolation-test.cc
new file mode 100644
index 0000000000..8b4f1f840b
--- /dev/null
+++ b/libs/ardour/tests/interpolation-test.cc
@@ -0,0 +1,46 @@
+#include <sigc++/sigc++.h>
+#include "interpolation-test.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION( InterpolationTest );
+
+void
+InterpolationTest::linearInterpolationTest ()
+{
+ std::cout << "\nLinear Interpolation Test\n";
+ std::cout << "\nSpeed: 1.0";
+ linear.set_speed (1.0);
+ nframes_t result = linear.interpolate (NUM_SAMPLES, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 1, result);
+
+ std::cout << "\nSpeed: 0.5";
+ linear.set_speed (0.5);
+ result = linear.interpolate (NUM_SAMPLES, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 2 - 1, result);
+
+ std::cout << "\nSpeed: 0.2";
+ linear.set_speed (0.2);
+ result = linear.interpolate (NUM_SAMPLES, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 5 - 2, result);
+
+ std::cout << "\nSpeed: 0.02";
+ linear.set_speed (0.02);
+ result = linear.interpolate (NUM_SAMPLES, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 50 - 2, result);
+
+ std::cout << "\nSpeed: 2.0";
+ linear.set_speed (2.0);
+ result = linear.interpolate (NUM_SAMPLES / 2, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 2, result);
+
+/*
+ for (int i=0; i < NUM_SAMPLES / 5; ++i) {
+ std::cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << std::endl;
+ }
+*/
+ std::cout << "\nSpeed: 10.0";
+ linear.set_speed (10.0);
+ result = linear.interpolate (NUM_SAMPLES / 10, input, output);
+ CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 10, result);
+}
+
+
diff --git a/libs/ardour/tests/interpolation-test.h b/libs/ardour/tests/interpolation-test.h
new file mode 100644
index 0000000000..762ca2bcef
--- /dev/null
+++ b/libs/ardour/tests/interpolation-test.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright(C) 2000-2008 Paul Davis
+ * Author: Hans Baier
+ *
+ * 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
+ */
+
+#include <cassert>
+#include <stdint.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "ardour/interpolation.h"
+
+
+class InterpolationTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(InterpolationTest);
+ CPPUNIT_TEST(linearInterpolationTest);
+ CPPUNIT_TEST_SUITE_END();
+
+ #define NUM_SAMPLES 100000000
+
+ Sample input[NUM_SAMPLES];
+ Sample output[NUM_SAMPLES];
+
+ LinearInterpolation linear;
+
+ public:
+
+ void setUp() {
+ for (int i = 0; i < NUM_SAMPLES; ++i) {
+ if (i % 100 == 0) {
+ input[i] = 1.0f;
+ } else {
+ input[i] = 0.0f;
+ }
+ output[i] = 0.0f;
+ }
+ }
+
+ void tearDown() {
+ }
+
+ void linearInterpolationTest();
+
+};
diff --git a/libs/ardour/tests/testrunner.cpp b/libs/ardour/tests/testrunner.cpp
new file mode 100644
index 0000000000..468af59ae4
--- /dev/null
+++ b/libs/ardour/tests/testrunner.cpp
@@ -0,0 +1,27 @@
+#include <cppunit/CompilerOutputter.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/TestResult.h>
+#include <cppunit/TestResultCollector.h>
+#include <cppunit/TestRunner.h>
+#include <cppunit/BriefTestProgressListener.h>
+
+int
+main()
+{
+ CppUnit::TestResult testresult;
+
+ CppUnit::TestResultCollector collectedresults;
+ testresult.addListener (&collectedresults);
+
+ CppUnit::BriefTestProgressListener progress;
+ testresult.addListener (&progress);
+
+ CppUnit::TestRunner testrunner;
+ testrunner.addTest (CppUnit::TestFactoryRegistry::getRegistry ().makeTest ());
+ testrunner.run (testresult);
+
+ CppUnit::CompilerOutputter compileroutputter (&collectedresults, std::cerr);
+ compileroutputter.write ();
+
+ return collectedresults.wasSuccessful () ? 0 : 1;
+}
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 98bc7106f3..ffdf75a277 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -48,6 +48,7 @@ def configure(conf):
autowaf.check_pkg(conf, 'slv2', uselib_store='SLV2', atleast_version='0.6.4', mandatory=False)
autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE', atleast_version='1.0.18')
autowaf.check_pkg(conf, 'soundtouch-1.0', uselib_store='SOUNDTOUCH', mandatory=False)
+ autowaf.check_pkg(conf, 'cppunit', uselib_store='CPPUNIT', atleast_version='1.12.0', mandatory=False)
conf.env.append_value('CXXFLAGS', '-DUSE_RUBBERBAND')
conf.define('HAVE_RUBBERBAND', 1)
@@ -241,6 +242,19 @@ def build(bld):
obj.source += ' lv2_plugin.cc lv2_event_buffer.cc uri_map.cc '
obj.uselib += ' SLV2 '
obj.cxxflags += ['-DHAVE_SLV2']
+
+ if bld.env['HAVE_CPPUNIT']:
+ # Unit tests
+ obj = bld.new_task_gen('cxx', 'program')
+ obj.source = '''
+ interpolation.cc
+ tests/interpolation-test.cc
+ tests/testrunner.cpp
+ '''
+ obj.includes = ['.', './ardour']
+ obj.uselib = 'CPPUNIT SIGCPP'
+ obj.target = 'run-tests'
+ obj.install_path = ''
def shutdown():
autowaf.shutdown()