diff options
author | Hans Baier <hansfbaier@googlemail.com> | 2009-06-23 14:53:37 +0000 |
---|---|---|
committer | Hans Baier <hansfbaier@googlemail.com> | 2009-06-23 14:53:37 +0000 |
commit | 26410407010e64dd6c00ffa17fc6bcdb7cf5168e (patch) | |
tree | 462be452724a4a4d251f8773178c108492f5ecb5 /libs/ardour/audio_diskstream.cc | |
parent | 417309d6d40e486e7a4757715b67780617a9c248 (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
Diffstat (limited to 'libs/ardour/audio_diskstream.cc')
-rw-r--r-- | libs/ardour/audio_diskstream.cc | 84 |
1 files changed, 73 insertions, 11 deletions
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 |