fix duplicate name check for new transport master
[ardour.git] / libs / ardour / disk_writer.cc
index 4eb4393861ef8eb6f3dd4587ac5c5cf0b570dd89..ed59da92316524f4fa874668814fbcd5c1ec42d9 100644 (file)
@@ -17,8 +17,6 @@
 
 */
 
-#include "pbd/i18n.h"
-
 #include "ardour/analyser.h"
 #include "ardour/audioengine.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/session.h"
 #include "ardour/smf_source.h"
 
+#include "pbd/i18n.h"
+
 using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
 
-ARDOUR::framecnt_t DiskWriter::_chunk_frames = DiskWriter::default_chunk_frames ();
+ARDOUR::samplecnt_t DiskWriter::_chunk_samples = DiskWriter::default_chunk_samples ();
 PBD::Signal0<void> DiskWriter::Overrun;
 
 DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f)
        : DiskIOProcessor (s, str, f)
-       , _input_latency (0)
        , _record_enabled (0)
        , _record_safe (0)
-       , capture_start_frame (0)
-        , capture_captured (0)
-        , was_recording (false)
-        , adjust_capture_position (0)
-        , _capture_offset (0)
-        , first_recordable_frame (max_framepos)
-        , last_recordable_frame (max_framepos)
-        , last_possibly_recording (0)
-        , _alignment_style (ExistingMaterial)
-        , _alignment_choice (Automatic)
+       , capture_start_sample (0)
+       , capture_captured (0)
+       , was_recording (false)
+       , first_recordable_sample (max_samplepos)
+       , last_recordable_sample (max_samplepos)
+       , last_possibly_recording (0)
+       , _alignment_style (ExistingMaterial)
        , _num_captured_loops (0)
        , _accumulated_capture_offset (0)
        , _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
@@ -68,14 +64,48 @@ DiskWriter::DiskWriter (Session& s, string const & str, DiskIOProcessor::Flag f)
 DiskWriter::~DiskWriter ()
 {
        DEBUG_TRACE (DEBUG::Destruction, string_compose ("DiskWriter %1 @ %2 deleted\n", _name, this));
+
+       boost::shared_ptr<ChannelList> c = channels.reader();
+
+       for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+               (*chan)->write_source.reset ();
+       }
 }
 
-framecnt_t
-DiskWriter::default_chunk_frames ()
+samplecnt_t
+DiskWriter::default_chunk_samples ()
 {
        return 65536;
 }
 
+void
+DiskWriter::WriterChannelInfo::resize (samplecnt_t bufsize)
+{
+       if (!capture_transition_buf) {
+               capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+       }
+       delete wbuf;
+       wbuf = new RingBufferNPT<Sample> (bufsize);
+       /* touch memory to lock it */
+       memset (wbuf->buffer(), 0, sizeof (Sample) * wbuf->bufsize());
+}
+
+int
+DiskWriter::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+       while (how_many--) {
+               c->push_back (new WriterChannelInfo (_session.butler()->audio_diskstream_capture_buffer_size()));
+               DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new writer channel, write space = %2 read = %3\n",
+                                                           name(),
+                                                           c->back()->wbuf->write_space(),
+                                                           c->back()->wbuf->read_space()));
+       }
+
+       return 0;
+}
+
+
+
 bool
 DiskWriter::set_write_source_name (string const & str)
 {
@@ -84,89 +114,79 @@ DiskWriter::set_write_source_name (string const & str)
 }
 
 void
-DiskWriter::check_record_status (framepos_t transport_frame, bool can_record)
+DiskWriter::check_record_status (samplepos_t transport_sample, double speed, bool can_record)
 {
        int possibly_recording;
-       int rolling;
-       int change;
        const int transport_rolling = 0x4;
        const int track_rec_enabled = 0x2;
        const int global_rec_enabled = 0x1;
-       const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
+       const int fully_rec_enabled = (transport_rolling |track_rec_enabled | global_rec_enabled);
 
-       /* merge together the 3 factors that affect record status, and compute
-        * what has changed.
-        */
+       /* merge together the 3 factors that affect record status, and compute what has changed. */
 
-       rolling = _session.transport_speed() != 0.0f;
-       possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
-       change = possibly_recording ^ last_possibly_recording;
+       possibly_recording = (speed != 0.0f ? 4 : 0)  | (record_enabled() ? 2 : 0) | (can_record ? 1 : 0);
 
        if (possibly_recording == last_possibly_recording) {
                return;
        }
 
-       const framecnt_t existing_material_offset = _session.worst_playback_latency();
-
        if (possibly_recording == fully_rec_enabled) {
 
                if (last_possibly_recording == fully_rec_enabled) {
                        return;
                }
 
-               capture_start_frame = _session.transport_frame();
-               first_recordable_frame = capture_start_frame + _capture_offset;
-               last_recordable_frame = max_framepos;
-
-                DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n",
-                                                                      name(), first_recordable_frame, last_recordable_frame, capture_start_frame,
-                                                                      _capture_offset,
-                                                                      existing_material_offset,
-                                                                      transport_frame,
-                                                                      _session.transport_frame(),
-                                                                      _session.worst_output_latency(),
-                                                                      _session.worst_track_latency()));
-
-
-                if (_alignment_style == ExistingMaterial) {
-                        first_recordable_frame += existing_material_offset;
-                        DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n",
-                                                                              first_recordable_frame));
-                }
-
-               prepare_record_status (capture_start_frame);
+               Location* loc;
+               if  (_session.config.get_punch_in () && 0 != (loc = _session.locations()->auto_punch_location ())) {
+                       capture_start_sample = loc->start ();
+               } else {
+                       capture_start_sample = _session.transport_sample ();
+               }
 
