we always only use the "C" locale when saving.
[ardour.git] / libs / ardour / audio_diskstream.cc
index dd2dcf324eac9db2e6dcf34d5a3d408f920cdd61..0a6a62017b8975bbf4890eadb86d2f3cb6ec63d0 100644 (file)
@@ -16,7 +16,6 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include <fstream>
 #include <cstdio>
 #include <unistd.h>
 #include <cmath>
 #include <cstdio>
 #include <unistd.h>
 #include <cmath>
@@ -28,6 +27,7 @@
 #include <cstdlib>
 #include <ctime>
 
 #include <cstdlib>
 #include <ctime>
 
+#include "pbd/gstdio_compat.h"
 #include "pbd/error.h"
 #include "pbd/xml++.h"
 #include "pbd/memento_command.h"
 #include "pbd/error.h"
 #include "pbd/xml++.h"
 #include "pbd/memento_command.h"
 #include "ardour/debug.h"
 #include "ardour/io.h"
 #include "ardour/playlist_factory.h"
 #include "ardour/debug.h"
 #include "ardour/io.h"
 #include "ardour/playlist_factory.h"
+#include "ardour/profile.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
 #include "ardour/region_factory.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
+#include "ardour/sndfile_helpers.h"
 #include "ardour/source_factory.h"
 #include "ardour/track.h"
 #include "ardour/types.h"
 #include "ardour/source_factory.h"
 #include "ardour/track.h"
 #include "ardour/types.h"
@@ -61,7 +63,6 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
 using namespace ARDOUR;
 using namespace PBD;
 
-size_t  AudioDiskstream::_working_buffers_size = 0;
 Sample* AudioDiskstream::_mixdown_buffer       = 0;
 gain_t* AudioDiskstream::_gain_buffer          = 0;
 
 Sample* AudioDiskstream::_mixdown_buffer       = 0;
 gain_t* AudioDiskstream::_gain_buffer          = 0;
 
@@ -74,6 +75,10 @@ AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream:
        in_set_state = true;
        use_new_playlist ();
        in_set_state = false;
        in_set_state = true;
        use_new_playlist ();
        in_set_state = false;
+
+       if (flag & Destructive) {
+               use_destructive_playlist ();
+       }
 }
 
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
 }
 
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
@@ -128,11 +133,13 @@ AudioDiskstream::~AudioDiskstream ()
 void
 AudioDiskstream::allocate_working_buffers()
 {
 void
 AudioDiskstream::allocate_working_buffers()
 {
-       assert(disk_io_frames() > 0);
-
-       _working_buffers_size = disk_io_frames();
-       _mixdown_buffer       = new Sample[_working_buffers_size];
-       _gain_buffer          = new gain_t[_working_buffers_size];
+       /* with varifill buffer refilling, we compute the read size in bytes (to optimize
+          for disk i/o bandwidth) and then convert back into samples. These buffers
+          need to reflect the maximum size we could use, which is 4MB reads, or 2M samples
+          using 16 bit samples.
+       */
+       _mixdown_buffer       = new Sample[2*1048576];
+       _gain_buffer          = new gain_t[2*1048576];
 }
 
 void
 }
 
 void
@@ -140,7 +147,6 @@ AudioDiskstream::free_working_buffers()
 {
        delete [] _mixdown_buffer;
        delete [] _gain_buffer;
 {
        delete [] _mixdown_buffer;
        delete [] _gain_buffer;
-       _working_buffers_size = 0;
        _mixdown_buffer       = 0;
        _gain_buffer          = 0;
 }
        _mixdown_buffer       = 0;
        _gain_buffer          = 0;
 }
