remove Track::hidden(); replace with Stripable::is_private_route()
[ardour.git] / libs / ardour / midi_track.cc
index 3aba6bf39ec6ea5aaf91355703e3213773d211b5..777db1d91d864b04eed98170dd5239f8c77e7664 100644 (file)
 #endif
 
 #include "pbd/enumwriter.h"
-#include "pbd/convert.h"
+#include "pbd/types_convert.h"
 #include "evoral/midi_util.h"
 
+#include "ardour/amp.h"
 #include "ardour/beats_frames_converter.h"
 #include "ardour/buffer_set.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
+#include "ardour/disk_reader.h"
+#include "ardour/disk_writer.h"
 #include "ardour/event_type_map.h"
 #include "ardour/meter.h"
-#include "ardour/midi_diskstream.h"
 #include "ardour/midi_playlist.h"
 #include "ardour/midi_port.h"
 #include "ardour/midi_region.h"
 #include "ardour/midi_track.h"
+#include "ardour/monitor_control.h"
 #include "ardour/parameter_types.h"
 #include "ardour/port.h"
 #include "ardour/processor.h"
 #include "ardour/route_group_specialized.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
+#include "ardour/types_convert.h"
 #include "ardour/utils.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 namespace ARDOUR {
 class InterThreadInfo;
@@ -66,14 +70,15 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
-       : Track (sess, name, flag, mode, DataType::MIDI)
-       , _immediate_events(1024) // FIXME: size?
+MidiTrack::MidiTrack (Session& sess, string name, TrackMode mode)
+       : Track (sess, name, PresentationInfo::MidiTrack, mode, DataType::MIDI)
+       , _immediate_events(6096) // FIXME: size?
        , _step_edit_ring_buffer(64) // FIXME: size?
-       , _note_mode(Sustained)
+       , _note_mode (Sustained)
        , _step_editing (false)
        , _input_active (true)
 {
+       _session.SessionLoaded.connect_same_thread (*this, boost::bind (&MidiTrack::restore_controls, this));
 }
 
 MidiTrack::~MidiTrack ()
@@ -89,87 +94,40 @@ MidiTrack::init ()
 
        _input->changed.connect_same_thread (*this, boost::bind (&MidiTrack::track_input_active, this, _1, _2));
 
-       return 0;
-}
-
-boost::shared_ptr<Diskstream>
-MidiTrack::create_diskstream ()
-{
-       MidiDiskstream::Flag dflags = MidiDiskstream::Flag (MidiDiskstream::Recordable);
+       _disk_writer->set_note_mode (_note_mode);
+       _disk_reader->reset_tracker ();
 
-       assert(_mode != Destructive);
-
-       return boost::shared_ptr<Diskstream> (new MidiDiskstream (_session, name(), dflags));
+       return 0;
 }
 
-
-void
-MidiTrack::set_record_enabled (bool yn, Controllable::GroupControlDisposition group_override)
+bool
+MidiTrack::can_be_record_safe ()
 {
        if (_step_editing) {
-               return;
-       }
-
-       Track::set_record_enabled (yn, group_override);
-}
-
-void
-MidiTrack::set_record_safe (bool yn, Controllable::GroupControlDisposition group_override)
-{
-       if (_step_editing) { /* REQUIRES REVIEW */
-               return;
+               return false;
        }
 
-       Track::set_record_safe (yn, group_override);
+       return Track::can_be_record_safe ();
 }
 