-       } else {
+               first_recordable_sample = capture_start_sample;
 
-               if (last_possibly_recording == fully_rec_enabled) {
-
-                       /* we were recording last time */
+               if (_alignment_style == ExistingMaterial) {
+                       first_recordable_sample += _capture_offset + _playback_offset;
+               }
 
-                       if (change & transport_rolling) {
+               if  (_session.config.get_punch_out () && 0 != (loc = _session.locations()->auto_punch_location ())) {
+                       /* this freezes the punch-out point when starting to record.
+                        *
+                        * We should allow to move it or at least allow to disable punch-out
+                        * while rolling..
+                        */
+                       last_recordable_sample = loc->end ();
+                       if (_alignment_style == ExistingMaterial) {
+                               last_recordable_sample += _capture_offset + _playback_offset;
+                       }
+               } else {
+                       last_recordable_sample = max_samplepos;
+               }
 
-                               /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
-                                * had to set it there because we likely rolled past the stopping point to declick out,
-                                * and then backed up.
-                                */
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %2 (STS: %3) CS:%4 FRS: %5 IL: %7, OL: %8 CO: %r9 PO: %10 WOL: %11 WIL: %12\n",
+                                                                     name(),
+                                                                     transport_sample,
+                                                                     _session.transport_sample(),
+                                                                                                                                                                                                                                       capture_start_sample,
+                                                                                                                                                                                                                                       first_recordable_sample,
+                                                                                                                                                                                                                                       last_recordable_sample,
+                                                                     _input_latency,
+                                                                     _output_latency,
+                                                                     _capture_offset,
+                                                                     _playback_offset,
+                                                                     _session.worst_output_latency(),
+                                                                     _session.worst_input_latency()));
 
-                       } else {
-                               /* punch out */
 
-                               last_recordable_frame = _session.transport_frame() + _capture_offset;
+               prepare_record_status (capture_start_sample);
 
-                               if (_alignment_style == ExistingMaterial) {
-                                       last_recordable_frame += existing_material_offset;
-                               }
-                       }
-               }
        }
 
        last_possibly_recording = possibly_recording;
 }
 
 void
-DiskWriter::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
-                                   framecnt_t & rec_nframes, framecnt_t & rec_offset)
+DiskWriter::calculate_record_range (Evoral::OverlapType ot, samplepos_t transport_sample, samplecnt_t nframes, samplecnt_t & rec_nframes, samplecnt_t & rec_offset)
 {
        switch (ot) {
        case Evoral::OverlapNone:
@@ -185,9 +205,9 @@ DiskWriter::calculate_record_range (Evoral::OverlapType ot, framepos_t transport
                /*    |--------|    recrange
                 *  -----|          transrange
                 */
-               rec_nframes = transport_frame + nframes - first_recordable_frame;
+               rec_nframes = transport_sample + nframes - first_recordable_sample;
                if (rec_nframes) {
-                       rec_offset = first_recordable_frame - transport_frame;
+                       rec_offset = first_recordable_sample - transport_sample;
                }
                break;
 
@@ -195,7 +215,7 @@ DiskWriter::calculate_record_range (Evoral::OverlapType ot, framepos_t transport
                /*    |--------|    recrange
                 *       |--------  transrange
                 */
-               rec_nframes = last_recordable_frame - transport_frame;
+               rec_nframes = last_recordable_sample - transport_sample;
                rec_offset = 0;
                break;
 
@@ -203,37 +223,14 @@ DiskWriter::calculate_record_range (Evoral::OverlapType ot, framepos_t transport
                /*    |--------|    recrange
                 *  --------------  transrange
                 */
-               rec_nframes = last_recordable_frame - first_recordable_frame;
-               rec_offset = first_recordable_frame - transport_frame;
-               break;
-       }
-
-        DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
-                                                              _name, enum_2_string (ot), transport_frame, nframes,
-                                                              first_recordable_frame, last_recordable_frame, rec_nframes, rec_offset));
-}
-
-void
-DiskWriter::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame)
-{
-       switch (_alignment_style) {
-       case ExistingMaterial:
-               last_recordable_frame = transport_frame + _capture_offset;
-               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to %2 + %3 = %4\n", _name, transport_frame, _capture_offset, last_recordable_frame));
-               break;
-
-       case CaptureTime:
-               last_recordable_frame = audible_frame; // note that capture_offset is zero
-               /* we may already have captured audio before the last_recordable_frame (audible frame),
-                  so deal with this.
-               */
-               if (last_recordable_frame > capture_start_frame) {
-                       capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame);
-               }
-               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame));
+               rec_nframes = last_recordable_sample - first_recordable_sample;
+               rec_offset = first_recordable_sample - transport_sample;
                break;
        }
 
