fix conflicts after merge with master
[ardour.git] / libs / ardour / audio_diskstream.cc
index f80e03b41faef31326f85ebcaf81882607129545..77c14de10338f07396a5caf7de59b5c0f8742ea6 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
-    Copyright (C) 2000-2006 Paul Davis 
+    Copyright (C) 2000-2006 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <sys/stat.h>
 #include <sys/mman.h>
 
 #include <sys/stat.h>
 #include <sys/mman.h>
 
-#include <pbd/error.h>
-#include <pbd/basename.h>
-#include <glibmm/thread.h>
-#include <pbd/xml++.h>
-#include <pbd/memento_command.h>
-#include <pbd/enumwriter.h>
-
-#include <ardour/ardour.h>
-#include <ardour/audioengine.h>
-#include <ardour/audio_diskstream.h>
-#include <ardour/utils.h>
-#include <ardour/configuration.h>
-#include <ardour/audiofilesource.h>
-#include <ardour/send.h>
-#include <ardour/region_factory.h>
-#include <ardour/audioplaylist.h>
-#include <ardour/playlist_factory.h>
-#include <ardour/cycle_timer.h>
-#include <ardour/audioregion.h>
-#include <ardour/source_factory.h>
+#include "pbd/error.h"
+#include "pbd/xml++.h"
+#include "pbd/memento_command.h"
+#include "pbd/enumwriter.h"
+#include "pbd/stateful_diff_command.h"
+
+#include "ardour/analyser.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/audio_diskstream.h"
+#include "ardour/audio_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/audiofilesource.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/butler.h"
+#include "ardour/debug.h"
+#include "ardour/io.h"
+#include "ardour/playlist_factory.h"
+#include "ardour/region_factory.h"
+#include "ardour/session.h"
+#include "ardour/session_playlists.h"
+#include "ardour/source_factory.h"
+#include "ardour/track.h"
+#include "ardour/types.h"
+#include "ardour/utils.h"
 
 #include "i18n.h"
 #include <locale.h>
 
 #include "i18n.h"
 #include <locale.h>
@@ -64,26 +69,23 @@ gain_t* AudioDiskstream::_gain_buffer          = 0;
 
 AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
        : Diskstream(sess, name, flag)
 
 AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
        : Diskstream(sess, name, flag)
-       , deprecated_io_node(NULL)
+       , channels (new ChannelList)
 {
        /* prevent any write sources from being created */
 
        in_set_state = true;
 {
        /* prevent any write sources from being created */
 
        in_set_state = true;
-
-       init(flag);
        use_new_playlist ();
        use_new_playlist ();
-
        in_set_state = false;
 }
        in_set_state = false;
 }
-       
+
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
        : Diskstream(sess, node)
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
        : Diskstream(sess, node)
-       , deprecated_io_node(NULL)
+       , channels (new ChannelList)
 {
        in_set_state = true;
 {
        in_set_state = true;
-       init (Recordable);
+       init ();
 
 
-       if (set_state (node)) {
+       if (set_state (node, Stateful::loading_state_version)) {
                in_set_state = false;
                throw failed_constructor();
        }
                in_set_state = false;
                throw failed_constructor();
        }
@@ -96,10 +98,8 @@ AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
 }
 
 void
 }
 
 void
-AudioDiskstream::init (Diskstream::Flag f)
+AudioDiskstream::init ()
 {
 {
-       Diskstream::init(f);
-
        /* there are no channels at this point, so these
           two calls just get speed_buffer_size and wrap_buffer
           size setup without duplicating their code.
        /* there are no channels at this point, so these
           two calls just get speed_buffer_size and wrap_buffer
           size setup without duplicating their code.
@@ -107,26 +107,24 @@ AudioDiskstream::init (Diskstream::Flag f)
 
        set_block_size (_session.get_block_size());
        allocate_temporary_buffers ();
 
        set_block_size (_session.get_block_size());
        allocate_temporary_buffers ();
-
-       add_channel ();
-       assert(_n_channels == 1);
 }
 
 AudioDiskstream::~AudioDiskstream ()
 {
 }
 
 AudioDiskstream::~AudioDiskstream ()
 {
-       notify_callbacks ();
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("Audio Diskstream %1 destructor\n", _name));
 
        {
 
        {
-               /* don't be holding this lock as we exit the destructor, glib will wince
-                  visibly since the mutex gets destroyed before we release it.
-               */
+               RCUWriter<ChannelList> writer (channels);
+               boost::shared_ptr<ChannelList> c = writer.get_copy();
 
 
-               Glib::Mutex::Lock lm (state_lock);
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       (*chan).release ();
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       delete *chan;
                }
                }
-               channels.clear();
+
+               c->clear();
        }
        }
+
+       channels.flush ();
 }
 
 void
 }
 
 void
@@ -152,48 +150,35 @@ AudioDiskstream::free_working_buffers()
 void
 AudioDiskstream::non_realtime_input_change ()
 {
 void
 AudioDiskstream::non_realtime_input_change ()
 {
-       { 
-               Glib::Mutex::Lock lm (state_lock);
+       {
+               Glib::Threads::Mutex::Lock lm (state_lock);
 
 
-               if (input_change_pending == NoChange) {
+               if (input_change_pending.type == IOChange::NoChange) {
                        return;
                }
 
                        return;
                }
 
-               if (input_change_pending & ConfigurationChanged) {
+               if (input_change_pending.type == IOChange::ConfigurationChanged) {
+                       RCUWriter<ChannelList> writer (channels);
+                       boost::shared_ptr<ChannelList> c = writer.get_copy();
 
 
-                       if (_io->n_inputs() > _n_channels) {
-                               
-                               // we need to add new channel infos
-                               
-                               int diff = _io->n_inputs() - channels.size();
-                               
-                               for (int i = 0; i < diff; ++i) {
-                                       add_channel ();
-                               }
-                               
-               } else if (_io->n_inputs() < _n_channels) {
-                               
-                               // we need to get rid of channels
-                               
-                               int diff = channels.size() - _io->n_inputs();
-                               
-                               for (int i = 0; i < diff; ++i) {
-                                       remove_channel ();
-                               }
+                       _n_channels.set(DataType::AUDIO, c->size());
+
+                       if (_io->n_ports().n_audio() > _n_channels.n_audio()) {
+                               add_channel_to (c, _io->n_ports().n_audio() - _n_channels.n_audio());
+                       } else if (_io->n_ports().n_audio() < _n_channels.n_audio()) {
+                               remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().n_audio());
                        }
                        }
-               } 
+               }
 
 
-               get_input_sources ();
-               set_capture_offset ();
-               
-               if (first_input_change) {
-                       set_align_style (_persistent_alignment_style);
-                       first_input_change = false;
-               } else {
+               if (input_change_pending.type & IOChange::ConnectionsChanged) {
+                       get_input_sources ();
+                       set_capture_offset ();
                        set_align_style_from_io ();
                }
 
                        set_align_style_from_io ();
                }
 
-               input_change_pending = NoChange;
+               input_change_pending = IOChange::NoChange;
+
+               /* implicit unlock */
        }
 
        /* reset capture files */
        }
 
        /* reset capture files */
@@ -203,47 +188,56 @@ AudioDiskstream::non_realtime_input_change ()
        /* now refill channel buffers */
 
        if (speed() != 1.0f || speed() != -1.0f) {
        /* now refill channel buffers */
 
        if (speed() != 1.0f || speed() != -1.0f) {
-               seek ((nframes_t) (_session.transport_frame() * (double) speed()));
+               seek ((framepos_t) (_session.transport_frame() * (double) speed()));
        } else {
                seek (_session.transport_frame());
        }
 }
 
        } else {
                seek (_session.transport_frame());
        }
 }
 
+void
+AudioDiskstream::non_realtime_locate (framepos_t location)
+{
+       /* now refill channel buffers */
+
+       if (speed() != 1.0f || speed() != -1.0f) {
+               seek ((framepos_t) (location * (double) speed()));
+       } else {
+               seek (location);
+       }
+}
+
 void
 AudioDiskstream::get_input_sources ()
 {
 void
 AudioDiskstream::get_input_sources ()
 {
-       uint32_t ni = _io->n_inputs();
-       
-       for (uint32_t n = 0; n < ni; ++n) {
-               
-               const char **connections = _io->input(n)->get_connections ();
-               ChannelInfo& chan = channels[n];
-               
-               if (connections == 0 || connections[0] == 0) {
-                       
-                       if (chan.source) {
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       uint32_t n;
+       ChannelList::iterator chan;
+       uint32_t ni = _io->n_ports().n_audio();
+       vector<string> connections;
+
+       for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) {
+
+               connections.clear ();
+
+               if (_io->nth (n)->get_connections (connections) == 0) {
+                       if (!(*chan)->source.name.empty()) {
                                // _source->disable_metering ();
                        }
                                // _source->disable_metering ();
                        }
-                       
-                       chan.source = 0;
-                       
+                       (*chan)->source.name = string();
                } else {
                } else {
-                       chan.source = _session.engine().get_port_by_name (connections[0]);
-               }
-               
-               if (connections) {
-                       free (connections);
+                       (*chan)->source.name = connections[0];
                }
        }
                }
        }
-}              
+}
 
 int
 AudioDiskstream::find_and_use_playlist (const string& name)
 {
        boost::shared_ptr<AudioPlaylist> playlist;
 
 int
 AudioDiskstream::find_and_use_playlist (const string& name)
 {
        boost::shared_ptr<AudioPlaylist> playlist;
-               
-       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlist_by_name (name))) == 0) {
-               playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, name));
+
+       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlists->by_name (name))) == 0) {
+               playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, name));
        }
 
        if (!playlist) {
        }
 
        if (!playlist) {
@@ -280,12 +274,11 @@ AudioDiskstream::use_new_playlist ()
                newname = Playlist::bump_name (_name, _session);
        }
 
                newname = Playlist::bump_name (_name, _session);
        }
 
-       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, newname, hidden()))) != 0) {
-               
-               playlist->set_orig_diskstream_id (id());
+       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) {
+
                return use_playlist (playlist);
 
                return use_playlist (playlist);
 
-       } else { 
+       } else {
                return -1;
        }
 }
                return -1;
        }
 }
@@ -308,11 +301,10 @@ AudioDiskstream::use_copy_playlist ()
        boost::shared_ptr<AudioPlaylist> playlist;
 
        newname = Playlist::bump_name (_playlist->name(), _session);
        boost::shared_ptr<AudioPlaylist> playlist;
 
        newname = Playlist::bump_name (_playlist->name(), _session);
-       
-       if ((playlist  = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) {
-               playlist->set_orig_diskstream_id (id());
+
+       if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) {
                return use_playlist (playlist);
                return use_playlist (playlist);
-       } else { 
+       } else {
                return -1;
        }
 }
                return -1;
        }
 }
@@ -321,15 +313,23 @@ void
 AudioDiskstream::setup_destructive_playlist ()
 {
        SourceList srcs;
 AudioDiskstream::setup_destructive_playlist ()
 {
        SourceList srcs;
+       boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-               srcs.push_back ((*chan).write_source);
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               srcs.push_back ((*chan)->write_source);
        }
 
        /* a single full-sized region */
 
        }
 
        /* a single full-sized region */
 
-       boost::shared_ptr<Region> region (RegionFactory::create (srcs, 0, max_frames - srcs.front()->natural_position(), _name));
-       _playlist->add_region (region, srcs.front()->natural_position());               
+       assert (!srcs.empty ());
+
+       PropertyList plist;
+       plist.add (Properties::name, _name.val());
+       plist.add (Properties::start, 0);
+       plist.add (Properties::length, max_framepos - (max_framepos - srcs.front()->natural_position()));
+
+       boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
+       _playlist->add_region (region, srcs.front()->natural_position());
 }
 
 void
 }
 
 void
@@ -337,7 +337,7 @@ AudioDiskstream::use_destructive_playlist ()
 {
        /* this is called from the XML-based constructor or ::set_destructive. when called,
           we already have a playlist and a region, but we need to
 {
        /* this is called from the XML-based constructor or ::set_destructive. when called,
           we already have a playlist and a region, but we need to
-          set up our sources for write. we use the sources associated 
+          set up our sources for write. we use the sources associated
           with the (presumed single, full-extent) region.
        */
 
           with the (presumed single, full-extent) region.
        */
 
@@ -356,242 +356,104 @@ AudioDiskstream::use_destructive_playlist ()
 
        /* be sure to stretch the region out to the maximum length */
 
 
        /* be sure to stretch the region out to the maximum length */
 
-       region->set_length (max_frames - region->position(), this);
+       region->set_length (max_framepos - region->position());
 
        uint32_t n;
        ChannelList::iterator chan;
 
        uint32_t n;
        ChannelList::iterator chan;
+       boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-       for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) {
-               (*chan).write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n));
-               assert((*chan).write_source);
-               (*chan).write_source->set_allow_remove_if_empty (false);
+       for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+               (*chan)->write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n));
+               assert((*chan)->write_source);
+               (*chan)->write_source->set_allow_remove_if_empty (false);
 
                /* this might be false if we switched modes, so force it */
 
 
                /* this might be false if we switched modes, so force it */
 
-               (*chan).write_source->set_destructive (true);
+               (*chan)->write_source->set_destructive (true);
        }
 
        /* the source list will never be reset for a destructive track */
 }
 
 void
        }
 
        /* the source list will never be reset for a destructive track */
 }
 
 void
-AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
+AudioDiskstream::prepare_record_status(framepos_t capture_start_frame)
 {
 {
-       int possibly_recording;
-       int rolling;
-       int change;
-       const int transport_rolling = 0x4;
-       const int track_rec_enabled = 0x2;
-       const int global_rec_enabled = 0x1;
-
-       /* merge together the 3 factors that affect record status, and compute
-          what has changed.
-       */
-
-       rolling = _session.transport_speed() != 0.0f;
-       possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record;
-       change = possibly_recording ^ last_possibly_recording;
-
-       if (possibly_recording == last_possibly_recording) {
-               return;
-       }
-
-       /* change state */
-
-       /* if per-track or global rec-enable turned on while the other was already on, we've started recording */
-
-       if ((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record) || 
-           ((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled()))) {
-               
-               /* starting to record: compute first+last frames */
-
-               first_recordable_frame = transport_frame + _capture_offset;
-               last_recordable_frame = max_frames;
-               capture_start_frame = transport_frame;
-
-               if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) {
-
-                       /* was stopped, now rolling (and recording) */
-
-                       if (_alignment_style == ExistingMaterial) {
-                               first_recordable_frame += _session.worst_output_latency();
-                       } else {
-                               first_recordable_frame += _roll_delay;
-                       }
-
-               } else {
-
-                       /* was rolling, but record state changed */
-
-                       if (_alignment_style == ExistingMaterial) {
-
-                               if (!Config->get_punch_in()) {
-
-                                       /* manual punch in happens at the correct transport frame
-                                          because the user hit a button. but to get alignment correct 
-                                          we have to back up the position of the new region to the 
-                                          appropriate spot given the roll delay.
-                                       */
-
-                                       capture_start_frame -= _roll_delay;
-
-                                       /* XXX paul notes (august 2005): i don't know why
-                                          this is needed.
-                                       */
-
-                                       first_recordable_frame += _capture_offset;
-
-                               } else {
-
-                                       /* autopunch toggles recording at the precise
-                                          transport frame, and then the DS waits
-                                          to start recording for a time that depends
-                                          on the output latency.
-                                       */
+       if (recordable() && destructive()) {
+               boost::shared_ptr<ChannelList> c = channels.reader();
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
 
 
-                                       first_recordable_frame += _session.worst_output_latency();
-                               }
+                       RingBufferNPT<CaptureTransition>::rw_vector transitions;
+                       (*chan)->capture_transition_buf->get_write_vector (&transitions);
 
 
+                       if (transitions.len[0] > 0) {
+                               transitions.buf[0]->type = CaptureStart;
+                               transitions.buf[0]->capture_val = capture_start_frame;
+                               (*chan)->capture_transition_buf->increment_write_ptr(1);
                        } else {
                        } else {
-
-                               if (Config->get_punch_in()) {
-                                       first_recordable_frame += _roll_delay;
-                               } else {
-                                       capture_start_frame -= _roll_delay;
-                               }
+                               // bad!
+                               fatal << X_("programming error: capture_transition_buf is full on rec start!  inconceivable!")
+                                       << endmsg;
                        }
                        }
-                       
-               }
-
-               if (_flags & Recordable) {
-                       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                               
-                               RingBufferNPT<CaptureTransition>::rw_vector transvec;
-                               (*chan).capture_transition_buf->get_write_vector(&transvec);
-                               
-                               if (transvec.len[0] > 0) {
-                                       transvec.buf[0]->type = CaptureStart;
-                                       transvec.buf[0]->capture_val = capture_start_frame;
-                                       (*chan).capture_transition_buf->increment_write_ptr(1);
-                               }
-                               else {
-                                       // bad!
-                                       fatal << X_("programming error: capture_transition_buf is full on rec start!  inconceivable!") 
-                                             << endmsg;
-                               }
-                       }
-               }
-
-       } else if (!record_enabled() || !can_record) {
-               
-               /* stop recording */
-
-               last_recordable_frame = transport_frame + _capture_offset;
-               
-               if (_alignment_style == ExistingMaterial) {
-                       last_recordable_frame += _session.worst_output_latency();
-               } else {
-                       last_recordable_frame += _roll_delay;
                }
        }
                }
        }
-
-       last_possibly_recording = possibly_recording;
 }
 
 }
 
+
+/** Do some record stuff [not described in this comment!]
+ *
+ *  Also:
+ *    - Setup playback_distance with the nframes, or nframes adjusted
+ *      for current varispeed, if appropriate.
+ *    - Setup current_playback_buffer in each ChannelInfo to point to data
+ *      that someone can read playback_distance worth of data from.
+ */
 int
 int
-AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input)
+AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal)
 {
        uint32_t n;
 {
        uint32_t n;
-       ChannelList::iterator c;
-       int ret = -1;
-       nframes_t rec_offset = 0;
-       nframes_t rec_nframes = 0;
-       bool nominally_recording;
-       bool re = record_enabled ();
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       ChannelList::iterator chan;
+       framecnt_t rec_offset = 0;
+       framecnt_t rec_nframes = 0;
        bool collect_playback = false;
        bool collect_playback = false;
+       bool can_record = _session.actively_recording ();
 
 
-       /* if we've already processed the frames corresponding to this call,
-          just return. this allows multiple routes that are taking input
-          from this diskstream to call our ::process() method, but have
-          this stuff only happen once. more commonly, it allows both
-          the AudioTrack that is using this AudioDiskstream *and* the Session
-          to call process() without problems.
-       */
+       playback_distance = 0;
 
 
-       if (_processed) {
+       if (!_io || !_io->active()) {
                return 0;
        }
 
                return 0;
        }
 
-       check_record_status (transport_frame, nframes, can_record);
-
-       nominally_recording = (can_record && re);
+       check_record_status (transport_frame, can_record);
 
        if (nframes == 0) {
 
        if (nframes == 0) {
-               _processed = true;
                return 0;
        }
 
                return 0;
        }
 
-       /* This lock is held until the end of AudioDiskstream::commit, so these two functions
-          must always be called as a pair. The only exception is if this function
-          returns a non-zero value, in which case, ::commit should not be called.
-       */
+       Glib::Threads::Mutex::Lock sm (state_lock, Glib::Threads::TRY_LOCK);
 
 
-       // If we can't take the state lock return.
-       if (!state_lock.trylock()) {
+       if (!sm.locked()) {
                return 1;
        }
 
        adjust_capture_position = 0;
 
                return 1;
        }
 
        adjust_capture_position = 0;
 
-       for (c = channels.begin(); c != channels.end(); ++c) {
-               (*c).current_capture_buffer = 0;
-               (*c).current_playback_buffer  = 0;
+       for (chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->current_capture_buffer = 0;
+               (*chan)->current_playback_buffer = 0;
        }
 
        }
 
-       if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
-               OverlapType ot;
-               
-               ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+       // Safeguard against situations where process() goes haywire when autopunching
+       // and last_recordable_frame < first_recordable_frame
 
 
-               switch (ot) {
-               case OverlapNone:
-                       rec_nframes = 0;
-                       break;
-                       
-               case OverlapInternal:
-               /*     ----------    recrange
-                         |---|       transrange
-               */
-                       rec_nframes = nframes;
-                       rec_offset = 0;
-                       break;
-                       
-               case OverlapStart:
-                       /*    |--------|    recrange
-                            -----|          transrange
-                       */
-                       rec_nframes = transport_frame + nframes - first_recordable_frame;
-                       if (rec_nframes) {
-                               rec_offset = first_recordable_frame - transport_frame;
-                       }
-                       break;
-                       
-               case OverlapEnd:
-                       /*    |--------|    recrange
-                                 |--------  transrange
-                       */
-                       rec_nframes = last_recordable_frame - transport_frame;
-                       rec_offset = 0;
-                       break;
-                       
-               case OverlapExternal:
-                       /*    |--------|    recrange
-                            --------------  transrange
-                       */
-                       rec_nframes = last_recordable_frame - last_recordable_frame;
-                       rec_offset = first_recordable_frame - transport_frame;
-                       break;
-               }
+       if (last_recordable_frame < first_recordable_frame) {
+               last_recordable_frame = max_framepos;
+       }
+
+       if (record_enabled()) {
+
+               Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+               calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
 
                if (rec_nframes && !was_recording) {
                        capture_captured = 0;
 
                if (rec_nframes && !was_recording) {
                        capture_captured = 0;
@@ -599,66 +461,82 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_
                }
        }
 
                }
        }
 
-
-       if (can_record && !_last_capture_regions.empty()) {
-               _last_capture_regions.clear ();
+       if (can_record && !_last_capture_sources.empty()) {
+               _last_capture_sources.clear ();
        }
 
        }
 
-       if (nominally_recording || rec_nframes) {
+       if (rec_nframes) {
 
 
-               for (n = 0, c = channels.begin(); c != channels.end(); ++c, ++n) {
-                       
-                       ChannelInfo& chan (*c);
-               
-                       chan.capture_buf->get_write_vector (&chan.capture_vector);
+               uint32_t limit = _io->n_ports ().n_audio();
 
 
-                       if (rec_nframes <= chan.capture_vector.len[0]) {
-                               
-                               chan.current_capture_buffer = chan.capture_vector.buf[0];
+               /* one or more ports could already have been removed from _io, but our
+                  channel setup hasn't yet been updated. prevent us from trying to
+                  use channels that correspond to missing ports. note that the
+                  process callback (from which this is called) is always atomic
+                  with respect to port removal/addition.
+               */
 
 
-                               /* note: grab the entire port buffer, but only copy what we were supposed to for recording, and use
-                                  rec_offset
+               for (n = 0, chan = c->begin(); chan != c->end() && n < limit; ++chan, ++n) {
+
+                       ChannelInfo* chaninfo (*chan);
+
+                       chaninfo->capture_buf->get_write_vector (&chaninfo->capture_vector);
+
+                       if (rec_nframes <= (framecnt_t) chaninfo->capture_vector.len[0]) {
+
+                               chaninfo->current_capture_buffer = chaninfo->capture_vector.buf[0];
+
+                               /* note: grab the entire port buffer, but only copy what we were supposed to
+                                  for recording, and use rec_offset
                                */
 
                                */
 
-                               memcpy (chan.current_capture_buffer, _io->input(n)->get_buffer (rec_nframes) + offset + rec_offset, sizeof (Sample) * rec_nframes);
+                               boost::shared_ptr<AudioPort> const ap = _io->audio (n);
+                               assert(ap);
+                               assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity());
 
 
+                               Sample *buf = bufs.get_audio (n).data(rec_offset);
+                               memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes);
+                               
                        } else {
 
                        } else {
 
-                               nframes_t total = chan.capture_vector.len[0] + chan.capture_vector.len[1];
+                               framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
 
                                if (rec_nframes > total) {
                                        DiskOverrun ();
 
                                if (rec_nframes > total) {
                                        DiskOverrun ();
-                                       goto out;
+                                       return -1;
                                }
 
                                }
 
-                               Sample* buf = _io->input (n)->get_buffer (nframes) + offset;
-                               nframes_t first = chan.capture_vector.len[0];
+                               boost::shared_ptr<AudioPort> const ap = _io->audio (n);
+                               assert(ap);
 
 
-                               memcpy (chan.capture_wrap_buffer, buf, sizeof (Sample) * first);
-                               memcpy (chan.capture_vector.buf[0], buf, sizeof (Sample) * first);
-                               memcpy (chan.capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first));
-                               memcpy (chan.capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first));
-                               
-                               chan.current_capture_buffer = chan.capture_wrap_buffer;
+                               Sample *buf = bufs.get_audio (n).data(rec_offset);
+                               framecnt_t first = chaninfo->capture_vector.len[0];
+
+                               memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
+                               memcpy (chaninfo->capture_vector.buf[0], buf, sizeof (Sample) * first);
+                               memcpy (chaninfo->capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first));
+                               memcpy (chaninfo->capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first));
+
+                               chaninfo->current_capture_buffer = chaninfo->capture_wrap_buffer;
                        }
                }
 
        } else {
 
                if (was_recording) {
                        }
                }
 
        } else {
 
                if (was_recording) {
-                       finish_capture (rec_monitors_input);
+                       finish_capture (c);
                }
 
        }
                }
 
        }
