remove old slave files
[ardour.git] / libs / ardour / disk_reader.cc
index 67e83cb1b818862e7c6468a77542ef9391c41626..375dda70759f0f4345e193223d07eb67b0c04fb5 100644 (file)
@@ -17,8 +17,9 @@
 
 */
 
 
 */
 
+#include <boost/smart_ptr/scoped_array.hpp>
+
 #include "pbd/enumwriter.h"
 #include "pbd/enumwriter.h"
-#include "pbd/i18n.h"
 #include "pbd/memento_command.h"
 
 #include "ardour/audioengine.h"
 #include "pbd/memento_command.h"
 
 #include "ardour/audioengine.h"
 #include "ardour/disk_reader.h"
 #include "ardour/midi_ring_buffer.h"
 #include "ardour/midi_playlist.h"
 #include "ardour/disk_reader.h"
 #include "ardour/midi_ring_buffer.h"
 #include "ardour/midi_playlist.h"
+#include "ardour/midi_track.h"
 #include "ardour/pannable.h"
 #include "ardour/playlist.h"
 #include "ardour/playlist_factory.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
 
 #include "ardour/pannable.h"
 #include "ardour/playlist.h"
 #include "ardour/playlist_factory.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
 
+#include "pbd/i18n.h"
+
 using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
 
 using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
 
-ARDOUR::framecnt_t DiskReader::_chunk_frames = default_chunk_frames ();
+ARDOUR::samplecnt_t DiskReader::_chunk_samples = default_chunk_samples ();
 PBD::Signal0<void> DiskReader::Underrun;
 Sample* DiskReader::_mixdown_buffer = 0;
 gain_t* DiskReader::_gain_buffer = 0;
 PBD::Signal0<void> DiskReader::Underrun;
 Sample* DiskReader::_mixdown_buffer = 0;
 gain_t* DiskReader::_gain_buffer = 0;
-framecnt_t DiskReader::midi_readahead = 4096;
+samplecnt_t DiskReader::midi_readahead = 4096;
+bool DiskReader::_no_disk_output = false;
 
 DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
        : DiskIOProcessor (s, str, f)
 
 DiskReader::DiskReader (Session& s, string const & str, DiskIOProcessor::Flag f)
        : DiskIOProcessor (s, str, f)
-       , _roll_delay (0)
-       , overwrite_frame (0)
-        , overwrite_offset (0)
-        , _pending_overwrite (false)
-        , overwrite_queued (false)
-       , _gui_feed_buffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
+       , overwrite_sample (0)
+       , overwrite_offset (0)
+       , _pending_overwrite (false)
+       , overwrite_queued (false)
+       , _declick_gain (0)
 {
 {
+       file_sample[DataType::AUDIO] = 0;
+       file_sample[DataType::MIDI] = 0;
 }
 
 DiskReader::~DiskReader ()
 }
 
 DiskReader::~DiskReader ()
@@ -65,21 +71,30 @@ DiskReader::~DiskReader ()
                        _playlists[n]->release ();
                }
        }
                        _playlists[n]->release ();
                }
        }
+       delete _midi_buf;
+}
 
 
-       {
-               RCUWriter<ChannelList> writer (channels);
-               boost::shared_ptr<ChannelList> c = writer.get_copy();
-
-               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
-                       delete *chan;
-               }
+void
+DiskReader::ReaderChannelInfo::resize (samplecnt_t bufsize)
+{
+       delete rbuf;
+       /* touch memory to lock it */
+       rbuf = new RingBufferNPT<Sample> (bufsize);
+       memset (rbuf->buffer(), 0, sizeof (Sample) * rbuf->bufsize());
+}
 
 
-               c->clear();
+int
+DiskReader::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+       while (how_many--) {
+               c->push_back (new ReaderChannelInfo (_session.butler()->audio_diskstream_playback_buffer_size()));
+               DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new reader channel, write space = %2 read = %3\n",
+                                                           name(),
+                                                           c->back()->rbuf->write_space(),
+                                                           c->back()->rbuf->read_space()));
        }
 
        }
 
-       channels.flush ();
-
-       delete _midi_buf;
+       return 0;
 }
 
 void
 }
 
 void
@@ -103,8 +118,8 @@ DiskReader::free_working_buffers()
        _gain_buffer          = 0;
 }
 
        _gain_buffer          = 0;
 }
 
-framecnt_t
-DiskReader::default_chunk_frames()
+samplecnt_t
+DiskReader::default_chunk_samples()
 {
        return 65536;
 }
 {
        return 65536;
 }
@@ -112,7 +127,7 @@ DiskReader::default_chunk_frames()
 bool
 DiskReader::set_name (string const & str)
 {
 bool
 DiskReader::set_name (string const & str)
 {
-       string my_name = X_("reader:");
+       string my_name = X_("player:");
        my_name += str;
 
        if (_name != my_name) {
        my_name += str;
 
        if (_name != my_name) {
@@ -122,16 +137,10 @@ DiskReader::set_name (string const & str)
        return true;
 }
 
        return true;
 }
 
-void
-DiskReader::set_roll_delay (ARDOUR::framecnt_t nframes)
-{
-       _roll_delay = nframes;
-}
-
 XMLNode&
 XMLNode&
-DiskReader::state (bool full)
+DiskReader::state ()
 {
 {
-       XMLNode& node (DiskIOProcessor::state (full));
+       XMLNode& node (DiskIOProcessor::state ());
        node.set_property(X_("type"), X_("diskreader"));
        return node;
 }
        node.set_property(X_("type"), X_("diskreader"));
        return node;
 }
@@ -149,7 +158,6 @@ DiskReader::set_state (const XMLNode& node, int version)
 void
 DiskReader::realtime_handle_transport_stopped ()
 {
 void
 DiskReader::realtime_handle_transport_stopped ()
 {
-       realtime_speed_change ();
 }
 
 void
 }
 
 void
@@ -177,7 +185,7 @@ DiskReader::buffer_load () const
                return 1.0;
        }
 
                return 1.0;
        }
 
-       PBD::RingBufferNPT<Sample> * b = c->front()->buf;
+       PBD::RingBufferNPT<Sample>* b = c->front()->rbuf;
        return (float) ((double) b->read_space() / (double) b->bufsize());
 }
 
        return (float) ((double) b->read_space() / (double) b->bufsize());
 }
 
@@ -233,13 +241,13 @@ DiskReader::use_playlist (DataType dt, boost::shared_ptr<Playlist> playlist)
 }
 
 void
 }
 
 void
-DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
+DiskReader::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample,
                  double speed, pframes_t nframes, bool result_required)
 {
        uint32_t n;
        boost::shared_ptr<ChannelList> c = channels.reader();
        ChannelList::iterator chan;
                  double speed, pframes_t nframes, bool result_required)
 {
        uint32_t n;
        boost::shared_ptr<ChannelList> c = channels.reader();
        ChannelList::iterator chan;
-       frameoffset_t playback_distance;
+       sampleoffset_t disk_samples_to_consume;
        MonitorState ms = _route->monitoring_state ();
 
        if (_active) {
        MonitorState ms = _route->monitoring_state ();
 
        if (_active) {
@@ -255,42 +263,45 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                }
        }
 
                }
        }
 
-       _need_butler = false;
-
-       if (speed == 0.0 && (ms == MonitoringDisk)) {
-               /* stopped. Don't accidentally pass any data from disk
-                * into our outputs (e.g. via interpolation)
+       if ((speed == 0.0) && (ms == MonitoringDisk)) {
+               /* no channels, or stopped. Don't accidentally pass any data
+                * from disk into our outputs (e.g. via interpolation)
                 */
                 */
-               bufs.silence (nframes, 0);
                return;
        }
 
                return;
        }
 