+       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
+                                                             _name, enum_2_string (ot), transport_sample, nframes,
+                                                             first_recordable_sample, last_recordable_sample, rec_nframes, rec_offset));
 }
 
 void
@@ -260,9 +257,9 @@ DiskWriter::disengage_record_safe ()
        g_atomic_int_set (&_record_safe, 0);
 }
 
-/** Get the start position (in session frames) of the nth capture in the current pass */
-ARDOUR::framepos_t
-DiskWriter::get_capture_start_frame (uint32_t n) const
+/** Get the start position (in session samples) of the nth capture in the current pass */
+ARDOUR::samplepos_t
+DiskWriter::get_capture_start_sample (uint32_t n) const
 {
        Glib::Threads::Mutex::Lock lm (capture_info_lock);
 
@@ -271,48 +268,24 @@ DiskWriter::get_capture_start_frame (uint32_t n) const
                return capture_info[n]->start;
        } else {
                /* this is the currently in-progress capture */
-               return capture_start_frame;
+               return capture_start_sample;
        }
 }
 
-ARDOUR::framecnt_t
-DiskWriter::get_captured_frames (uint32_t n) const
+ARDOUR::samplecnt_t
+DiskWriter::get_captured_samples (uint32_t n) const
 {
        Glib::Threads::Mutex::Lock lm (capture_info_lock);
 
        if (capture_info.size() > n) {
                /* this is a completed capture */
-               return capture_info[n]->frames;
+               return capture_info[n]->samples;
        } else {
                /* this is the currently in-progress capture */
                return capture_captured;
        }
 }
 
-void
-DiskWriter::set_input_latency (framecnt_t l)
-{
-       _input_latency = l;
-}
-
-void
-DiskWriter::set_capture_offset ()
-{
-       switch (_alignment_style) {
-       case ExistingMaterial:
-               _capture_offset = _input_latency;
-               break;
-
-       case CaptureTime:
-       default:
-               _capture_offset = 0;
-               break;
-       }
-
-        DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style)));
-}
-
-
 void
 DiskWriter::set_align_style (AlignStyle a, bool force)
 {
@@ -322,42 +295,15 @@ DiskWriter::set_align_style (AlignStyle a, bool force)
 
        if ((a != _alignment_style) || force) {
                _alignment_style = a;
-               cerr << name() << " using align style " << enum_2_string (_alignment_style) << endl;
-               set_capture_offset ();
                AlignmentStyleChanged ();
        }
 }
 
-void
-DiskWriter::set_align_choice (AlignChoice a, bool force)
-{
-       if (record_enabled() && _session.actively_recording()) {
-               return;
-       }
-
-       if ((a != _alignment_choice) || force) {
-               _alignment_choice = a;
-
-               switch (_alignment_choice) {
-               case UseExistingMaterial:
-                       set_align_style (ExistingMaterial);
-                       break;
-               case UseCaptureTime:
-                       set_align_style (CaptureTime);
-                       break;
-               default:
-                       error << string_compose (_("programming error: %1"), "DiskWriter: asked to use illegal alignment style") << endmsg;
-                       break;
-               }
-       }
-}
-
 XMLNode&
-DiskWriter::state (bool full)
+DiskWriter::state ()
 {
-       XMLNode& node (DiskIOProcessor::state (full));
+       XMLNode& node (DiskIOProcessor::state ());
        node.set_property (X_("type"), X_("diskwriter"));
-       node.set_property (X_("capture-alignment"), enum_2_string (_alignment_choice));
        node.set_property (X_("record-safe"), (_record_safe ? X_("yes" : "no")));
        return node;
 }
@@ -369,14 +315,6 @@ DiskWriter::set_state (const XMLNode& node, int version)
                return -1;
        }
 
-       AlignChoice ac;
-
-       if (node.get_property (X_("capture-alignment"), ac)) {
-               set_align_choice (ac, true);
-        } else {
-                set_align_choice (Automatic, true);
-        }
-
        if (!node.get_property (X_("record-safe"), _record_safe)) {
                _record_safe = false;
        }
@@ -387,7 +325,7 @@ DiskWriter::set_state (const XMLNode& node, int version)
 }
 
 void