-void
-MidiTrack::set_diskstream (boost::shared_ptr<Diskstream> ds)
+bool
+MidiTrack::can_be_record_enabled ()
 {
-       /* We have to do this here, as Track::set_diskstream will cause a buffer refill,
-          and the diskstream must be set up to fill its buffers using the correct _note_mode.
-       */
-       boost::shared_ptr<MidiDiskstream> mds = boost::dynamic_pointer_cast<MidiDiskstream> (ds);
-       mds->set_note_mode (_note_mode);
-
-       Track::set_diskstream (ds);
-
-       mds->reset_tracker ();
-
-       _diskstream->set_track (this);
-       if (Profile->get_trx()) {
-               _diskstream->set_destructive (false);
-       } else {
-               _diskstream->set_destructive (_mode == Destructive);
+       if (_step_editing) {
+               return false;
        }
-       _diskstream->set_record_enabled (false);
-
-       _diskstream_data_recorded_connection.disconnect ();
-       mds->DataRecorded.connect_same_thread (
-               _diskstream_data_recorded_connection,
-               boost::bind (&MidiTrack::diskstream_data_recorded, this, _1));
 
-       DiskstreamChanged (); /* EMIT SIGNAL */
-}
-
-boost::shared_ptr<MidiDiskstream>
-MidiTrack::midi_diskstream() const
-{
-       return boost::dynamic_pointer_cast<MidiDiskstream>(_diskstream);
+       return Track::can_be_record_enabled ();
 }
 
 int
 MidiTrack::set_state (const XMLNode& node, int version)
 {
-       XMLProperty const * prop;
-
        /* This must happen before Track::set_state(), as there will be a buffer
           fill during that call, and we must fill buffers using the correct
           _note_mode.
        */
-       if ((prop = node.property (X_("note-mode"))) != 0) {
-               _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode));
-       } else {
+       if (!node.get_property (X_("note-mode"), _note_mode)) {
                _note_mode = Sustained;
        }
 
@@ -180,25 +138,24 @@ MidiTrack::set_state (const XMLNode& node, int version)
        // No destructive MIDI tracks (yet?)
        _mode = Normal;
 
-       if ((prop = node.property ("input-active")) != 0) {
-               set_input_active (string_is_affirmative (prop->value()));
+       bool yn;
+       if (node.get_property ("input-active", yn)) {
+               set_input_active (yn);
        }
 
        ChannelMode playback_channel_mode = AllChannels;
        ChannelMode capture_channel_mode = AllChannels;
 
-       if ((prop = node.property ("playback-channel-mode")) != 0) {
-               playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
-       }
-       if ((prop = node.property ("capture-channel-mode")) != 0) {
-               capture_channel_mode = ChannelMode (string_2_enum(prop->value(), capture_channel_mode));
-       }
-       if ((prop = node.property ("channel-mode")) != 0) {
+       node.get_property ("playback-channel-mode", playback_channel_mode);
+       node.get_property ("capture-channel-mode", capture_channel_mode);
+
+       if (node.get_property ("channel-mode", playback_channel_mode)) {
                /* 3.0 behaviour where capture and playback modes were not separated */
-               playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
                capture_channel_mode = playback_channel_mode;
        }
 
+       XMLProperty const * prop;
+
        unsigned int playback_channel_mask = 0xffff;
        unsigned int capture_channel_mask = 0xffff;
 
@@ -239,13 +196,12 @@ MidiTrack::state(bool full_state)
                XMLNode* inode;
 
                freeze_node = new XMLNode (X_("freeze-info"));
-               freeze_node->add_property ("playlist", _freeze_record.playlist->name());
-               freeze_node->add_property ("state", enum_2_string (_freeze_record.state));
+               freeze_node->set_property ("playlist", _freeze_record.playlist->name());
+               freeze_node->set_property ("state", _freeze_record.state);
 
                for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
                        inode = new XMLNode (X_("processor"));
-                       (*i)->id.print (buf, sizeof(buf));
-                       inode->add_property (X_("id"), buf);
+                       inode->set_property (X_("id"), id());
                        inode->add_child_copy ((*i)->state);
 
                        freeze_node->add_child_nocopy (*inode);
@@ -254,16 +210,24 @@ MidiTrack::state(bool full_state)
                root.add_child_nocopy (*freeze_node);
        }
 
-       root.add_property("playback_channel-mode", enum_2_string(get_playback_channel_mode()));
-       root.add_property("capture_channel-mode", enum_2_string(get_capture_channel_mode()));
+       root.set_property("playback-channel-mode", get_playback_channel_mode());
+       root.set_property("capture-channel-mode", get_capture_channel_mode());
        snprintf (buf, sizeof(buf), "0x%x", get_playback_channel_mask());
-       root.add_property("playback-channel-mask", buf);
+       root.set_property("playback-channel-mask", std::string(buf));
        snprintf (buf, sizeof(buf), "0x%x", get_capture_channel_mask());
-       root.add_property("capture-channel-mask", buf);
+       root.set_property("capture-channel-mask", std::string(buf));
+
+       root.set_property ("note-mode", _note_mode);
+       root.set_property ("step-editing", _step_editing);
+       root.set_property ("input-active", _input_active);
 
-       root.add_property ("note-mode", enum_2_string (_note_mode));
-       root.add_property ("step-editing", (_step_editing ? "yes" : "no"));
-       root.add_property ("input-active", (_input_active ? "yes" : "no"));
+       for (Controls::const_iterator c = _controls.begin(); c != _controls.end(); ++c) {
+               if (boost::dynamic_pointer_cast<MidiTrack::MidiControl>(c->second)) {
+                       boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (c->second);
+                       assert (ac);
+                       root.add_child_nocopy (ac->get_state ());
+               }
+       }
 
        return root;
 }
@@ -272,8 +236,6 @@ void
 MidiTrack::set_state_part_two ()
 {
        XMLNode* fnode;
-       XMLProperty const * prop;
-       LocaleGuard lg (X_("C"));
 
        /* This is called after all session state has been restored but before
           have been made ports and connections are established.
@@ -292,20 +254,19 @@ MidiTrack::set_state_part_two ()
                }
                _freeze_record.processor_info.clear ();
 
-               if ((prop = fnode->property (X_("playlist"))) != 0) {
-                       boost::shared_ptr<Playlist> pl = _session.playlists->by_name (prop->value());
+               std::string str;
+               if (fnode->get_property (X_("playlist"), str)) {
+                       boost::shared_ptr<Playlist> pl = _session.playlists->by_name (str);
                        if (pl) {
                                _freeze_record.playlist = boost::dynamic_pointer_cast<MidiPlaylist> (pl);
                        } else {
                                _freeze_record.playlist.reset();
                                _freeze_record.state = NoFreeze;
-                       return;
+                               return;
                        }
                }
 
-               if ((prop = fnode->property (X_("state"))) != 0) {
-                       _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state));
-               }
+               fnode->get_property (X_("state"), _freeze_record.state);
 
                XMLNodeConstIterator citer;
                XMLNodeList clist = fnode->children();
@@ -315,34 +276,43 @@ MidiTrack::set_state_part_two ()
                                continue;
                        }
 
-                       if ((prop = (*citer)->property (X_("id"))) == 0) {
+                       if (!(*citer)->get_property (X_("id"), str)) {
                                continue;
                        }
 
                        FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo (*((*citer)->children().front()),
                                                                                   boost::shared_ptr<Processor>());
-                       frii->id = prop->value ();
+                       frii->id = str;
                        _freeze_record.processor_info.push_back (frii);
                }
        }
 
-       if (midi_diskstream ()) {
-               midi_diskstream()->set_block_size (_session.get_block_size ());
-       }
-
        return;
 }
 
+void
+MidiTrack::restore_controls ()
+{
+       // TODO order events (CC before PGM to set banks)
+       for (Controls::const_iterator c = _controls.begin(); c != _controls.end(); ++c) {
+               boost::shared_ptr<MidiTrack::MidiControl> mctrl = boost::dynamic_pointer_cast<MidiTrack::MidiControl>(c->second);
+               if (mctrl) {
+                       mctrl->restore_value();
+               }
+       }
+}
+
 void
 MidiTrack::update_controls(const BufferSet& bufs)
 {
        const MidiBuffer& buf = bufs.get_midi(0);
        for (MidiBuffer::const_iterator e = buf.begin(); e != buf.end(); ++e) {
-               const Evoral::MIDIEvent<framepos_t>&     ev      = *e;
+               const Evoral::Event<framepos_t>&         ev      = *e;
                const Evoral::Parameter                  param   = midi_parameter(ev.buffer(), ev.size());
-               const boost::shared_ptr<Evoral::Control> control = this->control(param);
+               const boost::shared_ptr<AutomationControl> control = automation_control (param);
                if (control) {
                        control->set_double(ev.value(), _session.transport_frame(), false);
+                       control->Changed (false, Controllable::NoGroup);
                }
        }
 }
@@ -354,56 +324,25 @@ int
 MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick, bool& need_butler)
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
+
        if (!lm.locked()) {
-               boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
-               framecnt_t playback_distance = diskstream->calculate_playback_distance(nframes);
-               if (can_internal_playback_seek(::llabs(playback_distance))) {
-                       /* TODO should declick, and/or note-off */
-                       internal_playback_seek(playback_distance);
-               }
                return 0;
        }
 
