MIDI vari-speed playback
authorRobin Gareus <robin@gareus.org>
Tue, 10 Mar 2015 21:05:21 +0000 (22:05 +0100)
committerRobin Gareus <robin@gareus.org>
Tue, 10 Mar 2015 21:15:44 +0000 (22:15 +0100)
libs/ardour/ardour/interpolation.h
libs/ardour/ardour/midi_diskstream.h
libs/ardour/interpolation.cc
libs/ardour/midi_diskstream.cc

index 64b0431e0f73d53c309f7d05d672372d713f46c5..3c661d859201586682ddd3e6480ad35c6ca8d053 100644 (file)
@@ -68,6 +68,13 @@ public:
        framecnt_t interpolate (int channel, framecnt_t nframes, Sample* input, Sample* output);
 };
 
+class BufferSet;
+
+class LIBARDOUR_API CubicMidiInterpolation : public Interpolation {
+public:
+       framecnt_t distance (framecnt_t nframes, bool roll = true);
+};
+
 } // namespace ARDOUR
 
 #endif
index 7e72ef9a7ed55f0d4d0749acc8cde940e16d5106..742b327b363c5eb234575d882b3547f2ae6da183 100644 (file)
@@ -38,6 +38,7 @@
 #include "ardour/diskstream.h"
 #include "ardour/midi_buffer.h"
 #include "ardour/utils.h"
+#include "ardour/interpolation.h"
 
 struct tm;
 
@@ -186,6 +187,8 @@ class LIBARDOUR_API MidiDiskstream : public Diskstream
        */
        MidiBuffer                   _gui_feed_buffer;
        mutable Glib::Threads::Mutex _gui_feed_buffer_mutex;
+
+       CubicMidiInterpolation interpolation;
 };
 
 }; /* namespace ARDOUR */
index bccaa45553484bb0eceb9e82eeb6b578a3b174a4..79b43bc58eefb2dd53252b7850103c81f56c8525 100644 (file)
@@ -21,6 +21,7 @@
 #include <cstdio>
 
 #include "ardour/interpolation.h"
+#include "ardour/midi_buffer.h"
 
 using namespace ARDOUR;
 
@@ -150,3 +151,38 @@ CubicInterpolation::interpolate (int channel, framecnt_t nframes, Sample *input,
 
     return i;
 }
+
+framecnt_t
+CubicMidiInterpolation::distance (framecnt_t nframes, bool roll)
+{
+       assert(phase.size() == 1);
+
+       framecnt_t i = 0;
+
+       double acceleration;
+       double distance = 0.0;
+
+       if (nframes < 3) {
+               return nframes;
+       }
+
+       if (_speed != _target_speed) {
+               acceleration = _target_speed - _speed;
+       } else {
+               acceleration = 0.0;
+       }
+
+       distance = phase[0];
+
+       for (framecnt_t outsample = 0; outsample < nframes; ++outsample) {
+               distance += _speed + acceleration;
+       }
+
+       if (roll) {
+               phase[0] = distance - floor(distance);
+       }
+
+       i = floor(distance);
+
+       return i;
+}
index ae0828a3a495703899c5dd076ae1a00c23aafbd6..f19c88d3b3948e4c053a26c3dfc886a6bb361f90 100644 (file)
@@ -135,6 +135,7 @@ MidiDiskstream::init ()
        _capture_buf = new MidiRingBuffer<framepos_t>(size);
 
        _n_channels = ChanCount(DataType::MIDI, 1);
+       interpolation.add_channel_to (0,0);
 }
 
 MidiDiskstream::~MidiDiskstream ()
@@ -522,44 +523,35 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
 
                playback_distance = nframes;
 
-       } else {
-
-               /* XXX: should be doing varispeed stuff here, similar to the code in AudioDiskstream::process */
+       } else if (_actual_speed != 1.0f && _target_speed > 0) {
 
-               playback_distance = nframes;
+               interpolation.set_speed (_target_speed);
 
-#ifndef NO_SIMPLE_MIDI_VARISPEED
-               if (_target_speed > 0) {
-                       playback_distance = nframes * _target_speed;
-               }
-#endif
+               playback_distance = interpolation.distance  (nframes);
 
+       } else {
+               playback_distance = nframes;
        }
 
        if (need_disk_signal) {
                /* copy the diskstream data to all output buffers */
                
                MidiBuffer& mbuf (bufs.get_midi (0));
-#ifndef NO_SIMPLE_MIDI_VARISPEED
-               get_playback (mbuf, nframes * _target_speed);
-#else
-               get_playback (mbuf, nframes);
-#endif
+               get_playback (mbuf, playback_distance);
 
                /* leave the audio count alone */
                ChanCount cnt (DataType::MIDI, 1);
                cnt.set (DataType::AUDIO, bufs.count().n_audio());
                bufs.set_count (cnt);
 
-#ifndef NO_SIMPLE_MIDI_VARISPEED
-               if (_target_speed > 0 && playback_distance != nframes) {
+               /* vari-speed */
+               if (_target_speed > 0 && _actual_speed != 1.0f) {
                        MidiBuffer& mbuf (bufs.get_midi (0));
                        for (MidiBuffer::iterator i = mbuf.begin(); i != mbuf.end(); ++i) {
                                MidiBuffer::TimeType *tme = i.timeptr();
-                               *tme = (*tme) / _target_speed;
+                               *tme = (*tme) * nframes / playback_distance;
                        }
                }
-#endif
        }
 
        return 0;
@@ -568,14 +560,13 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
 frameoffset_t
 MidiDiskstream::calculate_playback_distance (pframes_t nframes)
 {
-#ifndef NO_SIMPLE_MIDI_VARISPEED
-       frameoffset_t playback_distance = nframes * _target_speed;
-#else
        frameoffset_t playback_distance = nframes;
 
-       /* XXX: should be doing varispeed stuff once it's implemented in ::process() above */
+       if (!record_enabled() && _actual_speed != 1.0f && _actual_speed > 0.f) {
+               interpolation.set_speed (_target_speed);
+               playback_distance = interpolation.distance (nframes, false);
+       }
 
-#endif
        if (_actual_speed < 0.0) {
                return -playback_distance;
        } else {