change Session::convert_to_frames_at() to Session::convert_to_frames() to reflect...
[ardour.git] / libs / ardour / route.cc
index dd0c8579ffe8b236f036b6b8d3fe95e74c537fc9..6f9780d4b955c7781798e2bd56d579e6d22efa6c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000 Paul Davis 
+    Copyright (C) 2000 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <cassert>
 #include <algorithm>
 
-#include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
-#include "pbd/stacktrace.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
+#include "pbd/convert.h"
 
 #include "evoral/Curve.hpp"
 
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/debug.h"
+#include "ardour/delivery.h"
 #include "ardour/dB.h"
+#include "ardour/internal_send.h"
+#include "ardour/internal_return.h"
 #include "ardour/ladspa_plugin.h"
 #include "ardour/meter.h"
 #include "ardour/mix.h"
+#include "ardour/monitor_processor.h"
 #include "ardour/panner.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
@@ -53,6 +58,8 @@
 #include "ardour/session.h"
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
 
 #include "i18n.h"
 
@@ -61,102 +68,144 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
+PBD::Signal1<void,string const&> Route::SyncOrderKeys;
+PBD::Signal0<void> Route::RemoteControlIDChange;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
-       , AutomatableControls (sess)
+       , Automatable (sess)
+       , GraphNode( sess.route_graph )
+        , _active (true)
+        , _initial_delay (0)
+        , _roll_delay (0)
        , _flags (flg)
+        , _pending_declick (true)
+        , _meter_point (MeterPostFader)
+        , _self_solo (false)
+        , _soloed_by_others_upstream (0)
+        , _soloed_by_others_downstream (0)
+        , _solo_isolated (0)
+        , _denormal_protection (false)
+        , _recordable (true)
+        , _silent (false)
+        , _declickable (false)
        , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, name))
+        , _have_internal_generator (false)
+        , _solo_safe (false)
        , _default_type (default_type)
-         
-{
-       init ();
-}
-
-Route::Route (Session& sess, const XMLNode& node, DataType default_type)
-       : SessionObject (sess, "toBeReset")
-       , AutomatableControls (sess)
-       , _solo_control (new SoloControllable (X_("solo"), *this))
-       , _mute_master (new MuteMaster (sess, "toBeReset"))
-       , _default_type (default_type)
+        , _remote_control_id (0)
+        , _in_configure_processors (false)
 {
-       init ();
-       _set_state (node, false);
+       processor_max_streams.reset();
+       order_keys[N_("signal")] = order_key_cnt++;
 }
 
-void
+int
 Route::init ()
 {
-       _active = true;
-       processor_max_streams.reset();
-       _solo_safe = false;
-       _recordable = true;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
-       _silent = false;
-       _meter_point = MeterPostFader;
-       _initial_delay = 0;
-       _roll_delay = 0;
-       _have_internal_generator = false;
-       _declickable = false;
-       _pending_declick = true;
-       _remote_control_id = 0;
-       _in_configure_processors = false;
-       
-       _edit_group = 0;
-       _mix_group = 0;
-
-       _phase_invert = 0;
-       _denormal_protection = false;
-
        /* add standard controls */
 
+       _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
+
        add_control (_solo_control);
-       add_control (_mute_master);
-       
+       add_control (_mute_control);
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
        _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       _input->changed.connect (mem_fun (this, &Route::input_change_handler));
-       _output->changed.connect (mem_fun (this, &Route::output_change_handler));
+       _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
+       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
 
-       /* add standard processors */
+       _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
 
-       _amp.reset (new Amp (_session, _mute_master));
+       /* add amp processor  */
+
+       _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
+       /* add standard processors: meter, main outs, monitor out */
+
        _meter.reset (new PeakMeter (_session));
-       add_processor (_meter, PreFader);
-       
+       _meter->set_display_to_user (false);
+
+       add_processor (_meter, PostFader);
+
        _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-       add_processor (_main_outs, PostFader);
 
-       /* now we can meter */
-       
-       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+        add_processor (_main_outs, PostFader);
+
+       if (is_monitor()) {
+               /* where we listen to tracks */
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+
+                ProcessorList::iterator i;
+
+                for (i = _processors.begin(); i != _processors.end(); ++i) {
+                        if (*i == _intreturn) {
+                                ++i;
+                                break;
+                        }
+                }
+
+                /* the thing that provides proper control over a control/monitor/listen bus 
+                   (such as per-channel cut, dim, solo, invert, etc).
+                   It always goes right after the internal return;
+                 */
+                _monitor_control.reset (new MonitorProcessor (_session));
+                add_processor (_monitor_control, i);
+
+                /* no panning on the monitor main outs */
+
+                _main_outs->panner()->set_bypassed (true);
+       }
+
+        if (is_master() || is_monitor() || is_hidden()) {
+                _mute_master->set_solo_ignore (true);
+        }
+
+       /* now that we have _meter, its safe to connect to this */
+
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
+
+        return 0;
 }
 
 Route::~Route ()
 {
-       Metering::disconnect (_meter_connection);
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
+
+       /* do this early so that we don't get incoming signals as we are going through destruction 
+        */
+
+       drop_connections ();
 
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       /* don't use clear_processors here, as it depends on the session which may
+          be half-destroyed by now 
+       */
 
-       for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
-               free ((void*)(i->first));
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->drop_references ();
        }
+
+       _processors.clear ();
 }
 
 void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
+               if (notify_class_listeners) {
+                       RemoteControlIDChange ();
+               }
        }
 }
 
@@ -166,43 +215,61 @@ Route::remote_control_id() const
        return _remote_control_id;
 }
 
-long
-Route::order_key (const char* name) const
+int32_t
+Route::order_key (std::string const & name) const
 {
-       OrderKeys::const_iterator i;
-
-       for (i = order_keys.begin(); i != order_keys.end(); ++i) {
-               if (!strcmp (name, i->first)) {
-                       return i->second;
-               }
+       OrderKeys::const_iterator i = order_keys.find (name);
+       if (i == order_keys.end()) {
+               return -1;
        }
 
-       return -1;
+       return i->second;
 }
 
 void
-Route::set_order_key (const char* name, long n)
+Route::set_order_key (std::string const & name, int32_t n)
 {
-       order_keys[strdup(name)] = n;
+       bool changed = false;
+
+       /* This method looks more complicated than it should, but
+          it's important that we don't emit order_key_changed unless
+          it actually has, as expensive things happen on receipt of that
+          signal.
+       */
+
+       if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) {
+               order_keys[name] = n;
+               changed = true;
+       }
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
-                       x->second = n;
+                       if (x->second != n) {
+                               x->second = n;
+                               changed = true;
+                       }
                }
-       } 
+       }
 
-       _session.set_dirty ();
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
 }
 
+/** Set all order keys to be the same as that for `base', if such a key
+ *  exists in this route.
+ *  @param base Base key.
+ */
 void