-       boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
-
        if (n_outputs().n_total() == 0 && _processors.empty()) {
                return 0;
        }
 
        if (!_active) {
                silence (nframes);
-               if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
+               if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled())) {
                        _meter->reset();
                }
                return 0;
        }
 
-       framepos_t transport_frame = _session.transport_frame();
-
-       int dret;
-       framecnt_t playback_distance;
-
-       if ((nframes = check_initial_delay (nframes, transport_frame)) == 0) {
-               /* need to do this so that the diskstream sets its
-                  playback distance to zero, thus causing diskstream::commit
-                  to do nothing.
-                  */
-               BufferSet bufs; /* empty set - is OK, since nothing will happen */
-
-               dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false);
-               need_butler = diskstream->commit (playback_distance);
-               return dret;
-       }
-
-       if (_mute_control->list() && _mute_control->automation_playback()) {
-               bool        valid = false;
-               const float mute  = _mute_control->list()->rt_safe_eval(transport_frame, valid);
-               if (mute >= 0.5 && !muted()) {
-                       _mute_control->set_value_unchecked(1.0);  // mute
-               } else if (mute < 0.5 && muted()) {
-                       _mute_control->set_value_unchecked(0.0);  // unmute
-               }
-       }
+       _silent = false;
+       _amp->apply_gain_automation (false);
 
        BufferSet& bufs = _session.get_route_buffers (n_process_buffers());
 