-       
+
        if (rec_nframes) {
        if (rec_nframes) {
-               
+
                /* data will be written to disk */
 
                if (rec_nframes == nframes && rec_offset == 0) {
 
                /* data will be written to disk */
 
                if (rec_nframes == nframes && rec_offset == 0) {
 
-                       for (c = channels.begin(); c != channels.end(); ++c) {
-                               (*c).current_playback_buffer = (*c).current_capture_buffer;
+                       for (chan = c->begin(); chan != c->end(); ++chan) {
+                               (*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
                        }
 
                        playback_distance = nframes;
                        }
 
                        playback_distance = nframes;
@@ -676,12 +554,12 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_
 
                adjust_capture_position = rec_nframes;
 
 
                adjust_capture_position = rec_nframes;
 
-       } else if (nominally_recording) {
+       } else if (can_record && record_enabled()) {
 
                /* can't do actual capture yet - waiting for latency effects to finish before we start*/
 
 
                /* can't do actual capture yet - waiting for latency effects to finish before we start*/
 
-               for (c = channels.begin(); c != channels.end(); ++c) {
-                       (*c).current_playback_buffer = (*c).current_capture_buffer;
+               for (chan = c->begin(); chan != c->end(); ++chan) {
+                       (*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
                }
 
                playback_distance = nframes;
                }
 
                playback_distance = nframes;
@@ -691,143 +569,215 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_
                collect_playback = true;
        }
 
                collect_playback = true;
        }
 
-       if (collect_playback) {
-
+       if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) {
+               
                /* we're doing playback */
 
                /* we're doing playback */
 
-               nframes_t necessary_samples;
+               framecnt_t necessary_samples;
 
                /* no varispeed playback if we're recording, because the output .... TBD */
 
                if (rec_nframes == 0 && _actual_speed != 1.0f) {
 
                /* no varispeed playback if we're recording, because the output .... TBD */
 
                if (rec_nframes == 0 && _actual_speed != 1.0f) {
-                       necessary_samples = (nframes_t) floor ((nframes * fabs (_actual_speed))) + 1;
+                       necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
                } else {
                        necessary_samples = nframes;
                }
                } else {
                        necessary_samples = nframes;
                }
-               
-               for (c = channels.begin(); c != channels.end(); ++c) {
-                       (*c).playback_buf->get_read_vector (&(*c).playback_vector);
+
+               for (chan = c->begin(); chan != c->end(); ++chan) {
+                       (*chan)->playback_buf->get_read_vector (&(*chan)->playback_vector);
                }
 
                }
 
-               n = 0;                  
+               n = 0;
 
 
-               for (c = channels.begin(); c != channels.end(); ++c, ++n) {
-               
-                       ChannelInfo& chan (*c);
+               /* Setup current_playback_buffer in each ChannelInfo to point to data that someone
+                  can read necessary_samples (== nframes at a transport speed of 1) worth of data
+                  from right now.
+               */
+
+               for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
 
 
-                       if (necessary_samples <= chan.playback_vector.len[0]) {
+                       ChannelInfo* chaninfo (*chan);
 
 
-                               chan.current_playback_buffer = chan.playback_vector.buf[0];
+                       if (necessary_samples <= (framecnt_t) chaninfo->playback_vector.len[0]) {
+                               /* There are enough samples in the first part of the ringbuffer */
+                               chaninfo->current_playback_buffer = chaninfo->playback_vector.buf[0];
 
                        } else {
 
                        } else {
-                               nframes_t total = chan.playback_vector.len[0] + chan.playback_vector.len[1];
-                               
+                               framecnt_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1];
+
                                if (necessary_samples > total) {
                                if (necessary_samples > total) {
+                                       cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
+                                       cerr << "underrun for " << _name << endl;
                                        DiskUnderrun ();
                                        DiskUnderrun ();
-                                       goto out;
-                                       
+                                       return -1;
+
                                } else {
                                } else {
+
+                                       /* We have enough samples, but not in one lump.  Coalesce the two parts
+                                          into one in playback_wrap_buffer in our ChannelInfo, and specify that
+                                          as our current_playback_buffer.
+                                       */
+
+                                       assert(wrap_buffer_size >= necessary_samples);
+
+                                       /* Copy buf[0] from playback_buf */
+                                       memcpy ((char *) chaninfo->playback_wrap_buffer,
+                                                       chaninfo->playback_vector.buf[0],
+                                                       chaninfo->playback_vector.len[0] * sizeof (Sample));
                                        
                                        
-                                       memcpy ((char *) chan.playback_wrap_buffer, chan.playback_vector.buf[0],
-                                               chan.playback_vector.len[0] * sizeof (Sample));
-                                       memcpy (chan.playback_wrap_buffer + chan.playback_vector.len[0], chan.playback_vector.buf[1], 
-                                               (necessary_samples - chan.playback_vector.len[0]) * sizeof (Sample));
-                                       
-                                       chan.current_playback_buffer = chan.playback_wrap_buffer;
+                                       /* Copy buf[1] from playback_buf */
+                                       memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0],
+                                                       chaninfo->playback_vector.buf[1],
+                                                       (necessary_samples - chaninfo->playback_vector.len[0])
+                                                                       * sizeof (Sample));
+
+                                       chaninfo->current_playback_buffer = chaninfo->playback_wrap_buffer;
                                }
                        }
                                }
                        }
-               } 
+               }
 
                if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
 
                if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
-                       
-                       uint64_t phase = last_phase;
-                       nframes_t i = 0;
-
-                       // Linearly interpolate into the alt buffer
-                       // using 40.24 fixp maths (swh)
-
-                       for (c = channels.begin(); c != channels.end(); ++c) {
-
-                               float fr;
-                               ChannelInfo& chan (*c);
 
 
-                               i = 0;
-                               phase = last_phase;
+                       interpolation.set_speed (_target_speed);
 
 
-                               for (nframes_t outsample = 0; outsample < nframes; ++outsample) {
-                                       i = phase >> 24;
-                                       fr = (phase & 0xFFFFFF) / 16777216.0f;
-                                       chan.speed_buffer[outsample] = 
-                                               chan.current_playback_buffer[i] * (1.0f - fr) +
-                                               chan.current_playback_buffer[i+1] * fr;
-                                       phase += phi;
-                               }
+                       int channel = 0;
+                       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
+                               ChannelInfo* chaninfo (*chan);
+                               
+                               playback_distance = interpolation.interpolate (
+                                       channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
                                
                                
-                               chan.current_playback_buffer = chan.speed_buffer;
+                               chaninfo->current_playback_buffer = chaninfo->speed_buffer;
                        }
                        }
-
-                       playback_distance = i + 1;
-                       last_phase = (phase & 0xFFFFFF);
-
+                       
                } else {
                        playback_distance = nframes;
                }
 
                } else {
                        playback_distance = nframes;
                }
 
+               _speed = _target_speed;
        }
 
        }
 
-       ret = 0;
+       if (need_disk_signal) {
 
 
-  out:
-       _processed = true;
+               /* copy data over to buffer set */
+               
+               size_t n_buffers = bufs.count().n_audio();
+               size_t n_chans = c->size();
+               gain_t scaling = 1.0f;
+               
+               if (n_chans > n_buffers) {
+                       scaling = ((float) n_buffers)/n_chans;
+               }
+
+               for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+                       
+                       AudioBuffer& buf (bufs.get_audio (n%n_buffers));
+                       ChannelInfo* chaninfo (*chan);
+                       
+                       if (n < n_chans) {
+                               if (scaling != 1.0f) {
+                                       buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling);
+                               } else {
+                                       buf.read_from (chaninfo->current_playback_buffer, nframes);
+                               }
+                       } else {
+                               if (scaling != 1.0f) {
+                                       buf.accumulate_with_gain_from (chaninfo->current_playback_buffer, nframes, scaling);
+                               } else {
+                                       buf.accumulate_from (chaninfo->current_playback_buffer, nframes);
+                               }
+                       }
+               }
 
 
-       if (ret) {
+               /* leave the MIDI count alone */
+               ChanCount cnt (DataType::AUDIO, n_chans);
+               cnt.set (DataType::MIDI, bufs.count().n_midi());
+               bufs.set_count (cnt);
+       
+               /* extra buffers will already be silent, so leave them alone */
+       }
 
 
-               /* we're exiting with failure, so ::commit will not
-                  be called. unlock the state lock.
-               */
-               
-               state_lock.unlock();
-       } 
+       return 0;
+}
 
 
-       return ret;
+frameoffset_t
+AudioDiskstream::calculate_playback_distance (pframes_t nframes)
+{
+       frameoffset_t playback_distance = nframes;
+
+       if (record_enabled()) {
+               playback_distance = nframes;
+       } else if (_actual_speed != 1.0f && _actual_speed != -1.0f) {
+               interpolation.set_speed (_target_speed);
+               boost::shared_ptr<ChannelList> c = channels.reader();
+               int channel = 0;
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
+                       playback_distance = interpolation.interpolate (channel, nframes, NULL, NULL);
+               }
+       } else {
+               playback_distance = nframes;
+       }
+
+       if (_actual_speed < 0.0) {
+               return -playback_distance;
+       } else {
+               return playback_distance;
+       }
 }
 
 }
 
+/** Update various things including playback_sample, read pointer on each channel's playback_buf
+ *  and write pointer on each channel's capture_buf.  Also wout whether the butler is needed.
+ *  @return true if the butler is required.
+ */
 bool
 bool
-AudioDiskstream::commit (nframes_t nframes)
+AudioDiskstream::commit (framecnt_t playback_distance)
 {
        bool need_butler = false;
 
 {
        bool need_butler = false;
 
+       if (!_io || !_io->active()) {
+               return false;
+       }
+
        if (_actual_speed < 0.0) {
                playback_sample -= playback_distance;
        } else {
                playback_sample += playback_distance;
        }
 
        if (_actual_speed < 0.0) {
                playback_sample -= playback_distance;
        } else {
                playback_sample += playback_distance;
        }
 
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+               (*chan)->playback_buf->increment_read_ptr (playback_distance);
 
 
-               (*chan).playback_buf->increment_read_ptr (playback_distance);
-               
                if (adjust_capture_position) {
                if (adjust_capture_position) {
-                       (*chan).capture_buf->increment_write_ptr (adjust_capture_position);
+                       (*chan)->capture_buf->increment_write_ptr (adjust_capture_position);
                }
        }
                }
        }
-       
+
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
                adjust_capture_position = 0;
        }
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
                adjust_capture_position = 0;
        }
-       
+
+       if (c->empty()) {
+               return false;
+       }
+
        if (_slaved) {
        if (_slaved) {
-               need_butler = channels[0].playback_buf->write_space() >= channels[0].playback_buf->bufsize() / 2;
+               if (_io && _io->active()) {
+                       need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2;
+               } else {
+                       need_butler = false;
+               }
        } else {
        } else {
-               need_butler = channels[0].playback_buf->write_space() >= disk_io_chunk_frames
-                       || channels[0].capture_buf->read_space() >= disk_io_chunk_frames;
+               if (_io && _io->active()) {
+                       need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_io_chunk_frames)
+                               || ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames);
+               } else {
+                       need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames);
+               }
        }
 
        }
 
-       state_lock.unlock();
-
-       _processed = false;
-
        return need_butler;
 }
 
        return need_butler;
 }
 
@@ -835,40 +785,52 @@ void
 AudioDiskstream::set_pending_overwrite (bool yn)
 {
        /* called from audio thread, so we can use the read ptr and playback sample as we wish */
 AudioDiskstream::set_pending_overwrite (bool yn)
 {
        /* called from audio thread, so we can use the read ptr and playback sample as we wish */
-       
-       pending_overwrite = yn;
+
+       _pending_overwrite = yn;
 
        overwrite_frame = playback_sample;
 
        overwrite_frame = playback_sample;
-       overwrite_offset = channels.front().playback_buf->get_read_ptr();
+
+       boost::shared_ptr<ChannelList> c = channels.reader ();
+       if (!c->empty ()) {
+               overwrite_offset = c->front()->playback_buf->get_read_ptr();
+       }
 }
 
 int
 AudioDiskstream::overwrite_existing_buffers ()
 {
 }
 
 int
 AudioDiskstream::overwrite_existing_buffers ()
 {
-       Sample* mixdown_buffer;
-       float* gain_buffer;
-       int ret = -1;
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       if (c->empty ()) {
+               _pending_overwrite = false;
+               return 0;
+       }
+
+       Sample* mixdown_buffer;
+       float* gain_buffer;
+       int ret = -1;
        bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
 
        overwrite_queued = false;
 
        /* assume all are the same size */
        bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
 
        overwrite_queued = false;
 
        /* assume all are the same size */
-       nframes_t size = channels[0].playback_buf->bufsize();
-       
-       mixdown_buffer = new Sample[size];
-       gain_buffer = new float[size];
-       
-       /* reduce size so that we can fill the buffer correctly. */
+       framecnt_t size = c->front()->playback_buf->bufsize();
+
+       mixdown_buffer = new Sample[size];
+       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)
+       */
        size--;
        size--;
-       
+
        uint32_t n=0;
        uint32_t n=0;
-       nframes_t start;
+       framepos_t start;
 
 
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan, ++n) {
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
 
                start = overwrite_frame;
 
                start = overwrite_frame;
-               nframes_t cnt = size;
-               
+               framecnt_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).
 
@@ -877,52 +839,53 @@ AudioDiskstream::overwrite_existing_buffers ()
                                       ^
                                       overwrite_offset
                    |<- second chunk->||<----------------- first chunk ------------------>|
                                       ^
                                       overwrite_offset
                    |<- second chunk->||<----------------- first chunk ------------------>|
-                  
+
                */
                */
-               
-               nframes_t to_read = size - overwrite_offset;
 
 
-               if (read ((*chan).playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, *chan, n, reversed)) {
+               framecnt_t to_read = size - overwrite_offset;
+
+               if (read ((*chan)->playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, n, reversed)) {
                        error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
                        error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
-                                        _id, size, playback_sample) << endmsg;
+                                               id(), size, playback_sample) << endmsg;
                        goto out;
                }
                        goto out;
                }
-                       
+
                if (cnt > to_read) {
 
                        cnt -= to_read;
                if (cnt > to_read) {
 
                        cnt -= to_read;
-               
-                       if (read ((*chan).playback_buf->buffer(), mixdown_buffer, gain_buffer,
-                                 start, cnt, *chan, n, reversed)) {
+
+                       if (read ((*chan)->playback_buf->buffer(), mixdown_buffer, gain_buffer, start, cnt, n, reversed)) {
                                error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
                                error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
-                                                _id, size, playback_sample) << endmsg;
+                                                       id(), size, playback_sample) << endmsg;
                                goto out;
                        }
                }
        }
 
        ret = 0;
                                goto out;
                        }
                }
        }
 
        ret = 0;
+
   out:
   out:
-       pending_overwrite = false;
-       delete [] gain_buffer;
-       delete [] mixdown_buffer;
-       return ret;
+       _pending_overwrite = false;
+       delete [] gain_buffer;
+       delete [] mixdown_buffer;
+       return ret;
 }
 
 int
 }
 
 int