-Route::sync_order_keys (const char* base)
+Route::sync_order_keys (std::string const & base)
 {
        if (order_keys.empty()) {
                return;
        }
 
        OrderKeys::iterator i;
-       uint32_t key;
+       int32_t key;
 
        if ((i = order_keys.find (base)) == order_keys.end()) {
                /* key doesn't exist, use the first existing key (during session initialization) */
@@ -215,8 +282,17 @@ Route::sync_order_keys (const char* base)
                i = order_keys.begin();
        }
 
+       bool changed = false;
+       
        for (; i != order_keys.end(); ++i) {
-               i->second = key;
+               if (i->second != key) {
+                       i->second = key;
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -225,8 +301,8 @@ Route::ensure_track_or_route_name(string name, Session &session)
 {
        string newname = name;
 
-       while (session.route_by_name (newname) != NULL) {
-               newname = bump_name_once (newname);
+       while (!session.io_name_is_legal (newname)) {
+               newname = bump_name_once (newname, '.');
        }
 
        return newname;
@@ -242,15 +318,15 @@ Route::inc_gain (gain_t fraction, void *src)
 void
 Route::set_gain (gain_t val, void *src)
 {
-       if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
-               
-               if (_mix_group->is_relative()) {
-                       
+       if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) {
+
+               if (_route_group->is_relative()) {
+
                        gain_t usable_gain = _amp->gain();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
-                                               
+
                        gain_t delta = val;
                        if (delta < 0.000001f) {
                                delta = 0.000001f;
@@ -264,28 +340,28 @@ Route::set_gain (gain_t val, void *src)
                        gain_t factor = delta / usable_gain;
 
                        if (factor > 0.0f) {
-                               factor = _mix_group->get_max_factor(factor);
+                               factor = _route_group->get_max_factor(factor);
                                if (factor == 0.0f) {
                                        _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        } else {
-                               factor = _mix_group->get_min_factor(factor);
+                               factor = _route_group->get_min_factor(factor);
                                if (factor == 0.0f) {
                                        _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        }
-                                       
-                       _mix_group->apply (&Route::inc_gain, factor, _mix_group);
+
+                       _route_group->apply (&Route::inc_gain, factor, _route_group);
 
                } else {
-                       
-                       _mix_group->apply (&Route::set_gain, val, _mix_group);
+
+                       _route_group->apply (&Route::set_gain, val, _route_group);
                }
 
                return;
-       } 
+       }
 
        if (val == _amp->gain()) {
                return;
@@ -297,7 +373,7 @@ Route::set_gain (gain_t val, void *src)
 /** Process this route for one (sub) cycle (process thread)
  *
  * @param bufs Scratch buffers to use for the signal path
- * @param start_frame Initial transport frame 
+ * @param start_frame Initial transport frame
  * @param end_frame Final transport frame
  * @param nframes Number of frames to output (to ports)
  *
@@ -306,8 +382,9 @@ Route::set_gain (gain_t val, void *src)
  */
 void
 Route::process_output_buffers (BufferSet& bufs,
-                              sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
-                              bool with_processors, int declick)
+                              framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+                              bool /*with_processors*/, int declick,
+                               bool gain_automation_ok)
 {
        bool monitor;
 
@@ -316,7 +393,7 @@ Route::process_output_buffers (BufferSet& bufs,
        switch (Config->get_monitoring_model()) {
        case HardwareMonitoring:
        case ExternalMonitoring:
-               monitor = record_enabled() && (_session.config.get_auto_input() || _session.actively_recording());
+               monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
                break;
        default:
                monitor = true;
@@ -327,45 +404,48 @@ Route::process_output_buffers (BufferSet& bufs,
        }
 
        /* figure out if we're going to use gain automation */
-
-       _amp->setup_gain_automation (start_frame, end_frame, nframes);
-       
+        if (gain_automation_ok) {
+                _amp->setup_gain_automation (start_frame, end_frame, nframes);
+        } else {
+                _amp->apply_gain_automation (false);
+        }
+        
        /* tell main outs what to do about monitoring */
-
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
+
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0);
+               Amp::declick (bufs, nframes, 1);
        } else if (declick < 0) {
-               Amp::apply_gain (bufs, nframes, 1.0, 0.0);
-       } 
+               Amp::declick (bufs, nframes, -1);
+       }
 
        _pending_declick = 0;
-               
+
        /* -------------------------------------------------------------------------------------------
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (_phase_invert) {
-               
+       if (_phase_invert.any ()) {
+
                int chn = 0;
 
                if (_denormal_protection || Config->get_denormal_protection()) {
-                       
+
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
 
-                               if (_phase_invert & chn) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
                                        }
                                } else {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] += 1.0e-27f;
                                        }
                                }
@@ -375,13 +455,26 @@ Route::process_output_buffers (BufferSet& bufs,
 
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
-                               
-                               if (_phase_invert & chn) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
-                               } 
+                               }
+                       }
+               }
+
+       } else {
+
+               if (_denormal_protection || Config->get_denormal_protection()) {
+
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+                               Sample* const sp = i->data();
+                               for (pframes_t nx = 0; nx < nframes; ++nx) {
+                                       sp[nx] += 1.0e-27f;
+                               }
                        }
+
                }
        }
 
@@ -389,18 +482,25 @@ Route::process_output_buffers (BufferSet& bufs,
           and go ....
           ----------------------------------------------------------------------------------------- */
 
-       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-       if (rm.locked()) {
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->input_streams()));
-                       (*i)->run_in_place (bufs, start_frame, end_frame, nframes);
-                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
+                       break;
                }
-
-               if (!_processors.empty()) {
-                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+               
+               if (bufs.count() != (*i)->input_streams()) {
+                       cerr << _name << " bufs = " << bufs.count()
+                            << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+                            << endl;
                }
+               assert (bufs.count() == (*i)->input_streams());
+                
+                /* should we NOT run plugins here if the route is inactive?
+                   do we catch route != active somewhere higher?
+                */
+
+               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               bufs.set_count ((*i)->output_streams());
        }
 }
 
@@ -411,37 +511,99 @@ Route::n_process_buffers ()
 }
 
 void
-Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
        _silent = false;
 
-       assert (bufs.available() >= _input->n_ports());
-       
+       assert (bufs.available() >= input_streams());
+
        if (_input->n_ports() == ChanCount::ZERO) {
-               silence (nframes);
+               silence_unlocked (nframes);
        }
-       
+
+       bufs.set_count (input_streams());
+
+       if (is_monitor() && _session.listening() && !_session.is_auditioning()) {
+
+               /* control/monitor bus ignores input ports when something is
+                  feeding the listen "stream". data will "arrive" into the
+                  route from the intreturn processor element.
+               */
+               bufs.silence (nframes, 0);
+
+       } else {
+
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
+
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
+                       }
+               }
+       }
+
+       write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
+}
+
+void
+Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+{
+       BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
        bufs.set_count (_input->n_ports());
-       
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               
-               BufferSet::iterator o = bufs.begin(*t);
-               PortSet& ports (_input->ports());
+       write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
+}
 
-               for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
-                       o->read_from (i->get_buffer(nframes), nframes);
+void
+Route::set_listen (bool yn, void* src)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+       if (_monitor_send) {
+               if (yn != _monitor_send->active()) {
+                       if (yn) {
+                               _monitor_send->activate ();
+                                _mute_master->set_soloed (true);
+                        } else {
+                               _monitor_send->deactivate ();
+                                _mute_master->set_soloed (false);
+                       }
+
+                       listen_changed (src); /* EMIT SIGNAL */
                }
        }
+}
 
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+bool
+Route::listening () const
+{
+       if (_monitor_send) {
+               return _monitor_send->active ();
+       } else {
+               return false;
+       }
 }
 
 void
-Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::set_solo_safe (bool yn, void *src)
 {
-       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+       if (_solo_safe != yn) {
+               _solo_safe = yn;
+               solo_safe_changed (src);
+       } 
+}
+
+bool
+Route::solo_safe() const
+{
+       return _solo_safe;
 }
 
 void
@@ -451,82 +613,215 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
+               _route_group->apply (&Route::set_solo, yn, _route_group);
                return;
        }
 
-       if (_main_outs->soloed() != yn) {
-               _main_outs->mod_solo_level (yn ? 1 : -1);
-               solo_changed (src); /* EMIT SIGNAL */
+       if (self_soloed() != yn) {
+               set_self_solo (yn);
+                set_mute_master_solo ();
+               solo_changed (true, src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
-       }       
+       }
 }
 
-bool
-Route::soloed() const
+void
+Route::set_self_solo (bool yn)
+{
+        _self_solo = yn;
+}
+
+void
+Route::mod_solo_by_others_upstream (int32_t delta)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+        uint32_t old_sbu = _soloed_by_others_upstream;
+
+       if (delta < 0) {
+               if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_upstream += delta;
+               } else {
+                       _soloed_by_others_upstream = 0;
+               }
+       } else {
+               _soloed_by_others_upstream += delta;
+       }
+
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
+                                                  name(), delta, _soloed_by_others_upstream, old_sbu, 
+                                                  _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
+
+        /* push the inverse solo change to everything that feeds us. 
+           
+           This is important for solo-within-group. When we solo 1 track out of N that
+           feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
+           on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
+           tracks that feed it. This will silence them if they were audible because
+           of a bus solo, but the newly soloed track will still be audible (because 
+           it is self-soloed).
+           
+           but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
+           not in reverse.
+         */
+
+        if ((_self_solo || _soloed_by_others_downstream) &&
+            ((old_sbu == 0 && _soloed_by_others_upstream > 0) || 
+             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
+                
+                if (delta > 0 || !Config->get_exclusive_solo()) {
+                        DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                                boost::shared_ptr<Route> sr = i->r.lock();
+                                if (sr) {
+                                        sr->mod_solo_by_others_downstream (-delta);
+                                }
+                        }
+                } 
+        }
+
+        set_mute_master_solo ();
+        solo_changed (false, this);
+}
+
+void
+Route::mod_solo_by_others_downstream (int32_t delta)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+        if (delta < 0) {
+               if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_downstream += delta;
+               } else {
+                       _soloed_by_others_downstream = 0;
+               }
+       } else {
+               _soloed_by_others_downstream += delta;
+       }
+
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
+
+        set_mute_master_solo ();
+        solo_changed (false, this);
+}
+
+void
+Route::set_mute_master_solo ()
 {
-       return _main_outs->soloed ();
+        _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
 }
 
 void
 Route::set_solo_isolated (bool yn, void *src)
 {
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo_isolated, yn, _mix_group);
+       if (is_master() || is_monitor() || is_hidden()) {
                return;
        }
 
-       _main_outs->set_solo_isolated (yn);
-       solo_isolated_changed (src);
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
+               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               return;
+       }
+       
+       /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
+
+       boost::shared_ptr<RouteList> routes = _session.get_routes ();
+       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+
+               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
+                        continue;
+                }
+
+               bool sends_only;
+               bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
+               
+               if (does_feed && !sends_only) {
+                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+               }
+       }
+
+        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
+
+        bool changed = false;
+
+       if (yn) {
+                if (_solo_isolated == 0) {
+                        _mute_master->set_solo_ignore (true);
+                        changed = true;
+                }
+               _solo_isolated++;
+       } else {
+               if (_solo_isolated > 0) {
+                       _solo_isolated--;
+                        if (_solo_isolated == 0) {
+                                _mute_master->set_solo_ignore (false);
+                                changed = true;
+                        }
+               }
+       }
+
+        if (changed) {
+                solo_isolated_changed (src);
+        }
 }
 
 bool
-Route::solo_isolated () const 
+Route::solo_isolated () const
 {
-       return _main_outs->solo_isolated();
+       return _solo_isolated > 0;
+}
+
+void
+Route::set_mute_points (MuteMaster::MutePoint mp)
+{
+        _mute_master->set_mute_points (mp);
+        mute_points_changed (); /* EMIT SIGNAL */
+        
+        if (_mute_master->muted_by_self()) {
+                mute_changed (this); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
+        }
 }
 
 void
 Route::set_mute (bool yn, void *src)
 {
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_mute, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
+               _route_group->apply (&Route::set_mute, yn, _route_group);
                return;
        }
 
        if (muted() != yn) {
-               _mute_master->mute (yn);
-               mute_changed (src);
+                _mute_master->set_muted_by_self (yn);
+               mute_changed (src); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
        }
-}      
+}
 
 bool
-Route::muted() const 
+Route::muted () const
 {
-       return _mute_master->muted ();
+        return _mute_master->muted_by_self();
 }
 
-#if DEFINE_IF_YOU_NEED_THIS
+#if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 {
        cerr << name << " {" << endl;
        for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
                        p != procs.end(); ++p) {
-               cerr << "\t" << (*p)->name() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
        }
        cerr << "}" << endl;
 }
 #endif
 
-Route::ProcessorList::iterator
-Route::prefader_iterator() 
-{
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-       return find (_processors.begin(), _processors.end(), _amp);
-}
-
 int
 Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
 {
@@ -538,23 +833,10 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
 
        if (placement == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
+               loc = find (_processors.begin(), _processors.end(), _amp);
        } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-
-               if (processor->visible() && !_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
        return add_processor (processor, loc, err);
@@ -562,12 +844,12 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
 
 
 /** Add a processor to the route.
- * If @a iter is not NULL, it must point to an iterator in _processors and the new
+ * @a iter must point to an iterator in _processors and the new
  * processor will be inserted immediately before this location.  Otherwise,
  * @a position is used.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err)
+Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
 {
        ChanCount old_pms = processor_max_streams;
 
@@ -606,183 +888,130 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
-               
 
-               if (configure_processors_unlocked (err)) {
-                       ProcessorList::iterator ploc = loc;
-                       --ploc;
-                       _processors.erase(ploc);
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               ProcessorList::iterator ploc = loc;
+                               --ploc;
+                               _processors.erase(ploc);
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               cerr << "configure failed\n";
+                               return -1;
+                       }
                }
-       
+
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       
+
                        if (pi->natural_input_streams() == ChanCount::ZERO) {
                                /* generator plugin */
                                _have_internal_generator = true;
                        }
-                       
+
                }