@@ -412,55 +351,19 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame
        /* filter captured data before meter sees it */
        _capture_filter.filter (bufs);
 
-       if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) {
-               _meter->run (bufs, start_frame, end_frame, nframes, true);
-       }
-
-
-       _silent = false;
-
-       if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) {
-               need_butler = diskstream->commit (playback_distance);
-               silence (nframes);
-               return dret;
-       }
-
-       /* note diskstream uses our filter to filter/map playback channels appropriately. */
-
-       if (monitoring_state() == MonitoringInput) {
-
-               /* not actually recording, but we want to hear the input material anyway,
-                  at least potentially (depending on monitoring options)
-               */
-
-               /* because the playback buffer is event based and not a
-                * continuous stream, we need to make sure that we empty
-                * it of events every cycle to avoid it filling up with events
-                * read from disk, while we are actually monitoring input
-                */
-
-               diskstream->flush_playback (start_frame, end_frame);
-
+       if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _disk_writer->record_enabled())) {
+               _meter->run (bufs, start_frame, end_frame, 1.0 /*speed()*/, nframes, true);
        }
 
-
        /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
 
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
 
        /* final argument: don't waste time with automation if we're not recording or rolling */
 
-       process_output_buffers (bufs, start_frame, end_frame, nframes,
-                               declick, (!diskstream->record_enabled() && !_session.transport_stopped()));
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
-               if (d) {
-                       d->flush_buffers (nframes);
-               }
-       }
+       process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!_disk_writer->record_enabled() && !_session.transport_stopped()));
 
-       need_butler = diskstream->commit (playback_distance);
+       flush_processor_buffers_locked (nframes);
 
        return 0;
 }
@@ -490,21 +393,7 @@ MidiTrack::realtime_locate ()
                (*i)->realtime_locate ();
        }
 