-AudioDiskstream::seek (nframes_t frame, bool complete_refill)
+AudioDiskstream::seek (framepos_t frame, bool complete_refill)
 {
 {
-       Glib::Mutex::Lock lm (state_lock);
        uint32_t n;
        uint32_t n;
-       int ret;
+       int ret = -1;
        ChannelList::iterator chan;
        ChannelList::iterator chan;
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       Glib::Threads::Mutex::Lock lm (state_lock);
 
 
-       for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) {
-               (*chan).playback_buf->reset ();
-               (*chan).capture_buf->reset ();
+       for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+               (*chan)->playback_buf->reset ();
+               (*chan)->capture_buf->reset ();
        }
        }
-       
+
        /* can't rec-enable in destructive mode if transport is before start */
 
        if (destructive() && record_enabled() && frame < _session.current_start_frame()) {
        /* can't rec-enable in destructive mode if transport is before start */
 
        if (destructive() && record_enabled() && frame < _session.current_start_frame()) {
@@ -942,87 +905,99 @@ AudioDiskstream::seek (nframes_t frame, bool complete_refill)
 }
 
 int
 }
 
 int
-AudioDiskstream::can_internal_playback_seek (nframes_t distance)
+AudioDiskstream::can_internal_playback_seek (framecnt_t distance)
 {
        ChannelList::iterator chan;
 {
        ChannelList::iterator chan;
+       boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-       for (chan = channels.begin(); chan != channels.end(); ++chan) {
-               if ((*chan).playback_buf->read_space() < distance) {
+       for (chan = c->begin(); chan != c->end(); ++chan) {
+               if ((*chan)->playback_buf->read_space() < (size_t) distance) {
                        return false;
                        return false;
-               } 
+               }
        }
        return true;
 }
 
 int
        }
        return true;
 }
 
 int
-AudioDiskstream::internal_playback_seek (nframes_t distance)
+AudioDiskstream::internal_playback_seek (framecnt_t distance)
 {
        ChannelList::iterator chan;
 {
        ChannelList::iterator chan;
+       boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-       for (chan = channels.begin(); chan != channels.end(); ++chan) {
-               (*chan).playback_buf->increment_read_ptr (distance);
+       for (chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->playback_buf->increment_read_ptr (llabs(distance));
        }
 
        }
 
-       first_recordable_frame += distance;
+       if (first_recordable_frame < max_framepos) {
+               first_recordable_frame += distance;
+       }
        playback_sample += distance;
        playback_sample += distance;
-       
+
        return 0;
 }
 
        return 0;
 }
 
+/** 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
+ *         after the read.
+ *  @param cnt Count of samples to read.
+ *  @param reversed true if we are running backwards, otherwise false.
+ */
 int
 int
-AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, nframes_t& start, nframes_t cnt, 
-                 ChannelInfo& channel_info, int channel, bool reversed)
+AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
+                       framepos_t& start, framecnt_t cnt,
+                       int channel, bool reversed)
 {
 {
-       nframes_t this_read = 0;
+       framecnt_t this_read = 0;
        bool reloop = false;
        bool reloop = false;
-       nframes_t loop_end = 0;
-       nframes_t loop_start = 0;
-       nframes_t loop_length = 0;
-       nframes_t offset = 0;
+       framepos_t loop_end = 0;
+       framepos_t loop_start = 0;
+       framecnt_t offset = 0;
        Location *loc = 0;
 
        /* XXX we don't currently play loops in reverse. not sure why */
 
        if (!reversed) {
 
        Location *loc = 0;
 
        /* XXX we don't currently play loops in reverse. not sure why */
 
        if (!reversed) {
 
+               framecnt_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.
-                  
+
                   Note: Locations don't get deleted, so all we care about
                   when I say "atomic" is that we are always pointing to
                   the same one and using a start/length values obtained
                   just once.
                */
                   Note: Locations don't get deleted, so all we care about
                   when I say "atomic" is that we are always pointing to
                   the same one and using a start/length values obtained
                   just once.
                */
-               
+
                if ((loc = loop_location) != 0) {
                        loop_start = loc->start();
                        loop_end = loc->end();
                        loop_length = loop_end - loop_start;
                }
                if ((loc = loop_location) != 0) {
                        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
                   position within the loop.
                */
                /* if we are looping, ensure that the first frame we read is at the correct
                   position within the loop.
                */
-               
+
                if (loc && start >= loop_end) {
                if (loc && start >= loop_end) {
-                       //cerr << "start adjusted from " << start;
                        start = loop_start + ((start - loop_start) % loop_length);
                        start = loop_start + ((start - loop_start) % loop_length);
-                       //cerr << "to " << start << endl;
                }
                }
+       }
 
 
-               //cerr << "start is " << start << "  loopstart: " << loop_start << "  loopend: " << loop_end << endl;
+       if (reversed) {
+               start -= cnt;
        }
 
        }
 
+       /* We need this while loop in case we hit a loop boundary, in which case our read from
+          the playlist must be split into more than one section.
+       */
+
        while (cnt) {
 
        while (cnt) {
 
-               if (reversed) {
-                       start -= cnt;
-               }
-                       
                /* take any loop into account. we can't read past the end of the loop. */
 
                if (loc && (loop_end - start < cnt)) {
                        this_read = loop_end - start;
                /* take any loop into account. we can't read past the end of the loop. */
 
                if (loc && (loop_end - start < cnt)) {
                        this_read = loop_end - start;
-                       //cerr << "reloop true: thisread: " << this_read << "  cnt: " << cnt << endl;
                        reloop = true;
                } else {
                        reloop = false;
                        reloop = true;
                } else {
                        reloop = false;
@@ -1036,27 +1011,25 @@ AudioDiskstream::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(_("AudioDiskstream %1: cannot read %2 from playlist at frame %3"), _id, this_read, 
+                       error << string_compose(_("AudioDiskstream %1: cannot read %2 from playlist at frame %3"), id(), this_read,
                                         start) << endmsg;
                        return -1;
                }
 
                                         start) << endmsg;
                        return -1;
                }
 
-               _read_data_count = _playlist->read_data_count();
-               
                if (reversed) {
 
                        swap_by_ptr (buf, buf + this_read - 1);
                if (reversed) {
 
                        swap_by_ptr (buf, buf + this_read - 1);
-                       
+
                } else {
                } else {
-                       
+
                        /* if we read to the end of the loop, go back to the beginning */
                        /* if we read to the end of the loop, go back to the beginning */
-                       
+
                        if (reloop) {
                                start = loop_start;
                        } else {
                                start += this_read;
                        }
                        if (reloop) {
                                start = loop_start;
                        } else {
                                start += this_read;
                        }
-               } 
+               }
 
                cnt -= this_read;
                offset += this_read;
 
                cnt -= this_read;
                offset += this_read;
@@ -1066,31 +1039,39 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
 }
 
 int
 }
 
 int
-AudioDiskstream::do_refill_with_alloc()
+AudioDiskstream::do_refill_with_alloc ()
 {
        Sample* mix_buf  = new Sample[disk_io_chunk_frames];
        float*  gain_buf = new float[disk_io_chunk_frames];
 
        int ret = _do_refill(mix_buf, gain_buf);
 {
        Sample* mix_buf  = new Sample[disk_io_chunk_frames];
        float*  gain_buf = new float[disk_io_chunk_frames];
 
        int ret = _do_refill(mix_buf, gain_buf);
-       
+
        delete [] mix_buf;
        delete [] gain_buf;
 
        return ret;
 }
 
        delete [] mix_buf;
        delete [] gain_buf;
 
        return ret;
 }
 
+/** Get some more data from disk and put it in our channels' playback_bufs,
+ *  if there is suitable space in them.
+ */
 int
 AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 {
        int32_t ret = 0;
 int
 AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 {
        int32_t ret = 0;
-       nframes_t to_read;
+       framecnt_t to_read;
        RingBufferNPT<Sample>::rw_vector vector;
        RingBufferNPT<Sample>::rw_vector vector;
-       bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
-       nframes_t total_space;
-       nframes_t zero_fill;
+       bool const reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+       framecnt_t total_space;
+       framecnt_t zero_fill;
        uint32_t chan_n;
        ChannelList::iterator i;
        uint32_t chan_n;
        ChannelList::iterator i;
-       nframes_t ts;
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       framecnt_t ts;
+
+       if (c->empty()) {
+               return 0;
+       }
 
        assert(mixdown_buffer);
        assert(gain_buffer);
 
        assert(mixdown_buffer);
        assert(gain_buffer);
@@ -1100,10 +1081,10 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
        vector.buf[1] = 0;
        vector.len[1] = 0;
 
        vector.buf[1] = 0;
        vector.len[1] = 0;
 
-       channels.front().playback_buf->get_write_vector (&vector);
+       c->front()->playback_buf->get_write_vector (&vector);
 
 
-       
        if ((total_space = vector.len[0] + vector.len[1]) == 0) {
        if ((total_space = vector.len[0] + vector.len[1]) == 0) {
+               /* nowhere to write to */
                return 0;
        }
 
                return 0;
        }
 
@@ -1111,28 +1092,32 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
           this track, let the caller know so that it can arrange
           for us to be called again, ASAP.
        */
           this track, let the caller know so that it can arrange
           for us to be called again, ASAP.
        */
-       
-       if (total_space >= (_slaved?3:2) * disk_io_chunk_frames) {
+
+       if (total_space >= (_slaved ? 3 : 2) * disk_io_chunk_frames) {
                ret = 1;
        }
                ret = 1;
        }
-       
-       /* if we're running close to normal speed and there isn't enough 
-          space to do disk_io_chunk_frames of I/O, then don't bother.  
-          
+
+       /* if we're running close to normal speed and there isn't enough
+          space to do disk_io_chunk_frames 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_io_chunk_frames is smaller
+          than the playback buffer size, so this check should never trip when
+          the playback buffer is empty.
        */
        */
-       
+
        if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
                return 0;
        }
        if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
                return 0;
        }
-       
+
        /* when slaved, don't try to get too close to the read pointer. this
           leaves space for the buffer reversal to have something useful to
           work with.
        */
        /* when slaved, don't try to get too close to the read pointer. this
           leaves space for the buffer reversal to have something useful to
           work with.
        */
-       
-       if (_slaved && total_space < (channels.front().playback_buf->bufsize() / 2)) {
+
+       if (_slaved && total_space < (framecnt_t) (c->front()->playback_buf->bufsize() / 2)) {
                return 0;
        }
 
                return 0;
        }
 
@@ -1146,98 +1131,97 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                        /* at start: nothing to do but fill with silence */
 
 
                        /* at start: nothing to do but fill with silence */
 
-                       for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) {
-                                       
-                               ChannelInfo& chan (*i);
-                               chan.playback_buf->get_write_vector (&vector);
+                       for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
+
+                               ChannelInfo* chan (*i);
+                               chan->playback_buf->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.playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+                               chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
                        }
                        return 0;
                }
 
                if (file_frame < total_space) {
 
                        }
                        return 0;
                }
 
                if (file_frame < 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 - file_frame;
                        total_space = file_frame;
-                       file_frame = 0;
 
                } else {
 
                } else {
-                       
+
                        zero_fill = 0;
                }
 
        } else {
 
                        zero_fill = 0;
                }
 
        } else {
 
-               if (file_frame == max_frames) {
+               if (file_frame == max_framepos) {
 
                        /* at end: nothing to do but fill with silence */
 
                        /* at end: nothing to do but fill with silence */
-                       
-                       for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) {
-                                       
-                               ChannelInfo& chan (*i);
-                               chan.playback_buf->get_write_vector (&vector);
+
+                       for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
+
+                               ChannelInfo* chan (*i);
+                               chan->playback_buf->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.playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+                               chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
                        }
                        return 0;
                }
                        }
                        return 0;
                }
-               
-               if (file_frame > max_frames - total_space) {
+
+               if (file_frame > max_framepos - 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_frames - file_frame);
-                       total_space = max_frames - file_frame;
+                       zero_fill = total_space - (max_framepos - file_frame);
+                       total_space = max_framepos - file_frame;
 
                } else {
                        zero_fill = 0;
                }
        }
 
                } else {
                        zero_fill = 0;
                }
        }
-       
-       nframes_t file_frame_tmp = 0;
 
 
-       for (chan_n = 0, i = channels.begin(); i != channels.end(); ++i, ++chan_n) {
+       framepos_t file_frame_tmp = 0;
+
+       for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
 
-               ChannelInfo& chan (*i);
+               ChannelInfo* chan (*i);
                Sample* buf1;
                Sample* buf2;
                Sample* buf1;
                Sample* buf2;
-               nframes_t len1, len2;
+               framecnt_t len1, len2;
 
 
-               chan.playback_buf->get_write_vector (&vector);
+               chan->playback_buf->get_write_vector (&vector);
+
+               if ((framecnt_t) vector.len[0] > disk_io_chunk_frames) {
 
 
-               if (vector.len[0] > disk_io_chunk_frames) {
-                       
                        /* 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:
                        /* 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_io_chunk_frames that would be filled
                           .... => writable space
                           ++++ => readable space
                           ^^^^ => 1 x disk_io_chunk_frames that would be filled
-                          
+
                           |......|+++++++++++++|...............................|
                           buf1                buf0
                                                ^^^^^^^^^^^^^^^
                           |......|+++++++++++++|...............................|
                           buf1                buf0
                                                ^^^^^^^^^^^^^^^
-                          
-                          
-                          So, just pretend that the buf1 part isn't there.                                     
-                          
+
+
+                          So, just pretend that the buf1 part isn't there.
+
                        */
                        */
-               
+
                        vector.buf[1] = 0;
                        vector.len[1] = 0;
                        vector.buf[1] = 0;
                        vector.len[1] = 0;
-               
-               } 
+
+               }
 
                ts = total_space;
                file_frame_tmp = file_frame;
 
                ts = total_space;
                file_frame_tmp = file_frame;
@@ -1250,14 +1234,16 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                to_read = min (ts, len1);
                to_read = min (to_read, disk_io_chunk_frames);
 
                to_read = min (ts, len1);
                to_read = min (to_read, disk_io_chunk_frames);
 
+               assert (to_read >= 0);
+
                if (to_read) {
 
                if (to_read) {
 
-                       if (read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan, chan_n, reversed)) {
+                       if (read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
                                ret = -1;
                                goto out;
                        }
 
                                ret = -1;
                                goto out;
                        }
 
-                       chan.playback_buf->increment_write_ptr (to_read);
+                       chan->playback_buf->increment_write_ptr (to_read);
                        ts -= to_read;
                }
 
                        ts -= to_read;
                }
 