-               
-               if (_meter) {
-                       // Ensure peak vector sizes before the plugin is activated
-                       ChanCount potential_max_streams = ChanCount::max (processor->input_streams(), processor->output_streams());
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
+
+                /* is this the monitor send ? if so, make sure we keep track of it */
+
+                boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+
+                if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) {
+                        _monitor_send = isend;
+                }
+
+               if (activation_allowed && (processor != _monitor_send)) {
+                       processor->activate ();
                }
 
-               // XXX: do we want to emit the signal here ? change call order.
-               processor->activate ();
-               processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
 
                _output->set_user_latency (0);
        }
-       
-       processors_changed (); /* EMIT SIGNAL */
-       
-       return 0;
-}
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
-{
-       ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-       }
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
-       return add_processor_from_xml (node, loc);
+       return 0;
 }
 
 bool
-Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
+Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
        const XMLProperty *prop;
 
-       // legacy sessions use a different node name for sends
-       if (node.name() == "Send") {
-       
-               try {
-                       boost::shared_ptr<Send> send (new Send (_session, _mute_master, node));
-                       add_processor (send, iter); 
-                       return true;
-               } 
+       try {
+               boost::shared_ptr<Processor> processor;
+
+               /* bit of a hack: get the `placement' property from the <Redirect> tag here
+                  so that we can add the processor in the right place (pre/post-fader)
+               */
+
+               XMLNodeList const & children = node.children ();
+               XMLNodeList::const_iterator i = children.begin ();
                
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
+               while (i != children.end() && (*i)->name() != X_("Redirect")) {
+                       ++i;
                }
-               
-       } else if (node.name() == "Processor") {
-               
-               try {
-                       if ((prop = node.property ("type")) != 0) {
 
+               Placement placement = PreFader;
+
+               if (i != children.end()) {
+                       if ((prop = (*i)->property (X_("placement"))) != 0) {
+                               placement = Placement (string_2_enum (prop->value(), placement));
+                       }
+               }
 
-                               cerr << _name << " : got processor type " << prop->value() << endl;
+               if (node.name() == "Insert") {
 
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = false;
+                       if ((prop = node.property ("type")) != 0) {
 
                                if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
-                                   prop->value() == "lv2" ||
-                                   prop->value() == "vst" ||
-                                   prop->value() == "audiounit") {
-                                       
-                                       processor.reset (new PluginInsert(_session, node));
-                                       have_insert = true;
-                                       
-                               } else if (prop->value() == "port") {
+                                               prop->value() == "lv2" ||
+                                               prop->value() == "vst" ||
+                                               prop->value() == "audiounit") {
 
-                                       processor.reset (new PortInsert (_session, _mute_master, node));
-                               
-                               } else if (prop->value() == "send") {
+                                       processor.reset (new PluginInsert (_session));
 
-                                       processor.reset (new Send (_session, _mute_master, node));
-                                       have_insert = true;
-                               
-                               } else if (prop->value() == "meter") {
+                               } else {
 
-                                       processor = _meter;
-                                       processor->set_state (node);
-                               
-                               } else if (prop->value() == "amp") {
-                                       
-                                       processor = _amp;
-                                       processor->set_state (node);
-                                       
-                               } else if (prop->value() == "listen" || prop->value() == "deliver") {
+                                       processor.reset (new PortInsert (_session, _mute_master));
+                               }
 
-                                       /* XXX need to generalize */
+                       }
 
-                                       processor = _control_outs;
-                                       processor->set_state (node);
-                                       
-                               } else if (prop->value() == "main-outs") {
-                                       
-                                       processor = _main_outs;
-                                       processor->set_state (node);
+               } else if (node.name() == "Send") {
 
-                               } else {
+                       processor.reset (new Send (_session, _mute_master));
 
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
-                               }
-                               
-                               if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
-                                       /* check for invisible processors stacked at the end and leave them there */
-                                       ProcessorList::iterator p;
-                                       p = _processors.end();
-                                       --p;
-                                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                                       while (!(*p)->visible() && p != _processors.begin()) {
-                                               --p;
-                                       }
-                                       ++p;
-                                       iter = p;
-                               }
-
-                               return (add_processor (processor, iter) == 0);
-                               
-                       } else {
-                               error << _("Processor XML node has no type property") << endmsg;
-                       }
-               }
+               } else {
 
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
+                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), node.name()) << endmsg;
                        return false;
                }
+
+                if (processor->set_state (node, version)) {
+                        return false;
+                }
+
+               return (add_processor (processor, placement) == 0);
+       }
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
        }
-       return false;
 }
 
 int
-Route::add_processors (const ProcessorList& others, Placement placement, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
        ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
 
-               if (!_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+       if (before) {
+               loc = find(_processors.begin(), _processors.end(), before);
+       } else {
+               /* nothing specified - at end but before main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
        return add_processors (others, loc, err);
@@ -808,13 +1037,11 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList::iterator existing_end = _processors.end();
-               --existing_end;
 
                ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
-                       
+
                        // Ensure meter only appears in the list once
                        if (*i == _meter) {
                                ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
@@ -822,36 +1049,42 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                        _processors.erase(m);
                                }
                        }
-                       
+
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
-                               
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
+
+                               ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+                               if (m > potential_max_streams) {
                                        potential_max_streams = m;
+                               }
                        }
 
-                       // Ensure peak vector sizes before the plugin is activated
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
-                       
-                       _processors.insert (iter, *i);
-                       
-                       if (configure_processors_unlocked (err)) {
-                               ++existing_end;
-                               _processors.erase (existing_end, _processors.end());
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
+                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+                       if ((*i)->active()) {
+                               (*i)->activate ();
                        }
-                       
-                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (configure_processors_unlocked (err)) {
+                                       _processors.erase (inserted);
+                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       return -1;
+                               }
+                       }
+
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
                }
 
                _output->set_user_latency (0);
        }
-       
-       processors_changed (); /* EMIT SIGNAL */
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -876,10 +1109,10 @@ void
 Route::disable_processors (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                (*i)->deactivate ();
        }
@@ -887,17 +1120,17 @@ Route::disable_processors (Placement p)
        _session.set_dirty ();
 }
 
-/** Turn off all redirects 
+/** Turn off all redirects
  */
 void
 Route::disable_processors ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->deactivate ();
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -908,16 +1141,16 @@ void
 Route::disable_plugins (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -927,13 +1160,13 @@ void
 Route::disable_plugins ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -942,7 +1175,7 @@ void
 Route::ab_plugins (bool forward)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-                       
+
        if (forward) {
 
                /* forward = turn off all active redirects, and mark them so that the next time
@@ -979,33 +1212,8 @@ Route::ab_plugins (bool forward)
                        }
                }
        }
-       
-       _session.set_dirty ();
-}
-       
-       
-/* Figure out the streams that will feed into PreFader */
-ChanCount
-Route::pre_fader_streams() const
-{
-       boost::shared_ptr<Processor> processor;
 
-       /* Find the last pre-fader redirect that isn't a send; sends don't affect the number
-        * of streams. */
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i) == _amp) {
-                       break;
-               }
-               if (boost::dynamic_pointer_cast<Send> (*i) == 0) {
-                       processor = *i;
-               }
-       }
-       
-       if (processor) {
-               return processor->output_streams();
-       } else {
-               return _input->n_ports ();
-       }
+       _session.set_dirty ();
 }
 
 
@@ -1020,7 +1228,7 @@ Route::clear_processors (Placement p)
        if (!_session.engine().connected()) {
                return;
        }
-       
+
        bool already_deleting = _session.deletion_in_progress();
        if (!already_deleting) {
                _session.set_deletion_in_progress();
@@ -1030,36 +1238,58 @@ Route::clear_processors (Placement p)
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
                ProcessorStreams err;
+               bool seen_amp = false;
 
-               ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
-               if (p == PreFader) {
-                       // Get rid of PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
-                               (*i)->drop_references ();
-                       }
-                       // Keep the rest
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               new_list.push_back (*i);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+                       if (*i == _amp) {
+                               seen_amp = true;
                        }
-               } else {
-                       // Keep PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
-                       }
-                       new_list.push_back (_amp);
-                       // Get rid of PostFader processors
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               (*i)->drop_references ();
+
+                       } else {
+                               if (seen_amp) {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       case PostFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       }
+
+                               } else {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       case PostFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       }
+                               }
                        }
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (&err); // this can't fail
+               }
        }
 
        processor_max_streams.reset();
        _have_internal_generator = false;
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
@@ -1127,19 +1357,23 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors.insert (i, processor);
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
 
                for (i = _processors.begin(); i != _processors.end(); ++i) {
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                if (pi->is_generator()) {
                                        _have_internal_generator = true;
@@ -1150,14 +1384,114 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        }
 
        processor->drop_references ();
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
+
+       return 0;
+}
+
+int
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
+{
+       ProcessorList deleted;
+
+       if (!_session.engine().connected()) {
+               return 1;
+       }
+
+       processor_max_streams.reset();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
+
+               ProcessorList as_we_were = _processors;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+
+                       processor = *i;
+
+                       /* these can never be removed */
+
+                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                               ++i;
+                               continue;
+                       }
+
+                       /* see if its in the list of processors to delete */
+
+                       if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
+                               ++i;
+                               continue;
+                       }
+
+                       /* stop IOProcessors that send to JACK ports
+                          from causing noise as a result of no longer being
+                          run.
+                       */
+
+                       boost::shared_ptr<IOProcessor> iop;
+
+                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                               iop->disconnect ();
+                       }
+
+                       deleted.push_back (processor);
+                       i = _processors.erase (i);
+               }
+
+               if (deleted.empty()) {
+                       /* none of those in the requested list were found */
+                       return 0;
+               }
+
+               _output->set_user_latency (0);
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors = as_we_were;
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->is_generator()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* now try to do what we need to so that those that were removed will be deleted */
+
+       for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+               (*i)->drop_references ();
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
 