@@ -148,6 +154,8 @@ AudioDiskstream::free_working_buffers()
 void
 AudioDiskstream::non_realtime_input_change ()
 {
 void
 AudioDiskstream::non_realtime_input_change ()
 {
+       bool need_write_sources = false;
+
        {
                Glib::Threads::Mutex::Lock lm (state_lock);
 
        {
                Glib::Threads::Mutex::Lock lm (state_lock);
 
@@ -155,7 +163,12 @@ AudioDiskstream::non_realtime_input_change ()
                        return;
                }
 
                        return;
                }
 
-               if (input_change_pending.type == IOChange::ConfigurationChanged) {
+               boost::shared_ptr<ChannelList> cr = channels.reader();
+               if (!cr->empty() && !cr->front()->write_source) {
+                       need_write_sources = true;
+               }
+
+               if (input_change_pending.type & IOChange::ConfigurationChanged) {
                        RCUWriter<ChannelList> writer (channels);
                        boost::shared_ptr<ChannelList> c = writer.get_copy();
 
                        RCUWriter<ChannelList> writer (channels);
                        boost::shared_ptr<ChannelList> c = writer.get_copy();
 
@@ -166,6 +179,8 @@ AudioDiskstream::non_realtime_input_change ()
                        } else if (_io->n_ports().n_audio() < _n_channels.n_audio()) {
                                remove_channel_from (c, _n_channels.n_audio() - _io->n_ports().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());
                        }
+
+                       need_write_sources = true;
                }
 
                if (input_change_pending.type & IOChange::ConnectionsChanged) {
                }
 
                if (input_change_pending.type & IOChange::ConnectionsChanged) {
@@ -179,9 +194,9 @@ AudioDiskstream::non_realtime_input_change ()
                /* implicit unlock */
        }
 
                /* implicit unlock */
        }
 
-       /* reset capture files */
-
-       reset_write_sources (false);
+       if (need_write_sources) {
+               reset_write_sources (false);
+       }
 
        /* now refill channel buffers */
 
 
        /* now refill channel buffers */
 
@@ -198,9 +213,9 @@ AudioDiskstream::non_realtime_locate (framepos_t location)
        /* now refill channel buffers */
 
        if (speed() != 1.0f || speed() != -1.0f) {
        /* now refill channel buffers */
 
        if (speed() != 1.0f || speed() != -1.0f) {
-               seek ((framepos_t) (location * (double) speed()));
+               seek ((framepos_t) (location * (double) speed()), true);
        } else {
        } else {
-               seek (location);
+               seek (location, true);
        }
 }
 
        }
 }
 
@@ -218,7 +233,7 @@ AudioDiskstream::get_input_sources ()
 
                connections.clear ();
 
 
                connections.clear ();
 
-               if (_io->nth (n)->get_connections (connections) == 0) {
+               if ((_io->nth (n).get()) && (_io->nth (n)->get_connections (connections) == 0)) {
                        if (!(*chan)->source.name.empty()) {
                                // _source->disable_metering ();
                        }
                        if (!(*chan)->source.name.empty()) {
                                // _source->disable_metering ();
                        }
@@ -324,10 +339,13 @@ AudioDiskstream::setup_destructive_playlist ()
        PropertyList plist;
        plist.add (Properties::name, _name.val());
        plist.add (Properties::start, 0);
        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()));
+       plist.add (Properties::length, max_framepos - srcs.front()->natural_position());
 
        boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
        _playlist->add_region (region, srcs.front()->natural_position());
 
        boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
        _playlist->add_region (region, srcs.front()->natural_position());
+
+       /* apply region properties and update write sources */
+       use_destructive_playlist();
 }
 
 void
 }
 
 void
@@ -339,7 +357,14 @@ AudioDiskstream::use_destructive_playlist ()
           with the (presumed single, full-extent) region.
        */
 
           with the (presumed single, full-extent) region.
        */
 
