fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / audio_diskstream.cc
index f4d10c2043041dc41e83926acb9aceed428a944e..330b9d582acda0367d509993f54c0b1b2b98e339 100644 (file)
@@ -16,7 +16,6 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include <fstream>
 #include <cstdio>
 #include <unistd.h>
 #include <cmath>
@@ -28,6 +27,7 @@
 #include <cstdlib>
 #include <ctime>
 
+#include "pbd/gstdio_compat.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/profile.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/utils.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 #include <locale.h>
 
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-size_t  AudioDiskstream::_working_buffers_size = 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;
+
+       if (flag & Destructive) {
+               use_destructive_playlist ();
+       }
 }
 
 AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
@@ -128,11 +133,13 @@ AudioDiskstream::~AudioDiskstream ()
 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
@@ -140,7 +147,6 @@ AudioDiskstream::free_working_buffers()
 {
        delete [] _mixdown_buffer;
        delete [] _gain_buffer;
-       _working_buffers_size = 0;
        _mixdown_buffer       = 0;
        _gain_buffer          = 0;
 }
@@ -148,6 +154,8 @@ AudioDiskstream::free_working_buffers()
 void
 AudioDiskstream::non_realtime_input_change ()
 {
+       bool need_write_sources = false;
+
        {
                Glib::Threads::Mutex::Lock lm (state_lock);
 
@@ -155,7 +163,12 @@ AudioDiskstream::non_realtime_input_change ()
                        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();
 
@@ -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());
                        }
+
+                       need_write_sources = true;
                }
 
                if (input_change_pending.type & IOChange::ConnectionsChanged) {
@@ -179,9 +194,9 @@ AudioDiskstream::non_realtime_input_change ()
                /* implicit unlock */
        }
 
-       /* reset capture files */
-
-       reset_write_sources (false);
+       if (need_write_sources) {
+               reset_write_sources (false);
+       }
 
        /* 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) {
-               seek ((framepos_t) (location * (double) speed()));
+               seek ((framepos_t) (location * (double) speed()), true);
        } else {
-               seek (location);
+               seek (location, true);
        }
 }
 
@@ -218,7 +233,7 @@ AudioDiskstream::get_input_sources ()
 
                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 ();
                        }
@@ -324,10 +339,13 @@ AudioDiskstream::setup_destructive_playlist ()
        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());
+
+       /* apply region properties and update write sources */
+       use_destructive_playlist();
 }
 
 void
@@ -339,7 +357,14 @@ AudioDiskstream::use_destructive_playlist ()
           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_property().rlist());
+               if (rl.size() > 0) {
+                       assert((rl.size() == 1));
+                       rp = rl.front();
+               }
+       }
 
        if (!rp) {
                reset_write_sources (false, true);
@@ -352,9 +377,9 @@ AudioDiskstream::use_destructive_playlist ()
                throw failed_constructor();
        }
 
-       /* be sure to stretch the region out to the maximum length */
+       /* be sure to stretch the region out to the maximum length (non-musical)*/
 
-       region->set_length (max_framepos - region->position());
+       region->set_length (max_framepos - region->position(), 0);
 
        uint32_t n;
        ChannelList::iterator chan;
@@ -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);
+               // 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);
 
+               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;
@@ -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);
-                               
+
                        } 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;
                                }
@@ -568,14 +599,14 @@ AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
        }
 
        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 */
 
-               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;
@@ -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;
+                                        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;
 
@@ -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));
-                                       
+
                                        /* 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);
-                               
+
                                playback_distance = interpolation.interpolate (
                                        channel, nframes, chaninfo->current_playback_buffer, chaninfo->speed_buffer);
-                               
+
                                chaninfo->current_playback_buffer = chaninfo->speed_buffer;
                        }
-                       
+
                } 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 */
-               
+
                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);
@@ -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);
-       
+
                /* 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;
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 now captured %2 (by %3)\n", name(), capture_captured, adjust_capture_position));
                adjust_capture_position = 0;
        }
 
@@ -769,10 +803,10 @@ AudioDiskstream::commit (framecnt_t playback_distance)
                }
        } 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 {
-                       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) {
-               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 {
-               ret = do_refill_with_alloc ();
+               /* call _do_refill() to refill just one chunk, and then
+                  return.
+               */
+               ret = do_refill_with_alloc (true);
        }
 
        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) {
-               (*chan)->playback_buf->increment_read_ptr (llabs(distance));
+               (*chan)->playback_buf->increment_read_ptr (::llabs(distance));
        }
 
        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 (reversed) {
@@ -1037,12 +1078,18 @@ AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
 }
 
 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;
@@ -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.
+ *
+ * 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
-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;
@@ -1067,6 +1120,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
        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;
        }
@@ -1086,27 +1147,27 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                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
-          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.
 