@@ -1269,26 +1255,27 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                           so read some or all of vector.len[1] as well.
                        */
 
                           so read some or all of vector.len[1] as well.
                        */
 
-                       if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan, chan_n, reversed)) {
+                       if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
                                ret = -1;
                                goto out;
                        }
                                ret = -1;
                                goto out;
                        }
-               
-                       chan.playback_buf->increment_write_ptr (to_read);
+
+                       chan->playback_buf->increment_write_ptr (to_read);
                }
 
                if (zero_fill) {
                }
 
                if (zero_fill) {
-                       /* do something */
+                       /* XXX: do something */
                }
 
        }
                }
 
        }
-       
+
        file_frame = file_frame_tmp;
        file_frame = file_frame_tmp;
+       assert (file_frame >= 0);
 
   out:
 
        return ret;
 
   out:
 
        return ret;
-}      
+}
 
 /** Flush pending data to disk.
  *
 
 /** Flush pending data to disk.
  *
@@ -1296,29 +1283,28 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
  * of data to disk. it will never write more than that.  If it writes that
  * much and there is more than that waiting to be written, it will return 1,
  * otherwise 0 on success or -1 on failure.
  * of data to disk. it will never write more than that.  If it writes that
  * much and there is more than that waiting to be written, it will return 1,
  * otherwise 0 on success or -1 on failure.
- * 
+ *
  * If there is less than disk_io_chunk_frames to be written, no data will be
  * written at all unless @a force_flush is true.
  */
 int
  * If there is less than disk_io_chunk_frames to be written, no data will be
  * written at all unless @a force_flush is true.
  */
 int
-AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
+AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
 {
        uint32_t to_write;
        int32_t ret = 0;
        RingBufferNPT<Sample>::rw_vector vector;
        RingBufferNPT<CaptureTransition>::rw_vector transvec;
 {
        uint32_t to_write;
        int32_t ret = 0;
        RingBufferNPT<Sample>::rw_vector vector;
        RingBufferNPT<CaptureTransition>::rw_vector transvec;
-       nframes_t total;
-
-       _write_data_count = 0;
+       framecnt_t total;
 
        transvec.buf[0] = 0;
        transvec.buf[1] = 0;
        vector.buf[0] = 0;
        vector.buf[1] = 0;
 
 
        transvec.buf[0] = 0;
        transvec.buf[1] = 0;
        vector.buf[0] = 0;
        vector.buf[1] = 0;
 
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-       
-               (*chan).capture_buf->get_read_vector (&vector);
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+               (*chan)->capture_buf->get_read_vector (&vector);
 
                total = vector.len[0] + vector.len[1];
 
 
                total = vector.len[0] + vector.len[1];
 
@@ -1329,7 +1315,7 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
                /* if there are 2+ chunks of disk i/o possible for
                   this track, let the caller know so that it can arrange
                   for us to be called again, ASAP.
                /* if there are 2+ chunks of disk i/o possible for
                   this track, let the caller know so that it can arrange
                   for us to be called again, ASAP.
-                  
+
                   if we are forcing a flush, then if there is* any* extra
                   work, let the caller know.
 
                   if we are forcing a flush, then if there is* any* extra
                   work, let the caller know.
 
@@ -1339,46 +1325,43 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
 
                if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
                        ret = 1;
 
                if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
                        ret = 1;
-               } 
+               }
+
+               to_write = min (disk_io_chunk_frames, (framecnt_t) vector.len[0]);
 
 
-               to_write = min (disk_io_chunk_frames, (nframes_t) vector.len[0]);
-               
                // check the transition buffer when recording destructive
                // important that we get this after the capture buf
 
                if (destructive()) {
                // check the transition buffer when recording destructive
                // important that we get this after the capture buf
 
                if (destructive()) {
-                       (*chan).capture_transition_buf->get_read_vector(&transvec);
+                       (*chan)->capture_transition_buf->get_read_vector(&transvec);
                        size_t transcount = transvec.len[0] + transvec.len[1];
                        size_t transcount = transvec.len[0] + transvec.len[1];
-                       bool have_start = false;
                        size_t ti;
 
                        for (ti=0; ti < transcount; ++ti) {
                                CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]];
                        size_t ti;
 
                        for (ti=0; ti < transcount; ++ti) {
                                CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]];
-                               
+
                                if (captrans.type == CaptureStart) {
                                        // by definition, the first data we got above represents the given capture pos
 
                                if (captrans.type == CaptureStart) {
                                        // by definition, the first data we got above represents the given capture pos
 
-                                       (*chan).write_source->mark_capture_start (captrans.capture_val);
-                                       (*chan).curr_capture_cnt = 0;
+                                       (*chan)->write_source->mark_capture_start (captrans.capture_val);
+                                       (*chan)->curr_capture_cnt = 0;
 
 
-                                       have_start = true;
-                               }
-                               else if (captrans.type == CaptureEnd) {
+                               } else if (captrans.type == CaptureEnd) {
 
                                        // capture end, the capture_val represents total frames in capture
 
 
                                        // capture end, the capture_val represents total frames in capture
 
-                                       if (captrans.capture_val <= (*chan).curr_capture_cnt + to_write) {
+                                       if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) {
 
                                                // shorten to make the write a perfect fit
 
                                                // shorten to make the write a perfect fit
-                                               uint32_t nto_write = (captrans.capture_val - (*chan).curr_capture_cnt); 
+                                               uint32_t nto_write = (captrans.capture_val - (*chan)->curr_capture_cnt);
 
                                                if (nto_write < to_write) {
                                                        ret = 1; // should we?
                                                }
                                                to_write = nto_write;
 
 
                                                if (nto_write < to_write) {
                                                        ret = 1; // should we?
                                                }
                                                to_write = nto_write;
 
-                                               (*chan).write_source->mark_capture_end ();
-                                               
+                                               (*chan)->write_source->mark_capture_end ();
+
                                                // increment past this transition, but go no further
                                                ++ti;
                                                break;
                                                // increment past this transition, but go no further
                                                ++ti;
                                                break;
@@ -1392,36 +1375,34 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
                        }
 
                        if (ti > 0) {
                        }
 
                        if (ti > 0) {
-                               (*chan).capture_transition_buf->increment_read_ptr(ti);
+                               (*chan)->capture_transition_buf->increment_read_ptr(ti);
                        }
                }
 
                        }
                }
 
-               if ((!(*chan).write_source) || (*chan).write_source->write (vector.buf[0], to_write) != to_write) {
-                       error << string_compose(_("AudioDiskstream %1: cannot write to disk"), _id) << endmsg;
+               if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) {
+                       error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
                        return -1;
                }
 
                        return -1;
                }
 
-               (*chan).capture_buf->increment_read_ptr (to_write);
-               (*chan).curr_capture_cnt += to_write;
-               
+               (*chan)->capture_buf->increment_read_ptr (to_write);
+               (*chan)->curr_capture_cnt += to_write;
+
                if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames) && !destructive()) {
                if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames) && !destructive()) {
-               
+
                        /* we wrote all of vector.len[0] but it wasn't an entire
                        /* we wrote all of vector.len[0] but it wasn't an entire
-                          disk_io_chunk_frames of data, so arrange for some part 
+                          disk_io_chunk_frames of data, so arrange for some part
                           of vector.len[1] to be flushed to disk as well.
                        */
                           of vector.len[1] to be flushed to disk as well.
                        */
-                       
-                       to_write = min ((nframes_t)(disk_io_chunk_frames - to_write), (nframes_t) vector.len[1]);
 
 
-                       if ((*chan).write_source->write (vector.buf[1], to_write) != to_write) {
-                               error << string_compose(_("AudioDiskstream %1: cannot write to disk"), _id) << endmsg;
+                       to_write = min ((framecnt_t)(disk_io_chunk_frames - to_write), (framecnt_t) vector.len[1]);
+
+                       if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
+                               error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
                                return -1;
                        }
 
                                return -1;
                        }
 
-                       _write_data_count += (*chan).write_source->write_data_count();
-       
-                       (*chan).capture_buf->increment_read_ptr (to_write);
-                       (*chan).curr_capture_cnt += to_write;
+                       (*chan)->capture_buf->increment_read_ptr (to_write);
+                       (*chan)->curr_capture_cnt += to_write;
                }
        }
 
                }
        }
 
@@ -1430,28 +1411,29 @@ AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
 }
 
 void
 }
 
 void
-AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_capture)
+AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abort_capture)
 {
        uint32_t buffer_position;
        bool more_work = true;
        int err = 0;
        boost::shared_ptr<AudioRegion> region;
 {
        uint32_t buffer_position;
        bool more_work = true;
        int err = 0;
        boost::shared_ptr<AudioRegion> region;
-       nframes_t total_capture;
+       framecnt_t total_capture;
        SourceList srcs;
        SourceList::iterator src;
        ChannelList::iterator chan;
        vector<CaptureInfo*>::iterator ci;
        SourceList srcs;
        SourceList::iterator src;
        ChannelList::iterator chan;
        vector<CaptureInfo*>::iterator ci;
-       uint32_t n = 0; 
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       uint32_t n = 0;
        bool mark_write_completed = false;
 
        bool mark_write_completed = false;
 
-       finish_capture (true);
+       finish_capture (c);
 
 
-       /* butler is already stopped, but there may be work to do 
+       /* butler is already stopped, but there may be work to do
           to flush remaining data to disk.
        */
 
        while (more_work && !err) {
           to flush remaining data to disk.
        */
 
        while (more_work && !err) {
-               switch (do_flush (Session::TransportContext, true)) {
+               switch (do_flush (TransportContext, true)) {
                case 0:
                        more_work = false;
                        break;
                case 0:
                        more_work = false;
                        break;
@@ -1464,32 +1446,32 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
        }
 
        /* XXX is there anything we can do if err != 0 ? */
        }
 
        /* XXX is there anything we can do if err != 0 ? */
-       Glib::Mutex::Lock lm (capture_info_lock);
-       
+       Glib::Threads::Mutex::Lock lm (capture_info_lock);
+
        if (capture_info.empty()) {
                return;
        }
 
        if (abort_capture) {
        if (capture_info.empty()) {
                return;
        }
 
        if (abort_capture) {
-               
+
                if (destructive()) {
                        goto outout;
                }
 
                if (destructive()) {
                        goto outout;
                }
 
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
 
 
-                       if ((*chan).write_source) {
-                               
-                               (*chan).write_source->mark_for_remove ();
-                               (*chan).write_source->drop_references ();
-                               (*chan).write_source.reset ();
+                       if ((*chan)->write_source) {
+
+                               (*chan)->write_source->mark_for_remove ();
+                               (*chan)->write_source->drop_references ();
+                               (*chan)->write_source.reset ();
                        }
                        }
-                       
+
                        /* new source set up in "out" below */
                }
 
                goto out;
                        /* new source set up in "out" below */
                }
 
                goto out;
-       } 
+       }
 
        for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
                total_capture += (*ci)->frames;
 
        for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
                total_capture += (*ci)->frames;
@@ -1497,15 +1479,19 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
 
        /* figure out the name for this take */
 
 
        /* figure out the name for this take */
 
-       for (n = 0, chan = channels.begin(); chan != channels.end(); ++chan, ++n) {
+       for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+               boost::shared_ptr<AudioFileSource> s = (*chan)->write_source;
 
 
-               boost::shared_ptr<AudioFileSource> s = (*chan).write_source;
-               
                if (s) {
                        srcs.push_back (s);
                        s->update_header (capture_info.front()->start, when, twhen);
                if (s) {
                        srcs.push_back (s);
                        s->update_header (capture_info.front()->start, when, twhen);
-                       s->set_captured_for (_name);
+                       s->set_captured_for (_name.val());
                        s->mark_immutable ();
                        s->mark_immutable ();
+
+                       if (Config->get_auto_analyse_audio()) {
+                               Analyser::queue_source_for_analysis (s, true);
+                       }
                }
        }
 
                }
        }
 
@@ -1513,79 +1499,91 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
 
        if (destructive()) {
 
 
        if (destructive()) {
 
-               /* send a signal that any UI can pick up to do the right thing. there is 
+               /* send a signal that any UI can pick up to do the right thing. there is
                   a small problem here in that a UI may need the peak data to be ready
                   for the data that was recorded and this isn't interlocked with that
                   process. this problem is deferred to the UI.
                 */
                   a small problem here in that a UI may need the peak data to be ready
                   for the data that was recorded and this isn't interlocked with that
                   process. this problem is deferred to the UI.
                 */
-               
-               _playlist->Modified();
+
+               _playlist->LayeringChanged(); // XXX this may not get the UI to do the right thing
 
        } else {
 
                string whole_file_region_name;
 
        } else {
 
                string whole_file_region_name;
-               whole_file_region_name = region_name_from_path (channels[0].write_source->name(), true);
+               whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true);
 
                /* Register a new region with the Session that
                   describes the entire source. Do this first
                   so that any sub-regions will obviously be
                   children of this one (later!)
                */
 
                /* Register a new region with the Session that
                   describes the entire source. Do this first
                   so that any sub-regions will obviously be
                   children of this one (later!)
                */
-               
+
                try {
                try {
-                       boost::shared_ptr<Region> rx (RegionFactory::create (srcs, channels[0].write_source->last_capture_start_frame(), total_capture, 
-                                                                            whole_file_region_name,
-                                                                            0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
+                       PropertyList plist;
+
+                       plist.add (Properties::start, c->front()->write_source->last_capture_start_frame());
+                       plist.add (Properties::length, total_capture);
+                       plist.add (Properties::name, whole_file_region_name);
+                       boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
+                       rx->set_automatic (true);
+                       rx->set_whole_file (true);
 
                        region = boost::dynamic_pointer_cast<AudioRegion> (rx);
                        region->special_set_position (capture_info.front()->start);
                }
 
                        region = boost::dynamic_pointer_cast<AudioRegion> (rx);
                        region->special_set_position (capture_info.front()->start);
                }
-               
-               
+
+
                catch (failed_constructor& err) {
                        error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg;
                        /* XXX what now? */
                }
                catch (failed_constructor& err) {
                        error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg;
                        /* XXX what now? */
                }
-               
-               _last_capture_regions.push_back (region);
+
+               _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
 
                // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
 
                // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
-               
-                XMLNode &before = _playlist->get_state();
+
+               _playlist->clear_changes ();
+               _playlist->set_capture_insertion_in_progress (true);
                _playlist->freeze ();
                _playlist->freeze ();
-               
-               for (buffer_position = channels[0].write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
-                       
+
+               for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+
                        string region_name;
 
                        string region_name;
 
-                       _session.region_name (region_name, whole_file_region_name, false);
-                       
-                       // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl;
-                       
+                       RegionFactory::region_name (region_name, whole_file_region_name, false);
+
+                       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
+                                                                             _name, (*ci)->start, (*ci)->frames, region_name));
+
                        try {
                        try {
-                               boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
+
+                               PropertyList plist;
+
+                               plist.add (Properties::start, buffer_position);
+                               plist.add (Properties::length, (*ci)->frames);
+                               plist.add (Properties::name, region_name);
+
+                               boost::shared_ptr<Region> rx (RegionFactory::create (srcs, plist));
                                region = boost::dynamic_pointer_cast<AudioRegion> (rx);
                        }
                                region = boost::dynamic_pointer_cast<AudioRegion> (rx);
                        }
-                       
+
                        catch (failed_constructor& err) {
                                error << _("AudioDiskstream: could not create region for captured audio!") << endmsg;
                                continue; /* XXX is this OK? */
                        }
                        catch (failed_constructor& err) {
                                error << _("AudioDiskstream: could not create region for captured audio!") << endmsg;
                                continue; /* XXX is this OK? */
                        }
-                       
-                       region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr<Region>(region)));
-                       
-                       _last_capture_regions.push_back (region);
-                       
+
                        i_am_the_modifier++;
                        i_am_the_modifier++;
-                       _playlist->add_region (region, (*ci)->start);
+
+                       _playlist->add_region (region, (*ci)->start, 1, non_layered());
+                       _playlist->set_layer (region, DBL_MAX);
                        i_am_the_modifier--;
                        i_am_the_modifier--;
-                       
+
                        buffer_position += (*ci)->frames;
                }
 
                _playlist->thaw ();
                        buffer_position += (*ci)->frames;
                }
 
                _playlist->thaw ();