-       boost::shared_ptr<Region> rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+       boost::shared_ptr<Region> rp;
+       {
+               const RegionList& rl (_playlist->region_list().rlist());
+               if (rl.size() > 0) {
+                       assert((rl.size() == 1));
+                       rp = rl.front();
+               }
+       }
 
        if (!rp) {
                reset_write_sources (false, true);
 
        if (!rp) {
                reset_write_sources (false, true);
@@ -451,8 +476,12 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
        if (record_enabled()) {
 
                Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
        if (record_enabled()) {
 
                Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+               // XXX should this be transport_frame + nframes - 1 ? coverage() expects its parameter ranges to include their end points
+               // XXX also, first_recordable_frame & last_recordable_frame may both be == max_framepos: coverage() will return OverlapNone in that case. Is thak OK?
                calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
 
                calculate_record_range (ot, transport_frame, nframes, rec_nframes, rec_offset);
 
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 frames, offset %4\n", _name, rec_nframes, nframes, rec_offset));
+
                if (rec_nframes && !was_recording) {
                        capture_captured = 0;
                        was_recording = true;
                if (rec_nframes && !was_recording) {
                        capture_captured = 0;
                        was_recording = true;
@@ -494,12 +523,14 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
 
                                Sample *buf = bufs.get_audio (n).data(rec_offset);
                                memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes);
 
                                Sample *buf = bufs.get_audio (n).data(rec_offset);
                                memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes);
-                               
+
                        } else {
 
                                framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
 
                                if (rec_nframes > total) {
                        } else {
 
                                framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
 
                                if (rec_nframes > total) {
+                                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n",
+                                                                                    DEBUG_THREAD_SELF, name(), rec_nframes, total));
                                        DiskOverrun ();
                                        return -1;
                                }
                                        DiskOverrun ();
                                        return -1;
                                }
@@ -568,14 +599,14 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
        }
 
        if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) {
        }
 
        if ((_track->monitoring_state () & MonitoringDisk) || collect_playback) {
-               
+
                /* we're doing playback */
 
                framecnt_t necessary_samples;
 
                /* no varispeed playback if we're recording, because the output .... TBD */
 
                /* we're doing playback */
 
                framecnt_t necessary_samples;
 
                /* no varispeed playback if we're recording, because the output .... TBD */
 
-               if (rec_nframes == 0 && _actual_speed != 1.0f) {
+               if (rec_nframes == 0 && _actual_speed != 1.0) {
                        necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
                } else {
                        necessary_samples = nframes;
                        necessary_samples = (framecnt_t) ceil ((nframes * fabs (_actual_speed))) + 2;
                } else {
                        necessary_samples = nframes;
@@ -606,6 +637,8 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                                if (necessary_samples > total) {
                                        cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
                                        cerr << "underrun for " << _name << endl;
                                if (necessary_samples > total) {
                                        cerr << _name << " Need " << necessary_samples << " total = " << total << endl;
                                        cerr << "underrun for " << _name << endl;
+                                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 underrun in %2, rec_nframes = %3 total space = %4\n",
+                                                                                    DEBUG_THREAD_SELF, name(), rec_nframes, total));
                                        DiskUnderrun ();
                                        return -1;
 
                                        DiskUnderrun ();
                                        return -1;
 
@@ -622,7 +655,7 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                                        memcpy ((char *) chaninfo->playback_wrap_buffer,
                                                        chaninfo->playback_vector.buf[0],
                                                        chaninfo->playback_vector.len[0] * sizeof (Sample));
                                        memcpy ((char *) chaninfo->playback_wrap_buffer,
                                                        chaninfo->playback_vector.buf[0],
                                                        chaninfo->playback_vector.len[0] * sizeof (Sample));
-                                       
+
                                        /* Copy buf[1] from playback_buf */
                                        memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0],
                                                        chaninfo->playback_vector.buf[1],
                                        /* Copy buf[1] from playback_buf */
                                        memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0],
                                                        chaninfo->playback_vector.buf[1],
@@ -641,13 +674,13 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                        int channel = 0;
                        for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++channel) {
                                ChannelInfo* chaninfo (*chan);
                        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);
                                playback_distance = interpolation.interpolate (
                                        channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
-                               
+
                                chaninfo->current_playback_buffer = chaninfo->speed_buffer;
                        }
                                chaninfo->current_playback_buffer = chaninfo->speed_buffer;
                        }
-                       
+
                } else {
                        playback_distance = nframes;
                }
                } else {
                        playback_distance = nframes;
                }
@@ -658,20 +691,20 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
        if (need_disk_signal) {
 
                /* copy data over to buffer set */
        if (need_disk_signal) {
 
                /* 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;
                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) {
                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);
                        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);
                        if (n < n_chans) {
                                if (scaling != 1.0f) {
                                        buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling);
@@ -691,7 +724,7 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
                ChanCount cnt (DataType::AUDIO, n_chans);
                cnt.set (DataType::MIDI, bufs.count().n_midi());
                bufs.set_count (cnt);
                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 */
        }
 
                /* extra buffers will already be silent, so leave them alone */
        }
 
@@ -754,6 +787,7 @@ AudioDiskstream::commit (framecnt_t playback_distance)
 
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
 
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, adjust_capture_position));
                adjust_capture_position = 0;
        }
 
                adjust_capture_position = 0;
        }
 
@@ -769,10 +803,10 @@ AudioDiskstream::commit (framecnt_t playback_distance)
                }
        } else {
                if (_io && _io->active()) {
                }
        } else {
                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);
+                       need_butler = ((framecnt_t) c->front()->playback_buf->write_space() >= disk_read_chunk_frames)
+                               || ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames);
                } else {
                } else {
-                       need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_io_chunk_frames);
+                       need_butler = ((framecnt_t) c->front()->capture_buf->read_space() >= disk_write_chunk_frames);
                }
        }
 
                }
        }
 
@@ -894,9 +928,15 @@ AudioDiskstream::seek (framepos_t frame, bool complete_refill)
        file_frame = frame;
 
        if (complete_refill) {
        file_frame = frame;
 
        if (complete_refill) {
-               while ((ret = do_refill_with_alloc ()) > 0) ;
+               /* call _do_refill() to refill the entire buffer, using
+                  the largest reads possible.
+               */
+               while ((ret = do_refill_with_alloc (false)) > 0) ;
        } else {
        } else {
-               ret = do_refill_with_alloc ();
+               /* call _do_refill() to refill just one chunk, and then
+                  return.
+               */
+               ret = do_refill_with_alloc (true);
        }
 
        return ret;
        }
 
        return ret;
