diff options
-rw-r--r-- | libs/ardour/ardour/audio_diskstream.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/interpolation.h | 97 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 2 | ||||
-rw-r--r-- | libs/ardour/interpolation.cc | 141 | ||||
-rw-r--r-- | libs/ardour/session.cc | 4 | ||||
-rw-r--r-- | libs/ardour/session_process.cc | 6 | ||||
-rw-r--r-- | libs/ardour/tests/interpolation-test.cc | 199 | ||||
-rw-r--r-- | libs/ardour/tests/interpolation-test.h | 8 |
8 files changed, 384 insertions, 75 deletions
diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 55cb747a4c..ecced007cf 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -146,7 +146,7 @@ class AudioDiskstream : public Diskstream } } - LibSamplerateInterpolation interpolation; + FixedPointLinearInterpolation interpolation; XMLNode* deprecated_io_node; diff --git a/libs/ardour/ardour/interpolation.h b/libs/ardour/ardour/interpolation.h index cec951c841..deb0204794 100644 --- a/libs/ardour/ardour/interpolation.h +++ b/libs/ardour/ardour/interpolation.h @@ -8,18 +8,90 @@ namespace ARDOUR { -class LibSamplerateInterpolation { -protected: - double _speed; - - std::vector<SRC_STATE*> state; - std::vector<SRC_DATA*> data; - - int error; - - void reset_state (); +class Interpolation { + protected: + double _speed, _target_speed; + + public: + Interpolation () { _speed = 1.0; } + + void set_speed (double new_speed) { _speed = new_speed; } + void set_target_speed (double new_speed) { _target_speed = new_speed; } + + double target_speed() const { return _target_speed; } + double speed() const { return _speed; } + + void add_channel_to (int input_buffer_size, int output_buffer_size) {} + void remove_channel_from () {} + + nframes_t interpolate (int channel, nframes_t nframes, Sample* input, Sample* output) {} + void reset () {} +}; + +// 40.24 fixpoint math +#define FIXPOINT_ONE 0x1000000 + +class FixedPointLinearInterpolation : public Interpolation { + protected: + /// speed in fixed point math + uint64_t phi; + + /// target speed in fixed point math + uint64_t target_phi; + + std::vector<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) -public: + static const int64_t fractional_part_mask = 0xFFFFFF; + static const Sample binary_scaling_factor = 16777216.0f; + + public: + FixedPointLinearInterpolation () : phi (FIXPOINT_ONE), target_phi (FIXPOINT_ONE) {} + + void set_speed (double new_speed) { + target_phi = (uint64_t) (FIXPOINT_ONE * fabs(new_speed)); + phi = target_phi; + } + + void add_channel_to (int input_buffer_size, int output_buffer_size); + void remove_channel_from (); + + nframes_t interpolate (int channel, nframes_t nframes, Sample* input, Sample* output); + void reset (); +}; + + class LinearInterpolation : public Interpolation { + protected: + // the idea 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 + std::vector<double> phase; + + public: + void add_channel_to (int input_buffer_size, int output_buffer_size); + void remove_channel_from (); + + nframes_t interpolate (int channel, nframes_t nframes, Sample* input, Sample* output); + void reset (); + }; + +class LibSamplerateInterpolation : public Interpolation { + protected: + std::vector<SRC_STATE*> state; + std::vector<SRC_DATA*> data; + + int error; + + void reset_state (); + + public: LibSamplerateInterpolation (); ~LibSamplerateInterpolation (); @@ -31,8 +103,9 @@ public: void remove_channel_from (); nframes_t interpolate (int channel, nframes_t nframes, Sample* input, Sample* output); + void reset() { reset_state (); } }; } // namespace ARDOUR -#endif
\ No newline at end of file +#endif diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 8b291dd75b..1dd40affdb 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1011,7 +1011,7 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable volatile double _transport_speed; double _last_transport_speed; double _target_transport_speed; - LibSamplerateInterpolation interpolation; + FixedPointLinearInterpolation interpolation; bool auto_play_legal; nframes_t _last_slave_transport_frame; diff --git a/libs/ardour/interpolation.cc b/libs/ardour/interpolation.cc index 3d2f754da9..9b2ff03e23 100644 --- a/libs/ardour/interpolation.cc +++ b/libs/ardour/interpolation.cc @@ -4,8 +4,141 @@ using namespace ARDOUR; -LibSamplerateInterpolation::LibSamplerateInterpolation() : _speed (1.0L), state (0) +nframes_t +FixedPointLinearInterpolation::interpolate (int channel, 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[channel]; + + // 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; + + if (input && output) { + // 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[channel] = (phase & fractional_part_mask); + + // playback distance + return i; +} + +void +FixedPointLinearInterpolation::add_channel_to (int input_buffer_size, int output_buffer_size) +{ + last_phase.push_back (0); +} + +void +FixedPointLinearInterpolation::remove_channel_from () +{ + last_phase.pop_back (); +} + +void +FixedPointLinearInterpolation::reset() +{ + for(int i = 0; i <= last_phase.size(); i++) { + last_phase[i] = 0; + } +} + + +nframes_t +LinearInterpolation::interpolate (int channel, nframes_t nframes, Sample *input, Sample *output) +{ + // index in the input buffers + nframes_t i = 0; + + double acceleration; + double distance = 0.0; + + if (_speed != _target_speed) { + acceleration = _target_speed - _speed; + } else { + acceleration = 0.0; + } + + printf("phase before: %lf\n", phase[channel]); + distance = phase[channel]; + for (nframes_t outsample = 0; outsample < nframes; ++outsample) { + i = distance; + Sample fractional_phase_part = distance - i; + if (fractional_phase_part >= 1.0) { + fractional_phase_part -= 1.0; + i++; + } + //printf("I: %u, distance: %lf, fractional_phase_part: %lf\n", i, distance, fractional_phase_part); + + if (input && output) { + // Linearly interpolate into the output buffer + output[outsample] = + input[i] * (1.0f - fractional_phase_part) + + input[i+1] * fractional_phase_part; + } + //printf("distance before: %lf\n", distance); + distance += _speed + acceleration; + //printf("distance after: %lf, _speed: %lf\n", distance, _speed); + } + + printf("before assignment: i: %d, distance: %lf\n", i, distance); + i = floor(distance); + printf("after assignment: i: %d, distance: %16lf\n", i, distance); + phase[channel] = distance - floor(distance); + printf("speed: %16lf, i after: %d, distance after: %16lf, phase after: %16lf\n", _speed, i, distance, phase[channel]); + + return i; +} + +void +LinearInterpolation::add_channel_to (int input_buffer_size, int output_buffer_size) +{ + phase.push_back (0.0); +} + +void +LinearInterpolation::remove_channel_from () +{ + phase.pop_back (); +} + + +void +LinearInterpolation::reset() +{ + for(int i = 0; i <= phase.size(); i++) { + phase[i] = 0.0; + } +} + +LibSamplerateInterpolation::LibSamplerateInterpolation() : state (0) { + _speed = 1.0; } LibSamplerateInterpolation::~LibSamplerateInterpolation() @@ -77,12 +210,12 @@ LibSamplerateInterpolation::interpolate (int channel, nframes_t nframes, Sample return 0; } - data[channel]->data_in = input; - data[channel]->data_out = output; + data[channel]->data_in = input; + data[channel]->data_out = output; data[channel]->input_frames = nframes * _speed; data[channel]->output_frames = nframes; - data[channel]->src_ratio = 1.0/_speed; + data[channel]->src_ratio = 1.0/_speed; if ((error = src_process (state[channel], data[channel]))) { printf ("\nError : %s\n\n", src_strerror (error)); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 410933ed1a..cfab50552d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -147,6 +147,8 @@ Session::Session (AudioEngine &eng, { bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } @@ -231,6 +233,8 @@ Session::Session (AudioEngine &eng, { bool new_session; + interpolation.add_channel_to (0, 0); + if (!eng.connected()) { throw failed_constructor(); } diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index e7f2ad416f..8ab43df721 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -327,8 +327,7 @@ Session::process_with_events (nframes_t nframes) } else { interpolation.set_target_speed (_target_transport_speed); interpolation.set_speed (_transport_speed); - //FIXME frames_moved = (long) interpolation.interpolate (nframes, 0, 0); - frames_moved = _transport_speed * nframes; + frames_moved = (long) interpolation.interpolate (0, nframes, 0, 0); } end_frame = _transport_frame + (nframes_t)frames_moved; @@ -849,8 +848,7 @@ Session::process_without_events (nframes_t nframes) } else { interpolation.set_target_speed (_target_transport_speed); interpolation.set_speed (_transport_speed); - //FIXME frames_moved = (long) interpolation.interpolate (nframes, 0, 0); - frames_moved = _transport_speed * nframes; + frames_moved = (long) interpolation.interpolate (0, nframes, 0, 0); } if (process_routes (nframes)) { diff --git a/libs/ardour/tests/interpolation-test.cc b/libs/ardour/tests/interpolation-test.cc index 3ec7910e1b..87079b92a5 100644 --- a/libs/ardour/tests/interpolation-test.cc +++ b/libs/ardour/tests/interpolation-test.cc @@ -9,63 +9,160 @@ using namespace ARDOUR; void InterpolationTest::linearInterpolationTest () { - cout << "\nLinear Interpolation Test\n"; - cout << "\nSpeed: 1.0"; - interpolation.set_speed (1.0); - nframes_t result = interpolation.interpolate (0, NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); + nframes_t result = 0; + cout << "\nLinear Interpolation Test\n"; + + cout << "\nSpeed: 1/3"; + for (int i = 0; i < NUM_SAMPLES - 1024;) { + linear.set_speed (double(1.0)/double(3.0)); + linear.set_target_speed (double(1.0)/double(3.0)); + printf ("Interpolate: input: %d, output: %d, i: %d\n", input + i, output + i, i); + result = linear.interpolate (0, 1024, input + i, output + i); + printf ("Result: %d\n", result); + //CPPUNIT_ASSERT_EQUAL ((uint32_t)((NUM_SAMPLES - 100) * interpolation.speed()), result); + i += result; + } + + cout << "\nSpeed: 1.0"; + linear.reset(); + linear.set_speed (1.0); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); + for (int i = 0; i < NUM_SAMPLES; i += INTERVAL) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + + cout << "\nSpeed: 0.5"; + linear.reset(); + linear.set_speed (0.5); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); + for (int i = 0; i < NUM_SAMPLES; i += (INTERVAL / linear.speed() +0.5)) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + + cout << "\nSpeed: 0.2"; + linear.reset(); + linear.set_speed (0.2); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); + + cout << "\nSpeed: 0.02"; + linear.reset(); + linear.set_speed (0.02); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); + + cout << "\nSpeed: 0.002"; + linear.reset(); + linear.set_speed (0.002); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES, input, output); + linear.speed(); + printf("BOOM!: expexted: %d, result = %d\n", (nframes_t)(NUM_SAMPLES * linear.speed()), result); + CPPUNIT_ASSERT_EQUAL ((nframes_t)(NUM_SAMPLES * linear.speed()), result); + + cout << "\nSpeed: 2.0"; + linear.reset(); + linear.set_speed (2.0); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES / 2, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 2 * linear.speed()), result); + for (int i = 0; i < NUM_SAMPLES / 2; i += (INTERVAL / linear.speed() +0.5)) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + + cout << "\nSpeed: 10.0"; + linear.set_speed (10.0); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (0, NUM_SAMPLES / 10, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 10 * linear.speed()), result); + for (int i = 0; i < NUM_SAMPLES / 10; i += (INTERVAL / linear.speed() +0.5)) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + /* + for (int i=0; i < NUM_SAMPLES; ++i) { + cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << endl; + } + */ +} + +void +InterpolationTest::libSamplerateInterpolationTest () +{ + nframes_t result; + + cout << "\nLibSamplerate Interpolation Test\n"; /* + cout << "\nSpeed: 1.0"; + interpolation.set_speed (1.0); + for (int i = 0; i < NUM_SAMPLES;) { + interpolation.set_speed (1.0); + result = interpolation.interpolate (0, INTERVAL/10, input + i, output + i); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(INTERVAL/10 * interpolation.speed()), result); + i += result; + } + + for (int i = 0; i < NUM_SAMPLES; i += INTERVAL) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i+1]); + } */ - for (int i=0; i < NUM_SAMPLES; ++i) { - cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << endl; + + cout << "\nSpeed: 0.5"; + for (int i = 0; i < NUM_SAMPLES;) { + interpolation.set_speed (0.5); + printf ("Interpolate: input: %d, output: %d, i: %d\n", input + i, output + i, i); + result = interpolation.interpolate (0, NUM_SAMPLES - 100, input + i, output + i); + printf ("Result: %d\n", result); + //CPPUNIT_ASSERT_EQUAL ((uint32_t)((NUM_SAMPLES - 100) * interpolation.speed()), result); + //i += result; + break; + } + + for (int i=0; i < NUM_SAMPLES; ++i) { + cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << endl; } - for (int i = 0; i < NUM_SAMPLES; i += INTERVAL) { - CPPUNIT_ASSERT_EQUAL (1.0f, output[i+1]); - } - - cout << "\nSpeed: 0.5"; - interpolation.set_speed (0.5); - result = interpolation.interpolate (0, NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); - for (int i = 0; i < NUM_SAMPLES; i += (INTERVAL / interpolation.speed() +0.5)) { - CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); - } - - cout << "\nSpeed: 0.2"; - interpolation.set_speed (0.2); - result = interpolation.interpolate (0, NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); - cout << "\nSpeed: 0.02"; - interpolation.set_speed (0.02); - result = interpolation.interpolate (0, NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); - - cout << "\nSpeed: 0.002"; - interpolation.set_speed (0.002); - result = interpolation.interpolate (0, NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); - - cout << "\nSpeed: 2.0"; - interpolation.set_speed (2.0); - result = interpolation.interpolate (0, NUM_SAMPLES / 2, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 2 * interpolation.speed()), result); - for (int i = 0; i < NUM_SAMPLES / 2; i += (INTERVAL / interpolation.speed() +0.5)) { - CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); - } + + cout << "\nSpeed: 0.2"; + interpolation.set_speed (0.2); + result = interpolation.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); - cout << "\nSpeed: 10.0"; - interpolation.set_speed (10.0); - result = interpolation.interpolate (0, NUM_SAMPLES / 10, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 10 * interpolation.speed()), result); - for (int i = 0; i < NUM_SAMPLES / 10; i += (INTERVAL / interpolation.speed() +0.5)) { - CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); - } - /* - for (int i=0; i < NUM_SAMPLES; ++i) { - cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << endl; + cout << "\nSpeed: 0.02"; + interpolation.set_speed (0.02); + result = interpolation.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); + + cout << "\nSpeed: 0.002"; + interpolation.set_speed (0.002); + result = interpolation.interpolate (0, NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * interpolation.speed()), result); + + cout << "\nSpeed: 2.0"; + interpolation.set_speed (2.0); + result = interpolation.interpolate (0, NUM_SAMPLES / 2, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 2 * interpolation.speed()), result); + for (int i = 0; i < NUM_SAMPLES / 2; i += (INTERVAL / interpolation.speed() +0.5)) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + + cout << "\nSpeed: 10.0"; + interpolation.set_speed (10.0); + result = interpolation.interpolate (0, NUM_SAMPLES / 10, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES / 10 * interpolation.speed()), result); + for (int i = 0; i < NUM_SAMPLES / 10; i += (INTERVAL / interpolation.speed() +0.5)) { + CPPUNIT_ASSERT_EQUAL (1.0f, output[i]); + } + /* + for (int i=0; i < NUM_SAMPLES; ++i) { + cout << "input[" << i << "] = " << input[i] << " output[" << i << "] = " << output[i] << endl; } - */ + */ } diff --git a/libs/ardour/tests/interpolation-test.h b/libs/ardour/tests/interpolation-test.h index 0c1b0de6c2..34ec0daaae 100644 --- a/libs/ardour/tests/interpolation-test.h +++ b/libs/ardour/tests/interpolation-test.h @@ -27,15 +27,17 @@ class InterpolationTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(InterpolationTest); CPPUNIT_TEST(linearInterpolationTest); + //CPPUNIT_TEST(libSamplerateInterpolationTest); CPPUNIT_TEST_SUITE_END(); - #define NUM_SAMPLES 100000000 + #define NUM_SAMPLES 1000000 #define INTERVAL 100 ARDOUR::Sample input[NUM_SAMPLES]; ARDOUR::Sample output[NUM_SAMPLES]; - ARDOUR::Interpolation interpolation; + ARDOUR::LinearInterpolation linear; + ARDOUR::LibSamplerateInterpolation interpolation; public: @@ -48,6 +50,7 @@ class InterpolationTest : public CppUnit::TestFixture } output[i] = 0.0f; } + linear.add_channel_to (NUM_SAMPLES, NUM_SAMPLES); interpolation.add_channel_to (NUM_SAMPLES, NUM_SAMPLES); } @@ -55,5 +58,6 @@ class InterpolationTest : public CppUnit::TestFixture } void linearInterpolationTest(); + void libSamplerateInterpolationTest(); }; |