-                XMLNode &after = _playlist->get_state();
-               _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
+               _playlist->set_capture_insertion_in_progress (false);
+               _session.add_command (new StatefulDiffCommand (_playlist));
        }
 
        mark_write_completed = true;
        }
 
        mark_write_completed = true;
@@ -1604,25 +1602,77 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
 }
 
 void
 }
 
 void
-AudioDiskstream::finish_capture (bool rec_monitors_input)
+AudioDiskstream::transport_looped (framepos_t transport_frame)
+{
+       if (was_recording) {
+               // all we need to do is finish this capture, with modified capture length
+               boost::shared_ptr<ChannelList> c = channels.reader();
+
+               // adjust the capture length knowing that the data will be recorded to disk
+               // only necessary after the first loop where we're recording
+               if (capture_info.size() == 0) {
+                       capture_captured += _capture_offset;
+
+                       if (_alignment_style == ExistingMaterial) {
+                               capture_captured += _session.worst_output_latency();
+                       } else {
+                               capture_captured += _roll_delay;
+                       }
+               }
+
+               finish_capture (c);
+
+               // the next region will start recording via the normal mechanism
+               // we'll set the start position to the current transport pos
+               // no latency adjustment or capture offset needs to be made, as that already happened the first time
+               capture_start_frame = transport_frame;
+               first_recordable_frame = transport_frame; // mild lie
+               last_recordable_frame = max_framepos;
+               was_recording = true;
+
+               if (recordable() && destructive()) {
+                       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+                               RingBufferNPT<CaptureTransition>::rw_vector transvec;
+                               (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+                               if (transvec.len[0] > 0) {
+                                       transvec.buf[0]->type = CaptureStart;
+                                       transvec.buf[0]->capture_val = capture_start_frame;
+                                       (*chan)->capture_transition_buf->increment_write_ptr(1);
+                               }
+                               else {
+                                       // bad!
+                                       fatal << X_("programming error: capture_transition_buf is full on rec loop!  inconceivable!")
+                                             << endmsg;
+                               }
+                       }
+               }
+
+       }
+}
+
+void
+AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
 {
        was_recording = false;
 {
        was_recording = false;
-       
+       first_recordable_frame = max_framepos;
+       last_recordable_frame = max_framepos;
+
        if (capture_captured == 0) {
                return;
        }
 
        if (recordable() && destructive()) {
        if (capture_captured == 0) {
                return;
        }
 
        if (recordable() && destructive()) {
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
                        RingBufferNPT<CaptureTransition>::rw_vector transvec;
                        RingBufferNPT<CaptureTransition>::rw_vector transvec;
-                       (*chan).capture_transition_buf->get_write_vector(&transvec);
-                       
-                       
+                       (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
                        if (transvec.len[0] > 0) {
                                transvec.buf[0]->type = CaptureEnd;
                                transvec.buf[0]->capture_val = capture_captured;
                        if (transvec.len[0] > 0) {
                                transvec.buf[0]->type = CaptureEnd;
                                transvec.buf[0]->capture_val = capture_captured;
-                               (*chan).capture_transition_buf->increment_write_ptr(1);
+                               (*chan)->capture_transition_buf->increment_write_ptr(1);
                        }
                        else {
                                // bad!
                        }
                        else {
                                // bad!
@@ -1630,15 +1680,15 @@ AudioDiskstream::finish_capture (bool rec_monitors_input)
                        }
                }
        }
                        }
                }
        }
-       
-       
+
+
        CaptureInfo* ci = new CaptureInfo;
        CaptureInfo* ci = new CaptureInfo;
-       
+
        ci->start =  capture_start_frame;
        ci->frames = capture_captured;
        ci->start =  capture_start_frame;
        ci->frames = capture_captured;
-       
-       /* XXX theoretical race condition here. Need atomic exchange ? 
-          However, the circumstances when this is called right 
+
+       /* XXX theoretical race condition here. Need atomic exchange ?
+          However, the circumstances when this is called right
           now (either on record-disable or transport_stopped)
           mean that no actual race exists. I think ...
           We now have a capture_info_lock, but it is only to be used
           now (either on record-disable or transport_stopped)
           mean that no actual race exists. I think ...
           We now have a capture_info_lock, but it is only to be used
@@ -1650,12 +1700,15 @@ AudioDiskstream::finish_capture (bool rec_monitors_input)
 
        capture_info.push_back (ci);
        capture_captured = 0;
 
        capture_info.push_back (ci);
        capture_captured = 0;
+
+       /* now we've finished a capture, reset first_recordable_frame for next time */
+       first_recordable_frame = max_framepos;
 }
 
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
 }
 
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
-       if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs() == 0) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
                return;
        }
 
                return;
        }
 
@@ -1665,15 +1718,6 @@ AudioDiskstream::set_record_enabled (bool yn)
                return;
        }
 
                return;
        }
 
-       if (yn && channels[0].source == 0) {
-
-               /* pick up connections not initiated *from* the IO object
-                  we're associated with.
-               */
-
-               get_input_sources ();
-       }
-
        /* yes, i know that this not proof against race conditions, but its
           good enough. i think.
        */
        /* yes, i know that this not proof against race conditions, but its
           good enough. i think.
        */
@@ -1684,70 +1728,71 @@ AudioDiskstream::set_record_enabled (bool yn)
                } else {
                        disengage_record_enable ();
                }
                } else {
                        disengage_record_enable ();
                }
+
+               RecordEnableChanged (); /* EMIT SIGNAL */
        }
 }
 
        }
 }
 
-void
-AudioDiskstream::engage_record_enable ()
+bool
+AudioDiskstream::prep_record_enable ()
 {
 {
-    bool rolling = _session.transport_speed() != 0.0f;
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+               return false;
+       }
+
+       /* can't rec-enable in destructive mode if transport is before start */
+
+       if (destructive() && _session.transport_frame() < _session.current_start_frame()) {
+               return false;
+       }
+
+       bool rolling = _session.transport_speed() != 0.0f;
+       boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-       g_atomic_int_set (&_record_enabled, 1);
        capturing_sources.clear ();
 
        if (Config->get_monitoring_model() == HardwareMonitoring) {
 
        capturing_sources.clear ();
 
        if (Config->get_monitoring_model() == HardwareMonitoring) {
 
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       if ((*chan).source) {
-                               (*chan).source->ensure_monitor_input (!(Config->get_auto_input() && rolling));
-                       }
-                       capturing_sources.push_back ((*chan).write_source);
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       (*chan)->source.request_input_monitoring (!(_session.config.get_auto_input() && rolling));
+                       capturing_sources.push_back ((*chan)->write_source);
+                       (*chan)->write_source->mark_streaming_write_started ();
                }
                }
-               
+
        } else {
        } else {
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       capturing_sources.push_back ((*chan).write_source);
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       capturing_sources.push_back ((*chan)->write_source);
+                       (*chan)->write_source->mark_streaming_write_started ();
                }
        }
 
                }
        }
 
-       RecordEnableChanged (); /* EMIT SIGNAL */
+       return true;
 }
 
 }
 
-void
-AudioDiskstream::disengage_record_enable ()
+bool
+AudioDiskstream::prep_record_disable ()
 {
 {
-       g_atomic_int_set (&_record_enabled, 0);
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-               if (Config->get_monitoring_model() == HardwareMonitoring) {
-                       if ((*chan).source) {
-                               (*chan).source->ensure_monitor_input (false);
-                       }
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       if (Config->get_monitoring_model() == HardwareMonitoring) {
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       (*chan)->source.request_input_monitoring (false);
                }
        }
        capturing_sources.clear ();
                }
        }
        capturing_sources.clear ();
-       RecordEnableChanged (); /* EMIT SIGNAL */
+
+       return true;
 }
 
 XMLNode&
 AudioDiskstream::get_state ()
 {
 }
 
 XMLNode&
 AudioDiskstream::get_state ()
 {
-       XMLNode* node = new XMLNode ("AudioDiskstream");
+       XMLNode& node (Diskstream::get_state());
        char buf[64] = "";
        LocaleGuard lg (X_("POSIX"));
 
        char buf[64] = "";
        LocaleGuard lg (X_("POSIX"));
 
-       node->add_property ("flags", enum_2_string (_flags));
-
-       snprintf (buf, sizeof(buf), "%zd", channels.size());
-       node->add_property ("channels", buf);
-
-       node->add_property ("playlist", _playlist->name());
-       
-       snprintf (buf, sizeof(buf), "%.12g", _visible_speed);
-       node->add_property ("speed", buf);
-
-       node->add_property("name", _name);
-       id().print (buf, sizeof (buf));
-       node->add_property("id", buf);
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       snprintf (buf, sizeof(buf), "%zd", c->size());
+       node.add_property ("channels", buf);
 
        if (!capturing_sources.empty() && _session.get_record_enabled()) {
 
 
        if (!capturing_sources.empty() && _session.get_record_enabled()) {
 
@@ -1764,25 +1809,21 @@ AudioDiskstream::get_state ()
 
                Location* pi;
 
 
                Location* pi;
 
-               if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
-                       snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
+               if (_session.config.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
+                       snprintf (buf, sizeof (buf), "%" PRId64, pi->start());
                } else {
                } else {
-                       snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
+                       snprintf (buf, sizeof (buf), "%" PRId64, _session.transport_frame());
                }
 
                cs_child->add_property (X_("at"), buf);
                }
 
                cs_child->add_property (X_("at"), buf);
-               node->add_child_nocopy (*cs_child);
-       }
-
-       if (_extra_xml) {
-               node->add_child_copy (*_extra_xml);
+               node.add_child_nocopy (*cs_child);
        }
 
        }
 
-       return* node;
+       return node;
 }
 
 int
 }
 
 int