-DiskWriter::non_realtime_locate (framepos_t position)
+DiskWriter::non_realtime_locate (samplepos_t position)
 {
        if (_midi_write_source) {
                _midi_write_source->set_timeline_position (position);
@@ -398,10 +336,10 @@ DiskWriter::non_realtime_locate (framepos_t position)
 
 
 void
-DiskWriter::prepare_record_status(framepos_t capture_start_frame)
+DiskWriter::prepare_record_status (samplepos_t capture_start_sample)
 {
        if (recordable() && destructive()) {
-               boost::shared_ptr<ChannelList> c = channels.reader();
+               boost::shared_ptr<ChannelList> c = channels.reader ();
                for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
 
                        RingBufferNPT<CaptureTransition>::rw_vector transitions;
@@ -409,7 +347,7 @@ DiskWriter::prepare_record_status(framepos_t capture_start_frame)
 
                        if (transitions.len[0] > 0) {
                                transitions.buf[0]->type = CaptureStart;
-                               transitions.buf[0]->capture_val = capture_start_frame;
+                               transitions.buf[0]->capture_val = capture_start_sample;
                                (*chan)->capture_transition_buf->increment_write_ptr(1);
                        } else {
                                // bad!
@@ -430,34 +368,36 @@ DiskWriter::prepare_record_status(framepos_t capture_start_frame)
  *      that someone can read playback_distance worth of data from.
  */
 void
-DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
+DiskWriter::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample,
                  double speed, pframes_t nframes, bool result_required)
 {
+       if (!_active && !_pending_active) {
+               return;
+       }
+       _active = _pending_active;
+
        uint32_t n;
        boost::shared_ptr<ChannelList> c = channels.reader();
        ChannelList::iterator chan;
-       framecnt_t rec_offset = 0;
-       framecnt_t rec_nframes = 0;
+
+       samplecnt_t rec_offset = 0;
+       samplecnt_t rec_nframes = 0;
        bool nominally_recording;
+
        bool re = record_enabled ();
+       bool punch_in = _session.config.get_punch_in () && _session.locations()->auto_punch_location ();
        bool can_record = _session.actively_recording ();
-
-       if (_active) {
-               if (!_pending_active) {
-                       _active = false;
-                       return;
-               }
-       } else {
-               if (_pending_active) {
-                       _active = true;
-               } else {
-                       return;
-               }
-       }
+       can_record |= speed != 0 && _session.get_record_enabled () && punch_in && _session.transport_sample () <= _session.locations()->auto_punch_location ()->start ();
 
        _need_butler = false;
 
-       check_record_status (start_frame, can_record);
+#ifndef NDEBUG
+       if (speed != 0 && re) {
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: run() start: %2 end: %3 NF: %4\n", _name, start_sample, end_sample, nframes));
+       }
+#endif
+
+       check_record_status (start_sample, speed, can_record);
 
        if (nframes == 0) {
                return;
@@ -466,31 +406,29 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
        nominally_recording = (can_record && re);
 
        // Safeguard against situations where process() goes haywire when autopunching
-       // and last_recordable_frame < first_recordable_frame
+       // and last_recordable_sample < first_recordable_sample
 
-       if (last_recordable_frame < first_recordable_frame) {
-               last_recordable_frame = max_framepos;
+       if (last_recordable_sample < first_recordable_sample) {
+               last_recordable_sample = max_samplepos;
        }
 
-       const Location* const loop_loc    = loop_location;
-       framepos_t            loop_start  = 0;
-       framepos_t            loop_end    = 0;
-       framepos_t            loop_length = 0;
+       const Location* const loop_loc    = _loop_location;
+       samplepos_t           loop_start  = 0;
+       samplepos_t           loop_end    = 0;
+       samplepos_t           loop_length = 0;
 
        if (loop_loc) {
                get_location_times (loop_loc, &loop_start, &loop_end, &loop_length);
        }
 
-       adjust_capture_position = 0;
-
-       if (nominally_recording || (re && was_recording && _session.get_record_enabled() && (_session.config.get_punch_in() || _session.preroll_record_punch_enabled()))) {
+       if (nominally_recording || (re && was_recording && _session.get_record_enabled() && punch_in)) {
 
-               Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, start_frame, end_frame);
-               // 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, start_frame, nframes, rec_nframes, rec_offset);
+               Evoral::OverlapType ot = Evoral::coverage (first_recordable_sample, last_recordable_sample, start_sample, end_sample);
+               // XXX should this be transport_sample + nframes - 1 ? coverage() expects its parameter ranges to include their end points
+               // XXX also, first_recordable_sample & last_recordable_sample may both be == max_samplepos: coverage() will return OverlapNone in that case. Is thak OK?
+               calculate_record_range (ot, start_sample, 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));
+               DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: this time record %2 of %3 samples, offset %4\n", _name, rec_nframes, nframes, rec_offset));
 
                if (rec_nframes && !was_recording) {
                        capture_captured = 0;
@@ -501,22 +439,22 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                                   at the loop start and can handle time wrapping around.
                                   Otherwise, start the source right now as usual.
                                */
-                               capture_captured    = start_frame - loop_start;
-                               capture_start_frame = loop_start;
+                               capture_captured     = start_sample - loop_start;
+                               capture_start_sample = loop_start;
                        }
 
                        if (_midi_write_source) {
-                               _midi_write_source->mark_write_starting_now (capture_start_frame, capture_captured, loop_length);
+                               _midi_write_source->mark_write_starting_now (capture_start_sample, capture_captured, loop_length);
                        }
 
-                       g_atomic_int_set(const_cast<gint*> (&_frames_pending_write), 0);
-                       g_atomic_int_set(const_cast<gint*> (&_num_captured_loops), 0);
+                       g_atomic_int_set (const_cast<gint*> (&_samples_pending_write), 0);
+                       g_atomic_int_set (const_cast<gint*> (&_num_captured_loops), 0);
 
                        was_recording = true;
 
                }
 
-               /* For audio: not writing frames to the capture ringbuffer offsets
+               /* For audio: not writing samples to the capture ringbuffer offsets
                 * the recording. For midi: we need to keep track of the record range
                 * and subtract the accumulated difference from the event time.
                 */
@@ -528,7 +466,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
 
        }
 
-       if (can_record && !_last_capture_sources.empty()) {
+       if (can_record && !_last_capture_sources.empty ()) {
                _last_capture_sources.clear ();
        }
 
@@ -543,32 +481,32 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                        ChannelInfo* chaninfo (*chan);
                        AudioBuffer& buf (bufs.get_audio (n%n_buffers));
 
-                       chaninfo->buf->get_write_vector (&chaninfo->rw_vector);
+                       chaninfo->wbuf->get_write_vector (&chaninfo->rw_vector);
 
-                       if (rec_nframes <= (framecnt_t) chaninfo->rw_vector.len[0]) {
+                       if (rec_nframes <= (samplecnt_t) chaninfo->rw_vector.len[0]) {
 
                                Sample *incoming = buf.data (rec_offset);
                                memcpy (chaninfo->rw_vector.buf[0], incoming, sizeof (Sample) * rec_nframes);
 
                        } else {
 
-                               framecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_vector.len[1];
+                               samplecnt_t total = chaninfo->rw_vector.len[0] + chaninfo->rw_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));
-                                        Overrun ();
+                                       DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 overrun in %2, rec_nframes = %3 total space = %4\n",
+                                                                                   DEBUG_THREAD_SELF, name(), rec_nframes, total));
+                                       Overrun ();
                                        return;
                                }
 
                                Sample *incoming = buf.data (rec_offset);
-                               framecnt_t first = chaninfo->rw_vector.len[0];
+                               samplecnt_t first = chaninfo->rw_vector.len[0];
 
                                memcpy (chaninfo->rw_vector.buf[0], incoming, sizeof (Sample) * first);
                                memcpy (chaninfo->rw_vector.buf[1], incoming + first, sizeof (Sample) * (rec_nframes - first));
                        }
 
-                       chaninfo->buf->increment_write_ptr (rec_nframes);
+                       chaninfo->wbuf->increment_write_ptr (rec_nframes);
 
                }
 
@@ -580,7 +518,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                MidiChannelFilter* filter = mt ? &mt->capture_filter() : 0;
 
                for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
-                       Evoral::Event<MidiBuffer::TimeType> ev(*i, false);
+                       Evoral::Event<MidiBuffer::TimeType> ev (*i, false);
                        if (ev.time() + rec_offset > rec_nframes) {
                                break;
                        }
@@ -588,7 +526,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                        if (DEBUG_ENABLED(DEBUG::MidiIO)) {
                                const uint8_t* __data = ev.buffer();
                                DEBUG_STR_DECL(a);
-                               DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), start_frame, ev.size()));
+                               DEBUG_STR_APPEND(a, string_compose ("mididiskstream %1 capture event @ %2 + %3 sz %4 ", this, ev.time(), start_sample, ev.size()));
                                for (size_t i=0; i < ev.size(); ++i) {
                                        DEBUG_STR_APPEND(a,hex);
                                        DEBUG_STR_APPEND(a,"0x");
@@ -599,7 +537,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                                DEBUG_TRACE (DEBUG::MidiIO, DEBUG_STR(a).str());
                        }
 #endif