+/** Caller must hold process lock */
 int
 Route::configure_processors (ProcessorStreams* err)
 {
+       assert (!AudioEngine::instance()->process_lock().trylock());
+       
        if (!_in_configure_processors) {
                Glib::RWLock::WriterLock lm (_processor_lock);
                return configure_processors_unlocked (err);
@@ -1165,25 +1499,40 @@ Route::configure_processors (ProcessorStreams* err)
        return 0;
 }
 
-/** Configure the input/output configuration of each processor in the processors list.
- * Return 0 on success, otherwise configuration is impossible.
- */
-int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+ChanCount
+Route::input_streams () const
 {
-       if (_in_configure_processors) {
-          return 0;
-       }
+        return _input->n_ports ();
+}
 
-       _in_configure_processors = true;
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
+       return try_configure_processors_unlocked (in, err);
+}
+
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
        // Check each processor in order to see if we can configure as requested
-       ChanCount in = _input->n_ports ();
        ChanCount out;
-       list< pair<ChanCount,ChanCount> > configuration;
+       list<pair<ChanCount, ChanCount> > configuration;
        uint32_t index = 0;
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
+       DEBUG_TRACE (DEBUG::Processors, "{\n");
+
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+                       break;
+               }
+               
                if ((*p)->can_support_io_configuration(in, out)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1191,26 +1540,65 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                                err->index = index;
                                err->count = in;
                        }
-                       _in_configure_processors = false;
-                       return -1;
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, "}\n");
+                       return list<pair<ChanCount, ChanCount> > ();
                }
        }
+
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
        
-       // We can, so configure everything
+       return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ *  Caller must hold process lock.
+ *  Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       if (_in_configure_processors) {
+               return 0;
+       }
+
+       _in_configure_processors = true;
+
+       list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+       if (configuration.empty ()) {
+               _in_configure_processors = false;
+               return -1;
+       }
+
+       ChanCount out;
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       break;
+               }
+               
                (*p)->configure_io(c->first, c->second);
-               (*p)->activate();
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
                out = c->second;
        }
 
-       // Ensure route outputs match last processor's outputs
-       if (out != _output->n_ports ()) {
-               _output->ensure_io (out, false, this);
+       if (_meter) {
+               _meter->reset_max_channels (processor_max_streams);
        }
 
+       /* make sure we have sufficient scratch buffers to cope with the new processor
+          configuration */
+       _session.ensure_buffers (n_process_buffers ());
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
+
        _in_configure_processors = false;
        return 0;
 }
@@ -1225,7 +1613,7 @@ Route::all_processors_flip ()
        }
 
        bool first_is_on = _processors.front()->active();
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (first_is_on) {
                        (*i)->deactivate ();
@@ -1233,7 +1621,7 @@ Route::all_processors_flip ()
                        (*i)->activate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1266,15 +1654,40 @@ Route::all_processors_active (Placement p, bool state)
                        }
                }
        }
-       
+
        _session.set_dirty ();
 }
 
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
+{
+       bool pre_fader = true;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* semantic note: if p == amp, we want to return true, so test
+                  for equality before checking if this is the amp
+               */
+
+               if ((*i) == p) {
+                       break;
+               }
+
+               if ((*i) == _amp) {
+                       pre_fader = false;
+                       break;
+               }
+       }
+
+       return pre_fader;
+}
+
 int
-Route::reorder_processors (const ProcessorList& new_order, Placement placement, ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
        /* "new_order" is an ordered list of processors to be positioned according to "placement".
-          NOTE: all processors in "new_order" MUST be marked as visible. There maybe additional
+          NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
           processors in the current actual processor list that are hidden. Any visible processors
           in the current list but not in "new_order" will be assumed to be deleted.
        */
@@ -1286,41 +1699,36 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                ProcessorList::const_iterator niter;
                ProcessorList as_it_was_before = _processors;
                ProcessorList as_it_will_be;
-               ProcessorList::iterator start, end;
-
-               placement_range (placement, start, end);
 
-               oiter = start;
-               niter = new_order.begin(); 
+               oiter = _processors.begin();
+               niter = new_order.begin();
 
                while (niter !=  new_order.end()) {
-                       
+
                        /* if the next processor in the old list is invisible (i.e. should not be in the new order)
-                          then append it to the temp list. 
+                          then append it to the temp list.
 
                           Otherwise, see if the next processor in the old list is in the new list. if not,
                           its been deleted. If its there, append it to the temp list.
                        */
 
-                       if (oiter == end) {
+                       if (oiter == _processors.end()) {
 
-                               /* no more elements in the old list, so just stick the rest of 
+                               /* no more elements in the old list, so just stick the rest of
                                   the new order onto the temp list.
                                */
 
                                as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
                                while (niter != new_order.end()) {
-                                       (*niter)->set_placement (placement);
                                        ++niter;
                                }
                                break;
 
                        } else {
-                               
-                               if (!(*oiter)->visible()) {
+
+                               if (!(*oiter)->display_to_user()) {
 
                                        as_it_will_be.push_back (*oiter);
-                                       (*oiter)->set_placement (placement);
 
                                } else {
 
@@ -1331,28 +1739,34 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                                        } else {
                                                /* ignore this one, and add the next item from the new order instead */
                                                as_it_will_be.push_back (*niter);
-                                               (*niter)->set_placement (placement);
                                                ++niter;
                                        }
                                }
-                               
+
                                 /* now remove from old order - its taken care of no matter what */
                                oiter = _processors.erase (oiter);
                        }
-                       
+
                }
 
                _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
 
-               if (configure_processors_unlocked (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_streams = old_pms;
-                       return -1;
-               } 
-       } 
-
-       processors_changed (); /* EMIT SIGNAL */
-
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               _processors = as_it_was_before;
+                               processor_max_streams = old_pms;
+                               return -1;
+                       }
+               }
+       }
+
+        if (true) {
+                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+               set_processor_positions ();
+        }
+
        return 0;
 }
 
@@ -1375,6 +1789,8 @@ Route::state(bool full_state)
        ProcessorList::iterator i;
        char buf[32];
 
+       id().print (buf, sizeof (buf));
+       node->add_property("id", buf);
        node->add_property ("name", _name);
        node->add_property("default-type", _default_type.to_string());
 
@@ -1383,26 +1799,25 @@ Route::state(bool full_state)
        }
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("phase-invert", _phase_invert?"yes":"no");
+       string p;
+       boost::to_string (_phase_invert, p);
+       node->add_property("phase-invert", p);
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
-       if (_edit_group) {
-               node->add_property("edit-group", _edit_group->name());
-       }
-       if (_mix_group) {
-               node->add_property("mix-group", _mix_group->name());
+       if (_route_group) {
+               node->add_property("route-group", _route_group->name());
        }
 
        string order_string;
-       OrderKeys::iterator x = order_keys.begin(); 
+       OrderKeys::iterator x = order_keys.begin();
 
        while (x != order_keys.end()) {
                order_string += string ((*x).first);
                order_string += '=';
                snprintf (buf, sizeof(buf), "%ld", (*x).second);
                order_string += buf;
-               
+
                ++x;
 
                if (x == order_keys.end()) {
@@ -1412,10 +1827,18 @@ Route::state(bool full_state)
                order_string += ':';
        }
        node->add_property ("order-keys", order_string);
+       node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
+       node->add_property ("soloed-by-upstream", buf);
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
+       node->add_property ("soloed-by-downstream", buf);
+       node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
+       node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
+       node->add_child_nocopy (_mute_control->get_state ());
        node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
@@ -1435,24 +1858,26 @@ Route::state(bool full_state)
        if (_extra_xml){
                node->add_child_copy (*_extra_xml);
        }
-       
+
        return *node;
 }
 
 int
-Route::set_state (const XMLNode& node)
+Route::set_state (const XMLNode& node, int version)
 {
-       return _set_state (node, true);
+       return _set_state (node, version, true);
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 {
+       if (version < 3000) {
+               return _set_state_2X (node, version);
+       }
 
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       XMLPropertyList plist;
        const XMLProperty *prop;
 
        if (node.name() != "Route"){
@@ -1462,52 +1887,274 @@ Route::_set_state (const XMLNode& node, bool call_base)
 
        if ((prop = node.property (X_("name"))) != 0) {
                Route::set_name (prop->value());
-       } 
+       }
+
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
+       }
 
        if ((prop = node.property (X_("flags"))) != 0) {
                _flags = Flag (string_2_enum (prop->value(), _flags));
        } else {
                _flags = Flag (0);
        }
-       
+
+        if (is_master() || is_monitor() || is_hidden()) {
+                _mute_master->set_solo_ignore (true);
+        }
+
+       /* add all processors (except amp, which is always present) */
+
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+               child = *niter;
+
+               if (child->name() == IO::state_node_name) {
+                       if ((prop = child->property (X_("direction"))) == 0) {
+                               continue;
+                       }
+
+                       if (prop->value() == "Input") {
+                               _input->set_state (*child, version);
+                       } else if (prop->value() == "Output") {
+                               _output->set_state (*child, version);
+                       }
+               }
+
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
+               }
+       }
+
+       set_processor_state (processor_state);
+
+       if ((prop = node.property ("self-solo")) != 0) {
+               set_self_solo (string_is_affirmative (prop->value()));
+       }
+
+       if ((prop = node.property ("soloed-by-upstream")) != 0) {
+               _soloed_by_others_upstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_upstream (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("soloed-by-downstream")) != 0) {
+               _soloed_by_others_downstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_downstream (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("solo-isolated")) != 0) {
+               set_solo_isolated (string_is_affirmative (prop->value()), this);
+       }
+
+       if ((prop = node.property ("solo-safe")) != 0) {
+               set_solo_safe (string_is_affirmative (prop->value()), this);
+       }
+
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (prop->value()=="yes"?true:false, this);
+               set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
-               set_denormal_protection (prop->value()=="yes"?true:false, this);
+               set_denormal_protection (string_is_affirmative (prop->value()));
        }