-AudioDiskstream::set_state (const XMLNode& node)
+AudioDiskstream::set_state (const XMLNode& node, int version)
 {
        const XMLProperty* prop;
        XMLNodeList nlist = node.children();
 {
        const XMLProperty* prop;
        XMLNodeList nlist = node.children();
@@ -1791,116 +1832,62 @@ AudioDiskstream::set_state (const XMLNode& node)
        XMLNode* capture_pending_node = 0;
        LocaleGuard lg (X_("POSIX"));
 
        XMLNode* capture_pending_node = 0;
        LocaleGuard lg (X_("POSIX"));
 
+       /* prevent write sources from being created */
+
        in_set_state = true;
 
        in_set_state = true;
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               if ((*niter)->name() == IO::state_node_name) {
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+               if ((*niter)->name() == IO::state_node_name) {
                        deprecated_io_node = new XMLNode (**niter);
                        deprecated_io_node = new XMLNode (**niter);
-               }
+               }
 
                if ((*niter)->name() == X_("CapturingSources")) {
                        capture_pending_node = *niter;
                }
 
                if ((*niter)->name() == X_("CapturingSources")) {
                        capture_pending_node = *niter;
                }
-       }
-
-       /* prevent write sources from being created */
-       
-       in_set_state = true;
-       
-       if ((prop = node.property ("name")) != 0) {
-               _name = prop->value();
-       } 
-
-       if (deprecated_io_node) {
-               if ((prop = deprecated_io_node->property ("id")) != 0) {
-                       _id = prop->value ();
-               }
-       } else {
-               if ((prop = node.property ("id")) != 0) {
-                       _id = prop->value ();
-               }
        }
 
        }
 
-       if ((prop = node.property ("flags")) != 0) {
-               _flags = Flag (string_2_enum (prop->value(), _flags));
+       if (Diskstream::set_state (node, version)) {
+               return -1;
        }
 
        if ((prop = node.property ("channels")) != 0) {
                nchans = atoi (prop->value().c_str());
        }
        }
 
        if ((prop = node.property ("channels")) != 0) {
                nchans = atoi (prop->value().c_str());
        }
-       
+
        // create necessary extra channels
        // we are always constructed with one and we always need one
 
        // create necessary extra channels
        // we are always constructed with one and we always need one
 
-       if (nchans > _n_channels) {
-
-               // we need to add new channel infos
-               //LockMonitor lm (state_lock, __LINE__, __FILE__);
+       _n_channels.set(DataType::AUDIO, channels.reader()->size());
 
 
-               int diff = nchans - channels.size();
+       if (nchans > _n_channels.n_audio()) {
 
 
-               for (int i=0; i < diff; ++i) {
-                       add_channel ();
-               }
-
-       } else if (nchans < _n_channels) {
+               add_channel (nchans - _n_channels.n_audio());
+               IO::PortCountChanged(_n_channels);
 
 
-               // we need to get rid of channels
-               //LockMonitor lm (state_lock, __LINE__, __FILE__);
-
-               int diff = channels.size() - nchans;
-               
-               for (int i = 0; i < diff; ++i) {
-                       remove_channel ();
-               }
-       }
+       } else if (nchans < _n_channels.n_audio()) {
 
 
-       if ((prop = node.property ("playlist")) == 0) {
-               return -1;
+               remove_channel (_n_channels.n_audio() - nchans);
        }
 
        }
 
-       {
-               bool had_playlist = (_playlist != 0);
-       
-               if (find_and_use_playlist (prop->value())) {
-                       return -1;
-               }
-
-               if (!had_playlist) {
-                       _playlist->set_orig_diskstream_id (_id);
-               }
-               
-               if (!destructive() && capture_pending_node) {
-                       /* destructive streams have one and only one source per channel,
-                          and so they never end up in pending capture in any useful
-                          sense.
-                       */
-                       use_pending_capture_data (*capture_pending_node);
-               }
-
-       }
 
 
-       if ((prop = node.property ("speed")) != 0) {
-               double sp = atof (prop->value().c_str());
 
 
-               if (realtime_set_speed (sp, false)) {
-                       non_realtime_set_speed ();
-               }
+       if (!destructive() && capture_pending_node) {
+               /* destructive streams have one and only one source per channel,
+                  and so they never end up in pending capture in any useful
+                  sense.
+               */
+               use_pending_capture_data (*capture_pending_node);
        }
 
        }
 
-       _n_channels = channels.size();
-
        in_set_state = false;
 
        /* make sure this is clear before we do anything else */
 
        capturing_sources.clear ();
 
        in_set_state = false;
 
        /* make sure this is clear before we do anything else */
 
        capturing_sources.clear ();
 
-       /* write sources are handled when we handle the input set 
+       /* write sources are handled when we handle the input set
           up of the IO that owns this DS (::non_realtime_input_change())
        */
           up of the IO that owns this DS (::non_realtime_input_change())
        */
-               
-       in_set_state = false;
 
        return 0;
 }
 
        return 0;
 }
@@ -1908,74 +1895,94 @@ AudioDiskstream::set_state (const XMLNode& node)
 int
 AudioDiskstream::use_new_write_source (uint32_t n)
 {
 int
 AudioDiskstream::use_new_write_source (uint32_t n)
 {
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
        if (!recordable()) {
                return 1;
        }
 
        if (!recordable()) {
                return 1;
        }
 
-       if (n >= channels.size()) {
+       if (n >= c->size()) {
                error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg;
                return -1;
        }
 
                error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg;
                return -1;
        }
 
-       ChannelInfo &chan = channels[n];
-       
-       if (chan.write_source) {
-               chan.write_source->done_with_peakfile_writes ();
-               chan.write_source->set_allow_remove_if_empty (true);
-               chan.write_source.reset ();
-       }
+       ChannelInfo* chan = (*c)[n];
 
        try {
 
        try {
-               if ((chan.write_source = _session.create_audio_source_for_session (*this, n, destructive())) == 0) {
+               if ((chan->write_source = _session.create_audio_source_for_session (
+                            n_channels().n_audio(), name(), n, destructive())) == 0) {
                        throw failed_constructor();
                }
                        throw failed_constructor();
                }
-       } 
+       }
 
        catch (failed_constructor &err) {
                error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
 
        catch (failed_constructor &err) {
                error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
-               chan.write_source.reset ();
+               chan->write_source.reset ();
                return -1;
        }
 
        /* do not remove destructive files even if they are empty */
 
                return -1;
        }
 
        /* do not remove destructive files even if they are empty */
 
-       chan.write_source->set_allow_remove_if_empty (!destructive());
+       chan->write_source->set_allow_remove_if_empty (!destructive());
 
        return 0;
 }
 
 
        return 0;
 }
 
+list<boost::shared_ptr<Source> >
+AudioDiskstream::steal_write_sources()
+{
+       /* not possible to steal audio write sources */
+       list<boost::shared_ptr<Source> > ret;
+       return ret;
+}
+
 void
 void
-AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force)
+AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
 {
        ChannelList::iterator chan;
 {
        ChannelList::iterator chan;
+       boost::shared_ptr<ChannelList> c = channels.reader();
        uint32_t n;
 
        uint32_t n;
 
-       if (!recordable()) {
+       if (!_session.writable() || !recordable()) {
                return;
        }
                return;
        }
-       
+
        capturing_sources.clear ();
 
        capturing_sources.clear ();
 
-       for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) {
+       for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) {
+
                if (!destructive()) {
 
                if (!destructive()) {
 
-                       if ((*chan).write_source && mark_write_complete) {
-                               (*chan).write_source->mark_streaming_write_completed ();
+                       if ((*chan)->write_source) {
+
+                               if (mark_write_complete) {
+                                       (*chan)->write_source->mark_streaming_write_completed ();
+                                       (*chan)->write_source->done_with_peakfile_writes ();
+                               }
+
+                               if ((*chan)->write_source->removable()) {
+                                       (*chan)->write_source->mark_for_remove ();
+                                       (*chan)->write_source->drop_references ();
+                               }
+
+                               (*chan)->write_source.reset ();
                        }
                        }
+
                        use_new_write_source (n);
 
                        if (record_enabled()) {
                        use_new_write_source (n);
 
                        if (record_enabled()) {
-                               capturing_sources.push_back ((*chan).write_source);
+                               capturing_sources.push_back ((*chan)->write_source);
                        }
 
                } else {
                        }
 
                } else {
-                       if ((*chan).write_source == 0) {
+
+                       if ((*chan)->write_source == 0) {
                                use_new_write_source (n);
                        }
                }
        }
 
                                use_new_write_source (n);
                        }
                }
        }
 
-       if (destructive()) {
+       if (destructive() && !c->empty ()) {
 
                /* we now have all our write sources set up, so create the
                   playlist's single region.
 
                /* we now have all our write sources set up, so create the
                   playlist's single region.
@@ -1987,31 +1994,17 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force)
        }
 }
 
        }
 }
 
-int
-AudioDiskstream::rename_write_sources ()
-{
-       ChannelList::iterator chan;
-       uint32_t n;
-
-       for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) {
-               if ((*chan).write_source != 0) {
-                       (*chan).write_source->set_name (_name, destructive());
-                       /* XXX what to do if one of them fails ? */
-               }
-       }
-
-       return 0;
-}
-
 void
 void
-AudioDiskstream::set_block_size (nframes_t nframes)
+AudioDiskstream::set_block_size (pframes_t /*nframes*/)
 {
        if (_session.get_block_size() > speed_buffer_size) {
                speed_buffer_size = _session.get_block_size();
 {
        if (_session.get_block_size() > speed_buffer_size) {
                speed_buffer_size = _session.get_block_size();
+               boost::shared_ptr<ChannelList> c = channels.reader();
 
 
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       if ((*chan).speed_buffer) delete [] (*chan).speed_buffer;
-                       (*chan).speed_buffer = new Sample[speed_buffer_size];
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       if ((*chan)->speed_buffer)
+                               delete [] (*chan)->speed_buffer;
+                       (*chan)->speed_buffer = new Sample[speed_buffer_size];
                }
        }
        allocate_temporary_buffers ();
                }
        }
        allocate_temporary_buffers ();
@@ -2022,19 +2015,25 @@ AudioDiskstream::allocate_temporary_buffers ()
 {
        /* make sure the wrap buffer is at least large enough to deal
           with the speeds up to 1.2, to allow for micro-variation
 {
        /* make sure the wrap buffer is at least large enough to deal
           with the speeds up to 1.2, to allow for micro-variation
-          when slaving to MTC, SMPTE etc.
+          when slaving to MTC, Timecode etc.
        */
 
        */
 
-       double sp = max (fabsf (_actual_speed), 1.2f);
-       nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * sp) + 1;
+       double const sp = max (fabsf (_actual_speed), 1.2f);
+       framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
 
        if (required_wrap_size > wrap_buffer_size) {
 
 
        if (required_wrap_size > wrap_buffer_size) {
 
-               for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-                       if ((*chan).playback_wrap_buffer) delete [] (*chan).playback_wrap_buffer;
-                       (*chan).playback_wrap_buffer = new Sample[required_wrap_size];  
-                       if ((*chan).capture_wrap_buffer) delete [] (*chan).capture_wrap_buffer;
-                       (*chan).capture_wrap_buffer = new Sample[required_wrap_size];   
+               boost::shared_ptr<ChannelList> c = channels.reader();
+
+               for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+                       if ((*chan)->playback_wrap_buffer) {
+                               delete [] (*chan)->playback_wrap_buffer;
+                       }
+                       (*chan)->playback_wrap_buffer = new Sample[required_wrap_size];
+                       if ((*chan)->capture_wrap_buffer) {
+                               delete [] (*chan)->capture_wrap_buffer;
+                       }
+                       (*chan)->capture_wrap_buffer = new Sample[required_wrap_size];
                }
 
                wrap_buffer_size = required_wrap_size;
                }
 
                wrap_buffer_size = required_wrap_size;
@@ -2042,13 +2041,12 @@ AudioDiskstream::allocate_temporary_buffers ()
 }
 
 void
 }
 
 void
-AudioDiskstream::monitor_input (bool yn)
+AudioDiskstream::request_input_monitoring (bool yn)
 {
 {
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-               
-               if ((*chan).source) {
-                       (*chan).source->ensure_monitor_input (yn);
-               }
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->source.request_input_monitoring (yn);
        }
 }
 
        }
 }
 
@@ -2057,14 +2055,20 @@ AudioDiskstream::set_align_style_from_io ()
 {
        bool have_physical = false;
 
 {
        bool have_physical = false;
 
+       if (_alignment_choice != Automatic) {
+               return;
+       }
+
        if (_io == 0) {
                return;
        }
 
        get_input_sources ();
        if (_io == 0) {
                return;
        }
 
        get_input_sources ();
-       
-       for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
-               if ((*chan).source && (*chan).source->flags() & JackPortIsPhysical) {
+
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               if ((*chan)->source.is_physical ()) {
                        have_physical = true;
                        break;
                }
                        have_physical = true;
                        break;
                }
@@ -2078,55 +2082,79 @@ AudioDiskstream::set_align_style_from_io ()
 }
 
 int
 }
 
 int