@@ -923,7 +963,7 @@ AudioDiskstream::internal_playback_seek (framecnt_t distance)
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (chan = c->begin(); chan != c->end(); ++chan) {
-               (*chan)->playback_buf->increment_read_ptr (llabs(distance));
+               (*chan)->playback_buf->increment_read_ptr (::llabs(distance));
        }
 
        if (first_recordable_frame < max_framepos) {
        }
 
        if (first_recordable_frame < max_framepos) {
@@ -980,6 +1020,7 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
                if (loc && start >= loop_end) {
                        start = loop_start + ((start - loop_start) % loop_length);
                }
                if (loc && start >= loop_end) {
                        start = loop_start + ((start - loop_start) % loop_length);
                }
+
        }
 
        if (reversed) {
        }
 
        if (reversed) {
@@ -1037,12 +1078,18 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
 }
 
 int
 }
 
 int
-AudioDiskstream::do_refill_with_alloc ()
+AudioDiskstream::_do_refill_with_alloc (bool partial_fill)
 {
 {
-       Sample* mix_buf  = new Sample[disk_io_chunk_frames];
-       float*  gain_buf = new float[disk_io_chunk_frames];
+       /* We limit disk reads to at most 4MB chunks, which with floating point
+          samples would be 1M samples. But we might use 16 or 14 bit samples,
+          in which case 4MB is more samples than that. Therefore size this for
+          the smallest sample value .. 4MB = 2M samples (16 bit).
+       */
+
+       Sample* mix_buf  = new Sample[2*1048576];
+       float*  gain_buf = new float[2*1048576];
 
 
-       int ret = _do_refill(mix_buf, gain_buf);
+       int ret = _do_refill (mix_buf, gain_buf, (partial_fill ? disk_read_chunk_frames : 0));
 
        delete [] mix_buf;
        delete [] gain_buf;
 
        delete [] mix_buf;
        delete [] gain_buf;
@@ -1052,9 +1099,15 @@ AudioDiskstream::do_refill_with_alloc ()
 
 /** Get some more data from disk and put it in our channels' playback_bufs,
  *  if there is suitable space in them.
 
 /** Get some more data from disk and put it in our channels' playback_bufs,
  *  if there is suitable space in them.
+ *
+ * If fill_level is non-zero, then we will refill the buffer so that there is
+ * still at least fill_level samples of space left to be filled. This is used
+ * after locates so that we do not need to wait to fill the entire buffer.
+ *
  */
  */
+
 int
 int
-AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
+AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer, framecnt_t fill_level)
 {
        int32_t ret = 0;
        framecnt_t to_read;
 {
        int32_t ret = 0;
        framecnt_t to_read;
@@ -1067,6 +1120,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
        boost::shared_ptr<ChannelList> c = channels.reader();
        framecnt_t ts;
 
        boost::shared_ptr<ChannelList> c = channels.reader();
        framecnt_t ts;
 
+       /* do not read from disk while session is marked as Loading, to avoid
+          useless redundant I/O.
+       */
+
+       if (_session.state_of_the_state() & Session::Loading) {
+               return 0;
+       }
+
        if (c->empty()) {
                return 0;
        }
        if (c->empty()) {
                return 0;
        }
@@ -1086,27 +1147,27 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                return 0;
        }
 
                return 0;
        }
 
-       /* 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 (total_space >= (_slaved ? 3 : 2) * disk_io_chunk_frames) {
-               ret = 1;
+       if (fill_level) {
+               if (fill_level < total_space) {
+                       total_space -= fill_level;
+               } else {
+                       /* we can't do anything with it */
+                       fill_level = 0;
+               }
        }
 
        /* if we're running close to normal speed and there isn't enough
        }
 
        /* if we're running close to normal speed and there isn't enough
-          space to do disk_io_chunk_frames of I/O, then don't bother.
+          space to do disk_read_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
+          Note: it is a design assumption that disk_read_chunk_frames is smaller
           than the playback buffer size, so this check should never trip when
           the playback buffer is empty.
        */
 
           than the playback buffer size, so this check should never trip when
           the playback buffer is empty.
        */
 
-       if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
+       if ((total_space < disk_read_chunk_frames) && fabs (_actual_speed) < 2.0f) {
                return 0;
        }
 
                return 0;
        }
 
@@ -1119,10 +1180,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                return 0;
        }
 
                return 0;
        }
 