-       
+
        if ((prop = node.property (X_("active"))) != 0) {
-               bool yn = (prop->value() == "yes");
+               bool yn = string_is_affirmative (prop->value());
                _active = !yn; // force switch
                set_active (yn);
        }
 
+       if ((prop = node.property (X_("meter-point"))) != 0) {
+               MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
+                set_meter_point (mp);
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
+               }
+       }
+
+       if ((prop = node.property (X_("order-keys"))) != 0) {
+
+               int32_t n;
+
+               string::size_type colon, equal;
+               string remaining = prop->value();
+
+               while (remaining.length()) {
+
+                       if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
+                               error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                     << endmsg;
+                       } else {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
+                                       error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                             << endmsg;
+                               } else {
+                                       set_order_key (remaining.substr (0, equal), n);
+                               }
+                       }
+
+                       colon = remaining.find_first_of (':');
+
+                       if (colon != string::npos) {
+                               remaining = remaining.substr (colon+1);
+                       } else {
+                               break;
+                       }
+               }
+       }
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               child = *niter;
+
+               if (child->name() == X_("Comment")) {
+
+                       /* XXX this is a terrible API design in libxml++ */
+
+                       XMLNode *cmt = *(child->children().begin());
+                       _comment = cmt->content();
+
+               } else if (child->name() == X_("Extra")) {
+
+                       _extra_xml = new XMLNode (*child);
+
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == "solo") {
+                               _solo_control->set_state (*child, version);
+                       }
+
+               } else if (child->name() == X_("RemoteControl")) {
+                       if ((prop = child->property (X_("id"))) != 0) {
+                               int32_t x;
+                               sscanf (prop->value().c_str(), "%d", &x);
+                               set_remote_control_id (x);
+                       }
+
+               } else if (child->name() == X_("MuteMaster")) {
+                       _mute_master->set_state (*child, version);
+               }
+       }
+
+       return 0;
+}
+
+int
+Route::_set_state_2X (const XMLNode& node, int version)
+{
+       XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+       XMLNode *child;
+       const XMLProperty *prop;
+
+       /* 2X things which still remain to be handled:
+        * default-type
+        * automation
+        * controlouts
+        */
+
+       if (node.name() != "Route") {
+               error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
+               return -1;
+       }
+
+       if ((prop = node.property (X_("flags"))) != 0) {
+               _flags = Flag (string_2_enum (prop->value(), _flags));
+       } else {
+               _flags = Flag (0);
+       }
+       
+       if ((prop = node.property (X_("phase-invert"))) != 0) {
+               boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
+               if (string_is_affirmative (prop->value ())) {
+                       p.set ();
+               }                       
+               set_phase_invert (p);
+       }
+
+       if ((prop = node.property (X_("denormal-protection"))) != 0) {
+               set_denormal_protection (string_is_affirmative (prop->value()));
+       }
+
        if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = (prop->value()=="yes");
+               bool yn = string_is_affirmative (prop->value());
 
                /* XXX force reset of solo status */
 
                set_solo (yn, this);
        }
 
+       if ((prop = node.property (X_("muted"))) != 0) {
+               
+               bool first = true;
+               bool muted = string_is_affirmative (prop->value());
+               
+               if (muted){
+                 
+                       string mute_point;
+                       
+                       if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                       mute_point = mute_point + "PreFader";
+                                       first = false;
+                               }
+                       }
+                       
+                       if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                 
+                                       if (!first) {
+                                               mute_point = mute_point + ",";
+                                       }
+                                       
+                                       mute_point = mute_point + "PostFader";
+                                       first = false;
+                               }
+                       }
+
+                       if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                 
+                                       if (!first) {
+                                               mute_point = mute_point + ",";
+                                       }
+                                       
+                                       mute_point = mute_point + "Listen";
+                                       first = false;
+                               }
+                       }
+
+                       if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                 
+                                       if (!first) {
+                                               mute_point = mute_point + ",";
+                                       }
+                                       
+                                       mute_point = mute_point + "Main";
+                               }
+                       }
+                       
+                       _mute_master->set_mute_points (mute_point);
+               }
+       }
+
        if ((prop = node.property (X_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
-       
-       if ((prop = node.property (X_("edit-group"))) != 0) {
-               RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
-               if(edit_group == 0) {
-                       error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_edit_group(edit_group, this);
-               }
-       }
+
+       /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
+          don't mean the same thing.
+       */
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -1516,13 +2163,13 @@ Route::_set_state (const XMLNode& node, bool call_base)
 
                        if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
                                error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                     << endmsg;
+                                       << endmsg;
                        } else {
-                               if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                             << endmsg;
+                                               << endmsg;
                                } else {
-                                       set_order_key (remaining.substr (0, equal).c_str(), n);
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
                        }
 
@@ -1536,32 +2183,78 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       nlist = node.children();
-       XMLNode processor_state (X_("processor_state"));
+       /* IOs */
+
+       nlist = node.children ();
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               
                child = *niter;
 
                if (child->name() == IO::state_node_name) {
-                       if ((prop = child->property (X_("direction"))) == 0) {
-                               continue;
+
+                       /* there is a note in IO::set_state_2X() about why we have to call
+                          this directly.
+                          */
+
+                       _input->set_state_2X (*child, version, true);
+                       _output->set_state_2X (*child, version, false);
+
+                       if ((prop = child->property (X_("name"))) != 0) {
+                               Route::set_name (prop->value ());
+                       }
+
+                       if ((prop = child->property (X_("id"))) != 0) {
+                               _id = prop->value ();
+                       }
+
+                       if ((prop = child->property (X_("active"))) != 0) {
+                               bool yn = string_is_affirmative (prop->value());
+                               _active = !yn; // force switch
+                               set_active (yn);
                        }
                        
-                       if (prop->value() == "Input") {
-                               _input->set_state (*child);
-                       } else if (prop->value() == "Output") {
-                               _output->set_state (*child);
+                       if ((prop = child->property (X_("gain"))) != 0) {
+                               gain_t val;
+
+                               if (sscanf (prop->value().c_str(), "%f", &val) == 1) {
+                                       _amp->gain_control()->set_value (val);
+                               }
                        }
-               }
                        
-               if (child->name() == X_("Processor")) {
-                       processor_state.add_child_copy (*child);
+                       /* Set up Panners in the IO */
+                       XMLNodeList io_nlist = child->children ();
+                       
+                       XMLNodeConstIterator io_niter;
+                       XMLNode *io_child;
+                       
+                       for (io_niter = io_nlist.begin(); io_niter != io_nlist.end(); ++io_niter) {
+
+                               io_child = *io_niter;
+                               
+                               if (io_child->name() == X_("Panner")) {
+                                       _main_outs->panner()->set_state(*io_child, version);
+                               } else if (io_child->name() == X_("Automation")) {
+                                       /* IO's automation is for the fader */
+                                       _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
+                               }
+                       }
                }
        }
 
-       set_processor_state (processor_state);
-       
+       XMLNodeList redirect_nodes;
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+               child = *niter;
+
+               if (child->name() == X_("Send") || child->name() == X_("Insert")) {
+                       redirect_nodes.push_back(child);
+               }
+
+       }
+
+       set_processor_state_2X (redirect_nodes, version);
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -1572,33 +2265,25 @@ Route::_set_state (const XMLNode& node, bool call_base)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
+               } else if (child->name() == X_("extra")) {
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-                       
-                       if (prop->value() == "solo") {
-                               _solo_control->set_state (*child);
-                               _session.add_controllable (_solo_control);
-                       } 
-
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == X_("solo")) {
+                               _solo_control->set_state (*child, version);
+                       } else if (prop->value() == X_("mute")) {
+                               _mute_control->set_state (*child, version);
+                        }
+                                
                } else if (child->name() == X_("RemoteControl")) {
                        if ((prop = child->property (X_("id"))) != 0) {
                                int32_t x;
                                sscanf (prop->value().c_str(), "%d", &x);
                                set_remote_control_id (x);
                        }
-               } 
-       }
 
-       if ((prop = node.property (X_("mix-group"))) != 0) {
-               RouteGroup* mix_group = _session.mix_group_by_name(prop->value());
-               if (mix_group == 0) {
-                       error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               }  else {
-                       set_mix_group(mix_group, this);
-               }
+               } 
        }
 
        return 0;
@@ -1615,101 +2300,119 @@ Route::get_processor_state ()
        return *root;
 }
 
+void
+Route::set_processor_state_2X (XMLNodeList const & nList, int version)
+{
+       /* We don't bother removing existing processors not in nList, as this
+          method will only be called when creating a Route from scratch, not
+          for undo purposes.  Just put processors in at the appropriate place
+          in the list.
+       */
+
+       for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
+               add_processor_from_xml_2X (**i, version);
+       }
+}
+
 void
 Route::set_processor_state (const XMLNode& node)
 {
        const XMLNodeList &nlist = node.children();
        XMLNodeConstIterator niter;
-       bool has_meter_processor = false; // legacy sessions don't
-       ProcessorList::iterator i, o;
-
-       cerr << _name << " _set_processor_states\n";
-
-       // Iterate through existing processors, remove those which are not in the state list
-       for (i = _processors.begin(); i != _processors.end(); ) {
-               ProcessorList::iterator tmp = i;
-               ++tmp;
-
-               bool processorInStateList = false;
-       
-               for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-                       XMLProperty* id_prop = (*niter)->property(X_("id"));
-                       if (id_prop && (*i)->id() == id_prop->value()) {
-                               processorInStateList = true;
-                               break;
-                       }
-               }
-               
-               if (!processorInStateList) {
-                       remove_processor (*i);
-               }
+        ProcessorList new_order;
+        bool must_configure = false;
 
-               i = tmp;
-       }
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-       // Iterate through state list and make sure all processors are on the track and in the correct order,
-       // set the state of existing processors according to the new state on the same go
-       i = _processors.begin();
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
-               
                XMLProperty* prop = (*niter)->property ("type");
 