-          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.
        */
 
-       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;
        }
 
@@ -1119,10 +1180,6 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                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) {
@@ -1189,6 +1246,30 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
        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);
@@ -1198,14 +1279,14 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                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
-                          ^^^^ => 1 x disk_io_chunk_frames that would be filled
+                          ^^^^ => 1 x disk_read_chunk_frames that would be filled
 
                           |......|+++++++++++++|...............................|
                           buf1                buf0
@@ -1230,7 +1311,7 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
                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);
 
@@ -1249,8 +1330,9 @@ AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
 
                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)) {
@@ -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);
 
-  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.
  *
- * 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.
  *
- * 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
@@ -1306,7 +1394,7 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
 
                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;
                }
 
@@ -1321,11 +1409,11 @@ AudioDiskstream::do_flush (RunContext /*context*/, bool force_flush)
                   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;
                }
 
-               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
@@ -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;
 
-               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
-                          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.
                        */
 
-                       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;
@@ -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);
                        }
+
+                       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());
 
-               // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
-
                _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);
 
-                       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 {
 
@@ -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();
 
-               // 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
@@ -1694,7 +1772,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
           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;
@@ -1706,7 +1784,7 @@ AudioDiskstream::finish_capture (boost::shared_ptr<ChannelList> c)
 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;
        }
 
@@ -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 ()
 {
-       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;
        }
 
@@ -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);
-                       (*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);
-                       (*chan)->write_source->mark_streaming_write_started ();
+                       Source::Lock lock((*chan)->write_source->mutex());
+                       (*chan)->write_source->mark_streaming_write_started (lock);
                }
        }
 
@@ -1786,10 +1895,10 @@ AudioDiskstream::get_state ()
 {
        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), "%zd", c->size());
+       snprintf (buf, sizeof(buf), "%u", (unsigned int) c->size());
        node.add_property ("channels", buf);
 
        if (!capturing_sources.empty() && _session.get_record_enabled()) {
@@ -1823,12 +1932,12 @@ AudioDiskstream::get_state ()
 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;
-       LocaleGuard lg (X_("POSIX"));
+       LocaleGuard lg;
 
        /* 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 (
-                            n_channels().n_audio(), name(), n, destructive())) == 0) {
+                            n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
                        throw failed_constructor();
                }
        }
@@ -1926,14 +2035,6 @@ AudioDiskstream::use_new_write_source (uint32_t n)
        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*/)
 {
@@ -1954,7 +2055,8 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool /*force*/)
                        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 ();
                                }
 
@@ -2016,7 +2118,7 @@ AudioDiskstream::allocate_temporary_buffers ()
           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) {
@@ -2135,11 +2237,11 @@ AudioDiskstream::playback_buffer_load () const
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
-               return 0;
+               return 1.0;
        }
 
        return (float) ((double) c->front()->playback_buf->read_space()/
-                       (double) c->front()->playback_buf->bufsize());
+                          (double) c->front()->playback_buf->bufsize());
 }
 
 float
@@ -2148,7 +2250,7 @@ AudioDiskstream::capture_buffer_load () const
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        if (c->empty ()) {
-               return 0;
+               return 1.0;
        }
 
        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)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        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
-                       struct stat sbuf;
-                       if (stat (prop->value().c_str(), &sbuf)) {
+                       GStatBuf sbuf;
+                       if (g_stat (prop->value().c_str(), &sbuf)) {
                                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 {
-                               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) {
@@ -2222,21 +2329,31 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
-       boost::shared_ptr<AudioRegion> region;
-
        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));
 
+               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->set_automatic (true);
-               region->set_whole_file (true);
-               region->special_set_position (0);
+               _playlist->add_region (region, position);
        }
 
        catch (failed_constructor& err) {
@@ -2247,7 +2364,6 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
                return -1;
        }
 
-       _playlist->add_region (region, position);
 
        return 0;
 }
@@ -2294,11 +2410,22 @@ AudioDiskstream::set_destructive (bool yn)
 bool
 AudioDiskstream::can_become_destructive (bool& requires_bounce) const
 {
+       if (Profile->get_trx()) {
+               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) {
@@ -2306,7 +2433,14 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const
                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_property().rlist());
+               assert((rl.size() == 1));
+               first = rl.front();
+
+       }
+
        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()) {
+               // what is the idea here?  why start() ??
                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);
@@ -2420,6 +2566,24 @@ AudioDiskstream::ChannelInfo::resize_capture (framecnt_t capture_bufsize)
 
 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;
@@ -2445,6 +2609,9 @@ AudioDiskstream::ChannelInfo::~ChannelInfo ()
 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 */
@@ -2459,3 +2626,24 @@ AudioDiskstream::set_name (string const & name)
 
        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;
+}