-       midi_diskstream()->reset_tracker ();
-}
-
-void
-MidiTrack::realtime_handle_transport_stopped ()
-{
-       Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
-
-       if (!lm.locked ()) {
-               return;
-       }
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->realtime_handle_transport_stopped ();
-       }
+       _disk_reader->reset_tracker ();
 }
 
 void
@@ -512,7 +401,7 @@ MidiTrack::non_realtime_locate (framepos_t pos)
 {
        Track::non_realtime_locate(pos);
 
-       boost::shared_ptr<MidiPlaylist> playlist = midi_diskstream()->midi_playlist();
+       boost::shared_ptr<MidiPlaylist> playlist = _disk_writer->midi_playlist();
        if (!playlist) {
                return;
        }
@@ -563,7 +452,7 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
 
                for (MidiBuffer::const_iterator e = mb->begin(); e != mb->end(); ++e) {
 
-                       const Evoral::MIDIEvent<framepos_t> ev(*e, false);
+                       const Evoral::Event<framepos_t> ev(*e, false);
 
                        /* note on, since for step edit, note length is determined
                           elsewhere
@@ -571,7 +460,7 @@ MidiTrack::push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes)
 
                        if (ev.is_note_on()) {
                                /* we don't care about the time for this purpose */
-                               _step_edit_ring_buffer.write (0, ev.type(), ev.size(), ev.buffer());
+                               _step_edit_ring_buffer.write (0, ev.event_type(), ev.size(), ev.buffer());
                        }
                }
        }
@@ -617,11 +506,9 @@ MidiTrack::export_stuff (BufferSet&                   buffers,
                return -1;
        }
 
-       boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
-
        Glib::Threads::RWLock::ReaderLock rlock (_processor_lock);
 
-       boost::shared_ptr<MidiPlaylist> mpl = boost::dynamic_pointer_cast<MidiPlaylist>(diskstream->playlist());
+       boost::shared_ptr<MidiPlaylist> mpl = _disk_writer->midi_playlist();
        if (!mpl) {
                return -2;
        }
@@ -670,7 +557,7 @@ void
 MidiTrack::set_note_mode (NoteMode m)
 {
        _note_mode = m;
-       midi_diskstream()->set_note_mode(m);
+       _disk_writer->set_note_mode(m);
 }
 
 std::string
@@ -703,8 +590,7 @@ MidiTrack::write_immediate_event(size_t size, const uint8_t* buf)
                cerr << "WARNING: Ignoring illegal immediate MIDI event" << endl;
                return false;
        }
-       const uint32_t type = midi_parameter_type(buf[0]);
-       return (_immediate_events.write (0, type, size, buf) == size);
+       return (_immediate_events.write (0, Evoral::MIDI_EVENT, size, buf) == size);
 }
 
 void
@@ -715,6 +601,7 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st
        case MidiPgmChangeAutomation:
        case MidiPitchBenderAutomation:
        case MidiChannelPressureAutomation:
+       case MidiNotePressureAutomation:
        case MidiSystemExclusiveAutomation:
                /* The track control for MIDI parameters is for immediate events to act
                   as a control surface, write/touch for them is not currently
@@ -726,22 +613,13 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st
 }
 
 void
-MidiTrack::MidiControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
-{
-       if (writable()) {
-               _set_value (val, group_override);
-       }
-}
-
-void
-MidiTrack::MidiControl::set_value_unchecked (double val)
+MidiTrack::MidiControl::restore_value ()
 {
-       /* used only by automation playback */
-       _set_value (val, Controllable::NoGroup);
+       actually_set_value (get_value(), Controllable::NoGroup);
 }
 
 void
-MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
+MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
 {
        const Evoral::Parameter &parameter = _list ? _list->parameter() : Control::parameter();
        const Evoral::ParameterDescriptor &desc = EventTypeMap::instance().descriptor(parameter);
@@ -786,6 +664,12 @@ MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlD
                        ev[1] = int(val);
                        break;
 
+               case MidiNotePressureAutomation:
+                       ev[0] += MIDI_CMD_NOTE_PRESSURE;
+                       ev[1] = parameter.id();
+                       ev[2] = int(val);
+                       break;
+
                case MidiPitchBenderAutomation:
                        ev[0] += MIDI_CMD_BENDER;
                        ev[1] = 0x7F & int(val);
@@ -798,7 +682,7 @@ MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlD
                _route->write_immediate_event(size,  ev);
        }
 