-       /* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */
-
-       total_space = min (disk_io_chunk_frames, total_space);
-
        if (reversed) {
 
                if (file_frame == 0) {
        if (reversed) {
 
                if (file_frame == 0) {
@@ -1189,6 +1246,30 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
        framepos_t file_frame_tmp = 0;
 
 
        framepos_t file_frame_tmp = 0;
 
+       /* total_space is in samples. We want to optimize read sizes in various sizes using bytes */
+
+       const size_t bits_per_sample = format_data_width (_session.config.get_native_file_data_format());
+       size_t total_bytes = total_space * bits_per_sample / 8;
+
+       /* chunk size range is 256kB to 4MB. Bigger is faster in terms of MB/sec, but bigger chunk size always takes longer
+        */
+       size_t byte_size_for_read = max ((size_t) (256 * 1024), min ((size_t) (4 * 1048576), total_bytes));
+
+       /* find nearest (lower) multiple of 16384 */
+
+       byte_size_for_read = (byte_size_for_read / 16384) * 16384;
+
+       /* now back to samples */
+
+       framecnt_t samples_to_read = byte_size_for_read / (bits_per_sample / 8);
+
+       //cerr << name() << " will read " << byte_size_for_read << " out of total bytes " << total_bytes << " in buffer of "
+       // << c->front()->playback_buf->bufsize() * bits_per_sample / 8 << " bps = " << bits_per_sample << endl;
+       // cerr << name () << " read samples = " << samples_to_read << " out of total space " << total_space << " in buffer of " << c->front()->playback_buf->bufsize() << " samples\n";
+
+       // uint64_t before = g_get_monotonic_time ();
+       // uint64_t elapsed;
+
        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                ChannelInfo* chan (*i);
        for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
 
                ChannelInfo* chan (*i);
@@ -1198,14 +1279,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                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 ((framecnt_t) vector.len[0] > samples_to_read) {
 
                        /* we're not going to fill the first chunk, so certainly do not bother with the
                           other part. it won't be connected with the part we do fill, as in:
 
                           .... => writable space
                           ++++ => readable space
 
                        /* we're not going to fill the first chunk, so certainly do not bother with the
                           other part. it won't be connected with the part we do fill, as in:
 
                           .... => writable space
                           ++++ => readable space
-                          ^^^^ => 1 x disk_io_chunk_frames that would be filled
+                          ^^^^ => 1 x disk_read_chunk_frames that would be filled
 
                           |......|+++++++++++++|...............................|
                           buf1                buf0
 
                           |......|+++++++++++++|...............................|
                           buf1                buf0
@@ -1230,7 +1311,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                len2 = vector.len[1];
 
                to_read = min (ts, len1);
                len2 = vector.len[1];
 
                to_read = min (ts, len1);
-               to_read = min (to_read, disk_io_chunk_frames);
+               to_read = min (to_read, (framecnt_t) samples_to_read);
 
                assert (to_read >= 0);
 
 
                assert (to_read >= 0);
 
@@ -1249,8 +1330,9 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                if (to_read) {
 
 
                if (to_read) {
 
-                       /* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data,
-                          so read some or all of vector.len[1] as well.
+                       /* we read all of vector.len[0], but it wasn't the
+                          entire samples_to_read of data, so read some or
+                          all of vector.len[1] as well.
                        */
 
                        if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
                        */
 
                        if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan_n, reversed)) {
@@ -1267,22 +1349,28 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
        }
 
 
        }
 
+       // elapsed = g_get_monotonic_time () - before;
+       // cerr << "\tbandwidth = " << (byte_size_for_read / 1048576.0) / (elapsed/1000000.0) << "MB/sec\n";
+
        file_frame = file_frame_tmp;
        assert (file_frame >= 0);
 
        file_frame = file_frame_tmp;
        assert (file_frame >= 0);
 
-  out:
+       ret = ((total_space - samples_to_read) > disk_read_chunk_frames);
+
+       c->front()->playback_buf->get_write_vector (&vector);
 
 
+  out:
        return ret;
 }
 
 /** Flush pending data to disk.
  *
        return ret;
 }
 
 /** Flush pending data to disk.
  *
- * Important note: this function will write *AT MOST* disk_io_chunk_frames
+ * Important note: this function will write *AT MOST* disk_write_chunk_frames
  * 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
+ * If there is less than disk_write_chunk_frames to be written, no data will be
  * written at all unless @a force_flush is true.
  */
 int
  * written at all unless @a force_flush is true.
  */
 int
@@ -1306,7 +1394,7 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
 
                total = vector.len[0] + vector.len[1];
 
 
                total = vector.len[0] + vector.len[1];
 
-               if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
+               if (total == 0 || (total < disk_write_chunk_frames && !force_flush && was_recording)) {
                        goto out;
                }
 
                        goto out;
                }
 
@@ -1321,11 +1409,11 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
                   let the caller know too.
                */
 
                   let the caller know too.
                */
 
-               if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
+               if (total >= 2 * disk_write_chunk_frames || ((force_flush || !was_recording) && total > disk_write_chunk_frames)) {
                        ret = 1;
                }
 
                        ret = 1;
                }
 
-               to_write = min (disk_io_chunk_frames, (framecnt_t) vector.len[0]);
+               to_write = min (disk_write_chunk_frames, (framecnt_t) vector.len[0]);
 
                // check the transition buffer when recording destructive
                // important that we get this after the capture buf
 
                // check the transition buffer when recording destructive
                // important that we get this after the capture buf