-               if (prop && prop->value() == "meter")  {
-                       has_meter_processor = true;
-               }
-
-               o = i;
-
-               if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
-
-                       // Check whether the next processor in the list 
-                       
-                       while (o != _processors.end()) {
+               if (prop->value() == "amp") {
+                        _amp->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_amp);
+                } else if (prop->value() == "meter") {
+                        _meter->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_meter);
+                } else if (prop->value() == "main-outs") {
+                        _main_outs->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_main_outs);
+                } else if (prop->value() == "intreturn") {
+                        if (!_intreturn) {
+                                _intreturn.reset (new InternalReturn (_session));
+                                must_configure = true;
+                        }
+                        _intreturn->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_intreturn);
+                } else if (is_monitor() && prop->value() == "monitor") {
+                        if (!_monitor_control) {
+                                _monitor_control.reset (new MonitorProcessor (_session));
+                                must_configure = true;
+                        }
+                        _monitor_control->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_monitor_control);
+                } else {
+                        ProcessorList::iterator o;
+
+                       for (o = _processors.begin(); o != _processors.end(); ++o) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
+                                        (*o)->set_state (**niter, Stateful::current_state_version);
+                                        new_order.push_back (*o);
                                        break;
                                }
-                               
-                               ++o;
-                       }
-               }
-
-               // If the processor (*niter) is not on the route,
-               // create it and move it to the correct location
-               if (o == _processors.end()) {
-
-                       if (add_processor_from_xml (**niter, i)) {
-                               --i; // move iterator to the newly inserted processor
-                       } else {
-                               cerr << "Error restoring route: unable to restore processor" << endl;
-                       }
-
-               // Otherwise, the processor already exists; just
-               // ensure it is at the location provided in the XML state
-               } else {
-
-                       if (i != o) {
-                               boost::shared_ptr<Processor> tmp = (*o);
-                               _processors.erase (o); // remove the old copy
-                               _processors.insert (i, tmp); // insert the processor at the correct location
-                               --i; // move iterator to the correct processor
                        }
 
-                       (*i)->set_state (**niter);
-               }
-       }
-
-       /* note: there is no configure_processors() call because we figure that
-          the XML state represents a working signal route.
-       */
+                        // If the processor (*niter) is not on the route then create it 
+                        
+                        if (o == _processors.end()) {
+                                
+                                boost::shared_ptr<Processor> processor;
+
+                                if (prop->value() == "intsend") {
+                                        
+                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+                                        
+                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+                                           prop->value() == "lv2" ||
+                                           prop->value() == "vst" ||
+                                           prop->value() == "audiounit") {
+                                        
+                                        processor.reset (new PluginInsert(_session));
+                                        
+                                } else if (prop->value() == "port") {
+                                        
+                                        processor.reset (new PortInsert (_session, _mute_master));
+                                        
+                                } else if (prop->value() == "send") {
+                                        
+                                        processor.reset (new Send (_session, _mute_master));
+                                        
+                                } else {
+                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                                        continue;
+                                }
+
+                               if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+                                       /* This processor could not be configured.  Turn it into a UnknownProcessor */
+                                       processor.reset (new UnknownProcessor (_session, **niter));
+                               }
+                                       
+                               new_order.push_back (processor);
+                               must_configure = true;
+                        }
+                }
+        }
 
-       if (!has_meter_processor) {
-               set_meter_point (_meter_point, NULL);
-       }
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+                _processors = new_order;
+                if (must_configure) {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                        configure_processors_unlocked (0);
+                }
+        }
 
-       processors_changed ();
+        processors_changed (RouteProcessorChange ());
+       set_processor_positions ();
 }
 
 void
@@ -1720,229 +2423,280 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
 {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return;
+       }
+
+       silence_unlocked (nframes);
+}
+
+void
+Route::silence_unlocked (framecnt_t nframes)
+{
+       /* Must be called with the processor lock held */
+       
        if (!_silent) {
 
                _output->silence (nframes);
-               
-               { 
-                       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-                       
-                       if (lm.locked()) {
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       boost::shared_ptr<PluginInsert> pi;
-
-                                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
-                                               // skip plugins, they don't need anything when we're not active
-                                               continue;
-                                       }
-                                       
-                                       (*i)->silence (nframes);
-                               }
 
-                               if (nframes == _session.get_block_size()) {
-                                       // _silent = true;
-                               }
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       
+                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
+                               // skip plugins, they don't need anything when we're not active
+                               continue;
                        }
+                       
+                       (*i)->silence (nframes);
                }
                
+               if (nframes == _session.get_block_size()) {
+                       // _silent = true;
+               }
        }
-}      
+}
 
-boost::shared_ptr<Delivery>
-Route::add_listener (boost::shared_ptr<IO> io, const string& listen_name)
+void
+Route::add_internal_return ()
 {
-       string name = _name;
-       name += '[';
-       name += listen_name;
-       name += ']';
-       
-       boost::shared_ptr<Delivery> listener (new Delivery (_session, _mute_master, name, Delivery::Listen)); 
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+}
 
-       /* As an IO, our control outs need as many IO outputs as we have outputs
-        *   (we track the changes in ::output_change_handler()).
-        * As a processor, the listener is an identity processor
-        *   (i.e. it does not modify its input buffers whatsoever)
-        */
+BufferSet*
+Route::get_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
-       if (listener->output()->ensure_io (n_outputs(), true, this)) {
-               return boost::shared_ptr<Delivery>();
+               if (d) {
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
+               }
        }
-       
-       add_processor (listener, PostFader);
 
-       return listener;
+       return 0;
+}
+
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+
+               if (d) {
+                       return d->release_buffers ();
+               }
+       }
 }
 
 int
-Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
 {
        vector<string> ports;
        vector<string>::const_iterator i;
 
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
-               
-               for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       boost::shared_ptr<const Delivery> d = boost::dynamic_pointer_cast<const Delivery>(*x);
 
-                       if (d && d->output() == io) {
+               for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
+
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+                       if (d && d->target_route() == route) {
+
+                               /* if the target is the control outs, then make sure
+                                  we take note of which i-send is doing that.
+                               */
+
+                               if (route == _session.monitor_out()) {
+                                       _monitor_send = boost::dynamic_pointer_cast<Delivery>(d);
+                               }
+
                                /* already listening via the specified IO: do nothing */
+
                                return 0;
                        }
                }
        }
 
-       uint32_t ni = io->n_ports().n_total();
+       boost::shared_ptr<InternalSend> listener;
 
-       for (uint32_t n = 0; n < ni; ++n) {
-               ports.push_back (io->nth (n)->name());
-       }
+       try {
 
-       if (ports.empty()) {
-               return 0;
-       }
-       
-       boost::shared_ptr<Delivery> listen_point = add_listener (io, listen_name);
-       
-       /* XXX hack for now .... until we can generalize listen points */
+                if (is_master()) {
+                        
+                        if (route == _session.monitor_out()) {
+                                /* master never sends to control outs */
+                                return 0;
+                        } else {
+                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                        }
 
-       _control_outs = listen_point;
+                } else {
+                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                }
 
-       /* now connect to the named ports */
-       
-       ni = listen_point->output()->n_ports().n_total();
-       size_t psize = ports.size();
+       } catch (failed_constructor& err) {
+               return -1;
+       }
 
-       for (size_t n = 0; n < ni; ++n) {
-               if (listen_point->output()->connect (listen_point->output()->nth (n), ports[n % psize], this)) {
-                       error << string_compose (_("could not connect %1 to %2"),
-                                                listen_point->output()->nth (n)->name(), ports[n % psize]) << endmsg;
-                       return -1;
-               }
+       if (route == _session.monitor_out()) {
+               _monitor_send = listener;
        }
 
-       
-       return 0;
-}      
+        if (placement == PostFader) {
+                /* put it *really* at the end, not just after the panner (main outs)
+                 */
+                add_processor (listener, _processors.end());
+        } else {
+                add_processor (listener, PreFader);
+        }
+
+       return 0;
+}
 
 void
-Route::drop_listen (boost::shared_ptr<IO> io)
+Route::drop_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rm (_processor_lock);
-       
+       Glib::RWLock::ReaderLock rl(_processor_lock);
+       rl.acquire ();
+
+  again:
        for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
-               
-               tmp = x;
-               ++tmp;
-               
-               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*x);
-               
-               if (d && d->output() == io) {
-                       /* already listening via the specified IO: do nothing */
+
+               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+               if (d && d->target_route() == route) {
+                       rl.release ();
                        remove_processor (*x, &err);
-                       
-               } 
-               
-               x = tmp;
-       }
-}
+                       rl.acquire ();
 
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+                        /* list could have been demolished while we dropped the lock
+                          so start over.
+                       */
 
-{
-       if (eg == _edit_group) {
-               return;
+                       goto again;
+               }
        }
 
-       if (_edit_group) {
-               _edit_group->remove (this);
-       }
+       rl.release ();
 
-       if ((_edit_group = eg) != 0) {
-               _edit_group->add (this);
+       if (route == _session.monitor_out()) {
+               _monitor_send.reset ();
        }
-
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_edit_group (void *src)
+Route::set_comment (string cmt, void *src)
 {
-       _edit_group = 0;
+       _comment = cmt;
+       comment_changed (src);
        _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
-void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+bool
+Route::add_fed_by (boost::shared_ptr<Route> other, bool via_sends_only)
 {
-       if (mg == _mix_group) {
-               return;
-       }
+        FeedRecord fr (other, via_sends_only);
 
-       if (_mix_group) {
-               _mix_group->remove (this);
-       }
+        pair<FedBy::iterator,bool> result =  _fed_by.insert (fr);
 
-       if ((_mix_group = mg) != 0) {
-               _mix_group->add (this);
-       }
+        if (!result.second) {
 
-       _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+                /* already a record for "other" - make sure sends-only information is correct */
+                if (!via_sends_only && result.first->sends_only) {
+                        FeedRecord* frp = const_cast<FeedRecord*>(&(*result.first));
+                        frp->sends_only = false;
+                }
+        }
+        
+        return result.second;
 }
 
 void
-Route::drop_mix_group (void *src)
+Route::clear_fed_by ()
 {
-       _mix_group = 0;
-       _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+        _fed_by.clear ();
 }
 
-void
-Route::set_comment (string cmt, void *src)
-{
-       _comment = cmt;
-       comment_changed (src);
-       _session.set_dirty ();
+bool
+Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
+{
+        const FedBy& fed_by (other->fed_by());
+
+        for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
+                boost::shared_ptr<Route> sr = f->r.lock();
+
+                if (sr && (sr.get() == this)) {
+
+                        if (via_sends_only) {
+                                *via_sends_only = f->sends_only;
+                        }
+
+                        return true;
+                }
+        }
+
+        return false;
 }
 
 bool
-Route::feeds (boost::shared_ptr<IO> other)
+Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
 {
-       if (_output->connected_to (other)) {
+       DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
+
+       if (_output->connected_to (other->input())) {
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
+               if (only_send) {
+                       *only_send = false;
+               }
+
                return true;
        }
 
-       /* check IOProcessors which may also interconnect Routes */
-
-       for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
+       
+       for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
                boost::shared_ptr<IOProcessor> iop;
-               
+
                if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
-                       if (iop->output() && iop->output()->connected_to (other)) {
+                       if (iop->feeds (other)) {
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
+                               if (only_send) {
+                                       *only_send = true;
+                               }
                                return true;
+                       } else {
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name()));
                        }
+               } else {
+                       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tPROC %1 is not an IOP\n", (*r)->name()));
                }
+                       
        }
 
+       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tdoes NOT feed %1\n", other->name()));
        return false;
 }
 