-       if (speed != 1.0f && speed != -1.0f) {
-               interpolation.set_speed (speed);
-               midi_interpolation.set_speed (speed);
-               playback_distance = midi_interpolation.distance (nframes);
-               if (speed < 0.0) {
-                       playback_distance = -playback_distance;
-               }
-       } else {
-               playback_distance = nframes;
+       BufferSet& scratch_bufs (_session.get_scratch_buffers (bufs.count()));
+       const bool still_locating = _session.global_locate_pending();
+
+       if (c->empty()) {
+               /* do nothing with audio */
+               goto midi;
        }
 
        }
 
-       BufferSet& scratch_bufs (_session.get_scratch_buffers (bufs.count()));
-       const bool still_locating = _session.locate_pending();
+       assert (speed == -1 || speed == 0 || speed == 1);
+
+       if (speed < 0) {
+               disk_samples_to_consume = -nframes;
+       } else if (speed > 0) {
+               disk_samples_to_consume = nframes;
+       } else {
+               disk_samples_to_consume = 0;
+       }
 
 
-       if (!result_required || ((ms & MonitoringDisk) == 0) || still_locating) {
+       if (!result_required || ((ms & MonitoringDisk) == 0) || still_locating || _no_disk_output) {
 
                /* no need for actual disk data, just advance read pointer and return */
 
 
                /* no need for actual disk data, just advance read pointer and return */
 
-               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
-                       (*chan)->buf->increment_read_ptr (playback_distance);
+               if (!still_locating || _no_disk_output) {
+                       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                               (*chan)->rbuf->increment_read_ptr (disk_samples_to_consume);
+                       }
                }
 
                }
 
-               /* if monitoring disk but locating, put silence in the buffers */
+               /* if monitoring disk but locating put silence in the buffers */
 
 
-               if (still_locating && (ms == MonitoringDisk)) {
-                       bufs.silence (playback_distance, 0);
+               if ((_no_disk_output || still_locating) && (ms == MonitoringDisk)) {
+                       bufs.silence (nframes, 0);
                }
 
        } else {
                }
 
        } else {
@@ -321,48 +332,55 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                                disk_signal = output.data ();
                        }
 
                                disk_signal = output.data ();
                        }
 
-                       chaninfo->buf->get_read_vector (&(*chan)->rw_vector);
+                       if (speed > 0) {
+                               if (start_sample < playback_sample) {
+                                       cerr << owner()->name() << " SS = " << start_sample << " PS = " << playback_sample << endl;
+                                       abort ();
+                               }
+                       } else if (speed < 0) {
+                               if (playback_sample < start_sample) {
+                                       cerr << owner()->name() << " SS = " << start_sample << " PS = " << playback_sample << " REVERSE" << endl;
+                                       abort ();
+                               }
+                       }
+
+                       if ((speed > 0) && (start_sample != playback_sample)) {
+                               cerr << owner()->name() << " playback not aligned, jump ahead " << (start_sample - playback_sample) << endl;
 
 
-                       if (playback_distance <= (framecnt_t) chaninfo->rw_vector.len[0]) {
+                               if (can_internal_playback_seek (start_sample - playback_sample)) {
+                                       internal_playback_seek (start_sample - playback_sample);
+                               } else {
+                                       cerr << owner()->name() << " playback not possible: ss = " << start_sample << " ps = " << playback_sample << endl;
+                                       goto midi;
+                               }
+                       }
 
 
-                               if (fabsf (speed) != 1.0f) {
-                                       (void) interpolation.interpolate (
-                                               n, nframes,
-                                               chaninfo->rw_vector.buf[0],
-                                               disk_signal);
-                               } else if (speed != 0.0) {
-                                       memcpy (disk_signal, chaninfo->rw_vector.buf[0], sizeof (Sample) * playback_distance);
+                       chaninfo->rbuf->get_read_vector (&(*chan)->rw_vector);
+
+                       if (disk_samples_to_consume <= (samplecnt_t) chaninfo->rw_vector.len[0]) {
+
+                               if (speed != 0.0) {
+                                       memcpy (disk_signal, chaninfo->rw_vector.buf[0], sizeof (Sample) * disk_samples_to_consume);
                                }
 
                        } else {
 
                                }
 
                        } else {
 
-                               const framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
+                               const samplecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
 
 
-                               if (playback_distance <= total) {
+                               if (disk_samples_to_consume <= total) {
 
 
-                                       /* We have enough samples, but not in one lump.
-                                        */
-
-                                       if (fabsf (speed) != 1.0f) {
-                                               interpolation.interpolate (n, chaninfo->rw_vector.len[0],
-                                                                          chaninfo->rw_vector.buf[0],
-                                                                          disk_signal);
-                                               disk_signal += chaninfo->rw_vector.len[0];
-                                               interpolation.interpolate (n, playback_distance - chaninfo->rw_vector.len[0],
-                                                                          chaninfo->rw_vector.buf[1],
-                                                                          disk_signal);
-                                       } else if (speed != 0.0) {
-                                               memcpy (disk_signal,
+                                               if (speed != 0.0) {
+                                                       memcpy (disk_signal,
                                                        chaninfo->rw_vector.buf[0],
                                                        chaninfo->rw_vector.len[0] * sizeof (Sample));
                                                        chaninfo->rw_vector.buf[0],
                                                        chaninfo->rw_vector.len[0] * sizeof (Sample));
-                                               memcpy (disk_signal + chaninfo->rw_vector.len[0],
+                                                       memcpy (disk_signal + chaninfo->rw_vector.len[0],
                                                        chaninfo->rw_vector.buf[1],
                                                        chaninfo->rw_vector.buf[1],
-                                                       (playback_distance - chaninfo->rw_vector.len[0]) * sizeof (Sample));
+                                                       (disk_samples_to_consume - chaninfo->rw_vector.len[0]) * sizeof (Sample));
                                        }
 
                                } else {
 
                                        }
 
                                } else {
 
-                                       cerr << _name << " Need " << playback_distance << " total = " << total << endl;
+                                       cerr << _name << " Need " << disk_samples_to_consume << " total = " << total << endl;
                                        cerr << "underrun for " << _name << endl;
                                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
                                                                                    DEBUG_THREAD_SELF, name(), total));
                                        cerr << "underrun for " << _name << endl;
                                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, total space = %3\n",
                                                                                    DEBUG_THREAD_SELF, name(), total));
@@ -373,47 +391,57 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                        }
 
                        if (scaling != 1.0f && speed != 0.0) {
                        }
 
                        if (scaling != 1.0f && speed != 0.0) {
-                               apply_gain_to_buffer (disk_signal, nframes, scaling);
+                               apply_gain_to_buffer (disk_signal, disk_samples_to_consume, scaling);
                        }
 
                        }
 
-                       chaninfo->buf->increment_read_ptr (playback_distance);
+                       chaninfo->rbuf->increment_read_ptr (disk_samples_to_consume);
 
 
-                       if ((speed != 0.0) && (ms & MonitoringInput)) {
+                       if (ms & MonitoringInput) {
                                /* mix the disk signal into the input signal (already in bufs) */
                                /* mix the disk signal into the input signal (already in bufs) */
-                               mix_buffers_no_gain (output.data(), disk_signal, speed == 0.0 ? nframes : playback_distance);
+                               mix_buffers_no_gain (output.data(), disk_signal, disk_samples_to_consume);
                        }
                }
        }
 
        /* MIDI data handling */
 
                        }
                }
        }
 
        /* MIDI data handling */
 