@@ -1385,14 +1473,16 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
                (*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_write_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_write_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 ((framecnt_t)(disk_io_chunk_frames - to_write), (framecnt_t) vector.len[1]);
+                       to_write = min ((framecnt_t)(disk_write_chunk_frames - to_write), (framecnt_t) vector.len[1]);
+
+                        DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 additional write of %2\n", name(), to_write));
 
                        if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
                                error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
 
                        if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
                                error << string_compose(_("AudioDiskstream %1: cannot write to disk"), id()) << endmsg;
@@ -1490,6 +1580,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
                        if (Config->get_auto_analyse_audio()) {
                                Analyser::queue_source_for_analysis (s, true);
                        }
                        if (Config->get_auto_analyse_audio()) {
                                Analyser::queue_source_for_analysis (s, true);
                        }
+
+                       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("newly captured source %1 length %2\n", s->path(), s->length (0)));
                }
        }
 
                }
        }
 
@@ -1538,8 +1630,6 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
 
                _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
 
 
                _last_capture_sources.insert (_last_capture_sources.end(), srcs.begin(), srcs.end());
 
-               // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
-
                _playlist->clear_changes ();
                _playlist->set_capture_insertion_in_progress (true);
                _playlist->freeze ();
                _playlist->clear_changes ();
                _playlist->set_capture_insertion_in_progress (true);
                _playlist->freeze ();
@@ -1550,8 +1640,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
 
                        RegionFactory::region_name (region_name, whole_file_region_name, false);
 
 
                        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));
+                       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture bufpos %5 start @ %2 length %3 add new region %4\n",
+                                                                             _name, (*ci)->start, (*ci)->frames, region_name, buffer_position));
 
                        try {
 
 
                        try {
 
@@ -1606,18 +1696,6 @@ AudioDiskstream::transport_looped (framepos_t transport_frame)
                // all we need to do is finish this capture, with modified capture length
                boost::shared_ptr<ChannelList> c = channels.reader();
 
                // 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
                finish_capture (c);
 
                // the next region will start recording via the normal mechanism
@@ -1694,7 +1772,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
           accessors, so that invalidation will not occur (both non-realtime).
        */
 
           accessors, so that invalidation will not occur (both non-realtime).
        */
 
-       // cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl;
+       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames));
 
        capture_info.push_back (ci);
        capture_captured = 0;
 
        capture_info.push_back (ci);
        capture_captured = 0;
@@ -1706,7 +1784,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
 void
 AudioDiskstream::set_record_enabled (bool yn)
 {
-       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) {
                return;
        }
 
                return;
        }
 
@@ -1731,10 +1809,39 @@ AudioDiskstream::set_record_enabled (bool yn)
        }
 }
 
        }
 }
 
+void
+AudioDiskstream::set_record_safe (bool yn)
+{
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+               return;
+       }
+
+       /* can't rec-safe in destructive mode if transport is before start ????
+        REQUIRES REVIEW */
+
+       if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+               return;
+       }
+
+       /* yes, i know that this not proof against race conditions, but its
+        good enough. i think.
+        */
+
+       if (record_safe () != yn) {
+               if (yn) {
+                       engage_record_safe ();
+               } else {
+                       disengage_record_safe ();
+               }
+
+               RecordSafeChanged (); /* EMIT SIGNAL */
+       }
+}
+
 bool
 AudioDiskstream::prep_record_enable ()
 {
 bool
 AudioDiskstream::prep_record_enable ()
 {
-       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0) {
+       if (!recordable() || !_session.record_enabling_legal() || _io->n_ports().n_audio() == 0 || record_safe ()) { // REQUIRES REVIEW "|| record_safe ()"
                return false;
        }
 
                return false;
        }
 
@@ -1754,13 +1861,15 @@ AudioDiskstream::prep_record_enable ()
                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);
                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 ();
+                       Source::Lock lock((*chan)->write_source->mutex());
+                       (*chan)->write_source->mark_streaming_write_started (lock);
                }
 
        } else {
                for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
                        capturing_sources.push_back ((*chan)->write_source);
                }
 
        } else {
                for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
                        capturing_sources.push_back ((*chan)->write_source);
-                       (*chan)->write_source->mark_streaming_write_started ();
+                       Source::Lock lock((*chan)->write_source->mutex());
+                       (*chan)->write_source->mark_streaming_write_started (lock);
                }
        }
 
                }
        }
 
@@ -1786,7 +1895,7 @@ AudioDiskstream::get_state ()
 {
        XMLNode& node (Diskstream::get_state());
        char buf[64] = "";
 {
        XMLNode& node (Diskstream::get_state());
        char buf[64] = "";
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg ();
 
        boost::shared_ptr<ChannelList> c = channels.reader();
        snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size());
 
        boost::shared_ptr<ChannelList> c = channels.reader();
        snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size());