-                       /* Write events to the capture buffer in frames from session start,
+                       /* Write events to the capture buffer in samples from session start,
                           but ignoring looping so event time progresses monotonically.
                           The source knows the loop length so it knows exactly where the
                           event occurs in the series of recorded loops and can implement
@@ -608,18 +546,32 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                           reconstruct their actual time; future clever MIDI looping should
                           probably be implemented in the source instead of here.
                        */
-                       const framecnt_t loop_offset = _num_captured_loops * loop_length;
-                       const framepos_t event_time = start_frame + loop_offset - _accumulated_capture_offset + ev.time();
-                       if (event_time < 0 || event_time < first_recordable_frame) {
+                       const samplecnt_t loop_offset = _num_captured_loops * loop_length;
+                       const samplepos_t event_time = start_sample + loop_offset - _accumulated_capture_offset + ev.time();
+                       if (event_time < 0 || event_time < first_recordable_sample) {
                                /* Event out of range, skip */
                                continue;
                        }
 
+                       bool skip_event = false;
+                       if (mt) {
+                               /* skip injected immediate/out-of-band events */
+                               MidiBuffer const& ieb (mt->immediate_event_buffer());
+                               for (MidiBuffer::const_iterator j = ieb.begin(); j != ieb.end(); ++j) {
+                                       if (*j == ev) {
+                                               skip_event = true;
+                                       }
+                               }
+                       }
+                       if (skip_event) {
+                               continue;
+                       }
+
                        if (!filter || !filter->filter(ev.buffer(), ev.size())) {
                                _midi_buf->write (event_time, ev.event_type(), ev.size(), ev.buffer());
                        }
                }
-               g_atomic_int_add(const_cast<gint*>(&_frames_pending_write), nframes);
+               g_atomic_int_add (const_cast<gint*>(&_samples_pending_write), nframes);
 
                if (buf.size() != 0) {
                        Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex, Glib::Threads::TRY_LOCK);