-       AutomationControl::set_value(val, group_override);
+       AutomationControl::actually_set_value(val, group_override);
 }
 
 void
@@ -817,7 +701,7 @@ MidiTrack::set_step_editing (bool yn)
 boost::shared_ptr<SMFSource>
 MidiTrack::write_source (uint32_t)
 {
-       return midi_diskstream()->write_source ();
+       return _disk_writer->midi_write_source ();
 }
 
 void
@@ -855,13 +739,7 @@ MidiTrack::set_capture_channel_mask (uint16_t mask)
 boost::shared_ptr<MidiPlaylist>
 MidiTrack::midi_playlist ()
 {
-       return midi_diskstream()->midi_playlist ();
-}
-
-void
-MidiTrack::diskstream_data_recorded (boost::weak_ptr<MidiSource> src)
-{
-       DataRecorded (src); /* EMIT SIGNAL */
+       return boost::dynamic_pointer_cast<MidiPlaylist> (_playlists[DataType::MIDI]);
 }
 
 bool
@@ -905,16 +783,10 @@ MidiTrack::track_input_active (IOChange change, void* /* src */)
        }
 }
 
-boost::shared_ptr<Diskstream>
-MidiTrack::diskstream_factory (XMLNode const & node)
-{
-       return boost::shared_ptr<Diskstream> (new MidiDiskstream (_session, node));
-}
-
 boost::shared_ptr<MidiBuffer>
 MidiTrack::get_gui_feed_buffer () const
 {
-       return midi_diskstream()->get_gui_feed_buffer ();
+       return _disk_reader->get_gui_feed_buffer ();
 }
 
 void
@@ -930,11 +802,11 @@ MidiTrack::act_on_mute ()
        /* If we haven't got a diskstream yet, there's nothing to worry about,
           and we can't call get_channel_mask() anyway.
        */
-       if (!midi_diskstream()) {
+       if (!_disk_writer) {
                return;
        }
 
-       if (muted() || _mute_master->muted_by_others_at(MuteMaster::AllPoints)) {
+       if (muted() || _mute_master->muted_by_others_soloing_at (MuteMaster::AllPoints)) {
                /* only send messages for channels we are using */
 
                uint16_t mask = _playback_filter.get_channel_mask();
@@ -953,41 +825,29 @@ MidiTrack::act_on_mute ()
                }
 
                /* Resolve active notes. */
-               midi_diskstream()->resolve_tracker(_immediate_events, Port::port_offset());
+               _disk_reader->resolve_tracker(_immediate_events, Port::port_offset());
        }
 }
 
 void
-MidiTrack::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd)
+MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition gcd)
 {
-       if (use_group (gcd, &RouteGroup::is_monitoring)) {
-               _route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup);
-               return;
-       }
-
-       if (mc != _monitoring) {
+       Track::monitoring_changed (self, gcd);
 
-               Track::set_monitoring (mc, gcd);
-
-               /* monitoring state changed, so flush out any on notes at the
-                * port level.
-                */
+       /* monitoring state changed, so flush out any on notes at the
+        * port level.
+        */
 
-               PortSet& ports (_output->ports());
+       PortSet& ports (_output->ports());
 
-               for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) {
-                       boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p);
-                       if (mp) {
-                               mp->require_resolve ();
-                       }
-               }
-
-               boost::shared_ptr<MidiDiskstream> md (midi_diskstream());
-
-               if (md) {
-                       md->reset_tracker ();
+       for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) {
+               boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p);
+               if (mp) {
+                       mp->require_resolve ();
                }
        }
+
+       _disk_reader->reset_tracker ();
 }
 
 MonitorState
@@ -999,4 +859,3 @@ MidiTrack::monitoring_state () const
        }
        return ms;
 }
-