@@ -1823,12 +1932,12 @@ AudioDiskstream::get_state ()
 int
 AudioDiskstream::set_state (const XMLNode& node, int version)
 {
 int
 AudioDiskstream::set_state (const XMLNode& node, int version)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
        uint32_t nchans = 1;
        XMLNode* capture_pending_node = 0;
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
        uint32_t nchans = 1;
        XMLNode* capture_pending_node = 0;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg ();
 
        /* prevent write sources from being created */
 
 
        /* prevent write sources from being created */
 
@@ -1908,7 +2017,7 @@ AudioDiskstream::use_new_write_source (uint32_t n)
 
        try {
                if ((chan->write_source = _session.create_audio_source_for_session (
 
        try {
                if ((chan->write_source = _session.create_audio_source_for_session (
-                            n_channels().n_audio(), name(), n, destructive())) == 0) {
+                            n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
                        throw failed_constructor();
                }
        }
                        throw failed_constructor();
                }
        }
@@ -1926,14 +2035,6 @@ AudioDiskstream::use_new_write_source (uint32_t n)
        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
 AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
 {
 void
 AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
 {
@@ -1954,7 +2055,8 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
                        if ((*chan)->write_source) {
 
                                if (mark_write_complete) {
                        if ((*chan)->write_source) {
 
                                if (mark_write_complete) {
-                                       (*chan)->write_source->mark_streaming_write_completed ();
+                                       Source::Lock lock((*chan)->write_source->mutex());
+                                       (*chan)->write_source->mark_streaming_write_completed (lock);
                                        (*chan)->write_source->done_with_peakfile_writes ();
                                }
 
                                        (*chan)->write_source->done_with_peakfile_writes ();
                                }
 
@@ -2016,7 +2118,7 @@ AudioDiskstream::allocate_temporary_buffers ()
           when slaving to MTC, Timecode etc.
        */
 
           when slaving to MTC, Timecode etc.
        */
 
-       double const sp = max (fabsf (_actual_speed), 1.2f);
+       double const sp = max (fabs (_actual_speed), 1.2);
        framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
 
        if (required_wrap_size > wrap_buffer_size) {
        framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() * sp) + 2;
 
        if (required_wrap_size > wrap_buffer_size) {
@@ -2135,11 +2237,11 @@ AudioDiskstream::playback_buffer_load () const
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
-               return 0;
+               return 1.0;
        }
 
        return (float) ((double) c->front()->playback_buf->read_space()/
        }
 
        return (float) ((double) c->front()->playback_buf->read_space()/
-                       (double) c->front()->playback_buf->bufsize());
+                          (double) c->front()->playback_buf->bufsize());
 }
 
 float
 }
 
 float
@@ -2148,7 +2250,7 @@ AudioDiskstream::capture_buffer_load () const
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
-               return 0;
+               return 1.0;
        }
 
        return (float) ((double) c->front()->capture_buf->write_space()/
        }
 
        return (float) ((double) c->front()->capture_buf->write_space()/
@@ -2158,7 +2260,7 @@ AudioDiskstream::capture_buffer_load () const
 int
 AudioDiskstream::use_pending_capture_data (XMLNode& node)
 {
 int
 AudioDiskstream::use_pending_capture_data (XMLNode& node)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
        boost::shared_ptr<AudioFileSource> fs;
        XMLNodeList nlist = node.children();
        XMLNodeIterator niter;
        boost::shared_ptr<AudioFileSource> fs;
@@ -2182,16 +2284,21 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                        }
 
                        // This protects sessions from errant CapturingSources in stored sessions
                        }
 
                        // This protects sessions from errant CapturingSources in stored sessions
-                       struct stat sbuf;
-                       if (stat (prop->value().c_str(), &sbuf)) {
+                       GStatBuf sbuf;
+                       if (g_stat (prop->value().c_str(), &sbuf)) {
                                continue;
                        }
 
                                continue;
                        }
 
+                       /* XXX as of June 2014, we always record to mono
+                          files. Since this Source is being created as part of
+                          crash recovery, we know that we need the first
+                          channel (the final argument to the SourceFactory
+                          call below). If we ever support non-mono files for
+                          capture, this will need rethinking.
+                       */
+
                        try {
                        try {
-                               fs = boost::dynamic_pointer_cast<AudioFileSource> (
-                                       SourceFactory::createWritable (
-                                               DataType::AUDIO, _session,
-                                               prop->value(), false, _session.frame_rate()));
+                               fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
                        }
 
                        catch (failed_constructor& err) {
                        }
 
                        catch (failed_constructor& err) {
@@ -2222,21 +2329,31 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
                return -1;
        }
 
-       boost::shared_ptr<AudioRegion> region;
-
        try {
 
        try {
 
+               boost::shared_ptr<AudioRegion> wf_region;
+               boost::shared_ptr<AudioRegion> region;
+
+               /* First create the whole file region */
+
                PropertyList plist;
 
                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));
 
                PropertyList plist;
 
                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));
 
+               wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+               wf_region->set_automatic (true);
+               wf_region->set_whole_file (true);
+               wf_region->special_set_position (position);
+
+               /* Now create a region that isn't the whole file for adding to
+                * the playlist */
+
                region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
 
                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);
+               _playlist->add_region (region, position);
        }
 
        catch (failed_constructor& err) {
        }
 
        catch (failed_constructor& err) {
@@ -2247,7 +2364,6 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
                return -1;
        }
 