-AudioDiskstream::add_channel ()
+AudioDiskstream::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
 {
 {
-       /* XXX need to take lock??? */
+       while (how_many--) {
+               c->push_back (new ChannelInfo(
+                                     _session.butler()->audio_diskstream_playback_buffer_size(),
+                                     _session.butler()->audio_diskstream_capture_buffer_size(),
+                                     speed_buffer_size, wrap_buffer_size));
+               interpolation.add_channel_to (
+                       _session.butler()->audio_diskstream_playback_buffer_size(),
+                       speed_buffer_size);
+       }
 
 
-       /* this copies the ChannelInfo, which currently has no buffers. kind
-          of pointless really, but we want the channels list to contain
-          actual objects, not pointers to objects. mostly for convenience,
-          which isn't much in evidence.
-       */
+       _n_channels.set(DataType::AUDIO, c->size());
 
 
-       channels.push_back (ChannelInfo());
+       return 0;
+}
+
+int
+AudioDiskstream::add_channel (uint32_t how_many)
+{
+       RCUWriter<ChannelList> writer (channels);
+       boost::shared_ptr<ChannelList> c = writer.get_copy();
 
 
-       /* now allocate the buffers */
+       return add_channel_to (c, how_many);
+}
 
 
-       channels.back().init (_session.diskstream_buffer_size(), 
-                             speed_buffer_size,
-                             wrap_buffer_size);
+int
+AudioDiskstream::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+       while (how_many-- && !c->empty()) {
+               delete c->back();
+               c->pop_back();
+               interpolation.remove_channel_from ();
+       }
 
 
-       _n_channels = channels.size();
+       _n_channels.set(DataType::AUDIO, c->size());
 
        return 0;
 }
 
 int
 
        return 0;
 }
 
 int
-AudioDiskstream::remove_channel ()
+AudioDiskstream::remove_channel (uint32_t how_many)
 {
 {
-       if (channels.size() > 1) {
-               /* XXX need to take lock??? */
-               channels.back().release ();
-               channels.pop_back();
-               _n_channels = channels.size();
-               return 0;
-       }
+       RCUWriter<ChannelList> writer (channels);
+       boost::shared_ptr<ChannelList> c = writer.get_copy();
 
 
-       return -1;
+       return remove_channel_from (c, how_many);
 }
 
 float
 AudioDiskstream::playback_buffer_load () const
 {
 }
 
 float
 AudioDiskstream::playback_buffer_load () const
 {
-       return (float) ((double) channels.front().playback_buf->read_space()/
-                       (double) channels.front().playback_buf->bufsize());
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       if (c->empty ()) {
+               return 0;
+       }
+
+       return (float) ((double) c->front()->playback_buf->read_space()/
+                       (double) c->front()->playback_buf->bufsize());
 }
 
 float
 AudioDiskstream::capture_buffer_load () const
 {
 }
 
 float
 AudioDiskstream::capture_buffer_load () const
 {
-       return (float) ((double) channels.front().capture_buf->write_space()/
-                       (double) channels.front().capture_buf->bufsize());
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       if (c->empty ()) {
+               return 0;
+       }
+
+       return (float) ((double) c->front()->capture_buf->write_space()/
+                       (double) c->front()->capture_buf->bufsize());
 }
 
 int
 }
 
 int
@@ -2138,13 +2166,13 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
        boost::shared_ptr<AudioFileSource> fs;
        boost::shared_ptr<AudioFileSource> first_fs;
        SourceList pending_sources;
        boost::shared_ptr<AudioFileSource> fs;
        boost::shared_ptr<AudioFileSource> first_fs;
        SourceList pending_sources;
-       nframes_t position;
+       framepos_t position;
 
        if ((prop = node.property (X_("at"))) == 0) {
                return -1;
        }
 
 
        if ((prop = node.property (X_("at"))) == 0) {
                return -1;
        }
 
-       if (sscanf (prop->value().c_str(), "%" PRIu32, &position) != 1) {
+       if (sscanf (prop->value().c_str(), "%" PRIu64, &position) != 1) {
                return -1;
        }
 
                return -1;
        }
 
@@ -2155,8 +2183,17 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                                continue;
                        }
 
                                continue;
                        }
 
+                       // This protects sessions from errant CapturingSources in stored sessions
+                       struct stat sbuf;
+                       if (stat (prop->value().c_str(), &sbuf)) {
+                               continue;
+                       }
+
                        try {
                        try {
-                               fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (_session, prop->value(), false, _session.frame_rate()));
+                               fs = boost::dynamic_pointer_cast<AudioFileSource> (
+                                       SourceFactory::createWritable (
+                                               DataType::AUDIO, _session,
+                                               prop->value(), false, _session.frame_rate()));
                        }
 
                        catch (failed_constructor& err) {
                        }
 
                        catch (failed_constructor& err) {
@@ -2167,12 +2204,12 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                        }
 
                        pending_sources.push_back (fs);
                        }
 
                        pending_sources.push_back (fs);
-                       
+
                        if (first_fs == 0) {
                                first_fs = fs;
                        }
 
                        if (first_fs == 0) {
                                first_fs = fs;
                        }
 
-                       fs->set_captured_for (_name);
+                       fs->set_captured_for (_name.val());
                }
        }
 
                }
        }
 
@@ -2181,38 +2218,34 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return 1;
        }
 
                return 1;
        }
 
-       if (pending_sources.size() != _n_channels) {
+       if (pending_sources.size() != _n_channels.n_audio()) {
                error << string_compose (_("%1: incorrect number of pending sources listed - ignoring them all"), _name)
                      << endmsg;
                return -1;
        }
 
        boost::shared_ptr<AudioRegion> region;
                error << string_compose (_("%1: incorrect number of pending sources listed - ignoring them all"), _name)
                      << endmsg;
                return -1;
        }
 
        boost::shared_ptr<AudioRegion> region;
-       
+
        try {
        try {
-               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(),
-                                                                                         region_name_from_path (first_fs->name(), true), 
-                                                                                         0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
-               region->special_set_position (0);
-       }
 
 
-       catch (failed_constructor& err) {
-               error << string_compose (_("%1: cannot create whole-file region from pending capture sources"),
-                                 _name)
-                     << endmsg;
-               
-               return -1;
-       }
+               PropertyList plist;
 
 
-       try {
-               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true)));
+               plist.add (Properties::start, 0);
+               plist.add (Properties::length, first_fs->length (first_fs->timeline_position()));
+               plist.add (Properties::name, region_name_from_path (first_fs->name(), true));
+
+               region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+               region->set_automatic (true);
+               region->set_whole_file (true);
+               region->special_set_position (0);
        }
 
        catch (failed_constructor& err) {
        }
 
        catch (failed_constructor& err) {
-               error << string_compose (_("%1: cannot create region from pending capture sources"),
-                                 _name)
-                     << endmsg;
-               
+               error << string_compose (
+                               _("%1: cannot create whole-file region from pending capture sources"),
+                               _name) << endmsg;
+
                return -1;
        }
 
                return -1;
        }
 
@@ -2222,15 +2255,29 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
 }
 
 int
 }
 
 int
-AudioDiskstream::set_destructive (bool yn)
+AudioDiskstream::set_non_layered (bool yn)
 {
 {
-       bool bounce_ignored;
+       if (yn != non_layered()) {
+
+               if (yn) {
+                       _flags = Flag (_flags | NonLayered);
+               } else {
+                       _flags = Flag (_flags & ~NonLayered);
+               }
+       }
+
+       return 0;
+}
 
 
+int
+AudioDiskstream::set_destructive (bool yn)
+{
        if (yn != destructive()) {
        if (yn != destructive()) {
-               
+
                if (yn) {
                if (yn) {
+                       bool bounce_ignored;
                        /* requestor should already have checked this and
                        /* requestor should already have checked this and
-                          bounced if necessary and desired 
+                          bounced if necessary and desired
                        */
                        if (!can_become_destructive (bounce_ignored)) {
                                return -1;
                        */
                        if (!can_become_destructive (bounce_ignored)) {
                                return -1;
@@ -2262,10 +2309,13 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
        }
 
        boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
        }
 
        boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
-       assert (first);
+       if (!first) {
+               requires_bounce = false;
+               return true;
+       }
 
        /* do the source(s) for the region cover the session start position ? */
 
        /* do the source(s) for the region cover the session start position ? */
-       
+
        if (first->position() != _session.current_start_frame()) {
                if (first->start() > _session.current_start_frame()) {
                        requires_bounce = true;
        if (first->position() != _session.current_start_frame()) {
                if (first->start() > _session.current_start_frame()) {
                        requires_bounce = true;
@@ -2279,8 +2329,8 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
 
        assert (afirst);
 
 
        assert (afirst);
 
-       if (afirst->source()->used() > 1) {
-               requires_bounce = true; 
+       if (_session.playlists->source_use_count (afirst->source()) > 1) {
+               requires_bounce = true;
                return false;
        }
 
                return false;
        }
 
@@ -2288,36 +2338,64 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
        return true;
 }
 
        return true;
 }
 
-AudioDiskstream::ChannelInfo::ChannelInfo ()
+void
+AudioDiskstream::adjust_playback_buffering ()
 {
 {
-       playback_wrap_buffer = 0;
-       capture_wrap_buffer = 0;
-       speed_buffer = 0;
-       peak_power = 0.0f;
-       source = 0;
-       current_capture_buffer = 0;
-       current_playback_buffer = 0;
-       curr_capture_cnt = 0;
-       playback_buf = 0;
-       capture_buf = 0;
-       capture_transition_buf = 0;
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->resize_playback (_session.butler()->audio_diskstream_playback_buffer_size());
+       }
+}
+
+void
+AudioDiskstream::adjust_capture_buffering ()
+{
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->resize_capture (_session.butler()->audio_diskstream_capture_buffer_size());
+       }
+}
+
+bool
+AudioDiskstream::ChannelSource::is_physical () const
+{
+       if (name.empty()) {
+               return false;
+       }
+
+       return AudioEngine::instance()->port_is_physical (name);
 }
 
 void
 }
 
 void
-AudioDiskstream::ChannelInfo::init (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size)
+AudioDiskstream::ChannelSource::request_input_monitoring (bool yn) const
 {
 {
+       if (name.empty()) {
+               return;
+       }
+
+       return AudioEngine::instance()->request_input_monitoring (name, yn);
+}
+
+AudioDiskstream::ChannelInfo::ChannelInfo (framecnt_t playback_bufsize, framecnt_t capture_bufsize, framecnt_t speed_size, framecnt_t wrap_size)
+{
+       current_capture_buffer = 0;
+       current_playback_buffer = 0;
+       curr_capture_cnt = 0;
+
        speed_buffer = new Sample[speed_size];
        playback_wrap_buffer = new Sample[wrap_size];
        capture_wrap_buffer = new Sample[wrap_size];
 
        speed_buffer = new Sample[speed_size];
        playback_wrap_buffer = new Sample[wrap_size];
        capture_wrap_buffer = new Sample[wrap_size];
 
-       playback_buf = new RingBufferNPT<Sample> (bufsize);
-       capture_buf = new RingBufferNPT<Sample> (bufsize);
-       capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
-       
+       playback_buf = new RingBufferNPT<Sample> (playback_bufsize);
+       capture_buf = new RingBufferNPT<Sample> (capture_bufsize);
+       capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+
        /* touch the ringbuffer buffers, which will cause
           them to be mapped into locked physical RAM if
           we're running with mlockall(). this doesn't do
        /* touch the ringbuffer buffers, which will cause
           them to be mapped into locked physical RAM if
           we're running with mlockall(). this doesn't do
-          much if we're not.  
+          much if we're not.
        */
 
        memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
        */
 
        memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
@@ -2325,44 +2403,61 @@ AudioDiskstream::ChannelInfo::init (nframes_t bufsize, nframes_t speed_size, nfr
        memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
 }
 
        memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
 }
 
-AudioDiskstream::ChannelInfo::~ChannelInfo ()
+void
+AudioDiskstream::ChannelInfo::resize_playback (framecnt_t playback_bufsize)
 {
 {
+       delete playback_buf;
+       playback_buf = new RingBufferNPT<Sample> (playback_bufsize);
+       memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
 }
 
 void
 }
 
 void
-AudioDiskstream::ChannelInfo::release ()
+AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
 {
 {
-       if (write_source) {
-               write_source.reset ();
-       }
-               
-       if (speed_buffer) {
-               delete [] speed_buffer;
-               speed_buffer = 0;
-       }
+       delete capture_buf;
 
 
-       if (playback_wrap_buffer) {
-               delete [] playback_wrap_buffer;
-               playback_wrap_buffer = 0;
-       }
+       capture_buf = new RingBufferNPT<Sample> (capture_bufsize);
+       memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
+}
 
 
-       if (capture_wrap_buffer) {
-               delete [] capture_wrap_buffer;
-               capture_wrap_buffer = 0;
-       }
-       
-       if (playback_buf) {
-               delete playback_buf;
-               playback_buf = 0;
-       }
+AudioDiskstream::ChannelInfo::~ChannelInfo ()
+{
+       write_source.reset ();
 
 
-       if (capture_buf) {
-               delete capture_buf;
-               capture_buf = 0;
-       }
+       delete [] speed_buffer;
+       speed_buffer = 0;
+
+       delete [] playback_wrap_buffer;
+       playback_wrap_buffer = 0;
+
+       delete [] capture_wrap_buffer;
+       capture_wrap_buffer = 0;
 
 
-       if (capture_transition_buf) {
-               delete capture_transition_buf;
-               capture_transition_buf = 0;
+       delete playback_buf;
+       playback_buf = 0;
+
+       delete capture_buf;
+       capture_buf = 0;
+
+       delete capture_transition_buf;
+       capture_transition_buf = 0;
+}
+
+
+bool
+AudioDiskstream::set_name (string const & name)
+{
+       Diskstream::set_name (name);
+
+       /* get a new write source so that its name reflects the new diskstream name */
+
+       boost::shared_ptr<ChannelList> c = channels.reader();
+       ChannelList::iterator i;
+       int n = 0;
+
+       for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
+               use_new_write_source (n);
        }
        }
+
+       return true;
 }
 }