+/** Called from the (non-realtime) butler thread when the transport is stopped */
 void
-Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors)
+Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
-       nframes_t now = _session.transport_frame();
+       framepos_t now = _session.transport_frame();
 
        {
                Glib::RWLock::ReaderLock lm (_processor_lock);
@@ -1951,13 +2705,14 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
                        automation_snapshot (now, true);
                }
 
+                Automatable::transport_stopped (now);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
+
                        if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
-                               (*i)->deactivate ();
-                               (*i)->activate ();
+                                (*i)->flush ();
                        }
-                       
+
                        (*i)->transport_stopped (now);
                }
        }
@@ -1965,21 +2720,52 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
        _roll_delay = _initial_delay;
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
-Route::input_change_handler (IOChange change, void *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
                configure_processors (0);
+               _phase_invert.resize (_input->n_ports().n_audio ());
+               io_changed (); /* EMIT SIGNAL */
        }
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
-Route::output_change_handler (IOChange change, void *src)
+Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
-               
+       if ((change.type & IOChange::ConfigurationChanged)) {
+
                /* XXX resize all listeners to match _main_outs? */
-               
+
+               /* Auto-connect newly-created outputs, unless we're auto-connecting to master
+                  and we are master (as an auto-connect in this situation would cause a
+                  feedback loop)
+               */
+               AutoConnectOption ac = Config->get_output_auto_connect ();
+               if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) {
+
+                       ChanCount start = change.before;
+                       
+                       for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+                               if (change.before.get(*i) < change.after.get(*i)) {
+                                       /* the existing ChanCounts don't matter for this call as they are only
+                                          to do with matching input and output indices, and we are only changing
+                                          outputs here.
+                                       */
+                                       ChanCount dummy;
+
+                                       /* only auto-connect the newly-created outputs, not the ones that were
+                                          already there
+                                       */
+                                       start.set (*i, start.get (*i) + 1);
+                                       
+                                       _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before);
+                               }
+                       }
+               }
+
                // configure_processors (0);
        }
 }
@@ -1990,22 +2776,41 @@ Route::pans_required () const
        if (n_outputs().n_audio() < 2) {
                return 0;
        }
-       
+
        return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
 }
 
-int 
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,  
-               bool session_state_changing, bool can_record, bool rec_monitors_input)
+int
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
+               bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
+       }
+
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
-       if (session_state_changing || !_active || n_inputs() == ChanCount::ZERO)  {
-               silence (nframes);
+       if (!_active || n_inputs() == ChanCount::ZERO)  {
+               silence_unlocked (nframes);
                return 0;
        }
+       if (session_state_changing) {
+               if (_session.transport_speed() != 0.0f) {
+                       /* we're rolling but some state is changing (e.g. our diskstream contents)
+                          so we cannot use them. Be silent till this is over.
+                          
+                          XXX note the absurdity of ::no_roll() being called when we ARE rolling!
+                       */
+                       silence_unlocked (nframes);
+                       return 0;
+               }
+               /* we're really not rolling, so we're either delivery silence or actually
+                  monitoring, both of which are safe to do while session_state_changing is true.
+               */
+       }
 
        _amp->apply_gain_automation (false);
        passthru (start_frame, end_frame, nframes, 0);
@@ -2013,21 +2818,21 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
        return 0;
 }
 
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
                _roll_delay -= nframes;
-               silence (nframes);
+               silence_unlocked (nframes);
                /* transport frame is not legal for caller to use */
                return 0;
 
        } else if (_roll_delay > 0) {
 
                nframes -= _roll_delay;
-               silence (_roll_delay);
-               /* we've written _roll_delay of samples into the 
+               silence_unlocked (_roll_delay);
+               /* we've written _roll_delay of samples into the
                   output ports, so make a note of that for
                   future reference.
                */
@@ -2041,29 +2846,26 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 }
 
 int
-Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
-            bool can_record, bool rec_monitors_input)
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
+            bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
-       {
-               // automation snapshot can also be called from the non-rt context
-               // and it uses the processor list, so we try to acquire the lock here
-               Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-
-               if (lm.locked()) {
-                       automation_snapshot (_session.transport_frame(), false);
-               }
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
        }
+       
+       automation_snapshot (_session.transport_frame(), false);
 
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
        if (!_active || n_inputs().n_total() == 0) {
-               silence (nframes);
+               silence_unlocked (nframes);
                return 0;
        }
-       
-       nframes_t unused = 0;
+
+       framecnt_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2077,8 +2879,8 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
 }
 
 int
-Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, 
-                   bool can_record, bool rec_monitors_input)
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
+                   bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
        return 0;
@@ -2098,18 +2900,18 @@ Route::has_external_redirects () const
        // FIXME: what about sends? - they don't return a signal back to ardour?
 
        boost::shared_ptr<const PortInsert> pi;
-       
+
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
 
                        for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
-                               
+
                                string port_name = port->name();
                                string client_name = port_name.substr (0, port_name.find(':'));
 
                                /* only say "yes" if the redirect is actually in use */
-                               
+
                                if (client_name != "ardour" && pi->active()) {
                                        return true;
                                }
@@ -2130,44 +2932,127 @@ Route::flush_processors ()
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
-               (*i)->activate ();
+                (*i)->flush ();
+       }
+}
+
+void
+Route::set_meter_point (MeterPoint p)
+{
+       /* CAN BE CALLED FROM PROCESS CONTEXT */
+
+       if (_meter_point == p) {
+               return;
+       }
+
+       bool meter_was_visible_to_user = _meter->display_to_user ();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+       
+               if (p != MeterCustom) {
+                       // Move meter in the processors list to reflect the new position
+                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+                       _processors.erase(loc);
+                       switch (p) {
+                       case MeterInput:
+                               loc = _processors.begin();
+                               break;
+                       case MeterPreFader:
+                               loc = find (_processors.begin(), _processors.end(), _amp);
+                               break;
+                       case MeterPostFader:
+                               loc = _processors.end();
+                               break;
+                       default:
+                               break;
+                       }
+                       
+                       ChanCount m_in;
+                       
+                       if (loc == _processors.begin()) {
+                               m_in = _input->n_ports();
+                       } else {
+                               ProcessorList::iterator before = loc;
+                               --before;
+                               m_in = (*before)->output_streams ();
+                       }
+                       
+                       _meter->reflect_inputs (m_in);
+                       
+                       _processors.insert (loc, _meter);
+                       
+                       /* we do not need to reconfigure the processors, because the meter
+                          (a) is always ready to handle processor_max_streams
+                          (b) is always an N-in/N-out processor, and thus moving
+                          it doesn't require any changes to the other processors.
+                       */
+                       
+                       _meter->set_display_to_user (false);
+                       
+               } else {
+                       
+                       // just make it visible and let the user move it
+                       
+                       _meter->set_display_to_user (true);
+               }
        }
+
+       _meter_point = p;
+       meter_change (); /* EMIT SIGNAL */
+
+       bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+       
+       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
 }
 
 void
-Route::set_meter_point (MeterPoint p, void *src)
+Route::put_monitor_send_at (Placement p)
 {
-       if (_meter_point != p) {
-               _meter_point = p;
+       if (!_monitor_send) {
+               return;
+       }
 
-               // Move meter in the processors list
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList as_it_was (_processors);
+               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send);
                _processors.erase(loc);
+               
                switch (p) {
-               case MeterInput:
-                       loc = _processors.begin();
-                       break;
-               case MeterPreFader:
+               case PreFader:
                        loc = find(_processors.begin(), _processors.end(), _amp);
+                       if (loc != _processors.begin()) {
+                               --loc;
+                       }
                        break;
-               case MeterPostFader:
+               case PostFader:
                        loc = _processors.end();
                        break;
                }
-               _processors.insert(loc, _meter);
                
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
+               _processors.insert (loc, _monitor_send);
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       
+                       if (configure_processors_unlocked (0)) {
+                               _processors = as_it_was;
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
+               }
        }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
 }
 
-nframes_t
+framecnt_t
 Route::update_total_latency ()
 {
-       nframes_t old = _output->effective_latency();
-       nframes_t own_latency = _output->user_latency();
+       framecnt_t old = _output->effective_latency();
+       framecnt_t own_latency = _output->user_latency();
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
@@ -2175,13 +3060,10 @@ Route::update_total_latency ()
                }
        }
 
-#undef DEBUG_LATENCY
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << own_latency << endl;
-#endif
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency));
 
        _output->set_port_latency (own_latency);
-       
+
        if (_output->user_latency() == 0) {
 
                /* this (virtual) function is used for pure Routes,
@@ -2190,7 +3072,7 @@ Route::update_total_latency ()
                   port, not prerecorded material, and therefore we
                   have to take into account any input latency.
                */
-               
+
                own_latency += _input->signal_latency ();
        }
 
@@ -2198,26 +3080,23 @@ Route::update_total_latency ()
                _output->set_latency_delay (own_latency);
                signal_latency_changed (); /* EMIT SIGNAL */
        }
