Merged with trunk R992.
[ardour.git] / libs / ardour / midi_diskstream.cc
index 2c88d5daa8d0d2a2d3462299008dcf79b1d6b3ac..8247aac217c3249ffb305c04fe81d6be44f155b7 100644 (file)
@@ -45,6 +45,7 @@
 #include <ardour/smf_source.h>
 #include <ardour/destructive_filesource.h>
 #include <ardour/send.h>
+#include <ardour/region_factory.h>
 #include <ardour/midi_playlist.h>
 #include <ardour/cycle_timer.h>
 #include <ardour/midi_region.h>
@@ -61,13 +62,13 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        : Diskstream(sess, name, flag)
        , _playback_buf(0)
        , _capture_buf(0)
-       , _current_playback_buffer(0)
-       , _current_capture_buffer(0)
-       , _playback_wrap_buffer(0)
-       , _capture_wrap_buffer(0)
+       //, _current_playback_buffer(0)
+       //, _current_capture_buffer(0)
+       //, _playback_wrap_buffer(0)
+       //, _capture_wrap_buffer(0)
        , _source_port(0)
-       , _write_source(0)
        , _capture_transition_buf(0)
+       , _last_flush_frame(0)
 {
        /* prevent any write sources from being created */
 
@@ -79,20 +80,19 @@ MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::F
        in_set_state = false;
 
        assert(!destructive());
-       DiskstreamCreated (this); /* EMIT SIGNAL */
 }
        
 MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        : Diskstream(sess, node)
        , _playback_buf(0)
        , _capture_buf(0)
-       , _current_playback_buffer(0)
-       , _current_capture_buffer(0)
-       , _playback_wrap_buffer(0)
-       , _capture_wrap_buffer(0)
+       //, _current_playback_buffer(0)
+       //, _current_capture_buffer(0)
+       //, _playback_wrap_buffer(0)
+       //, _capture_wrap_buffer(0)
        , _source_port(0)
-       , _write_source(0)
        , _capture_transition_buf(0)
+       , _last_flush_frame(0)
 {
        in_set_state = true;
        init (Recordable);
@@ -107,8 +107,6 @@ MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
        if (destructive()) {
                use_destructive_playlist ();
        }
-
-       DiskstreamCreated (this); /* EMIT SIGNAL */
 }
 
 void
@@ -124,10 +122,10 @@ MidiDiskstream::init (Diskstream::Flag f)
        set_block_size (_session.get_block_size());
        allocate_temporary_buffers ();
 
-       _playback_wrap_buffer = new RawMidi[wrap_buffer_size];
-       _capture_wrap_buffer = new RawMidi[wrap_buffer_size];
-       _playback_buf = new RingBufferNPT<RawMidi> (_session.diskstream_buffer_size());
-       _capture_buf = new RingBufferNPT<RawMidi> (_session.diskstream_buffer_size());
+       //_playback_wrap_buffer = new RawMidi[wrap_buffer_size];
+       //_capture_wrap_buffer = new RawMidi[wrap_buffer_size];
+       _playback_buf = new MidiRingBuffer (_session.diskstream_buffer_size());
+       _capture_buf = new MidiRingBuffer (_session.diskstream_buffer_size());
        _capture_transition_buf = new RingBufferNPT<CaptureTransition> (128);
        
        _n_channels = ChanCount(DataType::MIDI, 1);
@@ -180,6 +178,8 @@ MidiDiskstream::non_realtime_input_change ()
        else {
                seek (_session.transport_frame());
        }
+
+       _last_flush_frame = _session.transport_frame();
 }
 
 void