-       _playlist->add_region (region, position);
 
        return 0;
 }
 
        return 0;
 }
@@ -2294,11 +2410,22 @@ AudioDiskstream::set_destructive (bool yn)
 bool
 AudioDiskstream::can_become_destructive (bool& requires_bounce) const
 {
 bool
 AudioDiskstream::can_become_destructive (bool& requires_bounce) const
 {
+       if (Profile->get_trx()) {
+               return false;
+       }
+
        if (!_playlist) {
                requires_bounce = false;
                return false;
        }
 
        if (!_playlist) {
                requires_bounce = false;
                return false;
        }
 
+       /* if no regions are present: easy */
+
+       if (_playlist->n_regions() == 0) {
+               requires_bounce = false;
+               return true;
+       }
+
        /* is there only one region ? */
 
        if (_playlist->n_regions() != 1) {
        /* is there only one region ? */
 
        if (_playlist->n_regions() != 1) {
@@ -2306,7 +2433,14 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
                return false;
        }
 
                return false;
        }
 
-       boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+       boost::shared_ptr<Region> first;
+       {
+               const RegionList& rl (_playlist->region_list().rlist());
+               assert((rl.size() == 1));
+               first = rl.front();
+
+       }
+
        if (!first) {
                requires_bounce = false;
                return true;
        if (!first) {
                requires_bounce = false;
                return true;
@@ -2315,12 +2449,24 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
        /* do the source(s) for the region cover the session start position ? */
 
        if (first->position() != _session.current_start_frame()) {
        /* do the source(s) for the region cover the session start position ? */
 
        if (first->position() != _session.current_start_frame()) {
+               // what is the idea here?  why start() ??
                if (first->start() > _session.current_start_frame()) {
                        requires_bounce = true;
                        return false;
                }
        }
 
                if (first->start() > _session.current_start_frame()) {
                        requires_bounce = true;
                        return false;
                }
        }
 
+       /* currently RouteTimeAxisView::set_track_mode does not
+        * implement bounce. Existing regions cannot be converted.
+        *
+        * so let's make sure this region is already set up
+        * as tape-track (spanning the complete range)
+        */
+       if (first->length() != max_framepos - first->position()) {
+               requires_bounce = true;
+               return false;
+       }
+
        /* is the source used by only 1 playlist ? */
 
        boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
        /* is the source used by only 1 playlist ? */
 
        boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
@@ -2420,6 +2566,24 @@ AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
 
 AudioDiskstream::ChannelInfo::~ChannelInfo ()
 {
 
 AudioDiskstream::ChannelInfo::~ChannelInfo ()
 {
+       if (write_source) {
+               if (write_source->removable()) {
+                       /* this is a "stub" write source which exists in the
+                          Session source list, but is removable. We must emit
+                          a drop references call because it should not
+                          continue to exist. If we do not do this, then the
+                          Session retains a reference to it, it is not
+                          deleted, and later attempts to create a new source
+                          file will use wierd naming because it already
+                          exists.
+
+                          XXX longer term TO-DO: do not add to session source
+                          list until we write to the source.
+                       */
+                       write_source->drop_references ();
+               }
+       }
+
        write_source.reset ();
 
        delete [] speed_buffer;
        write_source.reset ();
 
        delete [] speed_buffer;
@@ -2445,6 +2609,9 @@ AudioDiskstream::ChannelInfo::~ChannelInfo ()
 bool
 AudioDiskstream::set_name (string const & name)
 {
 bool
 AudioDiskstream::set_name (string const & name)
 {
+       if (_name == name) {
+               return true;
+       }
        Diskstream::set_name (name);
 
        /* get a new write source so that its name reflects the new diskstream name */
        Diskstream::set_name (name);
 
        /* get a new write source so that its name reflects the new diskstream name */
@@ -2459,3 +2626,24 @@ AudioDiskstream::set_name (string const & name)
 
        return true;
 }
 
        return true;
 }
+
+bool
+AudioDiskstream::set_write_source_name (const std::string& str) {
+       if (_write_source_name == str) {
+               return true;
+       }
+
+       Diskstream::set_write_source_name (str);
+
+       if (_write_source_name == name()) {
+               return true;
+       }
+       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;
+}