summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Baier <hansfbaier@googlemail.com>2009-06-23 14:53:37 +0000
committerHans Baier <hansfbaier@googlemail.com>2009-06-23 14:53:37 +0000
commit26410407010e64dd6c00ffa17fc6bcdb7cf5168e (patch)
tree462be452724a4a4d251f8773178c108492f5ecb5
parent417309d6d40e486e7a4757715b67780617a9c248 (diff)
interpolation.h / audio_diskstream.cc: make varispeed sound well again, by replacing the code by the original implementation for later comparison and step-by-step refactoring
git-svn-id: svn://localhost/ardour2/branches/3.0@5260 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--libs/ardour/ardour/interpolation.h28
-rw-r--r--libs/ardour/audio_diskstream.cc84
2 files changed, 90 insertions, 22 deletions
diff --git a/libs/ardour/ardour/interpolation.h b/libs/ardour/ardour/interpolation.h
index a2945ffc2b..e398171d2b 100644
--- a/libs/ardour/ardour/interpolation.h
+++ b/libs/ardour/ardour/interpolation.h
@@ -40,25 +40,31 @@ class FixedPointLinearInterpolation : public Interpolation {
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)
-
- static const int64_t fractional_part_mask = 0xFFFFFF;
- static const Sample binary_scaling_factor = 16777216.0f;
+ // 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:
- FixedPointLinearInterpolation () : phi (FIXPOINT_ONE), target_phi (FIXPOINT_ONE) {}
+
+ 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;
}
+ uint64_t get_phi() { return phi; }
+ uint64_t get_target_phi() { return target_phi; }
+ uint64_t get_last_phase() { assert(last_phase.size()); return last_phase[0]; }
+ void set_last_phase(uint64_t phase) { assert(last_phase.size()); last_phase[0] = phase; }
+
void add_channel_to (int input_buffer_size, int output_buffer_size);
void remove_channel_from ();
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 47d7b5c6e6..42467d540c 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -816,18 +816,80 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can
void
AudioDiskstream::process_varispeed_playback(nframes_t nframes, boost::shared_ptr<ChannelList> c)
{
- ChannelList::iterator chan;
-
- interpolation.set_target_speed (_target_speed);
- interpolation.set_speed (_speed);
+ ChannelList::iterator chan;
- int channel = 0;
- for (chan = c->begin(); chan != c->end(); ++chan, ++channel) {
- ChannelInfo* chaninfo (*chan);
-
- playback_distance = interpolation.interpolate (
- channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
- }
+ /*
+ interpolation.set_speed (_target_speed);
+
+ int channel = 0;
+ for (chan = c->begin(); chan != c->end(); ++chan, ++channel) {
+ ChannelInfo* chaninfo (*chan);
+
+ playback_distance = interpolation.interpolate (
+ channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
+ }
+ */
+
+ // 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 = interpolation.get_last_phase();
+
+ interpolation.set_speed (_target_speed);
+
+ // acceleration
+ uint64_t phi = interpolation.get_phi();
+ uint64_t target_phi = interpolation.get_target_phi();
+ 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;
+ }
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+
+ Sample fractional_phase_part;
+ ChannelInfo* chaninfo (*chan);
+
+ i = 0;
+ phase = interpolation.get_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;
+ interpolation.set_last_phase (phase & fractional_part_mask);
}
bool