@@ -356,7 +356,7 @@ MidiDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nframe
                        if (_alignment_style == ExistingMaterial) {
 
 
-                               if (!_session.get_punch_in()) {
+                               if (!Config->get_punch_in()) {
 
                                        /* manual punch in happens at the correct transport frame
                                           because the user hit a button. but to get alignment correct 
@@ -385,7 +385,7 @@ MidiDiskstream::check_record_status (jack_nframes_t transport_frame, jack_nframe
 
                        } else {
 
-                               if (_session.get_punch_in()) {
+                               if (Config->get_punch_in()) {
                                        first_recordable_frame += _roll_delay;
                                } else {
                                        capture_start_frame -= _roll_delay;
@@ -435,9 +435,9 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
        bool           nominally_recording;
        bool           re = record_enabled ();
        bool           collect_playback = false;
-       
-       _current_capture_buffer = 0;
-       _current_playback_buffer = 0;
+
+       /*_current_capture_buffer = 0;
+         _current_playback_buffer = 0;*/
 
        /* if we've already processed the frames corresponding to this call,
           just return. this allows multiple routes that are taking input
@@ -445,7 +445,7 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
           this stuff only happen once. more commonly, it allows both
           the AudioTrack that is using this AudioDiskstream *and* the Session
           to call process() without problems.
-       */
+          */
 
        if (_processed) {
                return 0;
@@ -463,58 +463,58 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
        /* This lock is held until the end of AudioDiskstream::commit, so these two functions
           must always be called as a pair. The only exception is if this function
           returns a non-zero value, in which case, ::commit should not be called.
-       */
+          */
 
        // If we can't take the state lock return.
        if (!state_lock.trylock()) {
                return 1;
        }
-       
+
        adjust_capture_position = 0;
 
-       if (nominally_recording || (_session.get_record_enabled() && _session.get_punch_in())) {
+       if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
                OverlapType ot;
-               
+
                ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
 
                switch (ot) {
-               case OverlapNone:
-                       rec_nframes = 0;
-                       break;
-                       
-               case OverlapInternal:
-               /*     ----------    recrange
-                         |---|       transrange
-               */
-                       rec_nframes = nframes;
-                       rec_offset = 0;
-                       break;
-                       
-               case OverlapStart:
-                       /*    |--------|    recrange
-                            -----|          transrange
-                       */
-                       rec_nframes = transport_frame + nframes - first_recordable_frame;
-                       if (rec_nframes) {
+                       case OverlapNone:
+                               rec_nframes = 0;
+                               break;
+
+                       case OverlapInternal:
+                               /*     ----------    recrange
+                                          |---|       transrange
+                                          */
+                               rec_nframes = nframes;
+                               rec_offset = 0;
+                               break;
+
+                       case OverlapStart:
+                               /*    |--------|    recrange
+                                         -----|          transrange
+                                         */
+                               rec_nframes = transport_frame + nframes - first_recordable_frame;
+                               if (rec_nframes) {
+                                       rec_offset = first_recordable_frame - transport_frame;
+                               }
+                               break;
+
+                       case OverlapEnd:
+                               /*    |--------|    recrange
+                                         |--------  transrange
+                                         */
+                               rec_nframes = last_recordable_frame - transport_frame;
+                               rec_offset = 0;
+                               break;
+
+                       case OverlapExternal:
+                               /*    |--------|    recrange
+                                         --------------  transrange
+                                         */
+                               rec_nframes = last_recordable_frame - last_recordable_frame;
                                rec_offset = first_recordable_frame - transport_frame;
-                       }
-                       break;
-                       
-               case OverlapEnd:
-                       /*    |--------|    recrange
-                                 |--------  transrange
-                       */
-                       rec_nframes = last_recordable_frame - transport_frame;
-                       rec_offset = 0;
-                       break;
-                       
-               case OverlapExternal:
-                       /*    |--------|    recrange
-                            --------------  transrange
-                       */
-                       rec_nframes = last_recordable_frame - last_recordable_frame;
-                       rec_offset = first_recordable_frame - transport_frame;
-                       break;
+                               break;
                }
 
                if (rec_nframes && !was_recording) {
@@ -529,58 +529,22 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
        }
 
        if (nominally_recording || rec_nframes) {
-               _capture_buf->get_write_vector (&_capture_vector);
-
-               if (rec_nframes <= _capture_vector.len[0]) {
-
-                       _current_capture_buffer = _capture_vector.buf[0];
 
-                       /* note: grab the entire port buffer, but only copy what we were supposed to for recording, and use
-                          rec_offset
-                          */
+               assert(_source_port);
 
-                       // FIXME: midi buffer size?
+               // Pump entire port buffer into the ring buffer (FIXME!)
+               _capture_buf->write(_source_port->get_midi_buffer(), transport_frame);
 
-                       // FIXME: reading from a MIDI port is different, can't just memcpy
-                       //memcpy (_current_capture_buffer, _io->input(0)->get_buffer (rec_nframes) + offset + rec_offset, sizeof (RawMidi) * rec_nframes);
-                       assert(_source_port);
-                       for (size_t i=0; i < _source_port->size(); ++i) {
-                               cerr << "DISKSTREAM GOT EVENT " << i << "!!\n";
-                       }
-
-                       //if (_source_port->size() == 0)
-                       //      cerr << "No events :/ (1)\n";
+               // FIXME: hackitty hack, don't come back
+               //_write_source->ViewDataRangeReady (_write_source->length(), rec_nframes); /* EMIT SIGNAL */
+               /*
+                  for (size_t i=0; i < _source_port->size(); ++i) {
+                  cerr << "DISKSTREAM GOT EVENT(1) " << i << "!!\n";
+                  }
 
-
-               } else {
-
-                       jack_nframes_t total = _capture_vector.len[0] + _capture_vector.len[1];
-
-                       if (rec_nframes > total) {
-                               cerr << "DiskOverrun\n";
-                               //DiskOverrun (); // FIXME
-                               goto out;
-                       }
-
-                       // FIXME (see above)
-                       //RawMidi* buf = _io->input (0)->get_buffer (nframes) + offset;
-                       assert(_source_port);
-                       for (size_t i=0; i < _source_port->size(); ++i) {
-                               cerr << "DISKSTREAM GOT EVENT " << i << "!!\n";
-                       }
-                       //if (_source_port->size() == 0)
-                       //      cerr << "No events :/ (2)\n";
-                       RawMidi* buf = NULL; // FIXME FIXME FIXME (make it compile)
-                       assert(false);
-                       jack_nframes_t first = _capture_vector.len[0];
-
-                       memcpy (_capture_wrap_buffer, buf, sizeof (RawMidi) * first);
-                       memcpy (_capture_vector.buf[0], buf, sizeof (RawMidi) * first);
-                       memcpy (_capture_wrap_buffer+first, buf + first, sizeof (RawMidi) * (rec_nframes - first));
-                       memcpy (_capture_vector.buf[1], buf + first, sizeof (RawMidi) * (rec_nframes - first));
-
-                       _current_capture_buffer = _capture_wrap_buffer;
-               }
+                  if (_source_port->size() == 0)
+                  cerr << "No events :/ (1)\n";
+                  */
        } else {
 
                if (was_recording) {
@@ -588,38 +552,19 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
                }
 
        }
-       
+
        if (rec_nframes) {
-               
-               // FIXME: filthy hack to fool the GUI into thinking we're doing something
-               if (_write_source)
-                       _write_source->ViewDataRangeReady (transport_frame, rec_nframes); /* EMIT SIGNAL */
 
+               /* XXX XXX XXX XXX XXX XXX XXX XXX */
                /* data will be written to disk */
 
-               if (rec_nframes == nframes && rec_offset == 0) {
-
-                       _current_playback_buffer = _current_capture_buffer;
-                       playback_distance = nframes;
-
-               } else {
-
-
-                       /* we can't use the capture buffer as the playback buffer, because
-                          we recorded only a part of the current process' cycle data
-                          for capture.
-                       */
-
-                       collect_playback = true;
-               }
-
                adjust_capture_position = rec_nframes;
 
        } else if (nominally_recording) {
 
                /* can't do actual capture yet - waiting for latency effects to finish before we start*/
 
-               _current_playback_buffer = _current_capture_buffer;
+               // Ummm.. well, I suppose we'll just hang out for a bit?
 
                playback_distance = nframes;
 
@@ -641,84 +586,22 @@ MidiDiskstream::process (jack_nframes_t transport_frame, jack_nframes_t nframes,
                } else {
                        necessary_samples = nframes;
                }
-               
-               _playback_buf->get_read_vector (&_playback_vector);
-               
-               if (necessary_samples <= _playback_vector.len[0]) {
 
-                       _current_playback_buffer = _playback_vector.buf[0];
-
-               } else {
-                       jack_nframes_t total = _playback_vector.len[0] + _playback_vector.len[1];
-                       
-                       if (necessary_samples > total) {
-                               //cerr << "DiskUnderrun\n";
-                               //DiskUnderrun (); // FIXME
-                               //goto out;
-                               
-                       } else {
-                               
-                               memcpy (_playback_wrap_buffer, _playback_vector.buf[0],
-                                       _playback_vector.len[0] * sizeof (RawMidi));
-                               memcpy (_playback_wrap_buffer + _playback_vector.len[0], _playback_vector.buf[1], 
-                                       (necessary_samples - _playback_vector.len[0]) * sizeof (RawMidi));
-                               
-                               _current_playback_buffer = _playback_wrap_buffer;
-                       }
-               }
-
-#if 0
-               if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
-                       
-                       uint64_t phase = last_phase;
-                       jack_nframes_t i = 0;
-
-                       // Linearly interpolate into the alt buffer
-                       // using 40.24 fixp maths (swh)
-
-                       for (c = channels.begin(); c != channels.end(); ++c) {
-
-                               float fr;
-                               ChannelInfo& chan (*c);
-
-                               i = 0;
-                               phase = last_phase;
-
-                               for (jack_nframes_t outsample = 0; outsample < nframes; ++outsample) {
-                                       i = phase >> 24;
-                                       fr = (phase & 0xFFFFFF) / 16777216.0f;
-                                       chan.speed_buffer[outsample] = 
-                                               chan._current_playback_buffer[i] * (1.0f - fr) +
-                                               chan._current_playback_buffer[i+1] * fr;
-                                       phase += phi;
-                               }
-                               
-                               chan._current_playback_buffer = chan.speed_buffer;
-                       }
-
-                       playback_distance = i + 1;
-                       last_phase = (phase & 0xFFFFFF);
-
-               } else {
-                       playback_distance = nframes;
-               }
-#endif
-
-               playback_distance = nframes;
+               // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+               // Write into playback buffer here, and whatnot
 
        }
 
        ret = 0;
 
-  out:
        _processed = true;
 
        if (ret) {
 
                /* we're exiting with failure, so ::commit will not
                   be called. unlock the state lock.
-               */
-               
+                  */
+
                state_lock.unlock();
        } 
 
@@ -740,24 +623,27 @@ MidiDiskstream::commit (jack_nframes_t nframes)
                playback_sample += playback_distance;
        }
 
-               _playback_buf->increment_read_ptr (playback_distance);
-               
-               if (adjust_capture_position) {
-                       _capture_buf->increment_write_ptr (adjust_capture_position);
-               }
-       
+       /* XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX */
+
+       /*
+       _playback_buf->increment_read_ptr (playback_distance);
+
+       if (adjust_capture_position) {
+               _capture_buf->increment_write_ptr (adjust_capture_position);
+       }
+*/
        if (adjust_capture_position != 0) {
                capture_captured += adjust_capture_position;
                adjust_capture_position = 0;
        }
-       
+
        if (_slaved) {
-               need_butler = _playback_buf->write_space() >= _playback_buf->bufsize() / 2;
+               need_butler = _playback_buf->write_space() >= _playback_buf->capacity() / 2;
        } else {
                need_butler = _playback_buf->write_space() >= disk_io_chunk_frames
                        || _capture_buf->read_space() >= disk_io_chunk_frames;
        }
-
+       
        state_lock.unlock();
 
        _processed = false;
@@ -786,24 +672,127 @@ int
 MidiDiskstream::seek (jack_nframes_t frame, bool complete_refill)
 {
        Glib::Mutex::Lock lm (state_lock);
-       return 0;
+       int ret = -1;
+
+       _playback_buf->reset();
+       _capture_buf->reset();
+
+       playback_sample = frame;
+       file_frame = frame;
+       _last_flush_frame = frame;
+
+       if (complete_refill) {
+               while ((ret = do_refill_with_alloc ()) > 0) ;
+       } else {
+               ret = do_refill_with_alloc ();
+       }
+
+       return ret;
 }
 
 int
 MidiDiskstream::can_internal_playback_seek (jack_nframes_t distance)
 {
-       return 0;
+       if (_playback_buf->read_space() < distance) {
+               return false;
+       } else {
+               return true;
+       }
 }
 
 int
 MidiDiskstream::internal_playback_seek (jack_nframes_t distance)
 {
+       first_recordable_frame += distance;
+       playback_sample += distance;
+
        return 0;
 }
 
+/** @a start is set to the new frame position (TIME) read up to */
 int
-MidiDiskstream::read (RawMidi* buf, jack_nframes_t& start, jack_nframes_t cnt, bool reversed)
-{
+MidiDiskstream::read (jack_nframes_t& start, jack_nframes_t dur, bool reversed)
+{      
+       jack_nframes_t this_read = 0;
+       bool reloop = false;
+       jack_nframes_t loop_end = 0;
+       jack_nframes_t loop_start = 0;
+       jack_nframes_t loop_length = 0;
+       Location *loc = 0;
+
+       if (!reversed) {
+               /* Make the use of a Location atomic for this read operation.
+                  
+                  Note: Locations don't get deleted, so all we care about
+                  when I say "atomic" is that we are always pointing to
+                  the same one and using a start/length values obtained
+                  just once.
+               */
+               
+               if ((loc = loop_location) != 0) {
+                       loop_start = loc->start();
+                       loop_end = loc->end();
+                       loop_length = loop_end - loop_start;
+               }
+               
+               /* if we are looping, ensure that the first frame we read is at the correct
+                  position within the loop.
+               */
+               
+               if (loc && start >= loop_end) {
+                       //cerr << "start adjusted from " << start;
+                       start = loop_start + ((start - loop_start) % loop_length);
+                       //cerr << "to " << start << endl;
+               }
+               //cerr << "start is " << start << "  loopstart: " << loop_start << "  loopend: " << loop_end << endl;
+       }
+
+       while (dur) {
+
+               /* take any loop into account. we can't read past the end of the loop. */
+
+               if (loc && (loop_end - start < dur)) {
+                       this_read = loop_end - start;
+                       //cerr << "reloop true: thisread: " << this_read << "  dur: " << dur << endl;
+                       reloop = true;
+               } else {
+                       reloop = false;
+                       this_read = dur;
+               }
+
+               if (this_read == 0) {
+                       break;
+               }
+
+               this_read = min(dur,this_read);
+
+               if (midi_playlist()->read (*_playback_buf, start, this_read) != this_read) {
+                       error << string_compose(_("MidiDiskstream %1: cannot read %2 from playlist at frame %3"), _id, this_read, 
+                                        start) << endmsg;
+                       return -1;
+               }
+
+               _read_data_count = _playlist->read_data_count();
+               
+               if (reversed) {
+
+                       cerr << "Reversed MIDI.. that's just crazy talk." << endl;
+                       // Swap note ons with note offs here
+
+               } else {
+                       
+                       /* if we read to the end of the loop, go back to the beginning */
+                       
+                       if (reloop) {
+                               start = loop_start;
+                       } else {
+                               start += this_read;
+                       }
+               } 
+
+               dur -= this_read;
+       }
+
        return 0;
 }
 
@@ -816,8 +805,94 @@ MidiDiskstream::do_refill_with_alloc ()
 int
 MidiDiskstream::do_refill ()
 {
-       // yeah, the data's ready.  promise.
-       return 0;
+       int32_t        ret = 0;
+       size_t         write_space = _playback_buf->write_space();
+
+       bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+
+       if (write_space == 0) {
+               return 0;
+       }
+
+       /* if there are 2+ chunks of disk i/o possible for
+          this track, let the caller know so that it can arrange
+          for us to be called again, ASAP.
+          */
+
+       // FIXME: using disk_io_chunk_frames as an event count, not good
+       if (_playback_buf->write_space() >= (_slaved?3:2) * disk_io_chunk_frames) {
+               ret = 1;
+       }
+
+       /* if we're running close to normal speed and there isn't enough 
+          space to do disk_io_chunk_frames of I/O, then don't bother.  
+
+          at higher speeds, just do it because the sync between butler
+          and audio thread may not be good enough.
+          */
+
+       if ((write_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
+               cerr << "No refill 1\n";
+               return 0;
+       }
+
+       /* when slaved, don't try to get too close to the read pointer. this
+          leaves space for the buffer reversal to have something useful to
+          work with.
+          */
+
+       if (_slaved && write_space < (_playback_buf->capacity() / 2)) {
+               cerr << "No refill 2\n";
+               return 0;
+       }
+
+       if (reversed) {
+               cerr << "No refill 3 (reverse)\n";
+               return 0;
+       }
+
+       if (file_frame == max_frames) {
+               //cerr << "No refill 4 (EOF)\n";
+
+               /* at end: nothing to do */
+
+               return 0;
+       }
+
+#if 0
+       // or this
+       if (file_frame > max_frames - total_space) {
+
+               /* to close to the end: read what we can, and zero fill the rest */
+
+               zero_fill = total_space - (max_frames - file_frame);
+               total_space = max_frames - file_frame;
+
+       } else {
+               zero_fill = 0;
+       }
+#endif
+
+       // At this point we:
+       assert(_playback_buf->write_space() > 0); // ... have something to write to, and
+       assert(file_frame <= max_frames); // ... something to write
+
+       // So (read it, then) write it:
+       
+       jack_nframes_t file_frame_tmp = file_frame;
+       jack_nframes_t to_read = min(disk_io_chunk_frames, (max_frames - file_frame));
+       
+       // FIXME: read count?
+       if (read (file_frame_tmp, to_read, reversed)) {
+               ret = -1;
+               goto out;
+       }
+
+       file_frame = file_frame_tmp;
+
+out:
+
+       return ret;
 }
 
 /** Flush pending data to disk.
@@ -833,10 +908,60 @@ MidiDiskstream::do_refill ()
 int
 MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
 {
-       /* hey, so did you write that data? */
+       uint32_t to_write;
+       int32_t ret = 0;
+       // FIXME: I'd be lying if I said I knew what this thing was
+       //RingBufferNPT<CaptureTransition>::rw_vector transvec;
+       jack_nframes_t total;
 
-       // oh yeah, you bet.  wrote it good.  honest.
-       
+       _write_data_count = 0;
+
+       if (_last_flush_frame > _session.transport_frame()) {
+               _last_flush_frame = _session.transport_frame();
+       }
+
+       total = _session.transport_frame() - _last_flush_frame;
+
+
+       // FIXME: put this condition back in! (removed for testing)
+       if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
+               //cerr << "MDS - no flush 1\n";
+               goto out;
+       }
+
+       /* if there are 2+ chunks of disk i/o possible for
+          this track, let the caller know so that it can arrange
+          for us to be called again, ASAP.
+
+          if we are forcing a flush, then if there is* any* extra
+          work, let the caller know.
+
+          if we are no longer recording and there is any extra work,
+          let the caller know too.
+          */
+
+       if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
+               ret = 1;
+       } 
+
+       //to_write = min (disk_io_chunk_frames, (jack_nframes_t) vector.len[0]);
+       to_write = disk_io_chunk_frames;
+
+       assert(!destructive());
+
+       if ((!_write_source) || _write_source->write (*_capture_buf, to_write) != to_write) {
+               //cerr << "MDS - no flush 2\n";
+               error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
+               return -1;
+       } else {
+               _last_flush_frame = _session.transport_frame();
+               //cerr << "MDS - flushed\n";
+       }
+
+       //(*chan).curr_capture_cnt += to_write;
+
+out:
+       //return ret;
        return 0;
 }
 
@@ -846,7 +971,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
        uint32_t buffer_position;
        bool more_work = true;
        int err = 0;
-       MidiRegion* region = 0;
+       boost::shared_ptr<MidiRegion> region;
        jack_nframes_t total_capture;
        MidiRegion::SourceList srcs;
        MidiRegion::SourceList::iterator src;
@@ -881,25 +1006,15 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
 
        if (abort_capture) {
 
-               list<Source*>* deletion_list = new list<Source*>;
-
                if (_write_source) {
-                       _write_source->mark_for_remove ();
-                       _write_source->release ();
-
-                       deletion_list->push_back (_write_source);
 
-                       _write_source = 0;
+                       _write_source->mark_for_remove ();
+                       _write_source->drop_references ();
+                       _write_source.reset();
                }
 
                /* new source set up in "out" below */
 
-               if (!deletion_list->empty()) {
-                       DeleteSources (deletion_list);
-               } else {
-                       delete deletion_list;
-               }
-
        } else {
 
                assert(_write_source);
@@ -910,7 +1025,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
 
                /* figure out the name for this take */
 
-               SMFSource* s = _write_source;
+               boost::shared_ptr<SMFSource> s = _write_source;
 
                if (s) {
 
@@ -930,10 +1045,12 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                   */
                try {
                        assert(_write_source);
-                       region = new MidiRegion (srcs, _write_source->last_capture_start_frame(), total_capture, 
-                                       region_name_from_path (_write_source->name()), 
-                                       0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile));
+                       
+                       boost::shared_ptr<Region> rx (RegionFactory::create (srcs, _write_source->last_capture_start_frame(), total_capture, 
+                                                                            region_name_from_path (_write_source->name()), 
+                                                                            0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile)));
 
+                       region = boost::dynamic_pointer_cast<MidiRegion> (rx);
                        region->special_set_position (capture_info.front()->start);
                }
 
@@ -958,11 +1075,12 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                        // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
 
                        try {
-                               region = new MidiRegion (srcs, buffer_position, (*ci)->frames, region_name);
+                               boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
+                               region = boost::dynamic_pointer_cast<MidiRegion> (rx);
                        }
 
                        catch (failed_constructor& err) {
-                               error << _("MidiDiskstream: could not create region for captured audio!") << endmsg;
+                               error << _("MidiDiskstream: could not create region for captured midi!") << endmsg;
                                continue; /* XXX is this OK? */
                        }
 
@@ -971,7 +1089,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
                        // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
 
                        i_am_the_modifier++;
-                       _playlist->add_region (*region, (*ci)->start);
+                       _playlist->add_region (region, (*ci)->start);
                        i_am_the_modifier--;
 
                        buffer_position += (*ci)->frames;
@@ -979,7 +1097,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap
 
                _playlist->thaw ();
                XMLNode &after = _playlist->get_state();
-               _session.add_command (new MementoCommand<Playlist>(*_playlist, before, after));
+               _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
 
                mark_write_completed = true;
 
@@ -1009,7 +1127,7 @@ MidiDiskstream::finish_capture (bool rec_monitors_input)
 
        CaptureInfo* ci = new CaptureInfo;
        
-       ci->start  =  capture_start_frame;
+       ci->start  = capture_start_frame;
        ci->frames = capture_captured;
        
        /* XXX theoretical race condition here. Need atomic exchange ? 
@@ -1065,8 +1183,8 @@ MidiDiskstream::engage_record_enable ()
 
        g_atomic_int_set (&_record_enabled, 1);
        
-       if (Config->get_use_hardware_monitoring() && _source_port) {
-               _source_port->request_monitor_input (!(_session.get_auto_input() && rolling));
+       if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
+               _source_port->request_monitor_input (!(Config->get_auto_input() && rolling));
        }
 
        RecordEnableChanged (); /* EMIT SIGNAL */
@@ -1076,7 +1194,7 @@ void
 MidiDiskstream::disengage_record_enable ()
 {
        g_atomic_int_set (&_record_enabled, 0);
-       if (Config->get_use_hardware_monitoring()) {
+       if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
                if (_source_port) {
                        _source_port->request_monitor_input (false);
                }
@@ -1101,7 +1219,7 @@ MidiDiskstream::get_state ()
        node->add_property ("speed", buf);
 
        node->add_property("name", _name);
-       id().print(buf);
+       id().print(buf, sizeof(buf));
        node->add_property("id", buf);
 
        if (_write_source && _session.get_record_enabled()) {
@@ -1117,7 +1235,7 @@ MidiDiskstream::get_state ()
 
                Location* pi;
 
-               if (_session.get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
+               if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
                        snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
                } else {
                        snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
@@ -1235,16 +1353,14 @@ MidiDiskstream::use_new_write_source (uint32_t n)
 
                if (SMFSource::is_empty (_write_source->path())) {
                        _write_source->mark_for_remove ();
-                       _write_source->release();
-                       delete _write_source;
+                       _write_source.reset();
                } else {
-                       _write_source->release();
-                       _write_source = 0;
+                       _write_source.reset();
                }
        }
 
        try {
-               _write_source = dynamic_cast<SMFSource*>(_session.create_midi_source_for_session (*this));
+               _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (*this));
                if (!_write_source) {
                        throw failed_constructor();
                }
@@ -1252,11 +1368,10 @@ MidiDiskstream::use_new_write_source (uint32_t n)
 
        catch (failed_constructor &err) {
                error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
-               _write_source = 0;
+               _write_source.reset();
                return -1;
        }
 
-       _write_source->use ();
        _write_source->set_allow_remove_if_empty (true);
 
        return 0;
@@ -1272,8 +1387,10 @@ MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force)
        if (_write_source && mark_write_complete) {
                _write_source->mark_streaming_write_completed ();
        }
-       use_new_write_source ();
-       assert(_write_source);
+
+       if (!_write_source) {
+               use_new_write_source ();
+       }
 }
 
 int
@@ -1332,14 +1449,14 @@ float
 MidiDiskstream::playback_buffer_load () const
 {
        return (float) ((double) _playback_buf->read_space()/
-                       (double) _playback_buf->bufsize());
+                       (double) _playback_buf->capacity());
 }
 
 float
 MidiDiskstream::capture_buffer_load () const
 {
        return (float) ((double) _capture_buf->write_space()/
-                       (double) _capture_buf->bufsize());
+                       (double) _capture_buf->capacity());
 }
 
 
@@ -1348,3 +1465,56 @@ MidiDiskstream::use_pending_capture_data (XMLNode& node)
 {
        return 0;
 }
+
+/** Writes playback events in the given range to dst, translating time stamps
+ * so that an event at start has time = 0
+ */
+void
+MidiDiskstream::get_playback(MidiBuffer& dst, jack_nframes_t start, jack_nframes_t end)
+{
+       dst.clear();
+       assert(dst.size() == 0);
+       
+       // I think this happens with reverse varispeed?  maybe?
+       if (end <= start) {
+               return;
+       }
+
+/*
+       cerr << "MIDI Diskstream pretending to read" << endl;
+
+       MidiEvent ev;
+       RawMidi data[4];
+
+       const char note = rand()%30 + 30;
+       
+       ev.buffer = data;
+       ev.time = 0;
+       ev.size = 3;
+
+       data[0] = 0x90;
+       data[1] = note;
+       data[2] = 120;
+
+       dst.push_back(ev);
+       
+       ev.buffer = data;
+       ev.time = (end - start) / 2;
+       ev.size = 3;
+
+       data[0] = 0x80;
+       data[1] = note;
+       data[2] = 64;
+*/
+       _playback_buf->read(dst, start, end);
+
+       // Translate time stamps to be relative to the start of this cycle
+       for (size_t i=0; i < dst.size(); ++i) {
+               assert(dst[i].time >= start);
+               assert(dst[i].time <= end);
+               //cerr << "Translating event stamp " << dst[i].time << " to ";
+               dst[i].time -= start;
+               //cerr << dst[i].time << endl;
+
+       }
+}