-       
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
-            << own_latency << endl;
-#endif
+
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency));
 
        return _output->effective_latency ();
 }
 
 void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
 {
        _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
 void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_delay (framecnt_t longest_session_latency)
 {
-       nframes_t old = _initial_delay;
+       framecnt_t old = _initial_delay;
 
        if (_output->effective_latency() < longest_session_latency) {
                _initial_delay = longest_session_latency - _output->effective_latency();
@@ -2235,15 +3114,17 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 }
 
 void
-Route::automation_snapshot (nframes_t now, bool force)
+Route::automation_snapshot (framepos_t now, bool force)
 {
+       panner()->automation_snapshot (now, force);
+       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
 }
 
 Route::SoloControllable::SoloControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation), 
+       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
                             boost::shared_ptr<AutomationList>(), name)
        , route (r)
 {
@@ -2252,26 +3133,73 @@ Route::SoloControllable::SoloControllable (std::string name, Route& r)
 }
 
 void
-Route::SoloControllable::set_value (float val)
+Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-       
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+
+       if (Config->get_solo_control_is_listen_control()) {
+               _session.set_listen (rl, bval);
+       } else {
+               _session.set_solo (rl, bval);
+       }
+#else
        route.set_solo (bval, this);
+#endif
 }
 
-float
+double
 Route::SoloControllable::get_value (void) const
 {
-       return route.soloed() ? 1.0f : 0.0f;
+       if (Config->get_solo_control_is_listen_control()) {
+               return route.listening() ? 1.0f : 0.0f;
+       } else {
+               return route.self_soloed() ? 1.0f : 0.0f;
+       }
+}
+
+Route::MuteControllable::MuteControllable (std::string name, Route& r)
+       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+                            boost::shared_ptr<AutomationList>(), name)
+       , route (r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+       set_list (gl);
+}
+
+void
+Route::MuteControllable::set_value (double val)
+{
+       bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+       _session.set_mute (rl, bval);
+#else
+       route.set_mute (bval, this);
+#endif
+}
+
+double
+Route::MuteControllable::get_value (void) const
+{
+       return route.muted() ? 1.0f : 0.0f;
 }
 
-void 
-Route::set_block_size (nframes_t nframes)
+void
+Route::set_block_size (pframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
-       _session.ensure_buffers(processor_max_streams);
+       
+       _session.ensure_buffers (n_process_buffers ());
 }
 
 void
@@ -2304,44 +3232,54 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (framepos_t pos, framecnt_t frames)
 {
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
        /* gain automation */
-       XMLNode &before = _gain_control->get_state ();
-       _gain_control->shift (pos, frames);
-       XMLNode &after = _gain_control->get_state ();
-       _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+        {
+                boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
+                
+                XMLNode &before = gc->alist()->get_state ();
+                gc->alist()->shift (pos, frames);
+                XMLNode &after = gc->alist()->get_state ();
+                _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+        }
 
        /* pan automation */
-       for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
-               Curve & c = (*i)->automation ();
-               XMLNode &before = c.get_state ();
-               c.shift (pos, frames);
-               XMLNode &after = c.get_state ();
-               _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
-       }
+        {
+                boost::shared_ptr<AutomationControl> pc;
+                uint32_t npans = _main_outs->panner()->npanners();
+
+                for (uint32_t p = 0; p < npans; ++p) {
+                        pc = _main_outs->panner()->pan_control (0, p);
+                        if (pc) {
+                                boost::shared_ptr<AutomationList> al = pc->alist();
+                                XMLNode& before = al->get_state ();
+                                al->shift (pos, frames);
+                                XMLNode& after = al->get_state ();
+                                _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                        }
+                }
+        }
 
        /* redirect automation */
        {
-               Glib::RWLock::ReaderLock lm (redirect_lock);
-               for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-                       
-                       set<uint32_t> a;
-                       (*i)->what_has_automation (a);
-                       
-                       for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
-                               AutomationList & al = (*i)->automation_list (*j);
-                               XMLNode &before = al.get_state ();
-                               al.shift (pos, frames);
-                               XMLNode &after = al.get_state ();
-                               _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
-                       }
-               }
-       }
-#endif
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+                       set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
 
+                       for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+                                boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+                                if (ac) {
+                                        boost::shared_ptr<AutomationList> al = ac->alist();
+                                        XMLNode &before = al->get_state ();
+                                        al->shift (pos, frames);
+                                        XMLNode &after = al->get_state ();
+                                        _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                                }
+                        }
+                }
+       }
 }
 
 
@@ -2350,9 +3288,9 @@ Route::save_as_template (const string& path, const string& name)
 {
        XMLNode& node (state (false));
        XMLTree tree;
-       
+
        IO::set_name_in_state (*node.children().front(), name);
-       
+
        tree.set_root (&node);
        return tree.write (path.c_str());
 }
@@ -2363,28 +3301,25 @@ Route::set_name (const string& str)
 {
        bool ret;
        string ioproc_name;
-       
-       SessionObject::set_name (str);
-       
-       ret = (_input->set_name(str) && _output->set_name(str));
+       string name;
+
+       name = Route::ensure_track_or_route_name (str, _session);
+       SessionObject::set_name (name);
+
+       ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
-               
+
                Glib::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
-                       /* rename all processors with outputs to reflect our new name */
+
+                       /* rename all I/O processors that have inputs or outputs */
 
                        boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
 
-                       if (iop) {
-                               string iop_name = str;
-                               iop_name += '[';
-                               iop_name += "XXX FIX ME XXX";
-                               iop_name += ']';
-                               
-                               if (!iop->set_name (iop_name)) {
+                       if (iop && (iop->output() || iop->input())) {
+                               if (!iop->set_name (name)) {
                                        ret = false;
                                }
                        }
@@ -2396,44 +3331,64 @@ Route::set_name (const string& str)
 }
 
 boost::shared_ptr<Send>
-Route::send_for (boost::shared_ptr<const IO> target) const
+Route::internal_send_for (boost::shared_ptr<const Route> target) const
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               boost::shared_ptr<Send> send;
-               
-               if ((send = boost::dynamic_pointer_cast<Send>(*i)) != 0) {
-                       if (send->output()->connected_to (target)) {
+               boost::shared_ptr<InternalSend> send;
+
+               if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+                       if (send->target_route() == target) {
                                return send;
                        }
                }
        }
-       
+
        return boost::shared_ptr<Send>();
 }
 
+/** @param c Audio channel index.
+ *  @param yn true to invert phase, otherwise false.
+ */
+void
+Route::set_phase_invert (uint32_t c, bool yn)
+{
+       if (_phase_invert[c] != yn) {
+               _phase_invert[c] = yn;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
+       }
+}
+
 void
-Route::set_phase_invert (bool yn, void *src)
+Route::set_phase_invert (boost::dynamic_bitset<> p)
 {
-       if (_phase_invert != yn) {
-               _phase_invert = yn;
-               //  phase_invert_changed (src); /* EMIT SIGNAL */
+       if (_phase_invert != p) {
+               _phase_invert = p;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
        }
 }
 
 bool
+Route::phase_invert (uint32_t c) const
+{
+       return _phase_invert[c];
+}
+
+boost::dynamic_bitset<>
 Route::phase_invert () const
 {
-       return _phase_invert != 0;
+       return _phase_invert;
 }
 
 void
-Route::set_denormal_protection (bool yn, void *src)
+Route::set_denormal_protection (bool yn)
 {
        if (_denormal_protection != yn) {
                _denormal_protection = yn;
-               //  denormal_protection_changed (src); /* EMIT SIGNAL */
+               denormal_protection_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -2458,20 +3413,33 @@ void
 Route::meter ()
 {
        Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+
+       assert (_meter);
+
        _meter->meter ();
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               boost::shared_ptr<Send> s;
+               boost::shared_ptr<Return> r;
+
+               if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
+                       s->meter()->meter();
+               } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
+                       r->meter()->meter ();
+               }
+       }
 }
 
 boost::shared_ptr<Panner>
 Route::panner() const
 {
-
        return _main_outs->panner();
 }
 
 boost::shared_ptr<AutomationControl>
 Route::gain_control() const
 {
-
        return _amp->gain_control();
 }
 
@@ -2480,20 +3448,20 @@ Route::get_control (const Evoral::Parameter& param)
 {
        /* either we own the control or .... */
 
-       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(data().control (param));
+       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(control (param));
 
        if (!c) {
 
                /* maybe one of our processors does or ... */
-               
+
                Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->data().control (param))) != 0) {
+                       if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->control (param))) != 0) {
                                break;
                        }
                }
        }
-               
+
        if (!c) {
 
                /* nobody does so we'll make a new one */
@@ -2504,3 +3472,106 @@ Route::get_control (const Evoral::Parameter& param)
 
        return c;
 }
+
+boost::shared_ptr<Processor>
+Route::nth_plugin (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+boost::shared_ptr<Processor>
+Route::nth_send (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<Send> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               } 
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+bool
+Route::has_io_processor_named (const string& name)
+{
+        Glib::RWLock::ReaderLock lm (_processor_lock);
+        ProcessorList::iterator i;
+        
+        for (i = _processors.begin(); i != _processors.end(); ++i) {
+                if (boost::dynamic_pointer_cast<Send> (*i) ||
+                    boost::dynamic_pointer_cast<PortInsert> (*i)) {
+                        if ((*i)->name() == name) {
+                                return true;
+                        }
+                }
+        }
+        
+        return false;
+}
+
+MuteMaster::MutePoint
+Route::mute_points () const
+{
+       return _mute_master->mute_points ();
+}
+
+void
+Route::set_processor_positions ()
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       bool had_amp = false;
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->set_pre_fader (!had_amp);
+               if (boost::dynamic_pointer_cast<Amp> (*i)) {
+                       had_amp = true;
+               }
+       }
+}
+
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+       list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+       if (c.empty()) {
+               /* The processors cannot be configured with the new input arrangement, so
+                  block the change.
+               */
+               return true;
+       }
+
+       /* The change is ok */
+       return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+       list<string> p;
+       
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+                       p.push_back ((*i)->name ());
+               }
+       }
+
+       return p;
+}