-       if (!_session.declick_out_pending()) {
-               if (ms & MonitoringDisk && !still_locating) {
-                       get_midi_playback (bufs.get_midi (0), playback_distance, ms, scratch_bufs, speed, playback_distance);
+  midi:
+       if (/*!_session.declick_out_pending() && */ bufs.count().n_midi()) {
+               MidiBuffer* dst;
+
+               if (_no_disk_output) {
+                       dst = &scratch_bufs.get_midi(0);
+               } else {
+                       dst = &bufs.get_midi (0);
+               }
+
+               if ((ms & MonitoringDisk) && !still_locating) {
+                       get_midi_playback (*dst, start_sample, end_sample, ms, scratch_bufs, speed, disk_samples_to_consume);
                }
        }
 
        if (!still_locating) {
 
                }
        }
 
        if (!still_locating) {
 
+               bool butler_required = false;
+
                if (speed < 0.0) {
                if (speed < 0.0) {
-                       playback_sample -= playback_distance;
+                       playback_sample -= disk_samples_to_consume;
                } else {
                } else {
-                       playback_sample += playback_distance;
+                       playback_sample += disk_samples_to_consume;
                }
 
                if (_playlists[DataType::AUDIO]) {
                        if (!c->empty()) {
                                if (_slaved) {
                }
 
                if (_playlists[DataType::AUDIO]) {
                        if (!c->empty()) {
                                if (_slaved) {
-                                       if (c->front()->buf->write_space() >= c->front()->buf->bufsize() / 2) {
-                                               DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: slaved, write space = %2 of %3\n", name(), c->front()->buf->write_space(),
-                                                                                           c->front()->buf->bufsize()));
-                                               _need_butler = true;
+                                       if (c->front()->rbuf->write_space() >= c->front()->rbuf->bufsize() / 2) {
+                                               DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: slaved, write space = %2 of %3\n", name(), c->front()->rbuf->write_space(), c->front()->rbuf->bufsize()));
+                                               butler_required = true;
                                        }
                                } else {
                                        }
                                } else {
-                                       if ((framecnt_t) c->front()->buf->write_space() >= _chunk_frames) {
-                                               DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: write space = %2 of %3\n", name(), c->front()->buf->write_space(),
-                                                                                           _chunk_frames));
-                                               _need_butler = true;
+                                       if ((samplecnt_t) c->front()->rbuf->write_space() >= _chunk_samples) {
+                                               DEBUG_TRACE (DEBUG::Butler, string_compose ("%1: write space = %2 of %3\n", name(), c->front()->rbuf->write_space(),
+                                                                                           _chunk_samples));
+                                               butler_required = true;
                                        }
                                }
                        }
                                        }
                                }
                        }
@@ -422,16 +450,16 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                if (_playlists[DataType::MIDI]) {
                        /* MIDI butler needed part */
 
                if (_playlists[DataType::MIDI]) {
                        /* MIDI butler needed part */
 
-                       uint32_t frames_read = g_atomic_int_get(const_cast<gint*>(&_frames_read_from_ringbuffer));
-                       uint32_t frames_written = g_atomic_int_get(const_cast<gint*>(&_frames_written_to_ringbuffer));
+                       uint32_t samples_read = g_atomic_int_get(const_cast<gint*>(&_samples_read_from_ringbuffer));
+                       uint32_t samples_written = g_atomic_int_get(const_cast<gint*>(&_samples_written_to_ringbuffer));
 
                        /*
 
                        /*
-                         cerr << name() << " MDS written: " << frames_written << " - read: " << frames_read <<
-                         " = " << frames_written - frames_read
-                         << " + " << playback_distance << " < " << midi_readahead << " = " << need_butler << ")" << endl;
+                         cerr << name() << " MDS written: " << samples_written << " - read: " << samples_read <<
+                         " = " << samples_written - samples_read
+                         << " + " << disk_samples_to_consume << " < " << midi_readahead << " = " << need_butler << ")" << endl;
                        */
 
                        */
 
-                       /* frames_read will generally be less than frames_written, but
+                       /* samples_read will generally be less than samples_written, but
                         * immediately after an overwrite, we can end up having read some data
                         * before we've written any. we don't need to trip an assert() on this,
                         * but we do need to check so that the decision on whether or not we
                         * immediately after an overwrite, we can end up having read some data
                         * before we've written any. we don't need to trip an assert() on this,
                         * but we do need to check so that the decision on whether or not we
@@ -442,13 +470,13 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                         *
                         * Doing heavy GUI operations[1] can stall also the butler.
                         * The RT-thread meanwhile will happily continue and
                         *
                         * Doing heavy GUI operations[1] can stall also the butler.
                         * The RT-thread meanwhile will happily continue and
-                        * â€˜frames_read’ (from buffer to output) will become larger
-                        * than â€˜frames_written’ (from disk to buffer).
+                        * â€˜samples_read’ (from buffer to output) will become larger
+                        * than â€˜samples_written’ (from disk to buffer).
                         *
                         * The disk-stream is now behind..
                         *
                         * In those cases the butler needs to be summed to refill the buffer (done now)
                         *
                         * The disk-stream is now behind..
                         *
                         * In those cases the butler needs to be summed to refill the buffer (done now)
-                        * AND we need to skip (frames_read - frames_written). ie remove old events
+                        * AND we need to skip (samples_read - samples_written). ie remove old events
                         * before playback_sample from the rinbuffer.
                         *
                         * [1] one way to do so is described at #6170.
                         * before playback_sample from the rinbuffer.
                         *
                         * [1] one way to do so is described at #6170.
@@ -459,15 +487,17 @@ DiskReader::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                         * In both cases the root cause is that redrawing MIDI regions on the GUI is still very slow
                         * and can stall
                         */
                         * In both cases the root cause is that redrawing MIDI regions on the GUI is still very slow
                         * and can stall
                         */
-                       if (frames_read <= frames_written) {
-                               if ((frames_written - frames_read) + playback_distance < midi_readahead) {
-                                       _need_butler = true;
+                       if (samples_read <= samples_written) {
+                               if ((samples_written - samples_read) + disk_samples_to_consume < midi_readahead) {
+                                       butler_required = true;
                                }
                        } else {
                                }
                        } else {
-                               _need_butler = true;
+                               butler_required = true;
                        }
 
                }
                        }
 
                }
+
+               _need_butler = butler_required;
        }
 
        // DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 reader run, needs butler = %2\n", name(), _need_butler));
        }
 
        // DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 reader run, needs butler = %2\n", name(), _need_butler));
@@ -480,11 +510,11 @@ DiskReader::set_pending_overwrite (bool yn)
 
        _pending_overwrite = yn;
 
 
        _pending_overwrite = yn;
 
-       overwrite_frame = playback_sample;
+       overwrite_sample = playback_sample;
 
        boost::shared_ptr<ChannelList> c = channels.reader ();
        if (!c->empty ()) {
 
        boost::shared_ptr<ChannelList> c = channels.reader ();
        if (!c->empty ()) {
-               overwrite_offset = c->front()->buf->get_read_ptr();
+               overwrite_offset = c->front()->rbuf->get_read_ptr();
        }
 }
 
        }
 }
 
@@ -497,7 +527,7 @@ DiskReader::overwrite_existing_buffers ()
 
        overwrite_queued = false;
 
 
        overwrite_queued = false;
 
