diff options
-rw-r--r-- | libs/ardour/ardour/audio_diskstream.h | 45 | ||||
-rw-r--r-- | libs/ardour/ardour/diskstream.h | 9 | ||||
-rw-r--r-- | libs/ardour/ardour/interpolation.h | 53 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 6 | ||||
-rw-r--r-- | libs/ardour/audio_diskstream.cc | 61 | ||||
-rw-r--r-- | libs/ardour/diskstream.cc | 8 | ||||
-rw-r--r-- | libs/ardour/interpolation.cc | 46 | ||||
-rw-r--r-- | libs/ardour/session.cc | 8 | ||||
-rw-r--r-- | libs/ardour/session_process.cc | 16 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 3 | ||||
-rw-r--r-- | libs/ardour/session_transport.cc | 10 | ||||
-rw-r--r-- | libs/ardour/tests/interpolation-test.cc | 67 | ||||
-rw-r--r-- | libs/ardour/tests/interpolation-test.h | 10 | ||||
-rw-r--r-- | libs/ardour/wscript | 15 |
14 files changed, 130 insertions, 227 deletions
diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 187c97b3e6..401ed9ec19 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -39,6 +39,7 @@ #include "ardour/diskstream.h" #include "ardour/audioplaylist.h" #include "ardour/port.h" +#include "ardour/interpolation.h" struct tm; @@ -144,50 +145,10 @@ class AudioDiskstream : public Diskstream *last-- = tmp; } } + + LinearInterpolation interpolation; XMLNode* deprecated_io_node; - - /** - * Calculate the playback distance during varispeed playback. - * Important for Session::process to know exactly, how many frames - * were passed by. - */ - static nframes_t calculate_varispeed_playback_distance( - nframes_t nframes, - uint64_t& the_last_phase, - uint64_t& the_phi, - uint64_t& the_target_phi) - { - // calculate playback distance in the same way - // as in AudioDiskstream::process_varispeed_playback - uint64_t phase = the_last_phase; - int64_t phi_delta; - nframes_t i = 0; - - const int64_t fractional_part_mask = 0xFFFFFF; - const Sample binary_scaling_factor = 16777216.0f; - - if (the_phi != the_target_phi) { - phi_delta = ((int64_t)(the_target_phi - the_phi)) / nframes; - } else { - phi_delta = 0; - } - - Sample fractional_phase_part; - - i = 0; - phase = the_last_phase; - - for (nframes_t outsample = 0; outsample < nframes; ++outsample) { - i = phase >> 24; - fractional_phase_part = (phase & fractional_part_mask) / binary_scaling_factor; - phase += the_phi + phi_delta; - } - - the_last_phase = (phase & fractional_part_mask); - the_phi = the_target_phi; - return i; // + 1; - } protected: friend class Session; diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index f6471e300a..ea5e748ebd 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -285,13 +285,8 @@ class Diskstream : public SessionObject, public boost::noncopyable nframes_t wrap_buffer_size; nframes_t speed_buffer_size; - uint64_t last_phase; - - /// diskstream speed in 40.24 fixed point math - uint64_t phi; - - /// target diskstream speed in 40.24 fixed point math - uint64_t target_phi; + double _speed; + double _target_speed; nframes_t file_frame; nframes_t playback_sample; diff --git a/libs/ardour/ardour/interpolation.h b/libs/ardour/ardour/interpolation.h index 09ae242490..06c7ddebe0 100644 --- a/libs/ardour/ardour/interpolation.h +++ b/libs/ardour/ardour/interpolation.h @@ -1,49 +1,32 @@ #include <math.h> -//#include "ardour/types.h" +#include "ardour/types.h" -typedef float Sample; -#define nframes_t uint32_t +#ifndef __interpolation_h__ +#define __interpolation_h__ -// 40.24 fixpoint math -#define FIXPOINT_ONE 0x1000000 +namespace ARDOUR { class Interpolation { - protected: - /// speed in fixed point math - uint64_t phi; +protected: + double _speed, _target_speed; + +public: + Interpolation () : _speed(0.0L) {} - /// target speed in fixed point math - uint64_t target_phi; - - uint64_t last_phase; + void set_speed (double new_speed) { _speed = new_speed; } + void set_target_speed (double new_speed) { _target_speed = new_speed; } - // 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; } + double target_speed() const { return _target_speed; } + double speed() const { return _speed; } virtual nframes_t interpolate (nframes_t nframes, Sample* input, Sample* output) = 0; }; class LinearInterpolation : public Interpolation { - public: +public: nframes_t interpolate (nframes_t nframes, Sample* input, Sample* output); }; + +} // namespace ARDOUR + +#endif
\ No newline at end of file diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index d476a958d8..614ab42eeb 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -55,6 +55,7 @@ #include "ardour/session_configuration.h" #include "ardour/location.h" #include "ardour/smpte.h" +#include "ardour/interpolation.h" class XMLTree; class XMLNode; @@ -1023,9 +1024,8 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable volatile double _transport_speed; double _last_transport_speed; // varispeed playback - uint64_t phi; // fixed point transport speed - uint64_t target_phi; // fixed point target transport speed - uint64_t phase; // fixed point phase + double _target_transport_speed; + LinearInterpolation interpolation; bool auto_play_legal; nframes_t _last_slave_transport_frame; nframes_t maximum_output_latency; diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 4e8e2bf12d..9a562acf9e 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -793,7 +793,7 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can playback_distance = nframes; } - phi = target_phi; + _speed = _target_speed; } @@ -819,63 +819,16 @@ void AudioDiskstream::process_varispeed_playback(nframes_t nframes, boost::shared_ptr<ChannelList> c) { ChannelList::iterator chan; - - // 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; - - // index in the input buffers - nframes_t i = 0; - - // Linearly interpolate into the speed buffer - // using 40.24 fixed point math - // - // 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) - - const int64_t fractional_part_mask = 0xFFFFFF; - const Sample binary_scaling_factor = 16777216.0f; - - // phi = fixed point speed - if (phi != target_phi) { - phi_delta = ((int64_t)(target_phi - phi)) / nframes; - } else { - phi_delta = 0; - } + interpolation.set_target_speed (_target_speed); + interpolation.set_speed (_speed); + for (chan = c->begin(); chan != c->end(); ++chan) { - - Sample fractional_phase_part; ChannelInfo* chaninfo (*chan); - i = 0; - phase = last_phase; - - for (nframes_t outsample = 0; outsample < nframes; ++outsample) { - i = phase >> 24; - fractional_phase_part = (phase & fractional_part_mask) / binary_scaling_factor; - chaninfo->speed_buffer[outsample] = - chaninfo->current_playback_buffer[i] * (1.0f - fractional_phase_part) + - chaninfo->current_playback_buffer[i+1] * fractional_phase_part; - phase += phi + phi_delta; - } - - chaninfo->current_playback_buffer = chaninfo->speed_buffer; - } - - playback_distance = i; // + 1; - last_phase = (phase & fractional_part_mask); + playback_distance = interpolation.interpolate ( + nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer); + } } bool diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 27a1c18ff4..e262fe240f 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -111,10 +111,8 @@ Diskstream::init (Flag f) loop_location = 0; wrap_buffer_size = 0; speed_buffer_size = 0; - last_phase = 0; - // speed = 1 in 40.24 fixed point math - phi = (uint64_t) (0x1000000); - target_phi = phi; + _speed = 1.0; + _target_speed = _speed; file_frame = 0; playback_sample = 0; playback_distance = 0; @@ -198,7 +196,7 @@ Diskstream::realtime_set_speed (double sp, bool global) } _actual_speed = new_speed; - target_phi = (uint64_t) (0x1000000 * fabs(_actual_speed)); + _target_speed = fabs(_actual_speed); } if (changed) { diff --git a/libs/ardour/interpolation.cc b/libs/ardour/interpolation.cc index 7aece6453c..066507283b 100644 --- a/libs/ardour/interpolation.cc +++ b/libs/ardour/interpolation.cc @@ -1,43 +1,41 @@ #include <stdint.h> #include "ardour/interpolation.h" +using namespace ARDOUR; + 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 + // 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 - // 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; + + double acceleration; + double distance = 0.0; + + if (_speed != _target_speed) { + acceleration = _target_speed - _speed; + } else { + acceleration = 0.0; + } for (nframes_t outsample = 0; outsample < nframes; ++outsample) { - i = phase >> 24; - Sample fractional_phase_part = (phase & fractional_part_mask) / binary_scaling_factor; + i = distance; + Sample fractional_phase_part = distance - i; - // 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; + if (input && output) { + // Linearly interpolate into the output buffer + output[outsample] = + input[i] * (1.0f - fractional_phase_part) + + input[i+1] * fractional_phase_part; + } + distance += _speed + acceleration; } - - last_phase = (phase & fractional_part_mask); + i = (distance + 0.5L); // playback distance return i; } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 25f3f6a368..9800b9a8c3 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -113,9 +113,7 @@ Session::Session (AudioEngine &eng, string mix_template) : _engine (eng), - phi (0), - target_phi (0), - phase (0), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), @@ -200,9 +198,7 @@ Session::Session (AudioEngine &eng, nframes_t initial_length) : _engine (eng), - phi (0), - target_phi (0), - phase (0), + _target_transport_speed (0.0), _requested_return_frame (-1), _scratch_buffers(new BufferSet()), _silent_buffers(new BufferSet()), diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 5bd1166fc6..d389f32696 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -322,14 +322,12 @@ Session::process_with_events (nframes_t nframes) return; } - /// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes - /// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process - /// is very likely broken too if (_transport_speed == 1.0) { frames_moved = (long) nframes; } else { - frames_moved = (long) AudioDiskstream:: - calculate_varispeed_playback_distance(nframes, phase, phi, target_phi); + interpolation.set_target_speed (_target_transport_speed); + interpolation.set_speed (_transport_speed); + frames_moved = (long) interpolation.interpolate (nframes, 0, 0); } end_frame = _transport_frame + (nframes_t)frames_moved; @@ -845,14 +843,12 @@ Session::process_without_events (nframes_t nframes) prepare_diskstreams (); - /// TODO: Figure out what happens to phi and phase, if transport speed momentarily becomes - /// 1.0 eg. during the adjustments of a slave. If that is a bug, then AudioDiskstream::process - /// is very likely broken too if (_transport_speed == 1.0) { frames_moved = (long) nframes; } else { - frames_moved = (long) AudioDiskstream:: - calculate_varispeed_playback_distance(nframes, phase, phi, target_phi); + interpolation.set_target_speed (_target_transport_speed); + interpolation.set_speed (_transport_speed); + frames_moved = (long) interpolation.interpolate (nframes, 0, 0); } if (process_routes (nframes)) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 0391e1f962..fff8803b5a 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -156,8 +156,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) insert_cnt = 0; _transport_speed = 0; _last_transport_speed = 0; - phi = (uint64_t) (0x1000000); - target_phi = phi; + _target_transport_speed = 0; auto_play_legal = false; transport_sub_state = 0; _transport_frame = 0; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 31dc52a11b..34b1e825d9 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -176,9 +176,7 @@ Session::realtime_stop (bool abort) reset_slave_state (); _transport_speed = 0; - phi = 0; - target_phi = 0; - phase = 0; + _target_transport_speed = 0; if (config.get_use_video_sync()) { waiting_for_sync_offset = true; @@ -804,7 +802,7 @@ Session::set_transport_speed (double speed, bool abort) return; } - target_phi = (uint64_t) (0x1000000 * fabs(speed)); + _target_transport_speed = fabs(speed); /* 8.0 max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability and user needs. We really need CD-style "skip" playback for ffwd and rewind. @@ -987,9 +985,7 @@ Session::start_transport () transport_sub_state |= PendingDeclickIn; _transport_speed = 1.0; - target_phi = 0x1000000; // speed = 1 - phi = target_phi; - phase = 0; + _target_transport_speed = 1.0; boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader(); for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { diff --git a/libs/ardour/tests/interpolation-test.cc b/libs/ardour/tests/interpolation-test.cc index 8b4f1f840b..5a559cc830 100644 --- a/libs/ardour/tests/interpolation-test.cc +++ b/libs/ardour/tests/interpolation-test.cc @@ -3,44 +3,71 @@ CPPUNIT_TEST_SUITE_REGISTRATION( InterpolationTest ); +using namespace std; +using namespace ARDOUR; + void InterpolationTest::linearInterpolationTest () { - std::cout << "\nLinear Interpolation Test\n"; - std::cout << "\nSpeed: 1.0"; + cout << "\nLinear Interpolation Test\n"; + cout << "\nSpeed: 1.0"; linear.set_speed (1.0); + linear.set_target_speed (linear.speed()); nframes_t result = linear.interpolate (NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 1, result); - - std::cout << "\nSpeed: 0.5"; + 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.set_speed (0.5); + linear.set_target_speed (linear.speed()); result = linear.interpolate (NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 2 - 1, result); - - std::cout << "\nSpeed: 0.2"; + 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.set_speed (0.2); + linear.set_target_speed (linear.speed()); result = linear.interpolate (NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 5 - 2, result); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); - std::cout << "\nSpeed: 0.02"; + cout << "\nSpeed: 0.02"; linear.set_speed (0.02); + linear.set_target_speed (linear.speed()); result = linear.interpolate (NUM_SAMPLES, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES / 50 - 2, result); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); - std::cout << "\nSpeed: 2.0"; + cout << "\nSpeed: 0.002"; + linear.set_speed (0.002); + linear.set_target_speed (linear.speed()); + result = linear.interpolate (NUM_SAMPLES, input, output); + CPPUNIT_ASSERT_EQUAL ((uint32_t)(NUM_SAMPLES * linear.speed()), result); + + cout << "\nSpeed: 2.0"; linear.set_speed (2.0); + linear.set_target_speed (linear.speed()); result = linear.interpolate (NUM_SAMPLES / 2, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 2, result); + 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]); + } -/* - 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"; + cout << "\nSpeed: 10.0"; linear.set_speed (10.0); + linear.set_target_speed (linear.speed()); result = linear.interpolate (NUM_SAMPLES / 10, input, output); - CPPUNIT_ASSERT_EQUAL ((uint32_t)NUM_SAMPLES - 10, result); + 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; + } + */ } diff --git a/libs/ardour/tests/interpolation-test.h b/libs/ardour/tests/interpolation-test.h index 762ca2bcef..3231f32464 100644 --- a/libs/ardour/tests/interpolation-test.h +++ b/libs/ardour/tests/interpolation-test.h @@ -23,7 +23,6 @@ #include "ardour/interpolation.h" - class InterpolationTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(InterpolationTest); @@ -31,17 +30,18 @@ class InterpolationTest : public CppUnit::TestFixture CPPUNIT_TEST_SUITE_END(); #define NUM_SAMPLES 100000000 + #define INTERVAL 100 - Sample input[NUM_SAMPLES]; - Sample output[NUM_SAMPLES]; + ARDOUR::Sample input[NUM_SAMPLES]; + ARDOUR::Sample output[NUM_SAMPLES]; - LinearInterpolation linear; + ARDOUR::LinearInterpolation linear; public: void setUp() { for (int i = 0; i < NUM_SAMPLES; ++i) { - if (i % 100 == 0) { + if (i % INTERVAL == 0) { input[i] = 1.0f; } else { input[i] = 0.0f; diff --git a/libs/ardour/wscript b/libs/ardour/wscript index ffdf75a277..89a7e06c04 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -136,7 +136,8 @@ def build(bld): gdither.cc globals.cc import.cc - io.cc + interpolation.cc + io.cc io_processor.cc jack_slave.cc ladspa_plugin.cc @@ -245,16 +246,16 @@ def build(bld): if bld.env['HAVE_CPPUNIT']: # Unit tests - obj = bld.new_task_gen('cxx', 'program') - obj.source = ''' + testobj = bld.new_task_gen('cxx', 'program') + testobj.source = ''' interpolation.cc tests/interpolation-test.cc tests/testrunner.cpp ''' - obj.includes = ['.', './ardour'] - obj.uselib = 'CPPUNIT SIGCPP' - obj.target = 'run-tests' - obj.install_path = '' + testobj.includes = obj.includes + ['../pbd/'] + testobj.uselib = 'CPPUNIT SIGCPP JACK GLIBMM' + testobj.target = 'run-tests' + testobj.install_path = '' def shutdown(): autowaf.shutdown() |