@@ -634,7 +586,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                                        /* This may fail if buf is larger than _gui_feed_buffer, but it's not really
                                           the end of the world if it does.
                                        */
-                                       _gui_feed_buffer.push_back ((*i).time() + start_frame, (*i).size(), (*i).buffer());
+                                       _gui_feed_buffer.push_back ((*i).time() + start_sample, (*i).size(), (*i).buffer());
                                }
                        }
 
@@ -657,7 +609,7 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
        /* AUDIO BUTLER REQUIRED CODE */
 
        if (_playlists[DataType::AUDIO] && !c->empty()) {
-               if (((framecnt_t) c->front()->buf->read_space() >= _chunk_frames)) {
+               if (((samplecnt_t) c->front()->wbuf->read_space() >= _chunk_samples)) {
                        _need_butler = true;
                }
        }
@@ -668,15 +620,15 @@ DiskWriter::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame,
                _need_butler = true;
        }
 
-       DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 writer run, needs butler = %2\n", name(), _need_butler));
+       // DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 writer run, needs butler = %2\n", name(), _need_butler));
 }
 
 void
 DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c)
 {
        was_recording = false;
-       first_recordable_frame = max_framepos;
-       last_recordable_frame = max_framepos;
+       first_recordable_sample = max_samplepos;
+       last_recordable_sample = max_samplepos;
 
        if (capture_captured == 0) {
                return;
@@ -703,10 +655,10 @@ DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c)
 
        CaptureInfo* ci = new CaptureInfo;
 
-       ci->start =  capture_start_frame;
-       ci->frames = capture_captured;
+       ci->start =  capture_start_sample;
+       ci->samples = capture_captured;
 
-       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->frames));
+       DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("Finish capture, add new CI, %1 + %2\n", ci->start, ci->samples));
 
        /* XXX theoretical race condition here. Need atomic exchange ?
           However, the circumstances when this is called right
@@ -720,8 +672,18 @@ DiskWriter::finish_capture (boost::shared_ptr<ChannelList> c)
        capture_info.push_back (ci);
        capture_captured = 0;
 
-       /* now we've finished a capture, reset first_recordable_frame for next time */
-       first_recordable_frame = max_framepos;
+       /* now we've finished a capture, reset first_recordable_sample for next time */
+       first_recordable_sample = max_samplepos;
+}
+
+boost::shared_ptr<MidiBuffer>
+DiskWriter::get_gui_feed_buffer () const
+{
+       boost::shared_ptr<MidiBuffer> b (new MidiBuffer (AudioEngine::instance()->raw_buffer_size (DataType::MIDI)));
+
+       Glib::Threads::Mutex::Lock lm (_gui_feed_buffer_mutex);
+       b->copy (_gui_feed_buffer);
+       return b;
 }
 
 void
@@ -733,7 +695,7 @@ DiskWriter::set_record_enabled (bool yn)
 
        /* can't rec-enable in destructive mode if transport is before start */
 
-       if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+       if (destructive() && yn && _session.transport_sample() < _session.current_start_sample()) {
                return;
        }
 
@@ -762,7 +724,7 @@ DiskWriter::set_record_safe (bool yn)
        /* can't rec-safe in destructive mode if transport is before start ????
         REQUIRES REVIEW */
 
-       if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+       if (destructive() && yn && _session.transport_sample() < _session.current_start_sample()) {
                return;
        }
 
@@ -790,7 +752,7 @@ DiskWriter::prep_record_enable ()
 
        /* can't rec-enable in destructive mode if transport is before start */
 
-       if (destructive() && _session.transport_frame() < _session.current_start_frame()) {
+       if (destructive() && _session.transport_sample() < _session.current_start_sample()) {
                return false;
        }
 
@@ -823,8 +785,8 @@ DiskWriter::buffer_load () const
                return 1.0;
        }
 
-       return (float) ((double) c->front()->buf->write_space()/
-                       (double) c->front()->buf->bufsize());
+       return (float) ((double) c->front()->wbuf->write_space()/
+                       (double) c->front()->wbuf->bufsize());
 }
 
 void
@@ -843,28 +805,27 @@ DiskWriter::set_note_mode (NoteMode m)
 }
 
 int
-DiskWriter::seek (framepos_t frame, bool complete_refill)
+DiskWriter::seek (samplepos_t sample, bool complete_refill)
 {
        uint32_t n;
        ChannelList::iterator chan;
        boost::shared_ptr<ChannelList> c = channels.reader();
 
        for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
-               (*chan)->buf->reset ();
+               (*chan)->wbuf->reset ();
        }
 
        _midi_buf->reset ();
-       g_atomic_int_set(&_frames_read_from_ringbuffer, 0);
-       g_atomic_int_set(&_frames_written_to_ringbuffer, 0);
+       g_atomic_int_set(&_samples_read_from_ringbuffer, 0);
+       g_atomic_int_set(&_samples_written_to_ringbuffer, 0);
 
        /* can't rec-enable in destructive mode if transport is before start */
 