-       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 overwriting existing buffers at %2\n", overwrite_frame));
+       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1 overwriting existing buffers at %2\n", overwrite_sample));
 
        if (!c->empty ()) {
 
 
        if (!c->empty ()) {
 
@@ -506,10 +536,10 @@ DiskReader::overwrite_existing_buffers ()
                const bool reversed = _session.transport_speed() < 0.0f;
 
                /* assume all are the same size */
                const bool reversed = _session.transport_speed() < 0.0f;
 
                /* assume all are the same size */
-               framecnt_t size = c->front()->buf->bufsize();
+               samplecnt_t size = c->front()->rbuf->bufsize();
 
 
-               std::auto_ptr<Sample> mixdown_buffer (new Sample[size]);
-               std::auto_ptr<float> gain_buffer (new float[size]);
+               boost::scoped_array<Sample> mixdown_buffer (new Sample[size]);
+               boost::scoped_array<float> gain_buffer (new float[size]);
 
                /* reduce size so that we can fill the buffer correctly (ringbuffers
                   can only handle size-1, otherwise they appear to be empty)
 
                /* reduce size so that we can fill the buffer correctly (ringbuffers
                   can only handle size-1, otherwise they appear to be empty)
@@ -517,12 +547,12 @@ DiskReader::overwrite_existing_buffers ()
                size--;
 
                uint32_t n=0;
                size--;
 
                uint32_t n=0;
-               framepos_t start;
+               samplepos_t start;
 
                for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
 
 
                for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
 
-                       start = overwrite_frame;
-                       framecnt_t cnt = size;
+                       start = overwrite_sample;
+                       samplecnt_t cnt = size;
 
                        /* to fill the buffer without resetting the playback sample, we need to
                           do it one or two chunks (normally two).
 
                        /* to fill the buffer without resetting the playback sample, we need to
                           do it one or two chunks (normally two).
@@ -535,10 +565,10 @@ DiskReader::overwrite_existing_buffers ()
 
                        */
 
 
                        */
 
-                       framecnt_t to_read = size - overwrite_offset;
+                       samplecnt_t to_read = size - overwrite_offset;
 
 
-                       if (audio_read ((*chan)->buf->buffer() + overwrite_offset, mixdown_buffer.get(), gain_buffer.get(), start, to_read, n, reversed)) {
-                               error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at frame %3"),
+                       if (audio_read ((*chan)->rbuf->buffer() + overwrite_offset, mixdown_buffer.get(), gain_buffer.get(), start, to_read, n, reversed)) {
+                               error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3"),
                                                        id(), size, playback_sample) << endmsg;
                                goto midi;
                        }
                                                        id(), size, playback_sample) << endmsg;
                                goto midi;
                        }
@@ -547,8 +577,8 @@ DiskReader::overwrite_existing_buffers ()
 
                                cnt -= to_read;
 
 
                                cnt -= to_read;
 
-                               if (audio_read ((*chan)->buf->buffer(), mixdown_buffer.get(), gain_buffer.get(), start, cnt, n, reversed)) {
-                                       error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at frame %3"),
+                               if (audio_read ((*chan)->rbuf->buffer(), mixdown_buffer.get(), gain_buffer.get(), start, cnt, n, reversed)) {
+                                       error << string_compose(_("DiskReader %1: when refilling, cannot read %2 from playlist at sample %3"),
                                                                id(), size, playback_sample) << endmsg;
                                        goto midi;
                                }
                                                                id(), size, playback_sample) << endmsg;
                                        goto midi;
                                }
@@ -569,19 +599,18 @@ DiskReader::overwrite_existing_buffers ()
                _midi_buf->reset ();
                _midi_buf->reset_tracker ();
 
                _midi_buf->reset ();
                _midi_buf->reset_tracker ();
 
-               g_atomic_int_set (&_frames_read_from_ringbuffer, 0);
-               g_atomic_int_set (&_frames_written_to_ringbuffer, 0);
+               g_atomic_int_set (&_samples_read_from_ringbuffer, 0);
+               g_atomic_int_set (&_samples_written_to_ringbuffer, 0);
 
                /* Resolve all currently active notes in the playlist.  This is more
                   aggressive than it needs to be: ideally we would only resolve what is
                   absolutely necessary, but this seems difficult and/or impossible without
                   having the old data or knowing what change caused the overwrite.
                */
 
                /* Resolve all currently active notes in the playlist.  This is more
                   aggressive than it needs to be: ideally we would only resolve what is
                   absolutely necessary, but this seems difficult and/or impossible without
                   having the old data or knowing what change caused the overwrite.
                */
-               midi_playlist()->resolve_note_trackers (*_midi_buf, overwrite_frame);
+               midi_playlist()->resolve_note_trackers (*_midi_buf, overwrite_sample);
 
 
-               midi_read (overwrite_frame, _chunk_frames, false);
-
-               file_frame = overwrite_frame; // it was adjusted by ::midi_read()
+               midi_read (overwrite_sample, _chunk_samples, false);
+               file_sample[DataType::MIDI] = overwrite_sample; // overwrite_sample was adjusted by ::midi_read() to the new position
        }
 
        _pending_overwrite = false;
        }
 
        _pending_overwrite = false;
@@ -590,18 +619,20 @@ DiskReader::overwrite_existing_buffers ()
 }
 
 int
 }
 
 int
-DiskReader::seek (framepos_t frame, bool complete_refill)
+DiskReader::seek (samplepos_t sample, bool complete_refill)
 {
        uint32_t n;
        int ret = -1;
        ChannelList::iterator chan;
        boost::shared_ptr<ChannelList> c = channels.reader();
 
 {
        uint32_t n;
        int ret = -1;
        ChannelList::iterator chan;
        boost::shared_ptr<ChannelList> c = channels.reader();
 
+       //sample = std::max ((samplecnt_t)0, sample -_session.worst_output_latency ());
+
        for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
        for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
-               (*chan)->buf->reset ();
+               (*chan)->rbuf->reset ();
        }
 
        }
 
-       if (g_atomic_int_get (&_frames_read_from_ringbuffer) == 0) {
+       if (g_atomic_int_get (&_samples_read_from_ringbuffer) == 0) {
                /* we haven't read anything since the last seek,
                   so flush all note trackers to prevent
                   wierdness
                /* we haven't read anything since the last seek,
                   so flush all note trackers to prevent
                   wierdness
@@ -610,11 +641,12 @@ DiskReader::seek (framepos_t frame, bool complete_refill)
        }
 
        _midi_buf->reset();
        }
 
        _midi_buf->reset();
-       g_atomic_int_set(&_frames_read_from_ringbuffer, 0);
-       g_atomic_int_set(&_frames_written_to_ringbuffer, 0);
+       g_atomic_int_set(&_samples_read_from_ringbuffer, 0);
+       g_atomic_int_set(&_samples_written_to_ringbuffer, 0);
 
 
-       playback_sample = frame;
-       file_frame = frame;
+       playback_sample = sample;
+       file_sample[DataType::AUDIO] = sample;
+       file_sample[DataType::MIDI] = sample;
 
        if (complete_refill) {
                /* call _do_refill() to refill the entire buffer, using
 
        if (complete_refill) {
                /* call _do_refill() to refill the entire buffer, using
@@ -633,7 +665,7 @@ DiskReader::seek (framepos_t frame, bool complete_refill)
 }
 
 int
 }
 
 int
-DiskReader::can_internal_playback_seek (framecnt_t distance)
+DiskReader::can_internal_playback_seek (samplecnt_t distance)
 {
        /* 1. Audio */
 
 {
        /* 1. Audio */
 
@@ -641,27 +673,27 @@ DiskReader::can_internal_playback_seek (framecnt_t distance)
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
-               if ((*chan)->buf->read_space() < (size_t) distance) {
+               if ((*chan)->rbuf->read_space() < (size_t) distance) {
                        return false;
                }
        }
 
        /* 2. MIDI */
 
                        return false;
                }
        }
 
        /* 2. MIDI */
 
-       uint32_t frames_read    = g_atomic_int_get(&_frames_read_from_ringbuffer);
-       uint32_t frames_written = g_atomic_int_get(&_frames_written_to_ringbuffer);
+       uint32_t samples_read    = g_atomic_int_get(&_samples_read_from_ringbuffer);
+       uint32_t samples_written = g_atomic_int_get(&_samples_written_to_ringbuffer);
 
 
-       return ((frames_written - frames_read) < distance);
+       return ((samples_written - samples_read) < distance);
 }
 
 int
 }
 
 int
-DiskReader::internal_playback_seek (framecnt_t distance)
+DiskReader::internal_playback_seek (samplecnt_t distance)
 {
        ChannelList::iterator chan;
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
 {
        ChannelList::iterator chan;
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
-               (*chan)->buf->increment_read_ptr (::llabs(distance));
+               (*chan)->rbuf->increment_read_ptr (::llabs(distance));
        }
 
        playback_sample += distance;
        }
 
        playback_sample += distance;
