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
This commit is contained in:
parent
d039e2e80f
commit
f284d28d53
49
libs/ardour/ardour/interpolation.h
Normal file
49
libs/ardour/ardour/interpolation.h
Normal file
@ -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);
|
||||
};
|
43
libs/ardour/interpolation.cc
Normal file
43
libs/ardour/interpolation.cc
Normal file
@ -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;
|
||||
}
|
46
libs/ardour/tests/interpolation-test.cc
Normal file
46
libs/ardour/tests/interpolation-test.cc
Normal file
@ -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);
|
||||
}
|
||||
|
||||
|
58
libs/ardour/tests/interpolation-test.h
Normal file
58
libs/ardour/tests/interpolation-test.h
Normal file
@ -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();
|
||||
|
||||
};
|
27
libs/ardour/tests/testrunner.cpp
Normal file
27
libs/ardour/tests/testrunner.cpp
Normal file
@ -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;
|
||||
}
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user