-       if (destructive() && record_enabled() && frame < _session.current_start_frame()) {
+       if (destructive() && record_enabled() && sample < _session.current_start_sample()) {
                disengage_record_enable ();
        }
 
-       playback_sample = frame;
-       file_frame = frame;
+       playback_sample = sample;
 
        return 0;
 }
@@ -876,7 +837,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
        int32_t ret = 0;
        RingBufferNPT<Sample>::rw_vector vector;
        RingBufferNPT<CaptureTransition>::rw_vector transvec;
-       framecnt_t total;
+       samplecnt_t total;
 
        transvec.buf[0] = 0;
        transvec.buf[1] = 0;
@@ -886,11 +847,11 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
        boost::shared_ptr<ChannelList> c = channels.reader();
        for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
 
-               (*chan)->buf->get_read_vector (&vector);
+               (*chan)->wbuf->get_read_vector (&vector);
 
                total = vector.len[0] + vector.len[1];
 
-               if (total == 0 || (total < _chunk_frames && !force_flush && was_recording)) {
+               if (total == 0 || (total < _chunk_samples && !force_flush && was_recording)) {
                        goto out;
                }
 
@@ -905,11 +866,11 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
                   let the caller know too.
                */
 
-               if (total >= 2 * _chunk_frames || ((force_flush || !was_recording) && total > _chunk_frames)) {
+               if (total >= 2 * _chunk_samples || ((force_flush || !was_recording) && total > _chunk_samples)) {
                        ret = 1;
                }
 
-               to_write = min (_chunk_frames, (framecnt_t) vector.len[0]);
+               to_write = min (_chunk_samples, (samplecnt_t) vector.len[0]);
 
                // check the transition buffer when recording destructive
                // important that we get this after the capture buf
@@ -930,7 +891,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
 
                                } else if (captrans.type == CaptureEnd) {
 
-                                       // capture end, the capture_val represents total frames in capture
+                                       // capture end, the capture_val represents total samples in capture
 
                                        if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) {
 
@@ -966,17 +927,17 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
                        return -1;
                }
 
-               (*chan)->buf->increment_read_ptr (to_write);
+               (*chan)->wbuf->increment_read_ptr (to_write);
                (*chan)->curr_capture_cnt += to_write;
 
-               if ((to_write == vector.len[0]) && (total > to_write) && (to_write < _chunk_frames) && !destructive()) {
+               if ((to_write == vector.len[0]) && (total > to_write) && (to_write < _chunk_samples) && !destructive()) {
 
                        /* we wrote all of vector.len[0] but it wasn't an entire
-                          disk_write_chunk_frames of data, so arrange for some part
+                          disk_write_chunk_samples of data, so arrange for some part
                           of vector.len[1] to be flushed to disk as well.
                        */
 
-                       to_write = min ((framecnt_t)(_chunk_frames - to_write), (framecnt_t) vector.len[1]);
+                       to_write = min ((samplecnt_t)(_chunk_samples - to_write), (samplecnt_t) vector.len[1]);
 
                         DEBUG_TRACE (DEBUG::Butler, string_compose ("%1 additional write of %2\n", name(), to_write));
 
@@ -985,7 +946,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
                                return -1;
                        }
 
-                       (*chan)->buf->increment_read_ptr (to_write);
+                       (*chan)->wbuf->increment_read_ptr (to_write);
                        (*chan)->curr_capture_cnt += to_write;
                }
        }
@@ -994,11 +955,11 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
 
        if (_midi_write_source) {
 
-               const framecnt_t total = g_atomic_int_get(const_cast<gint*> (&_frames_pending_write));
+               const samplecnt_t total = g_atomic_int_get(const_cast<gint*> (&_samples_pending_write));
 
                if (total == 0 ||
                    _midi_buf->read_space() == 0 ||
-                   (!force_flush && (total < _chunk_frames) && was_recording)) {
+                   (!force_flush && (total < _chunk_samples) && was_recording)) {
                        goto out;
                }
 
@@ -1013,7 +974,7 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
                   let the caller know too.
                */
 
-               if (total >= 2 * _chunk_frames || ((force_flush || !was_recording) && total > _chunk_frames)) {
+               if (total >= 2 * _chunk_samples || ((force_flush || !was_recording) && total > _chunk_samples)) {
                        ret = 1;
                }
 
@@ -1021,16 +982,16 @@ DiskWriter::do_flush (RunContext ctxt, bool force_flush)
                        /* push out everything we have, right now */
                        to_write = UINT32_MAX;
                } else {
-                       to_write = _chunk_frames;
+                       to_write = _chunk_samples;
                }
 
-               if (record_enabled() && ((total > _chunk_frames) || force_flush)) {
+               if (record_enabled() && ((total > _chunk_samples) || force_flush)) {
                        Source::Lock lm(_midi_write_source->mutex());
-                       if (_midi_write_source->midi_write (lm, *_midi_buf, get_capture_start_frame (0), to_write) != to_write) {
+                       if (_midi_write_source->midi_write (lm, *_midi_buf, get_capture_start_sample (0), to_write) != to_write) {
                                error << string_compose(_("MidiDiskstream %1: cannot write to disk"), id()) << endmsg;
                                return -1;
                        }
-                       g_atomic_int_add(const_cast<gint*> (&_frames_pending_write), -to_write);
+                       g_atomic_int_add(const_cast<gint*> (&_samples_pending_write), -to_write);
                }
        }
 
@@ -1171,7 +1132,7 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
 {
        bool more_work = true;
        int err = 0;
-       framecnt_t total_capture;
+       samplecnt_t total_capture;
        SourceList audio_srcs;
        SourceList midi_srcs;
        ChannelList::iterator chan;
@@ -1235,7 +1196,7 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
        }
 
        for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
-               total_capture += (*ci)->frames;
+               total_capture += (*ci)->samples;
        }
 
        /* figure out the name for this take */
@@ -1294,15 +1255,15 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
 
                /* set length in beats to entire capture length */
 
-               BeatsFramesConverter converter (_session.tempo_map(), capture_info.front()->start);
-               const Evoral::Beats total_capture_beats = converter.from (total_capture);
+               BeatsSamplesConverter converter (_session.tempo_map(), capture_info.front()->start);
+               const Temporal::Beats total_capture_beats = converter.from (total_capture);
                _midi_write_source->set_length_beats (total_capture_beats);
 
                /* flush to disk: this step differs from the audio path,
                   where all the data is already on disk.
                */
 
-               _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Evoral::Beats>::ResolveStuckNotes, total_capture_beats);
+               _midi_write_source->mark_midi_streaming_write_completed (source_lock, Evoral::Sequence<Temporal::Beats>::ResolveStuckNotes, total_capture_beats);
        }
 
        _last_capture_sources.insert (_last_capture_sources.end(), audio_srcs.begin(), audio_srcs.end());