@@ -681,21 +713,21 @@ void swap_by_ptr (Sample *first, Sample *last)
 
 /** Read some data for 1 channel from our playlist into a buffer.
  *  @param buf Buffer to write to.
 
 /** Read some data for 1 channel from our playlist into a buffer.
  *  @param buf Buffer to write to.
- *  @param start Session frame to start reading from; updated to where we end up
+ *  @param start Session sample to start reading from; updated to where we end up
  *         after the read.
  *  @param cnt Count of samples to read.
  *  @param reversed true if we are running backwards, otherwise false.
  */
 int
 DiskReader::audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
  *         after the read.
  *  @param cnt Count of samples to read.
  *  @param reversed true if we are running backwards, otherwise false.
  */
 int
 DiskReader::audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
-                        framepos_t& start, framecnt_t cnt,
+                        samplepos_t& start, samplecnt_t cnt,
                         int channel, bool reversed)
 {
                         int channel, bool reversed)
 {
-       framecnt_t this_read = 0;
+       samplecnt_t this_read = 0;
        bool reloop = false;
        bool reloop = false;
-       framepos_t loop_end = 0;
-       framepos_t loop_start = 0;
-       framecnt_t offset = 0;
+       samplepos_t loop_end = 0;
+       samplepos_t loop_start = 0;
+       samplecnt_t offset = 0;
        Location *loc = 0;
 
        if (!_playlists[DataType::AUDIO]) {
        Location *loc = 0;
 
        if (!_playlists[DataType::AUDIO]) {
@@ -707,7 +739,7 @@ DiskReader::audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
 
        if (!reversed) {
 
 
        if (!reversed) {
 
-               framecnt_t loop_length = 0;
+               samplecnt_t loop_length = 0;
 
                /* Make the use of a Location atomic for this read operation.
 
 
                /* Make the use of a Location atomic for this read operation.
 
@@ -717,13 +749,13 @@ DiskReader::audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                   just once.
                */
 
                   just once.
                */
 
-               if ((loc = loop_location) != 0) {
+               if ((loc = _loop_location) != 0) {
                        loop_start = loc->start();
                        loop_end = loc->end();
                        loop_length = loop_end - loop_start;
                }
 
                        loop_start = loc->start();
                        loop_end = loc->end();
                        loop_length = loop_end - loop_start;
                }
 
-               /* if we are looping, ensure that the first frame we read is at the correct
+               /* if we are looping, ensure that the first sample we read is at the correct
                   position within the loop.
                */
 
                   position within the loop.
                */
 
@@ -760,7 +792,7 @@ DiskReader::audio_read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                this_read = min(cnt,this_read);
 
                if (audio_playlist()->read (buf+offset, mixdown_buffer, gain_buffer, start, this_read, channel) != this_read) {
                this_read = min(cnt,this_read);
 
                if (audio_playlist()->read (buf+offset, mixdown_buffer, gain_buffer, start, this_read, channel) != this_read) {
-                       error << string_compose(_("DiskReader %1: cannot read %2 from playlist at frame %3"), id(), this_read,
+                       error << string_compose(_("DiskReader %1: cannot read %2 from playlist at sample %3"), id(), this_read,
                                         start) << endmsg;
                        return -1;
                }
                                         start) << endmsg;
                        return -1;
                }
@@ -797,10 +829,10 @@ DiskReader::_do_refill_with_alloc (bool partial_fill)
        */
 
        {
        */
 
        {
-               std::auto_ptr<Sample> mix_buf (new Sample[2*1048576]);
-               std::auto_ptr<float>  gain_buf (new float[2*1048576]);
+               boost::scoped_array<Sample> mix_buf (new Sample[2*1048576]);
+               boost::scoped_array<float>  gain_buf (new float[2*1048576]);
 
 
-               int ret = refill_audio (mix_buf.get(), gain_buf.get(), (partial_fill ? _chunk_frames : 0));
+               int ret = refill_audio (mix_buf.get(), gain_buf.get(), (partial_fill ? _chunk_samples : 0));
 
                if (ret) {
                        return ret;
 
                if (ret) {
                        return ret;
@@ -811,7 +843,7 @@ DiskReader::_do_refill_with_alloc (bool partial_fill)
 }
 
 int
 }
 
 int
-DiskReader::refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level)
+DiskReader::refill (Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level)
 {
        int ret = refill_audio (mixdown_buffer, gain_buffer, fill_level);
 
 {
        int ret = refill_audio (mixdown_buffer, gain_buffer, fill_level);
 
@@ -833,7 +865,7 @@ DiskReader::refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_
  */
 
 int
  */
 
 int
-DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level)
+DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, samplecnt_t fill_level)
 {
        /* do not read from disk while session is marked as Loading, to avoid
           useless redundant I/O.
 {
        /* do not read from disk while session is marked as Loading, to avoid
           useless redundant I/O.
@@ -844,15 +876,15 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
        }
 
        int32_t ret = 0;
        }
 
        int32_t ret = 0;
-       framecnt_t to_read;
+       samplecnt_t to_read;
        RingBufferNPT<Sample>::rw_vector vector;
        bool const reversed = _session.transport_speed() < 0.0f;
        RingBufferNPT<Sample>::rw_vector vector;
        bool const reversed = _session.transport_speed() < 0.0f;
-       framecnt_t total_space;
-       framecnt_t zero_fill;
+       samplecnt_t total_space;
+       samplecnt_t zero_fill;
        uint32_t chan_n;
        ChannelList::iterator i;
        boost::shared_ptr<ChannelList> c = channels.reader();
        uint32_t chan_n;
        ChannelList::iterator i;
        boost::shared_ptr<ChannelList> c = channels.reader();
-       framecnt_t ts;
+       samplecnt_t ts;
 
        if (c->empty()) {
                return 0;
 
        if (c->empty()) {
                return 0;
@@ -866,7 +898,7 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
        vector.buf[1] = 0;
        vector.len[1] = 0;
 
        vector.buf[1] = 0;
        vector.len[1] = 0;
 
-       c->front()->buf->get_write_vector (&vector);
+       c->front()->rbuf->get_write_vector (&vector);
 
        if ((total_space = vector.len[0] + vector.len[1]) == 0) {
                DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: no space to refill\n", name()));
 
        if ((total_space = vector.len[0] + vector.len[1]) == 0) {
                DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: no space to refill\n", name()));
@@ -884,18 +916,18 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
        }
 
        /* if we're running close to normal speed and there isn't enough
        }
 
        /* if we're running close to normal speed and there isn't enough
-          space to do disk_read_chunk_frames of I/O, then don't bother.
+          space to do disk_read_chunk_samples of I/O, then don't bother.
 
           at higher speeds, just do it because the sync between butler
           and audio thread may not be good enough.
 
 
           at higher speeds, just do it because the sync between butler
           and audio thread may not be good enough.
 
-          Note: it is a design assumption that disk_read_chunk_frames is smaller
+          Note: it is a design assumption that disk_read_chunk_samples is smaller
           than the playback buffer size, so this check should never trip when
           the playback buffer is empty.
        */
 
           than the playback buffer size, so this check should never trip when
           the playback buffer is empty.
        */
 
-       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: space to refill %2 vs. chunk %3 (speed = %4)\n", name(), total_space, _chunk_frames, _session.transport_speed()));
-       if ((total_space < _chunk_frames) && fabs (_session.transport_speed()) < 2.0f) {
+       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: space to refill %2 vs. chunk %3 (speed = %4)\n", name(), total_space, _chunk_samples, _session.transport_speed()));
+       if ((total_space < _chunk_samples) && fabs (_session.transport_speed()) < 2.0f) {
                return 0;
        }
 
                return 0;
        }
 
@@ -904,38 +936,40 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
           work with.
        */
 
           work with.
        */
 
-       if (_slaved && total_space < (framecnt_t) (c->front()->buf->bufsize() / 2)) {
+       if (_slaved && total_space < (samplecnt_t) (c->front()->rbuf->bufsize() / 2)) {
                DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: not enough to refill while slaved\n", this));
                return 0;
        }
 
                DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: not enough to refill while slaved\n", this));
                return 0;
        }
 
+       samplepos_t ffa = file_sample[DataType::AUDIO];
+
        if (reversed) {
 
        if (reversed) {
 
-               if (file_frame == 0) {
+               if (ffa == 0) {
 
                        /* at start: nothing to do but fill with silence */
 
                        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                                ChannelInfo* chan (*i);
 
                        /* at start: nothing to do but fill with silence */
 
                        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                                ChannelInfo* chan (*i);
-                               chan->buf->get_write_vector (&vector);
+                               chan->rbuf->get_write_vector (&vector);
                                memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
                                if (vector.len[1]) {
                                        memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
                                }
                                memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
                                if (vector.len[1]) {
                                        memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
                                }
-                               chan->buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+                               chan->rbuf->increment_write_ptr (vector.len[0] + vector.len[1]);
                        }
                        return 0;
                }
 
                        }
                        return 0;
                }
 
-               if (file_frame < total_space) {
+               if (ffa < total_space) {
 
                        /* too close to the start: read what we can,
                           and then zero fill the rest
                        */
 
 
                        /* too close to the start: read what we can,
                           and then zero fill the rest
                        */
 
-                       zero_fill = total_space - file_frame;
-                       total_space = file_frame;
+                       zero_fill = total_space - ffa;
+                       total_space = ffa;
 
                } else {
 
 
                } else {
 
@@ -944,36 +978,36 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
 
        } else {
 
 
        } else {
 
-               if (file_frame == max_framepos) {
+               if (ffa == max_samplepos) {
 
                        /* at end: nothing to do but fill with silence */
 
                        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                                ChannelInfo* chan (*i);
 
                        /* at end: nothing to do but fill with silence */
 
                        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                                ChannelInfo* chan (*i);
-                               chan->buf->get_write_vector (&vector);
+                               chan->rbuf->get_write_vector (&vector);
                                memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
                                if (vector.len[1]) {
                                        memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
                                }
                                memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
                                if (vector.len[1]) {
                                        memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
                                }
-                               chan->buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+                               chan->rbuf->increment_write_ptr (vector.len[0] + vector.len[1]);
                        }
                        return 0;
                }
 
                        }
                        return 0;
                }
 
-               if (file_frame > max_framepos - total_space) {
+               if (ffa > max_samplepos - total_space) {
 
                        /* to close to the end: read what we can, and zero fill the rest */
 
 
                        /* to close to the end: read what we can, and zero fill the rest */
 
-                       zero_fill = total_space - (max_framepos - file_frame);
-                       total_space = max_framepos - file_frame;
+                       zero_fill = total_space - (max_samplepos - ffa);
+                       total_space = max_samplepos - ffa;
 
                } else {
                        zero_fill = 0;
                }
        }
 
 
                } else {
                        zero_fill = 0;
                }
        }
 
-       framepos_t file_frame_tmp = 0;
+       samplepos_t file_sample_tmp = 0;
 
        /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */
 
 
        /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */
 
@@ -990,7 +1024,7 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
 
        /* now back to samples */
 
 
        /* now back to samples */
 
-       framecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8);
+       samplecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8);
 
        DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: will refill %2 channels with %3 samples\n", name(), c->size(), total_space));
 
 
        DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: will refill %2 channels with %3 samples\n", name(), c->size(), total_space));
 
@@ -1002,18 +1036,18 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
                ChannelInfo* chan (*i);
                Sample* buf1;
                Sample* buf2;
                ChannelInfo* chan (*i);
                Sample* buf1;
                Sample* buf2;
-               framecnt_t len1, len2;
+               samplecnt_t len1, len2;
 
 
-               chan->buf->get_write_vector (&vector);
+               chan->rbuf->get_write_vector (&vector);
 
 
-               if ((framecnt_t) vector.len[0] > samples_to_read) {
+               if ((samplecnt_t) vector.len[0] > samples_to_read) {
 
                        /* we're not going to fill the first chunk, so certainly do not bother with the
                           other part. it won't be connected with the part we do fill, as in:
 
                           .... => writable space
                           ++++ => readable space
 
                        /* we're not going to fill the first chunk, so certainly do not bother with the
                           other part. it won't be connected with the part we do fill, as in:
 
                           .... => writable space
                           ++++ => readable space
-                          ^^^^ => 1 x disk_read_chunk_frames that would be filled
+                          ^^^^ => 1 x disk_read_chunk_samples that would be filled
 
                           |......|+++++++++++++|...............................|
                           buf1                buf0
 
                           |......|+++++++++++++|...............................|
                           buf1                buf0
@@ -1030,7 +1064,7 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
                }
 
                ts = total_space;
                }
 
                ts = total_space;
-               file_frame_tmp = file_frame;
+               file_sample_tmp = ffa;
 
                buf1 = vector.buf[0];
                len1 = vector.len[0];
 
                buf1 = vector.buf[0];
                len1 = vector.len[0];
@@ -1038,18 +1072,17 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
                len2 = vector.len[1];
 
                to_read = min (ts, len1);
                len2 = vector.len[1];
 
                to_read = min (ts, len1);
-               to_read = min (to_read, (framecnt_t) samples_to_read);
+               to_read = min (to_read, (samplecnt_t) samples_to_read);
 
                assert (to_read >= 0);
 
                if (to_read) {
 
 
                assert (to_read >= 0);
 
                if (to_read) {
 
-                       if (audio_read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
+                       if (audio_read (buf1, mixdown_buffer, gain_buffer, file_sample_tmp, to_read, chan_n, reversed)) {
                                ret = -1;
                                goto out;
                        }
                                ret = -1;
                                goto out;
                        }
-
-                       chan->buf->increment_write_ptr (to_read);
+                       chan->rbuf->increment_write_ptr (to_read);
                        ts -= to_read;
                }
 
                        ts -= to_read;
                }
 
@@ -1062,12 +1095,12 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
                           all of vector.len[1] as well.
                        */
 
                           all of vector.len[1] as well.
                        */
 
-                       if (audio_read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
+                       if (audio_read (buf2, mixdown_buffer, gain_buffer, file_sample_tmp, to_read, chan_n, reversed)) {
                                ret = -1;
                                goto out;
                        }
 
                                ret = -1;
                                goto out;
                        }
 
-                       chan->buf->increment_write_ptr (to_read);
+                       chan->rbuf->increment_write_ptr (to_read);
                }
 
                if (zero_fill) {
                }
 
                if (zero_fill) {
@@ -1079,19 +1112,19 @@ DiskReader::refill_audio (Sample* mixdown_buffer, float* gain_buffer, framecnt_t
        // elapsed = g_get_monotonic_time () - before;
        // cerr << '\t' << name() << ": bandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n";
 
        // elapsed = g_get_monotonic_time () - before;
        // cerr << '\t' << name() << ": bandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n";
 
-       file_frame = file_frame_tmp;
-       assert (file_frame >= 0);
+       file_sample[DataType::AUDIO] = file_sample_tmp;
+       assert (file_sample[DataType::AUDIO] >= 0);
 
 
-       ret = ((total_space - samples_to_read) > _chunk_frames);
+       ret = ((total_space - samples_to_read) > _chunk_samples);
 
 
-       c->front()->buf->get_write_vector (&vector);
+       c->front()->rbuf->get_write_vector (&vector);
 
   out:
        return ret;
 }
 
 void
 
   out:
        return ret;
 }
 
 void
-DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const & movements_frames, bool from_undo)
+DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<samplepos_t> > const & movements_samples, bool from_undo)
 {
        /* If we're coming from an undo, it will have handled
           automation undo (it must, since automation-follows-regions
 {
        /* If we're coming from an undo, it will have handled
           automation undo (it must, since automation-follows-regions
@@ -1108,8 +1141,8 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
 
        list< Evoral::RangeMove<double> > movements;
 
 
        list< Evoral::RangeMove<double> > movements;
 
-       for (list< Evoral::RangeMove<framepos_t> >::const_iterator i = movements_frames.begin();
-            i != movements_frames.end();
+       for (list< Evoral::RangeMove<samplepos_t> >::const_iterator i = movements_samples.begin();
+            i != movements_samples.end();
             ++i) {
 
                movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
             ++i) {
 
                movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
@@ -1136,11 +1169,11 @@ DiskReader::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
                 }
         }
        /* move processor automation */
                 }
         }
        /* move processor automation */
-        _route->foreach_processor (boost::bind (&DiskReader::move_processor_automation, this, _1, movements_frames));
+        _route->foreach_processor (boost::bind (&DiskReader::move_processor_automation, this, _1, movements_samples));
 }
 
 void
 }
 
 void
-DiskReader::move_processor_automation (boost::weak_ptr<Processor> p, list< Evoral::RangeMove<framepos_t> > const & movements_frames)
+DiskReader::move_processor_automation (boost::weak_ptr<Processor> p, list< Evoral::RangeMove<samplepos_t> > const & movements_samples)
 {
        boost::shared_ptr<Processor> processor (p.lock ());
        if (!processor) {
 {
        boost::shared_ptr<Processor> processor (p.lock ());
        if (!processor) {
@@ -1148,7 +1181,7 @@ DiskReader::move_processor_automation (boost::weak_ptr<Processor> p, list< Evora
        }
 
        list< Evoral::RangeMove<double> > movements;
        }
 
        list< Evoral::RangeMove<double> > movements;
-       for (list< Evoral::RangeMove<framepos_t> >::const_iterator i = movements_frames.begin(); i != movements_frames.end(); ++i) {
+       for (list< Evoral::RangeMove<samplepos_t> >::const_iterator i = movements_samples.begin(); i != movements_samples.end(); ++i) {
                movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
        }
 
                movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
        }
 
@@ -1171,16 +1204,6 @@ DiskReader::move_processor_automation (boost::weak_ptr<Processor> p, list< Evora
        }
 }
 
        }
 }
 
-boost::shared_ptr<MidiBuffer>
-DiskReader::get_gui_feed_buffer () const
-{
-       boost::shared_ptr<MidiBuffer> b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)));
-
-       Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex);
-       b->copy (_gui_feed_buffer);
-       return b;
-}
-
 void
 DiskReader::reset_tracker ()
 {
 void
 DiskReader::reset_tracker ()
 {
@@ -1194,7 +1217,7 @@ DiskReader::reset_tracker ()
 }
 
 void
 }
 
 void