@@ -1326,11 +1287,11 @@ DiskWriter::transport_stopped_wallclock (struct tm& when, time_t twhen, bool abo
        }
 
        capture_info.clear ();
-       capture_start_frame = 0;
+       capture_start_sample = 0;
 }
 
 void
-DiskWriter::transport_looped (framepos_t transport_frame)
+DiskWriter::transport_looped (samplepos_t transport_sample)
 {
        if (was_recording) {
                // all we need to do is finish this capture, with modified capture length
@@ -1341,9 +1302,9 @@ DiskWriter::transport_looped (framepos_t transport_frame)
                // the next region will start recording via the normal mechanism
                // we'll set the start position to the current transport pos
                // no latency adjustment or capture offset needs to be made, as that already happened the first time
-               capture_start_frame = transport_frame;
-               first_recordable_frame = transport_frame; // mild lie
-               last_recordable_frame = max_framepos;
+               capture_start_sample = transport_sample;
+               first_recordable_sample = transport_sample; // mild lie
+               last_recordable_sample = max_samplepos;
                was_recording = true;
 
                if (recordable() && destructive()) {
@@ -1354,7 +1315,7 @@ DiskWriter::transport_looped (framepos_t transport_frame)
 
                                if (transvec.len[0] > 0) {
                                        transvec.buf[0]->type = CaptureStart;
-                                       transvec.buf[0]->capture_val = capture_start_frame;
+                                       transvec.buf[0]->capture_val = capture_start_sample;
                                        (*chan)->capture_transition_buf->increment_write_ptr(1);
                                }
                                else {
@@ -1396,7 +1357,7 @@ DiskWriter::setup_destructive_playlist ()
        PropertyList plist;
        plist.add (Properties::name, _name.val());
        plist.add (Properties::start, 0);
-       plist.add (Properties::length, max_framepos - srcs.front()->natural_position());
+       plist.add (Properties::length, max_samplepos - srcs.front()->natural_position());
 
        boost::shared_ptr<Region> region (RegionFactory::create (srcs, plist));
        _playlists[DataType::AUDIO]->add_region (region, srcs.front()->natural_position());
@@ -1437,7 +1398,7 @@ DiskWriter::use_destructive_playlist ()
 
        /* be sure to stretch the region out to the maximum length (non-musical)*/
 
-       region->set_length (max_framepos - region->position(), 0);
+       region->set_length (max_samplepos - region->position(), 0);
 
        uint32_t n;
        ChannelList::iterator chan;
@@ -1448,14 +1409,8 @@ DiskWriter::use_destructive_playlist ()
                assert((*chan)->write_source);
                (*chan)->write_source->set_allow_remove_if_empty (false);
 
-               /* this might be false if we switched modes, so force it */
-
-#ifdef XXX_OLD_DESTRUCTIVE_API_XXX
-               (*chan)->write_source->set_destructive (true);
-#else
                // should be set when creating the source or loading the state
                assert ((*chan)->write_source->destructive());
-#endif
        }
 
        /* the source list will never be reset for a destructive track */
@@ -1474,13 +1429,12 @@ DiskWriter::adjust_buffering ()
 void
 DiskWriter::realtime_handle_transport_stopped ()
 {
-       realtime_speed_change ();
 }
 
 bool
 DiskWriter::set_name (string const & str)
 {
-       string my_name = X_("writer:");
+       string my_name = X_("recorder:");
        my_name += str;
 
        if (_name != my_name) {