-DiskReader::resolve_tracker (Evoral::EventSink<framepos_t>& buffer, framepos_t time)
+DiskReader::resolve_tracker (Evoral::EventSink<samplepos_t>& buffer, samplepos_t time)
 {
        _midi_buf->resolve_tracker(buffer, time);
 
 {
        _midi_buf->resolve_tracker(buffer, time);
 
@@ -1209,25 +1232,26 @@ DiskReader::resolve_tracker (Evoral::EventSink<framepos_t>& buffer, framepos_t t
  *  so that an event at playback_sample has time = 0
  */
 void
  *  so that an event at playback_sample has time = 0
  */
 void
-DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState ms, BufferSet& scratch_bufs, double speed, framecnt_t playback_distance)
+DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, samplepos_t end_sample, MonitorState ms, BufferSet& scratch_bufs, double speed, samplecnt_t disk_samples_to_consume)
 {
        MidiBuffer* target;
 {
        MidiBuffer* target;
+       samplepos_t nframes = end_sample - start_sample;
 
        if ((ms & MonitoringInput) == 0) {
 
        if ((ms & MonitoringInput) == 0) {
-               dst.clear();
+               /* Route::process_output_buffers() clears the buffer as-needed */
                target = &dst;
        } else {
                target = &scratch_bufs.get_midi (0);
        }
 
        if (ms & MonitoringDisk) {
                target = &dst;
        } else {
                target = &scratch_bufs.get_midi (0);
        }
 
        if (ms & MonitoringDisk) {
-               /* no disk data needed */
+               /* disk data needed */
 
 
-               Location* loc = loop_location;
+               Location* loc = _loop_location;
 
                DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
                                     "%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name,
 
                DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
                                     "%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name,
-                                    _midi_buf->get_read_ptr(), _midi_buf->get_write_ptr(), playback_sample, playback_sample + nframes,
+                                    _midi_buf->get_read_ptr(), _midi_buf->get_write_ptr(), start_sample, end_sample,
                                     (loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset()));
 
                //cerr << "======== PRE ========\n";
                                     (loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset()));
 
                //cerr << "======== PRE ========\n";
@@ -1237,10 +1261,10 @@ DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState
                size_t events_read = 0;
 
                if (loc) {
                size_t events_read = 0;
 
                if (loc) {
-                       framepos_t effective_start;
+                       samplepos_t effective_start;
 
 
-                       Evoral::Range<framepos_t> loop_range (loc->start(), loc->end() - 1);
-                       effective_start = loop_range.squish (playback_sample);
+                       Evoral::Range<samplepos_t> loop_range (loc->start(), loc->end() - 1);
+                       effective_start = loop_range.squish (start_sample);
 
                        DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
 
 
                        DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
 
@@ -1261,7 +1285,7 @@ DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState
                                   for the 2nd read
                                */
 
                                   for the 2nd read
                                */
 
-                               framecnt_t first, second;
+                               samplecnt_t first, second;
 
                                first = loc->end() - effective_start;
                                second = nframes - first;
 
                                first = loc->end() - effective_start;
                                second = nframes - first;
@@ -1287,12 +1311,12 @@ DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState
                                events_read = _midi_buf->read (*target, effective_start, effective_start + nframes);
                        }
                } else {
                                events_read = _midi_buf->read (*target, effective_start, effective_start + nframes);
                        }
                } else {
-                       const size_t n_skipped = _midi_buf->skip_to (playback_sample);
+                       const size_t n_skipped = _midi_buf->skip_to (start_sample);
                        if (n_skipped > 0) {
                                warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg;
                        }
                        if (n_skipped > 0) {
                                warning << string_compose(_("MidiDiskstream %1: skipped %2 events, possible underflow"), id(), n_skipped) << endmsg;
                        }
-                       DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", playback_sample, playback_sample + nframes, nframes));
-                       events_read = _midi_buf->read (*target, playback_sample, playback_sample + nframes);
+                       DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("playback buffer read, from %1 to %2 (%3)", start_sample, end_sample, nframes));
+                       events_read = _midi_buf->read (*target, start_sample, end_sample, Port::port_offset ());
                }
 
                DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
                }
 
                DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
@@ -1302,14 +1326,16 @@ DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState
                                     _midi_buf->get_read_ptr(), _midi_buf->get_write_ptr()));
        }
 
                                     _midi_buf->get_read_ptr(), _midi_buf->get_write_ptr()));
        }
 
-       g_atomic_int_add (&_frames_read_from_ringbuffer, nframes);
+       g_atomic_int_add (&_samples_read_from_ringbuffer, nframes);
 
        /* vari-speed */
 
        /* vari-speed */
-
        if (speed != 0.0 && fabsf (speed) != 1.0f) {
                for (MidiBuffer::iterator i = target->begin(); i != target->end(); ++i) {
                        MidiBuffer::TimeType *tme = i.timeptr();
        if (speed != 0.0 && fabsf (speed) != 1.0f) {
                for (MidiBuffer::iterator i = target->begin(); i != target->end(); ++i) {
                        MidiBuffer::TimeType *tme = i.timeptr();
-                       *tme = (*tme) * nframes / playback_distance;
+                       // XXX need to subtract port offsets before scaling
+                       // also we must only scale events read from disk
+                       // and not existing input data in the buffer.
+                       *tme = (*tme) * nframes / disk_samples_to_consume;
                }
        }
 
                }
        }
 
@@ -1317,28 +1343,44 @@ DiskReader::get_midi_playback (MidiBuffer& dst, framecnt_t nframes, MonitorState
                dst.merge_from (*target, nframes);
        }
 
                dst.merge_from (*target, nframes);
        }
 
-       //cerr << "======== POST ========\n";
-       //_midi_buf->dump (cerr);
-       //cerr << "----------------\n";
+#if 0
+       if (!target->empty ()) {
+               cerr << "======== MIDI OUT ========\n";
+               for (MidiBuffer::iterator i = target->begin(); i != target->end(); ++i) {
+                       const Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
+                       cerr << "MIDI EVENT (from disk) @ " << ev.time();
+                       for (size_t xx = 0; xx < ev.size(); ++xx) {
+                               cerr << ' ' << hex << (int) ev.buffer()[xx];
+                       }
+                       cerr << dec << endl;
+               }
+               cerr << "----------------\n";
+       }
+#endif
+#if 0
+       cerr << "======== MIDI Disk Buffer ========\n";
+       _midi_buf->dump (cerr);
+       cerr << "----------------\n";
+#endif
 }
 
 }
 
-/** @a start is set to the new frame position (TIME) read up to */
+/** @a start is set to the new sample position (TIME) read up to */
 int
 int
-DiskReader::midi_read (framepos_t& start, framecnt_t dur, bool reversed)
+DiskReader::midi_read (samplepos_t& start, samplecnt_t dur, bool reversed)
 {
 {
-       framecnt_t this_read   = 0;
-       framepos_t loop_end    = 0;
-       framepos_t loop_start  = 0;
-       framecnt_t loop_length = 0;
-       Location*  loc         = loop_location;
-       framepos_t effective_start = start;
-       Evoral::Range<framepos_t>*  loop_range (0);
+       samplecnt_t this_read   = 0;
+       samplepos_t loop_end    = 0;
+       samplepos_t loop_start  = 0;
+       samplecnt_t loop_length = 0;
+       Location*  loc         = _loop_location;
+       samplepos_t effective_start = start;
+       Evoral::Range<samplepos_t>*  loop_range (0);
 
 
-//     MidiTrack*         mt     = dynamic_cast<MidiTrack*>(_track);
-//     MidiChannelFilter* filter = mt ? &mt->playback_filter() : 0;
-       MidiChannelFilter* filter = 0;
+       DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS::midi_read @ %1 cnt %2\n", start, dur));
 
 
-       frameoffset_t loop_offset = 0;
+       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack>(_route);
+       MidiChannelFilter* filter = mt ? &mt->playback_filter() : 0;
+       sampleoffset_t loop_offset = 0;
 
        if (!reversed && loc) {
                get_location_times (loc, &loop_start, &loop_end, &loop_length);
 
        if (!reversed && loc) {
                get_location_times (loc, &loop_start, &loop_end, &loop_length);
@@ -1351,10 +1393,10 @@ DiskReader::midi_read (framepos_t& start, framecnt_t dur, bool reversed)
                if (loc && !reversed) {
 
                        if (!loop_range) {
                if (loc && !reversed) {
 
                        if (!loop_range) {
-                               loop_range = new Evoral::Range<framepos_t> (loop_start, loop_end-1); // inclusive semantics require -1
+                               loop_range = new Evoral::Range<samplepos_t> (loop_start, loop_end-1); // inclusive semantics require -1
                        }
 
                        }
 
-                       /* if we are (seamlessly) looping, ensure that the first frame we read is at the correct
+                       /* if we are (seamlessly) looping, ensure that the first sample we read is at the correct
                           position within the loop.
                        */
 
                           position within the loop.
                        */
 
@@ -1383,12 +1425,12 @@ DiskReader::midi_read (framepos_t& start, framecnt_t dur, bool reversed)
 
                if (midi_playlist()->read (*_midi_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
                        error << string_compose(
 
                if (midi_playlist()->read (*_midi_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
                        error << string_compose(
-                                       _("MidiDiskstream %1: cannot read %2 from playlist at frame %3"),
+                                       _("MidiDiskstream %1: cannot read %2 from playlist at sample %3"),
                                        id(), this_read, start) << endmsg;
                        return -1;
                }
 
                                        id(), this_read, start) << endmsg;
                        return -1;
                }
 
-               g_atomic_int_add (&_frames_written_to_ringbuffer, this_read);
+               g_atomic_int_add (&_samples_written_to_ringbuffer, this_read);
 
                if (reversed) {
 
 
                if (reversed) {
 
@@ -1411,7 +1453,6 @@ DiskReader::midi_read (framepos_t& start, framecnt_t dur, bool reversed)
                }
 
                dur -= this_read;
                }
 
                dur -= this_read;
-               //offset += this_read;
        }
 
        return 0;
        }
 
        return 0;
@@ -1424,10 +1465,10 @@ DiskReader::refill_midi ()
                return 0;
        }
 
                return 0;
        }
 
-       size_t  write_space = _midi_buf->write_space();
+       const size_t  write_space = _midi_buf->write_space();
        const bool reversed    = _session.transport_speed() < 0.0f;
 
        const bool reversed    = _session.transport_speed() < 0.0f;
 
-       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("MIDI refill, write space = %1 file frame = %2\n", write_space, file_frame));
+       DEBUG_TRACE (DEBUG::DiskIO, string_compose ("MIDI refill, write space = %1 file sample = %2\n", write_space, file_sample[DataType::MIDI]));
 
        /* no space to write */
        if (write_space == 0) {
 
        /* no space to write */
        if (write_space == 0) {
@@ -1440,26 +1481,43 @@ DiskReader::refill_midi ()
 
        /* at end: nothing to do */
 
 
        /* at end: nothing to do */
 
-       if (file_frame == max_framepos) {
+       samplepos_t ffm = file_sample[DataType::MIDI];
+
+       if (ffm == max_samplepos) {
                return 0;
        }
 
        int ret = 0;
                return 0;
        }
 
        int ret = 0;
-       const uint32_t frames_read = g_atomic_int_get (&_frames_read_from_ringbuffer);
-       const uint32_t frames_written = g_atomic_int_get (&_frames_written_to_ringbuffer);
+       const uint32_t samples_read = g_atomic_int_get (&_samples_read_from_ringbuffer);
+       const uint32_t samples_written = g_atomic_int_get (&_samples_written_to_ringbuffer);
 
 
-       if ((frames_read < frames_written) && (frames_written - frames_read) >= midi_readahead) {
+       if ((samples_read < samples_written) && (samples_written - samples_read) >= midi_readahead) {
                return 0;
        }
 
                return 0;
        }
 
-       framecnt_t to_read = midi_readahead - ((framecnt_t)frames_written - (framecnt_t)frames_read);
+       samplecnt_t to_read = midi_readahead - ((samplecnt_t)samples_written - (samplecnt_t)samples_read);
 
 
-       to_read = min (to_read, (framecnt_t) (max_framepos - file_frame));
-       to_read = min (to_read, (framecnt_t) write_space);
+       to_read = min (to_read, (samplecnt_t) (max_samplepos - ffm));
+       to_read = min (to_read, (samplecnt_t) write_space);
 
 
-       if (midi_read (file_frame, to_read, reversed)) {
+       if (midi_read (ffm, to_read, reversed)) {
                ret = -1;
        }
 
                ret = -1;
        }
 
+       file_sample[DataType::MIDI] = ffm;
+
        return ret;
 }
        return ret;
 }
+
+void
+DiskReader::set_no_disk_output (bool yn)
+{
+       /* this MUST be called as part of the process call tree, before any
+          disk readers are invoked. We use it when the session needs the
+          transport (and thus effective read position for DiskReaders) to keep
+          advancing as part of syncing up with a transport master, but we
+          don't want any actual disk output yet because we are still not
+          synced.
+       */
+       _no_disk_output = yn;
+}