fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / route.cc
index 18dec7c8b93fb7b7cfddb169c805145d4d72e3a9..11db73241cbd304c629a556828f02611134678c9 100644 (file)
@@ -22,7 +22,6 @@
 #endif
 
 #include <cmath>
-#include <fstream>
 #include <cassert>
 #include <algorithm>
 
 #include "pbd/memento_command.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
-#include "pbd/boost_debug.h"
+#include "pbd/unwind.h"
 
 #include "ardour/amp.h"
 #include "ardour/audio_buffer.h"
 #include "ardour/audio_track.h"
 #include "ardour/audio_port.h"
 #include "ardour/audioengine.h"
+#include "ardour/boost_debug.h"
 #include "ardour/buffer.h"
 #include "ardour/buffer_set.h"
 #include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
+#include "ardour/gain_control.h"
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
+#include "ardour/parameter_descriptor.h"
+#include "ardour/phase_control.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
 #include "ardour/processor.h"
+#include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
 #include "ardour/session.h"
+#include "ardour/solo_control.h"
+#include "ardour/solo_isolate_control.h"
 #include "ardour/unknown_processor.h"
 #include "ardour/utils.h"
+#include "ardour/vca.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-PBD::Signal0<void> Route::SyncOrderKeys;
-PBD::Signal0<void> Route::RemoteControlIDChange;
+PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Route::PluginSetupOptions > Route::PluginSetup;
 
-Route::Route (Session& sess, string name, Flag flg, DataType default_type)
-       : SessionObject (sess, name)
-       , Automatable (sess)
+/** Base class for all routable/mixable objects (tracks and busses) */
+Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type)
+       : Stripable (sess, name, PresentationInfo (flag))
        , GraphNode (sess._process_graph)
+       , Muteable (sess, name)
+       , Automatable (sess)
        , _active (true)
        , _signal_latency (0)
        , _signal_latency_at_amp_position (0)
@@ -87,54 +95,68 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _initial_delay (0)
        , _roll_delay (0)
        , _pending_process_reorder (0)
-       , _flags (flg)
+       , _pending_signals (0)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
        , _pending_meter_point (MeterPostFader)
        , _meter_type (MeterPeak)
-       , _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)
-       , _mute_master (new MuteMaster (sess, name))
        , _have_internal_generator (false)
-       , _solo_safe (false)
        , _default_type (default_type)
-       , _order_key (0)
-       , _has_order_key (false)
-       , _remote_control_id (0)
        , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
+       , _in_sidechain_setup (false)
+       , _strict_io (false)
        , _custom_meter_position_noted (false)
+       , _pinmgr_proxy (0)
 {
-       if (is_master()) {
-               _meter_type = MeterK20;
-       }
        processor_max_streams.reset();
 }
 
 int
 Route::init ()
 {
+       /* set default meter type */
+       if (is_master()) {
+               _meter_type = Config->get_meter_type_master ();
+       }
+       else if (dynamic_cast<Track*>(this)) {
+               _meter_type = Config->get_meter_type_track ();
+       } else {
+               _meter_type = Config->get_meter_type_bus ();
+       }
+
        /* add standard controls */
 
-       _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
-       _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
+       _gain_control.reset (new GainControl (_session, GainAutomation));
+       add_control (_gain_control);
 
-       _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
+       _trim_control.reset (new GainControl (_session, TrimAutomation));
+       add_control (_trim_control);
 
+       _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
        add_control (_solo_control);
+       _solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2));
+
+       _mute_control.reset (new MuteControl (_session, X_("mute"), *this));
        add_control (_mute_control);
 
+       _phase_control.reset (new PhaseControl (_session, X_("phase")));
+       add_control (_phase_control);
+
+       _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this));
+       add_control (_solo_isolate_control);
+
+       _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe")));
+       add_control (_solo_safe_control);
+
        /* panning */
 
-       if (!(_flags & Route::MonitorOut)) {
+       if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
                _pannable.reset (new Pannable (_session));
        }
 
@@ -149,6 +171,17 @@ Route::init ()
        _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
        _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
 
+       /* add the amp/fader processor.
+        * it should be the first processor to be added on every route.
+        */
+
+       _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
+       add_processor (_amp, PostFader);
+
+       if (is_monitor ()) {
+               _amp->set_display_name (_("Monitor"));
+       }
+
 #if 0 // not used - just yet
        if (!is_master() && !is_monitor() && !is_auditioner()) {
                _delayline.reset (new DelayLine (_session, _name));
@@ -156,13 +189,9 @@ Route::init ()
        }
 #endif
 
-       /* add amp processor  */
-
-       _amp.reset (new Amp (_session));
-       add_processor (_amp, PostFader);
-
        /* and input trim */
-       _trim.reset (new Amp (_session, "trim"));
+
+       _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
        _trim->set_display_to_user (false);
 
        if (dynamic_cast<AudioTrack*>(this)) {
@@ -200,10 +229,6 @@ Route::init ()
                _monitor_control->activate ();
        }
 
-       if (is_master() || is_monitor() || is_auditioner()) {
-               _mute_master->set_solo_ignore (true);
-       }
-
        /* now that we have _meter, its safe to connect to this */
 
        {
@@ -235,204 +260,23 @@ Route::~Route ()
        _processors.clear ();
 }
 
-void
-Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
-{
-       if (Config->get_remote_model() != UserOrdered) {
-               return;
-       }
-
-       set_remote_control_id_internal (id, notify_class_listeners);
-}
-
-void
-Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
-{
-       /* force IDs for master/monitor busses and prevent 
-          any other route from accidentally getting these IDs
-          (i.e. legacy sessions)
-       */
-
-       if (is_master() && id != MasterBusRemoteControlID) {
-               id = MasterBusRemoteControlID;
-       }
-
-       if (is_monitor() && id != MonitorBusRemoteControlID) {
-               id = MonitorBusRemoteControlID;
-       }
-
-       if (id < 1) {
-               return;
-       }
-
-       /* don't allow it to collide */
-
-       if (!is_master () && !is_monitor() && 
-           (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
-               id += MonitorBusRemoteControlID;
-       }
-
-       if (id != remote_control_id()) {
-               _remote_control_id = id;
-               RemoteControlIDChanged ();
-
-               if (notify_class_listeners) {
-                       RemoteControlIDChange ();
-               }
-       }
-}
-
-uint32_t
-Route::remote_control_id() const
-{
-       if (is_master()) {
-               return MasterBusRemoteControlID;
-       } 
-
-       if (is_monitor()) {
-               return MonitorBusRemoteControlID;
-       }
-
-       return _remote_control_id;
-}
-
-bool
-Route::has_order_key () const
-{
-       return _has_order_key;
-}
-
-uint32_t
-Route::order_key () const
-{
-       return _order_key;
-}
-
-void
-Route::set_remote_control_id_explicit (uint32_t rid)
-{
-       if (is_master() || is_monitor() || is_auditioner()) {
-               /* hard-coded remote IDs, or no remote ID */
-               return;
-       }
-
-       if (_remote_control_id != rid) {
-               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: set edit-based RID to %2\n", name(), rid));
-               _remote_control_id = rid;
-               RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */
-       }
-
-       /* don't emit the class-level RID signal RemoteControlIDChange here,
-          leave that to the entity that changed the order key, so that we
-          don't get lots of emissions for no good reasons (e.g. when changing
-          all route order keys).
-
-          See Session::sync_remote_id_from_order_keys() for the (primary|only)
-          spot where that is emitted.
-       */
-}
-
-void
-Route::set_order_key (uint32_t n)
-{
-       _has_order_key = true;
-
-       if (_order_key == n) {
-               return;
-       }
-
-       _order_key = n;
-
-       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key set to %2\n",
-                                                      name(), order_key ()));
-
-       _session.set_dirty ();
-}
-
 string
 Route::ensure_track_or_route_name(string name, Session &session)
 {
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
-               newname = bump_name_once (newname, '.');
+               newname = bump_name_once (newname, ' ');
        }
 
        return newname;
 }
 
 void
-Route::inc_gain (gain_t fraction, void *src)
-{
-       _amp->inc_gain (fraction, src);
-}
-
-void
-Route::set_gain (gain_t val, void *src)
-{
-       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;
-                       }
-
-                       delta -= usable_gain;
-
-                       if (delta == 0.0f)
-                               return;
-
-                       gain_t factor = delta / usable_gain;
-
-                       if (factor > 0.0f) {
-                               factor = _route_group->get_max_factor(factor);
-                               if (factor == 0.0f) {
-                                       _amp->gain_control()->Changed(); /* EMIT SIGNAL */
-                                       return;
-                               }
-                       } else {
-                               factor = _route_group->get_min_factor(factor);
-                               if (factor == 0.0f) {
-                                       _amp->gain_control()->Changed(); /* EMIT SIGNAL */
-                                       return;
-                               }
-                       }
-
-                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
-
-               } else {
-
-                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
-               }
-
-               return;
-       }
-
-       if (val == _amp->gain()) {
-               return;
-       }
-
-       _amp->set_gain (val, src);
-}
-
-void
-Route::inc_trim (gain_t fraction, void *src)
-{
-       _trim->inc_gain (fraction, src);
-}
-
-void
-Route::set_trim (gain_t val, void * /* src */)
+Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
 {
        // TODO route group, see set_gain()
-       _trim->set_gain (val, 0);
+       // _trim_control->route_set_value (val);
 }
 
 void
@@ -469,6 +313,8 @@ Route::process_output_buffers (BufferSet& bufs,
                return;
        }
 
+       _mute_control->automation_run (start_frame, nframes);
+
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
                _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
@@ -489,13 +335,12 @@ Route::process_output_buffers (BufferSet& bufs,
 
        /* Tell main outs what to do about monitoring.  We do this so that
           on a transition between monitoring states we get a de-clicking gain
-          change in the _main_outs delivery.
-       */
-       bool silence = monitoring_state () == MonitoringSilence;
+          change in the _main_outs delivery, if config.get_use_monitor_fades()
+          is true.
 
-       //but we override this in the case where we have an internal generator
-       if ( _have_internal_generator )
-               silence = false;
+          We override this in the case where we have an internal generator.
+       */
+       bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
 
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
@@ -510,7 +355,7 @@ Route::process_output_buffers (BufferSet& bufs,
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (_phase_invert.any ()) {
+       if (!_phase_control->none()) {
 
                int chn = 0;
 
@@ -519,7 +364,7 @@ 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]) {
+                               if (_phase_control->inverted (chn)) {
                                        for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
@@ -536,7 +381,7 @@ 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]) {
+                               if (_phase_control->inverted (chn)) {
                                        for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
@@ -566,6 +411,7 @@ Route::process_output_buffers (BufferSet& bufs,
        bool const meter_already_run = metering_state() == MeteringInput;
 
        framecnt_t latency = 0;
+       const double speed = _session.transport_speed ();
 
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
@@ -595,8 +441,13 @@ Route::process_output_buffers (BufferSet& bufs,
                if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
                        boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
                }
+               if (boost::dynamic_pointer_cast<PluginInsert>(*i) != 0) {
+                       const framecnt_t longest_session_latency = _initial_delay + _signal_latency;
+                       boost::dynamic_pointer_cast<PluginInsert>(*i)->set_sidechain_latency (
+                                       _initial_delay + latency, longest_session_latency - latency);
+               }
 
-               (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
+               (*i)->run (bufs, start_frame - latency, end_frame - latency, speed, nframes, *i != _processors.back());
                bufs.set_count ((*i)->output_streams());
 
                if ((*i)->active ()) {
@@ -624,13 +475,14 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
        _trim->setup_gain_automation (start, start + nframes, nframes);
 
        latency = 0;
+       const double speed = _session.transport_speed ();
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                if (!include_endpoint && (*i) == endpoint) {
                        break;
                }
 
-               /* if we're not exporting, stop processing if we come across a routing processor. */
+               /* if we're *not* exporting, stop processing if we come across a routing processor. */
                if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
                        break;
                }
@@ -638,11 +490,23 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        break;
                }
 
-               /* don't run any processors that does routing.
-                * oh, and don't bother with the peak meter either.
+               /* special case the panner (export outputs)
+                * Ideally we'd only run the panner, not the delivery itself...
+                * but panners need separate input/output buffers and some context
+                * (panshell, panner type, etc). AFAICT there is no ill side effect
+                * of re-using the main delivery when freewheeling/exporting a region.
+                */
+               if ((*i) == _main_outs) {
+                       assert ((*i)->does_routing());
+                       (*i)->run (buffers, start - latency, start - latency + nframes, speed, nframes, true);
+                       buffers.set_count ((*i)->output_streams());
+               }
+
+               /* don't run any processors that do routing.
+                * Also don't bother with metering.
                 */
                if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
-                       (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+                       (*i)->run (buffers, start - latency, start - latency + nframes, 1.0, nframes, true);
                        buffers.set_count ((*i)->output_streams());
                        latency += (*i)->signal_latency ();
                }
@@ -755,361 +619,131 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
 }
 
 void
-Route::set_listen (bool yn, void* src)
+Route::set_listen (bool yn)
 {
-       if (_solo_safe) {
-               return;
-       }
-
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group));
-               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 */
+               if (yn) {
+                       _monitor_send->activate ();
+               } else {
+                       _monitor_send->deactivate ();
                }
        }
 }
 
-bool
-Route::listening_via_monitor () const
-{
-       if (_monitor_send) {
-               return _monitor_send->active ();
-       } else {
-               return false;
-       }
-}
-
 void
-Route::set_solo_safe (bool yn, void *src)
+Route::solo_control_changed (bool, Controllable::GroupControlDisposition)
 {
-       if (_solo_safe != yn) {
-               _solo_safe = yn;
-               solo_safe_changed (src);
-       }
-}
+       /* nothing to do if we're not using AFL/PFL. But if we are, we need
+          to alter the active state of the monitor send.
+       */
 
-bool
-Route::solo_safe() const
-{
-       return _solo_safe;
+       if (Config->get_solo_control_is_listen_control ()) {
+               set_listen (_solo_control->self_soloed() || _solo_control->get_masters_value());
+       }
 }
 
 void
-Route::set_solo (bool yn, void *src)
+Route::push_solo_isolate_upstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
-               return;
-       }
+       /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
-       if (is_master() || is_monitor() || is_auditioner()) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name()));
-               return;
-       }
+       boost::shared_ptr<RouteList> routes = _session.get_routes ();
+       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
-               return;
-       }
+               if ((*i).get() == this || !(*i)->can_solo()) {
+                       continue;
+               }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n", 
-                                                 name(), yn, src, (src == _route_group), self_soloed()));
+               bool sends_only;
+               bool does_feed = feeds (*i, &sends_only);
 
-       if (self_soloed() != yn) {
-               set_self_solo (yn);
-               set_mute_master_solo ();
-               solo_changed (true, src); /* EMIT SIGNAL */
-               _solo_control->Changed (); /* EMIT SIGNAL */
+               if (does_feed && !sends_only) {
+                       (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta);
+               }
        }
 }
 
 void
-Route::set_self_solo (bool yn)
+Route::push_solo_upstream (int delta)
 {
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
-       _self_solo = yn;
+       DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
+       for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+               if (i->sends_only) {
+                       continue;
+               }
+               boost::shared_ptr<Route> sr (i->r.lock());
+               if (sr) {
+                       sr->solo_control()->mod_solo_by_others_downstream (-delta);
+               }
+       }
 }
 
-void
-Route::mod_solo_by_others_upstream (int32_t delta)
+#if 0
+static void
+dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
-               return;
+       cerr << name << " {" << endl;
+       for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
+                       p != procs.end(); ++p) {
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << " @ " << (*p) << endl;
        }
+       cerr << "}" << endl;
+}
+#endif
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", 
-                                                 name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
+/** Supposing that we want to insert a Processor at a given Placement, return
+ *  the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_placement (Placement p)
+{
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
-       uint32_t old_sbu = _soloed_by_others_upstream;
+       ProcessorList::iterator loc;
 
-       if (delta < 0) {
-               if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
-                       _soloed_by_others_upstream += delta;
-               } else {
-                       _soloed_by_others_upstream = 0;
-               }
+       if (p == PreFader) {
+               /* generic pre-fader: insert immediately before the amp */
+               loc = find (_processors.begin(), _processors.end(), _amp);
        } 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);
-                               }
-                       }
-               }
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
-       set_mute_master_solo ();
-       solo_changed (false, this);
+       return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
 }
 
-void
-Route::mod_solo_by_others_downstream (int32_t delta)
+/** Supposing that we want to insert a Processor at a given index, return
+ *  the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_index (int index)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
-               return;
+       if (index == -1) {
+               return boost::shared_ptr<Processor> ();
        }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", 
-                                                 name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
-       if (delta < 0) {
-               if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
-                       _soloed_by_others_downstream += delta;
-               } else {
-                       _soloed_by_others_downstream = 0;
+       ProcessorList::iterator i = _processors.begin ();
+       int j = 0;
+       while (i != _processors.end() && j < index) {
+               if ((*i)->display_to_user()) {
+                       ++j;
                }
-       } else {
-               _soloed_by_others_downstream += delta;
-       }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
+               ++i;
+       }
 
-       set_mute_master_solo ();
-       solo_changed (false, this);
+       return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
 }
 
-void
-Route::set_mute_master_solo ()
+/** Add a processor either pre- or post-fader
+ *  @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
 {
-       _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
-}
-
-void
-Route::set_solo_isolated (bool yn, void *src)
-{
-       if (is_master() || is_monitor() || is_auditioner()) {
-               return;
-       }
-
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, 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_auditioner()) {
-                       continue;
-               }
-
-               bool sends_only;
-               bool does_feed = direct_feeds_according_to_graph (*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
-{
-       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 (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
-               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
-               return;
-       }
-
-       if (muted() != yn) {
-               _mute_master->set_muted_by_self (yn);
-               /* allow any derived classes to respond to the mute change
-                  before anybody else knows about it.
-               */
-               act_on_mute ();
-               /* tell everyone else */
-               mute_changed (src); /* EMIT SIGNAL */
-               _mute_control->Changed (); /* EMIT SIGNAL */
-       }
-}
-
-bool
-Route::muted () const
-{
-       return _mute_master->muted_by_self();
-}
-
-bool
-Route::muted_by_others () const
-{
-       //master is never muted by others
-       if (is_master())
-               return false;
-               
-       //now check to see if something is soloed (and I am not)
-       return (_session.soloing() && !self_soloed() && !solo_isolated());
-}
-
-#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() << " ID = " << (*p)->id() << " @ " << (*p) << endl;
-       }
-       cerr << "}" << endl;
-}
-#endif
-
-/** Supposing that we want to insert a Processor at a given Placement, return
- *  the processor to add the new one before (or 0 to add at the end).
- */
-boost::shared_ptr<Processor>
-Route::before_processor_for_placement (Placement p)
-{
-       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-
-       ProcessorList::iterator loc;
-       
-       if (p == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find (_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert right before the main outs */
-               loc = find (_processors.begin(), _processors.end(), _main_outs);
-       }
-
-       return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
-}
-
-/** Supposing that we want to insert a Processor at a given index, return
- *  the processor to add the new one before (or 0 to add at the end).
- */
-boost::shared_ptr<Processor>
-Route::before_processor_for_index (int index)
-{
-       if (index == -1) {
-               return boost::shared_ptr<Processor> ();
-       }
-
-       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       
-       ProcessorList::iterator i = _processors.begin ();
-       int j = 0;
-       while (i != _processors.end() && j < index) {
-               if ((*i)->display_to_user()) {
-                       ++j;
-               }
-               
-               ++i;
-       }
-
-       return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
-}
-
-/** Add a processor either pre- or post-fader
- *  @return 0 on success, non-0 on failure.
- */
-int
-Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
-{
-       return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed);
+       return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed);
 }
 
 
@@ -1136,89 +770,37 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
        DEBUG_TRACE (DEBUG::Processors, string_compose (
                             "%1 adding processor %2\n", name(), processor->name()));
 
-       if (!AudioEngine::instance()->connected() || !processor) {
-               return 1;
-       }
-
-       {
-               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               ProcessorState pstate (this);
-
-               boost::shared_ptr<PluginInsert> pi;
-               boost::shared_ptr<PortInsert> porti;
-
-               if (processor == _amp) {
-                       /* Ensure that only one amp is in the list at any time */
-                       ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
-                       if (check != _processors.end()) {
-                               if (before == _amp) {
-                                       /* Already in position; all is well */
-                                       return 0;
-                               } else {
-                                       _processors.erase (check);
-                               }
-                       }
-               }
-
-               assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
-
-               ProcessorList::iterator loc;
-               if (before) {
-                       /* inserting before a processor; find it */
-                       loc = find (_processors.begin(), _processors.end(), before);
-                       if (loc == _processors.end ()) {
-                               /* Not found */
-                               return 1;
-                       }
-               } else {
-                       /* inserting at end */
-                       loc = _processors.end ();
-               }
-
-               _processors.insert (loc, processor);
-               processor->set_owner (this);
-
-               // Set up processor list channels.  This will set processor->[input|output]_streams(),
-               // configure redirect ports properly, etc.
-
-               {
-                       if (configure_processors_unlocked (err)) {
-                               pstate.restore ();
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
-                       }
-               }
-
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-
-                       if (pi->has_no_inputs ()) {
-                               /* generator plugin */
-                               _have_internal_generator = true;
-                       }
-
-               }
-
-               if (activation_allowed && !_session.get_disable_all_loaded_plugins()) {
-                       processor->activate ();
-               }
+       ProcessorList pl;
 
-               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+       pl.push_back (processor);
+       int rv = add_processors (pl, before, err);
 
-               _output->set_user_latency (0);
+       if (rv) {
+               return rv;
        }
 
-       reset_instrument_info ();
-       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
-       set_processor_positions ();
+       if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
+               processor->activate ();
+       }
 
        return 0;
 }
 
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
+{
+       /* We cannot destruct the processor here (usually RT-thread
+        * with various locks held - in case of sends also io_locks).
+        * Queue for deletion in low-priority thread.
+        */
+       Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+       selfdestruct_sequence.push_back (wp);
+}
+
 bool
 Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        try {
                boost::shared_ptr<Processor> processor;
@@ -1252,7 +834,12 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                                                prop->value() == "lxvst" ||
                                                prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert (_session));
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, node));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                               processor->set_owner (this);
+                                       }
 
                                } else {
 
@@ -1275,11 +862,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                if (processor->set_state (node, version)) {
                        return false;
                }
-               
+
                //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
                if (i != children.end()) {
                        if ((prop = (*i)->property (X_("active"))) != 0) {
-                               if ( string_is_affirmative (prop->value()) && !_session.get_disable_all_loaded_plugins() )
+                               if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
                                        processor->activate();
                                else
                                        processor->deactivate();
@@ -1295,24 +882,31 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
        }
 }
 
+
+inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+       return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+       return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) & static_cast<int> (b));
+}
+
 int
 Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
-       /* NOTE: this is intended to be used ONLY when copying
-          processors from another Route. Hence the subtle
-          differences between this and ::add_processor()
-       */
-
        ProcessorList::iterator loc;
 
        if (before) {
                loc = find(_processors.begin(), _processors.end(), before);
+               if (loc == _processors.end ()) {
+                       return 1;
+               }
        } else {
                /* nothing specified - at end */
                loc = _processors.end ();
        }
 
-       if (!_session.engine().connected()) {
+       if (!AudioEngine::instance()->connected()) {
                return 1;
        }
 
@@ -1320,6 +914,59 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                return 0;
        }
 
+       ProcessorList to_skip;
+
+       // check if there's an instrument to replace or configure
+       for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) == 0) {
+                       continue;
+               }
+               if (!pi->plugin ()->get_info ()->is_instrument ()) {
+                       continue;
+               }
+               boost::shared_ptr<Processor> instrument = the_instrument ();
+               ChanCount in (DataType::MIDI, 1);
+               ChanCount out (DataType::AUDIO, 2); // XXX route's out?!
+
+               PluginSetupOptions flags = None;
+               if (instrument) {
+                       flags |= CanReplace;
+                       in = instrument->input_streams ();
+                       out = instrument->output_streams ();
+               }
+               if (pi->has_output_presets (in, out)) {
+                       flags |= MultiOut;
+               }
+
+               pi->set_strict_io (_strict_io);
+
+               PluginSetupOptions mask = None;
+               if (Config->get_ask_replace_instrument ()) {
+                       mask |= CanReplace;
+               }
+               if (Config->get_ask_setup_instrument ()) {
+                       mask |= MultiOut;
+               }
+
+               flags &= mask;
+
+               if (flags != None) {
+                       boost::optional<int> rv = PluginSetup (shared_from_this (), pi, flags);  /* EMIT SIGNAL */
+                       switch (rv.get_value_or (0)) {
+                               case 1:
+                                       to_skip.push_back (*i); // don't add this one;
+                                       break;
+                               case 2:
+                                       replace_processor (instrument, *i, err);
+                                       to_skip.push_back (*i);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
@@ -1330,30 +977,59 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        if (*i == _meter) {
                                continue;
                        }
+                       ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i);
+                       if (check != to_skip.end()) {
+                               continue;
+                       }
 
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1);
+                               pi->set_strict_io (_strict_io);
+                       }
+
+                       if (*i == _amp) {
+                               /* Ensure that only one amp is in the list at any time */
+                               ProcessorList::iterator check = find (_processors.begin(), _processors.end(), *i);
+                               if (check != _processors.end()) {
+                                       if (before == _amp) {
+                                               /* Already in position; all is well */
+                                               continue;
+                                       } else {
+                                               _processors.erase (check);
+                                       }
+                               }
                        }
 
+                       assert (find (_processors.begin(), _processors.end(), *i) == _processors.end ());
+
                        _processors.insert (loc, *i);
                        (*i)->set_owner (this);
 
-                       if ((*i)->active()) {
-                               (*i)->activate ();
-                       }
-
-                       /* Think: does this really need to be called for every processor in the loop? */
                        {
-                               if (configure_processors_unlocked (err)) {
+                               if (configure_processors_unlocked (err, &lm)) {
                                        pstate.restore ();
-                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                                        return -1;
                                }
                        }
 
+                       if (pi && pi->has_sidechain ()) {
+                               pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+                       }
+
+                       if ((*i)->active()) {
+                               // emit ActiveChanged() and latency_changed() if needed
+                               (*i)->activate ();
+                       }
+
                        (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+
+                       boost::shared_ptr<Send> send;
+                       if ((send = boost::dynamic_pointer_cast<Send> (*i))) {
+                               send->SelfDestruct.connect_same_thread (*this,
+                                               boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (*i)));
+                       }
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -1402,7 +1078,7 @@ Route::disable_processors (Placement p)
        placement_range(p, start, end);
 
        for (ProcessorList::iterator i = start; i != end; ++i) {
-               (*i)->deactivate ();
+               (*i)->enable (false);
        }
 
        _session.set_dirty ();
@@ -1416,7 +1092,7 @@ Route::disable_processors ()
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
+               (*i)->enable (false);
        }
 
        _session.set_dirty ();
@@ -1435,7 +1111,7 @@ Route::disable_plugins (Placement p)
 
        for (ProcessorList::iterator i = start; i != end; ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
-                       (*i)->deactivate ();
+                       (*i)->enable (false);
                }
        }
 
@@ -1451,7 +1127,7 @@ Route::disable_plugins ()
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
-                       (*i)->deactivate ();
+                       (*i)->enable (false);
                }
        }
 
@@ -1475,8 +1151,8 @@ Route::ab_plugins (bool forward)
                                continue;
                        }
 
-                       if ((*i)->active()) {
-                               (*i)->deactivate ();
+                       if ((*i)->enabled ()) {
+                               (*i)->enable (false);
                                (*i)->set_next_ab_is_active (true);
                        } else {
                                (*i)->set_next_ab_is_active (false);
@@ -1493,11 +1169,7 @@ Route::ab_plugins (bool forward)
                                continue;
                        }
 
-                       if ((*i)->get_next_ab_is_active()) {
-                               (*i)->activate ();
-                       } else {
-                               (*i)->deactivate ();
-                       }
+                       (*i)->enable ((*i)->get_next_ab_is_active ());
                }
        }
 
@@ -1566,7 +1238,7 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+               configure_processors_unlocked (&err, &lm); // this can't fail
        }
 
        processor_max_streams.reset();
@@ -1638,9 +1310,15 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                   run.
                                */
 
-                               boost::shared_ptr<IOProcessor> iop;
+                               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
+                               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+
+                               if (pi != 0) {
+                                       assert (iop == 0);
+                                       iop = pi->sidechain();
+                               }
 
-                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                               if (iop != 0) {
                                        iop->disconnect ();
                                }
 
@@ -1658,12 +1336,12 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
-               } 
+               }
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
 
@@ -1693,40 +1371,138 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 }
 
 int
-Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
+Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Processor> sub, ProcessorStreams* err)
 {
-       ProcessorList deleted;
+       /* these can never be removed */
+       if (old == _amp || old == _meter || old == _main_outs || old == _delayline || old == _trim) {
+               return 1;
+       }
+       /* and can't be used as substitute, either */
+       if (sub == _amp || sub == _meter || sub == _main_outs || sub == _delayline || sub == _trim) {
+               return 1;
+       }
 
-       if (!_session.engine().connected()) {
+       /* I/Os are out, too */
+       if (boost::dynamic_pointer_cast<IOProcessor> (old) || boost::dynamic_pointer_cast<IOProcessor> (sub)) {
                return 1;
        }
 
-       processor_max_streams.reset();
+       /* this function cannot be used to swap/reorder processors */
+       if (find (_processors.begin(), _processors.end(), sub) != _processors.end ()) {
+               return 1;
+       }
+
+       if (!AudioEngine::instance()->connected() || !old || !sub) {
+               return 1;
+       }
+
+       /* ensure that sub is not owned by another route */
+       if (sub->owner ()) {
+               return 1;
+       }
 
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
                ProcessorState pstate (this);
 
+               assert (find (_processors.begin(), _processors.end(), sub) == _processors.end ());
+
                ProcessorList::iterator i;
-               boost::shared_ptr<Processor> processor;
+               bool replaced = false;
+               bool enable = old->enabled ();
 
                for (i = _processors.begin(); i != _processors.end(); ) {
-
-                       processor = *i;
-
-                       /* these can never be removed */
-
-                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
+                       if (*i == old) {
+                               i = _processors.erase (i);
+                               _processors.insert (i, sub);
+                               sub->set_owner (this);
+                               replaced = true;
+                               break;
+                       } else {
                                ++i;
-                               continue;
                        }
+               }
 
-                       /* see if its in the list of processors to delete */
+               if (!replaced) {
+                       return 1;
+               }
 
-                       if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
-                               ++i;
-                               continue;
+               if (_strict_io) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(sub)) != 0) {
+                               pi->set_strict_io (true);
+                       }
+               }
+
+               if (configure_processors_unlocked (err, &lm)) {
+                       pstate.restore ();
+                       configure_processors_unlocked (0, &lm);
+                       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->has_no_inputs ()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (enable) {
+                       sub->enable (true);
+               }
+
+               sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+               _output->set_user_latency (0);
+       }
+
+       reset_instrument_info ();
+       old->drop_references ();
+       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::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+
+                       processor = *i;
+
+                       /* these can never be removed */
+
+                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
+                               ++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
@@ -1734,9 +1510,14 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                           run.
                        */
 
-                       boost::shared_ptr<IOProcessor> iop;
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(processor);
+                       boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+                       if (pi != 0) {
+                               assert (iop == 0);
+                               iop = pi->sidechain();
+                       }
 
-                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                       if (iop != 0) {
                                iop->disconnect ();
                        }
 
@@ -1751,10 +1532,10 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _output->set_user_latency (0);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
                //lx.unlock();
@@ -1805,7 +1586,7 @@ Route::configure_processors (ProcessorStreams* err)
 
        if (!_in_configure_processors) {
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               return configure_processors_unlocked (err);
+               return configure_processors_unlocked (err, &lm);
        }
 
        return 0;
@@ -1838,15 +1619,67 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
 
        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");
-                       DEBUG_TRACE (DEBUG::Processors, "}\n");
-                       return list<pair<ChanCount, ChanCount> > ();
-               }
-
                if ((*p)->can_support_io_configuration(in, out)) {
+
+                       if (boost::dynamic_pointer_cast<Delivery> (*p)
+                                       && boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
+                                       && !is_auditioner()
+                                       && (is_monitor() || _strict_io || Profile->get_mixbus ())) {
+                               /* with strict I/O the panner + output are forced to
+                                * follow the last processor's output.
+                                *
+                                * Delivery::can_support_io_configuration() will only add ports,
+                                * but not remove excess ports.
+                                *
+                                * This works because the delivery only requires
+                                * as many outputs as there are inputs.
+                                * Delivery::configure_io() will do the actual removal
+                                * by calling _output->ensure_io()
+                                */
+                               if (!is_master() && _session.master_out () && in.n_audio() > 0) {
+                                       /* ..but at least as many as there are master-inputs, if
+                                        * the delivery is dealing with audio */
+                                       // XXX this may need special-casing for mixbus (master-outputs)
+                                       // and should maybe be a preference anyway ?!
+                                       out = ChanCount::max (in, _session.master_out ()->n_inputs ());
+                               } else {
+                                       out = in;
+                               }
+                       }
+
                        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));
+
+                       if (is_monitor()) {
+                               // restriction for Monitor Section Processors
+                               if (in.n_audio() != out.n_audio() || out.n_midi() > 0) {
+                                       /* do not allow to add/remove channels (for now)
+                                        * The Monitor follows the master-bus and has no panner (unpan)
+                                        * but do allow processors with midi-in to be added (e.g VSTs with control that
+                                        * will remain unconnected)
+                                        */
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<InternalSend> (*p)) {
+                                       // internal sends make no sense, only feedback
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<PortInsert> (*p)) {
+                                       /* External Sends can be problematic. one can add/remove ports
+                                        * there signal leaves the DAW to external monitors anyway, so there's
+                                        * no real use for allowing them here anyway.
+                                        */
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No External Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                               if (boost::dynamic_pointer_cast<Send> (*p)) {
+                                       // ditto
+                                       DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n");
+                                       return list<pair<ChanCount, ChanCount> > ();
+                               }
+                       }
                        in = out;
                } else {
                        if (err) {
@@ -1870,7 +1703,7 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
  *  Return 0 on success, otherwise configuration is impossible.
  */
 int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLock::WriterLock* lm)
 {
 #ifndef PLATFORM_WINDOWS
        assert (!AudioEngine::instance()->process_lock().trylock());
@@ -1897,23 +1730,54 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        processor_out_streams = _input->n_ports();
        processor_max_streams.reset();
 
+       /* processor configure_io() may result in adding ports
+        * e.g. Delivery::configure_io -> ARDOUR::IO::ensure_io ()
+        *
+        * with jack2 adding ports results in a graph-order callback,
+        * which calls Session::resort_routes() and eventually
+        * Route::direct_feeds_according_to_reality()
+        * which takes a ReaderLock (_processor_lock).
+        *
+        * so we can't hold a WriterLock here until jack2 threading
+        * is fixed.
+        *
+        * NB. we still hold the process lock
+        *
+        * (ardour's own engines do call graph-order from the
+        * process-thread and hence do not have this issue; besides
+        * merely adding ports won't trigger a graph-order, only
+        * making connections does)
+        */
+       lm->release ();
+
+       // TODO check for a potential ReaderLock after ReaderLock ??
+       Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
+
        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;
+               if (!(*p)->configure_io(c->first, c->second)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
+                       _in_configure_processors = false;
+                       lr.release ();
+                       lm->acquire ();
+                       return -1;
                }
-
-               (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
 
+               boost::shared_ptr<IOProcessor> iop;
                boost::shared_ptr<PluginInsert> pi;
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
-                       /* plugins connected via Split Match may have more channels.
-                        * route/scratch buffers are needed for all of them*/
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams());
+                       /* plugins connected via Split or Hide Match may have more channels.
+                        * route/scratch buffers are needed for all of them
+                        * The configuration may only be a subset (both input and output)
+                        */
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->required_buffers());
+               }
+               else if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*p)) != 0) {
+                       processor_max_streams = ChanCount::max(processor_max_streams, iop->natural_input_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, iop->natural_output_streams());
                }
                out = c->second;
 
@@ -1930,13 +1794,16 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
+       lr.release ();
+       lm->acquire ();
+
 
        if (_meter) {
                _meter->set_max_channels (processor_max_streams);
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
-          configuration 
+          configuration
        */
        _session.ensure_buffers (n_process_buffers ());
 
@@ -1957,17 +1824,12 @@ Route::all_visible_processors_active (bool state)
        if (_processors.empty()) {
                return;
        }
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
-               
-               if (state) {
-                       (*i)->activate ();
-               } else {
-                       (*i)->deactivate ();
-               }
+               (*i)->enable (state);
        }
 
        _session.set_dirty ();
@@ -2123,7 +1985,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                apply_processor_order (new_order);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        return -1;
                }
@@ -2146,6 +2008,206 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
+bool
+Route::add_remove_sidechain (boost::shared_ptr<Processor> proc, bool add)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       if (pi->has_sidechain () == add) {
+               return true; // ?? call failed, but result is as expected.
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); // take before Writerlock to avoid deadlock
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               PBD::Unwinder<bool> uw (_in_sidechain_setup, true);
+
+               lx.release (); // IO::add_port() and ~IO takes process lock  - XXX check if this is safe
+               if (add) {
+                       if (!pi->add_sidechain ()) {
+                               return false;
+                       }
+               } else {
+                       if (!pi->del_sidechain ()) {
+                               return false;
+                       }
+               }
+
+               lx.acquire ();
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               lx.release ();
+
+               if (c.empty()) {
+                       if (add) {
+                               pi->del_sidechain ();
+                       } else {
+                               pi->add_sidechain ();
+                               // TODO restore side-chain's state.
+                       }
+                       return false;
+               }
+               lx.acquire ();
+               configure_processors_unlocked (0, &lm);
+       }
+
+       if (pi->has_sidechain ()) {
+               pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+               const ChanCount& old (pi->preset_out ());
+               if (!pi->set_preset_out (outs)) {
+                       return true; // no change, OK
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+                       pi->set_preset_out (old);
+                       return false;
+               }
+               configure_processors_unlocked (0, &lm);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
+{
+       ChanCount unused;
+       return customize_plugin_insert (proc, 0, unused, unused);
+}
+
+bool
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+               bool      old_cust  = pi->custom_cfg ();
+               uint32_t  old_cnt   = pi->get_count ();
+               ChanCount old_chan  = pi->output_streams ();
+               ChanCount old_sinks = pi->natural_input_streams ();
+
+               if (count == 0) {
+                       pi->set_custom_cfg (false);
+               } else {
+                       pi->set_custom_cfg (true);
+                       pi->set_count (count);
+                       pi->set_outputs (outs);
+                       pi->set_sinks (sinks);
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+
+                       pi->set_count (old_cnt);
+                       pi->set_sinks (old_sinks);
+                       pi->set_outputs (old_chan);
+                       pi->set_custom_cfg (old_cust);
+
+                       return false;
+               }
+               configure_processors_unlocked (0, &lm);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::set_strict_io (const bool enable)
+{
+       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+
+       if (_strict_io != enable) {
+               _strict_io = enable;
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                               pi->set_strict_io (_strict_io);
+                       }
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+
+               if (c.empty()) {
+                       // not possible
+                       _strict_io = !enable; // restore old value
+                       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                               boost::shared_ptr<PluginInsert> pi;
+                               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                                       pi->set_strict_io (_strict_io);
+                               }
+                       }
+                       return false;
+               }
+               lm.release ();
+
+               configure_processors (0);
+               lx.release ();
+
+               processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
+       return true;
+}
+
 XMLNode&
 Route::get_state()
 {
@@ -2161,6 +2223,12 @@ Route::get_template()
 XMLNode&
 Route::state(bool full_state)
 {
+       LocaleGuard lg;
+       if (!_session._template_state_dir.empty()) {
+               assert (!full_state); // only for templates
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
+       }
+
        XMLNode *node = new XMLNode("Route");
        ProcessorList::iterator i;
        char buf[32];
@@ -2169,15 +2237,12 @@ Route::state(bool full_state)
        node->add_property("id", buf);
        node->add_property ("name", _name);
        node->add_property("default-type", _default_type.to_string());
+       node->add_property ("strict-io", _strict_io);
 
-       if (_flags) {
-               node->add_property("flags", enum_2_string (_flags));
-       }
+       node->add_child_nocopy (_presentation_info.get_state());
 
        node->add_property("active", _active?"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));
 
@@ -2187,31 +2252,21 @@ Route::state(bool full_state)
                node->add_property("route-group", _route_group->name());
        }
 
-       snprintf (buf, sizeof (buf), "%d", _order_key);
-       node->add_property ("order-key", buf);
-       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 (_solo_control->get_state ());
+       node->add_child_nocopy (_solo_isolate_control->get_state ());
+       node->add_child_nocopy (_solo_safe_control->get_state ());
 
        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 ());
 
+       node->add_child_nocopy (_mute_control->get_state ());
+       node->add_child_nocopy (_phase_control->get_state ());
+
        if (full_state) {
                node->add_child_nocopy (Automatable::get_automation_xml_state ());
        }
 
-       XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
-       snprintf (buf, sizeof (buf), "%d", _remote_control_id);
-       remote_control_node->add_property (X_("id"), buf);
-       node->add_child_nocopy (*remote_control_node);
-
        if (_comment.length()) {
                XMLNode *cmt = node->add_child ("Comment");
                cmt->add_content (_comment);
@@ -2221,26 +2276,29 @@ Route::state(bool full_state)
                node->add_child_nocopy (_pannable->state (full_state));
        }
 
-       for (i = _processors.begin(); i != _processors.end(); ++i) {
-               if (!full_state) {
-                       /* template save: do not include internal sends functioning as 
-                          aux sends because the chance of the target ID
-                          in the session where this template is used
-                          is not very likely.
-
-                          similarly, do not save listen sends which connect to
-                          the monitor section, because these will always be
-                          added if necessary.
-                       */
-                       boost::shared_ptr<InternalSend> is;
-
-                       if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
-                               if (is->role() == Delivery::Listen) {
-                                       continue;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       if (!full_state) {
+                               /* template save: do not include internal sends functioning as
+                                        aux sends because the chance of the target ID
+                                        in the session where this template is used
+                                        is not very likely.
+
+                                        similarly, do not save listen sends which connect to
+                                        the monitor section, because these will always be
+                                        added if necessary.
+                                        */
+                               boost::shared_ptr<InternalSend> is;
+
+                               if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+                                       if (is->role() == Delivery::Listen) {
+                                               continue;
+                                       }
                                }
                        }
+                       node->add_child_nocopy((*i)->state (full_state));
                }
-               node->add_child_nocopy((*i)->state (full_state));
        }
 
        if (_extra_xml) {
@@ -2255,6 +2313,12 @@ Route::state(bool full_state)
                }
        }
 
+       if (!_session._template_state_dir.empty()) {
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
+       }
+
+       node->add_child_copy (Slavable::get_state());
+
        return *node;
 }
 
@@ -2268,7 +2332,7 @@ Route::set_state (const XMLNode& node, int version)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        if (node.name() != "Route"){
                error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
@@ -2282,14 +2346,10 @@ Route::set_state (const XMLNode& node, int version)
        set_id (node);
        _initial_io_setup = true;
 
-       if ((prop = node.property (X_("flags"))) != 0) {
-               _flags = Flag (string_2_enum (prop->value(), _flags));
-       } else {
-               _flags = Flag (0);
-       }
+       Stripable::set_state (node, version);
 
-       if (is_master() || is_monitor() || is_auditioner()) {
-               _mute_master->set_solo_ignore (true);
+       if ((prop = node.property (X_("strict-io"))) != 0) {
+               _strict_io = string_is_affirmative (prop->value());
        }
 
        if (is_monitor()) {
@@ -2321,18 +2381,17 @@ Route::set_state (const XMLNode& node, int version)
                        } else if (prop->value() == "Output") {
                                _output->set_state (*child, version);
                        }
-               }
 
-               if (child->name() == X_("Processor")) {
+               } else if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
-               }
-
-               if (child->name() == X_("Pannable")) {
+               } else if (child->name() == X_("Pannable")) {
                        if (_pannable) {
                                _pannable->set_state (*child, version);
                        } else {
                                warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg;
                        }
+               } else if (child->name() == Slavable::xml_node_name) {
+                       Slavable::set_state (*child, version);
                }
        }
 
@@ -2355,82 +2414,15 @@ Route::set_state (const XMLNode& node, int version)
        // this looks up the internal instrument in processors
        reset_instrument_info();
 
-       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 (boost::dynamic_bitset<> (prop->value ()));
-       }
-
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
                set_denormal_protection (string_is_affirmative (prop->value()));
        }
 
        if ((prop = node.property (X_("active"))) != 0) {
                bool yn = string_is_affirmative (prop->value());
-               _active = !yn; // force switch
                set_active (yn, this);
        }
 
-       if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
-               set_order_key (atoi(prop->value()));
-       }
-
-       if ((prop = node.property (X_("order-keys"))) != 0) { // Deprecated order keys
-
-               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 {
-                                       string keyname = remaining.substr (0, equal);
-
-                                       if ((keyname == "EditorSort") || (keyname == "editor")) {
-                                               cerr << "Setting " << name() << " order key to " << n << " using saved Editor order." << endl;
-                                               set_order_key (n);
-                                       }
-                               }
-                       }
-
-                       colon = remaining.find_first_of (':');
-
-                       if (colon != string::npos) {
-                               remaining = remaining.substr (colon+1);
-                       } else {
-                               break;
-                       }
-               }
-       }
-
        if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) {
                PBD::ID id (prop->value ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
@@ -2455,21 +2447,25 @@ Route::set_state (const XMLNode& node, int version)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } 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 (prop->value() == "mute") {
-                               _mute_control->set_state (*child, version);
+               }  else if (child->name() == Controllable::xml_node_name) {
+                       if ((prop = child->property (X_("name"))) == 0) {
+                               continue;
                        }
 
-               } 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_internal (x);
+                       if (prop->value() == _gain_control->name()) {
+                               _gain_control->set_state (*child, version);
+                       } else if (prop->value() == _solo_control->name()) {
+                               _solo_control->set_state (*child, version);
+                       } else if (prop->value() == _solo_safe_control->name()) {
+                               _solo_safe_control->set_state (*child, version);
+                       } else if (prop->value() == _solo_isolate_control->name()) {
+                               _solo_isolate_control->set_state (*child, version);
+                       } else if (prop->value() == _solo_control->name()) {
+                               _mute_control->set_state (*child, version);
+                       } else if (prop->value() == _phase_control->name()) {
+                               _phase_control->set_state (*child, version);
                        }
-
-               } else if (child->name() == X_("MuteMaster")) {
+               } else if (child->name() == MuteMaster::xml_node_name) {
                        _mute_master->set_state (*child, version);
 
                } else if (child->name() == Automatable::xml_node_name) {
@@ -2483,11 +2479,11 @@ Route::set_state (const XMLNode& node, int version)
 int
 Route::set_state_2X (const XMLNode& node, int version)
 {
-       LocaleGuard lg (X_("C"));
+       LocaleGuard lg;
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        /* 2X things which still remain to be handled:
         * default-type
@@ -2500,38 +2496,12 @@ Route::set_state_2X (const XMLNode& node, int version)
                return -1;
        }
 
-       if ((prop = node.property (X_("flags"))) != 0) {
-               string f = prop->value ();
-               boost::replace_all (f, "ControlOut", "MonitorOut");
-               _flags = Flag (string_2_enum (f, _flags));
-       } else {
-               _flags = Flag (0);
-       }
-
-       if (is_master() || is_monitor() || is_auditioner()) {
-               _mute_master->set_solo_ignore (true);
-       }
-
-       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);
-       }
+       Stripable::set_state (node, version);
 
        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 = 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;
@@ -2596,46 +2566,6 @@ Route::set_state_2X (const XMLNode& node, int version)
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
 
-       /* 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) {
-
-               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 {
-                                       string keyname = remaining.substr (0, equal);
-
-                                       if (keyname == "EditorSort" || keyname == "editor") {
-                                               info << string_compose(_("Converting deprecated order key for %1 using Editor order %2"), name (), n) << endmsg;
-                                               set_order_key (n);
-                                       }
-                               }
-                       }
-
-                       colon = remaining.find_first_of (':');
-
-                       if (colon != string::npos) {
-                               remaining = remaining.substr (colon+1);
-                       } else {
-                               break;
-                       }
-               }
-       }
-
        /* IOs */
 
        nlist = node.children ();
@@ -2668,7 +2598,7 @@ Route::set_state_2X (const XMLNode& node, int version)
                                gain_t val;
 
                                if (sscanf (prop->value().c_str(), "%f", &val) == 1) {
-                                       _amp->gain_control()->set_value (val);
+                                       _amp->gain_control()->set_value (val, Controllable::NoGroup);
                                }
                        }
 
@@ -2725,13 +2655,6 @@ Route::set_state_2X (const XMLNode& node, int version)
                                _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_internal (x);
-                       }
-
                }
        }
 
@@ -2810,7 +2733,7 @@ Route::set_processor_state (const XMLNode& node)
                        ProcessorList::iterator o;
 
                        for (o = _processors.begin(); o != _processors.end(); ++o) {
-                               XMLProperty* id_prop = (*niter)->property(X_("id"));
+                               XMLProperty const * 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);
@@ -2831,11 +2754,21 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
                                           prop->value() == "windows-vst" ||
-                                          prop->value() == "lxvst" ||
+                                          prop->value() == "lxvst" ||
+                                          prop->value() == "luaproc" ||
                                           prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert(_session));
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, **niter));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                               processor->set_owner (this);
+                                               if (_strict_io) {
+                                                       boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+                                                       pi->set_strict_io (true);
+                                               }
 
+                                       }
                                } else if (prop->value() == "port") {
 
                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
@@ -2843,6 +2776,9 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "send") {
 
                                        processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
+                                       boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+                                       send->SelfDestruct.connect_same_thread (*this,
+                                                       boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
 
                                } else {
                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -2854,6 +2790,12 @@ Route::set_processor_state (const XMLNode& node)
                                        processor.reset (new UnknownProcessor (_session, **niter));
                                }
 
+                               /* subscribe to Sidechain IO changes */
+                               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
+                               if (pi && pi->has_sidechain ()) {
+                                       pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+                               }
+
                                /* we have to note the monitor send here, otherwise a new one will be created
                                   and the state of this one will be lost.
                                */
@@ -2873,12 +2815,17 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        {
-               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               /* re-assign _processors w/o process-lock.
+                * if there's an IO-processor present in _processors but
+                * not in new_order, it will be deleted and ~IO takes
+                * a process lock.
+                */
                _processors = new_order;
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
 
                if (must_configure) {
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -2925,6 +2872,8 @@ Route::silence_unlocked (framecnt_t nframes)
 {
        /* Must be called with the processor lock held */
 
+       const framepos_t now = _session.transport_frame ();
+
        if (!_silent) {
 
                _output->silence (nframes);
@@ -2937,7 +2886,7 @@ Route::silence_unlocked (framecnt_t nframes)
                                continue;
                        }
 
-                       (*i)->silence (nframes);
+                       (*i)->silence (nframes, now);
                }
 
                if (nframes == _session.get_block_size()) {
@@ -3064,19 +3013,26 @@ Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 
          again:
                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) {
                                rl.release ();
-                               remove_processor (*x, &err, false);
+                               if (remove_processor (*x, &err, false) > 0) {
+                                       rl.acquire ();
+                                       continue;
+                               }
                                rl.acquire ();
 
                                /* list could have been demolished while we dropped the lock
                                   so start over.
                                */
-                               
-                               goto again;
+                               if (_session.engine().connected()) {
+                                       /* i/o processors cannot be removed if the engine is not running
+                                        * so don't live-loop in case the engine is N/A or dies
+                                        */
+                                       goto again;
+                               }
                        }
                }
        }
@@ -3086,7 +3042,7 @@ void
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
-       comment_changed (src);
+       comment_changed ();
        _session.set_dirty ();
 }
 
@@ -3136,12 +3092,53 @@ Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
        return false;
 }
 
+IOVector
+Route::all_inputs () const
+{
+       /* TODO, if this works as expected,
+        * cache the IOVector and maintain it via
+        * input_change_handler(), sidechain_change_handler() etc
+        */
+       IOVector ios;
+       ios.push_back (_input);
+
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator r = _processors.begin(); r != _processors.end(); ++r) {
+
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*r);
+               if (pi != 0) {
+                       assert (iop == 0);
+                       iop = pi->sidechain();
+               }
+
+               if (iop != 0 && iop->input()) {
+                       ios.push_back (iop->input());
+               }
+       }
+       return ios;
+}
+
+IOVector
+Route::all_outputs () const
+{
+       IOVector ios;
+       // _output is included via Delivery
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator r = _processors.begin(); r != _processors.end(); ++r) {
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               if (iop != 0 && iop->output()) {
+                       ios.push_back (iop->output());
+               }
+       }
+       return ios;
+}
+
 bool
 Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool* via_send_only)
 {
        DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
-
-       if (_output->connected_to (other->input())) {
+       if (other->all_inputs().fed_by (_output)) {
                DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
                if (via_send_only) {
                        *via_send_only = false;
@@ -3150,13 +3147,25 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool*
                return true;
        }
 
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
-               boost::shared_ptr<IOProcessor> iop;
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*r);
+               if (pi != 0) {
+                       assert (iop == 0);
+                       iop = pi->sidechain();
+               }
 
-               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
-                       if (iop->feeds (other)) {
+               if (iop != 0) {
+                       boost::shared_ptr<const IO> iop_out = iop->output();
+                       if (other.get() == this && iop_out && iop->input() && iop_out->connected_to (iop->input())) {
+                               // TODO this needs a delaylines in the Insert to align connections (!)
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed its own return (%2)\n", iop->name(), other->name()));
+                               continue;
+                       }
+                       if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) {
                                DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
                                if (via_send_only) {
                                        *via_send_only = true;
@@ -3181,6 +3190,12 @@ Route::direct_feeds_according_to_graph (boost::shared_ptr<Route> other, bool* vi
        return _session._current_route_graph.has (shared_from_this (), other, via_send_only);
 }
 
+bool
+Route::feeds_according_to_graph (boost::shared_ptr<Route> other)
+{
+       return _session._current_route_graph.feeds (shared_from_this (), other);
+}
+
 /** Called from the (non-realtime) butler thread when the transport is stopped */
 void
 Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_locate*/, bool can_flush_processors)
@@ -3208,23 +3223,78 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
-
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
-               _phase_invert.resize (_input->n_ports().n_audio ());
+               _phase_control->resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_input->connected() && _soloed_by_others_upstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), true);
-               } else {
-                       cancel_solo_after_disconnect (true);
+       if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) {
+               int sbou = 0;
+               int ibou = 0;
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               if (_input->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbou;
+                                       }
+                                       if ((*i)->solo_isolate_control()->solo_isolated()) {
+                                               ++ibou;
+                                       }
+                               }
+                       }
+               }
+
+               int delta  = sbou - _solo_control->soloed_by_others_upstream();
+               int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream();
+
+               if (idelta < -1) {
+                       PBD::warning << string_compose (
+                                       _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
+                                       _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta)
+                                    << endmsg;
+
+               }
+
+               if (_solo_control->soloed_by_others_upstream()) {
+                       // ignore new connections (they're not propagated)
+                       if (delta <= 0) {
+                               _solo_control->mod_solo_by_others_upstream (delta);
+                       }
+               }
+
+               if (_solo_isolate_control->solo_isolated_by_upstream()) {
+                       // solo-isolate currently only propagates downstream
+                       if (idelta < 0) {
+                               _solo_isolate_control->mod_solo_isolated_by_upstream (1);
+                       }
+                       //_solo_isolated_by_upstream = ibou;
+               }
+
+               // Session::route_solo_changed  does not propagate indirect solo-changes
+               // propagate downstream to tracks
+               for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                       if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                               continue;
+                       }
+                       bool sends_only;
+                       bool does_feed = feeds (*i, &sends_only);
+                       if (delta <= 0 && does_feed && !sends_only) {
+                               (*i)->solo_control()->mod_solo_by_others_upstream (delta);
+                       }
+
+                       if (idelta < 0 && does_feed && !sends_only) {
+                               (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1);
+                       }
                }
        }
 }
@@ -3232,16 +3302,14 @@ Route::input_change_handler (IOChange change, void * /*src*/)
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
        if (_initial_io_setup) {
                return;
        }
 
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
 
                if (is_master()) {
@@ -3251,25 +3319,58 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_output->connected() && _soloed_by_others_downstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), false);
-               } else {
-                       cancel_solo_after_disconnect (false);
+       if (_solo_control->soloed_by_others_downstream()) {
+               int sbod = 0;
+               /* checking all all downstream routes for
+                * explicit of implict solo is a rather drastic measure,
+                * ideally the input_change_handler() of the other route
+                * would propagate the change to us.
+                */
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               if (_output->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbod;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               int delta = sbod - _solo_control->soloed_by_others_downstream();
+               if (delta <= 0) {
+                       // do not allow new connections to change implicit solo (no propagation)
+                       _solo_control->mod_solo_by_others_downstream (delta);
+                       // Session::route_solo_changed() does not propagate indirect solo-changes
+                       // propagate upstream to tracks
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || !can_solo()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
+                               if (delta != 0 && does_feed && !sends_only) {
+                                       (*i)->solo_control()->mod_solo_by_others_downstream (delta);
+                               }
+                       }
+
                }
        }
 }
 
 void
-Route::cancel_solo_after_disconnect (bool upstream)
+Route::sidechain_change_handler (IOChange change, void* src)
 {
-       if (upstream) {
-               _soloed_by_others_upstream = 0;
-       } else {
-               _soloed_by_others_downstream = 0;
+       if (_initial_io_setup || _in_sidechain_setup) {
+               return;
        }
-       set_mute_master_solo ();
-       solo_changed (false, this);
+
+       input_change_handler (change, src);
 }
 
 uint32_t
@@ -3282,6 +3383,22 @@ Route::pans_required () const
        return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
 }
 
+void
+Route::flush_processor_buffers_locked (framecnt_t nframes)
+{
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+               if (d) {
+                       d->flush_buffers (nframes);
+               } else {
+                       boost::shared_ptr<PortInsert> p = boost::dynamic_pointer_cast<PortInsert> (*i);
+                       if (p) {
+                               p->flush_buffers (nframes);
+                       }
+               }
+       }
+}
+
 int
 Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing)
 {
@@ -3320,13 +3437,15 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
        fill_buffers_with_input (bufs, _input, nframes);
 
        if (_meter_point == MeterInput) {
-               _meter->run (bufs, start_frame, end_frame, nframes, true);
+               _meter->run (bufs, start_frame, end_frame, 0.0, nframes, true);
        }
 
        _amp->apply_gain_automation (false);
        _trim->apply_gain_automation (false);
        passthru (bufs, start_frame, end_frame, nframes, 0);
 
+       flush_processor_buffers_locked (nframes);
+
        return 0;
 }
 
@@ -3360,11 +3479,13 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
        fill_buffers_with_input (bufs, _input, nframes);
 
        if (_meter_point == MeterInput) {
-               _meter->run (bufs, start_frame, end_frame, nframes, true);
+               _meter->run (bufs, start_frame, end_frame, 1.0, nframes, true);
        }
 
        passthru (bufs, start_frame, end_frame, nframes, declick);
 
+       flush_processor_buffers_locked (nframes);
+
        return 0;
 }
 
@@ -3372,6 +3493,7 @@ int
 Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */)
 {
        silence (nframes);
+       flush_processor_buffers_locked (nframes);
        return 0;
 }
 
@@ -3429,13 +3551,12 @@ Route::apply_processor_changes_rt ()
                g_atomic_int_set (&_pending_signals, emissions);
                return true;
        }
-       return false;
+       return (!selfdestruct_sequence.empty ());
 }
 
 void
 Route::emit_pending_signals ()
 {
-
        int sig = g_atomic_int_and (&_pending_signals, 0);
        if (sig & EmitMeterChanged) {
                _meter->emit_configuration_changed();
@@ -3449,6 +3570,24 @@ Route::emit_pending_signals ()
        if (sig & EmitRtProcessorChange) {
                processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
        }
+
+       /* this would be a job for the butler.
+        * Conceptually we should not take processe/processor locks here.
+        * OTOH its more efficient (less overhead for summoning the butler and
+        * telling her what do do) and signal emission is called
+        * directly after the process callback, which decreases the chance
+        * of x-runs when taking the locks.
+        */
+       while (!selfdestruct_sequence.empty ()) {
+               Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+               if (selfdestruct_sequence.empty ()) { break; } // re-check with lock
+               boost::shared_ptr<Processor> proc = selfdestruct_sequence.back ().lock ();
+               selfdestruct_sequence.pop_back ();
+               lx.release ();
+               if (proc) {
+                       remove_processor (proc);
+               }
+       }
 }
 
 void
@@ -3523,9 +3662,9 @@ Route::set_meter_point_unlocked ()
        /* Set up the meter for its new position */
 
        ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-       
+
        ChanCount m_in;
-       
+
        if (loc == _processors.begin()) {
                m_in = _input->n_ports();
        } else {
@@ -3557,9 +3696,10 @@ Route::listen_position_changed ()
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
                ProcessorState pstate (this);
 
-               if (configure_processors_unlocked (0)) {
+               if (configure_processors_unlocked (0, &lm)) {
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
                        pstate.restore ();
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                        return;
                }
        }
@@ -3577,10 +3717,12 @@ Route::add_export_point()
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lw (_processor_lock);
 
-               _capturing_processor.reset (new CapturingProcessor (_session));
+               // this aligns all tracks; but not tracks + busses
+               assert (_session.worst_track_latency () >= _initial_delay);
+               _capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay));
                _capturing_processor->activate ();
 
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lw);
 
        }
 
@@ -3660,115 +3802,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
        }
 }
 
-Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (SoloAutomation),
-                            ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
-                            boost::shared_ptr<AutomationList>(), name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::SoloControllable::set_value (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<RouteList> rl (new RouteList);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       rl->push_back (r);
-
-       if (Config->get_solo_control_is_listen_control()) {
-               _session.set_listen (rl, bval);
-       } else {
-               _session.set_solo (rl, bval);
-       }
-}
-
-double
-Route::SoloControllable::get_value () const
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return 0;
-       }
-
-       if (Config->get_solo_control_is_listen_control()) {
-               return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       } else {
-               return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       }
-}
-
-Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (MuteAutomation),
-                            ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
-                            boost::shared_ptr<AutomationList>(),
-                            name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::MuteControllable::set_superficial_value(bool muted)
-{
-       /* Note we can not use AutomationControl::set_value here since it will emit
-          Changed(), but the value will not be correct to the observer. */
-
-       bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
-
-       Control::set_double (muted, _session.transport_frame(), to_list);
-}
-
-void
-Route::MuteControllable::set_value (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
-               // Playing back automation, set route mute directly
-               r->set_mute (bval, this);
-       } else {
-               // Set from user, queue mute event
-               boost::shared_ptr<RouteList> rl (new RouteList);
-               rl->push_back (r);
-               _session.set_mute (rl, bval, Session::rt_cleanup);
-       }
-
-       // Set superficial/automation value to drive controller (and possibly record)
-       set_superficial_value(bval);
-}
-
-double
-Route::MuteControllable::get_value () const
-{
-       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
-               // Playing back automation, get the value from the list
-               return AutomationControl::get_value();
-       }
-
-       // Not playing back automation, get the actual route mute value
-       boost::shared_ptr<Route> r = _route.lock ();
-       return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-}
-
 void
 Route::set_block_size (pframes_t nframes)
 {
@@ -3871,17 +3904,33 @@ Route::shift (framepos_t pos, framecnt_t frames)
        }
 }
 
+void
+Route::set_plugin_state_dir (boost::weak_ptr<Processor> p, const std::string& d)
+{
+       boost::shared_ptr<Processor> processor (p.lock ());
+       boost::shared_ptr<PluginInsert> pi  = boost::dynamic_pointer_cast<PluginInsert> (processor);
+       if (!pi) {
+               return;
+       }
+       pi->set_state_dir (d);
+}
 
 int
 Route::save_as_template (const string& path, const string& name)
 {
+       std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix
+       PBD::Unwinder<std::string> uw (_session._template_state_dir, state_dir);
+
        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());
+
+       /* return zero on success, non-zero otherwise */
+       return !tree.write (path.c_str());
 }
 
 
@@ -3922,29 +3971,31 @@ Route::set_name (const string& str)
  *  @param name New name.
  */
 void
-Route::set_name_in_state (XMLNode& node, string const & name)
+Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
 {
        node.add_property (X_("name"), name);
 
        XMLNodeList children = node.children();
        for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
-               
+
                if ((*i)->name() == X_("IO")) {
 
                        IO::set_name_in_state (**i, name);
 
                } else if ((*i)->name() == X_("Processor")) {
 
-                       XMLProperty* role = (*i)->property (X_("role"));
+                       XMLProperty const * role = (*i)->property (X_("role"));
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
-                       
+
                } else if ((*i)->name() == X_("Diskstream")) {
 
-                       (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       if (rename_playlist) {
+                               (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       }
                        (*i)->add_property (X_("name"), name);
-                       
+
                }
        }
 }
@@ -3967,41 +4018,6 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
        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 (boost::dynamic_bitset<> p)
-{
-       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;
-}
-
 void
 Route::set_denormal_protection (bool yn)
 {
@@ -4057,10 +4073,22 @@ Route::panner_shell() const
        return _main_outs->panner_shell();
 }
 
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
 Route::gain_control() const
 {
-       return _amp->gain_control();
+       return _gain_control;
+}
+
+boost::shared_ptr<GainControl>
+Route::trim_control() const
+{
+       return _trim_control;
+}
+
+boost::shared_ptr<PhaseControl>
+Route::phase_control() const
+{
+       return _phase_control;
 }
 
 boost::shared_ptr<AutomationControl>
@@ -4094,10 +4122,10 @@ Route::get_control (const Evoral::Parameter& param)
 }
 
 boost::shared_ptr<Processor>
-Route::nth_plugin (uint32_t n)
+Route::nth_plugin (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       ProcessorList::iterator i;
+       ProcessorList::const_iterator i;
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
@@ -4111,13 +4139,21 @@ Route::nth_plugin (uint32_t n)
 }
 
 boost::shared_ptr<Processor>
-Route::nth_send (uint32_t n)
+Route::nth_send (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       ProcessorList::iterator i;
+       ProcessorList::const_iterator i;
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<Send> (*i)) {
+
+                       if ((*i)->name().find (_("Monitor")) == 0) {
+                               /* send to monitor section is not considered
+                                  to be an accessible send.
+                               */
+                               continue;
+                       }
+
                        if (n-- == 0) {
                                return *i;
                        }
@@ -4145,12 +4181,6 @@ Route::has_io_processor_named (const string& name)
        return false;
 }
 
-MuteMaster::MutePoint
-Route::mute_points () const
-{
-       return _mute_master->mute_points ();
-}
-
 void
 Route::set_processor_positions ()
 {
@@ -4185,7 +4215,10 @@ Route::input_port_count_changing (ChanCount to)
 bool
 Route::output_port_count_changing (ChanCount to)
 {
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+       if (_strict_io && !_in_configure_processors) {
+               return true;
+       }
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                if (processor_out_streams.get(*t) > to.get(*t)) {
                        return true;
                }
@@ -4199,6 +4232,11 @@ Route::unknown_processors () const
 {
        list<string> p;
 
+       if (_session.get_disable_all_loaded_plugins ()) {
+               // Do not list "missing plugins" if they are explicitly disabled
+               return p;
+       }
+
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
@@ -4225,17 +4263,17 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
        } else {
                all_connections.min = ~((pframes_t) 0);
                all_connections.max = 0;
-               
+
                /* iterate over all "from" ports and determine the latency range for all of their
                   connections to the "outside" (outside of this Route).
                */
-               
+
                for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
-                       
+
                        LatencyRange range;
-                       
+
                        p->get_connected_latency_range (range, playback);
-                       
+
                        all_connections.min = min (all_connections.min, range.min);
                        all_connections.max = max (all_connections.max, range.max);
                }
@@ -4338,7 +4376,7 @@ Route::setup_invisible_processors ()
 
        /* we'll build this new list here and then use it
         *
-        * TODO put the ProcessorList is on the stack for RT-safety. 
+        * TODO put the ProcessorList is on the stack for RT-safety.
         */
 
        ProcessorList new_processors;
@@ -4353,12 +4391,13 @@ Route::setup_invisible_processors ()
 
        /* find the amp */
 
-       ProcessorList::iterator amp = new_processors.begin ();
-       while (amp != new_processors.end() && *amp != _amp) {
-               ++amp;
-       }
+       ProcessorList::iterator amp = find (new_processors.begin(), new_processors.end(), _amp);
 
-       assert (amp != new_processors.end ());
+       if (amp == new_processors.end ()) {
+               error << string_compose (_("Amp/Fader on Route '%1' went AWOL. Re-added."), name()) << endmsg;
+               new_processors.push_front (_amp);
+               amp = find (new_processors.begin(), new_processors.end(), _amp);
+       }
 
        /* and the processor after the amp */
 
@@ -4419,34 +4458,29 @@ Route::setup_invisible_processors ()
 
        if (_monitor_send && !is_monitor ()) {
                assert (!_monitor_send->display_to_user ());
-               if (Config->get_solo_control_is_listen_control()) {
-                       switch (Config->get_listen_position ()) {
-                       case PreFaderListen:
-                               switch (Config->get_pfl_position ()) {
-                               case PFLFromBeforeProcessors:
-                                       new_processors.push_front (_monitor_send);
-                                       break;
-                               case PFLFromAfterProcessors:
-                                       new_processors.insert (amp, _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (false);
+               switch (Config->get_listen_position ()) {
+               case PreFaderListen:
+                       switch (Config->get_pfl_position ()) {
+                       case PFLFromBeforeProcessors:
+                               new_processors.push_front (_monitor_send);
                                break;
-                       case AfterFaderListen:
-                               switch (Config->get_afl_position ()) {
-                               case AFLFromBeforeProcessors:
-                                       new_processors.insert (after_amp, _monitor_send);
-                                       break;
-                               case AFLFromAfterProcessors:
-                                       new_processors.insert (new_processors.end(), _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (true);
+                       case PFLFromAfterProcessors:
+                               new_processors.insert (amp, _monitor_send);
                                break;
                        }
-               }  else {
-                       new_processors.insert (new_processors.end(), _monitor_send);
                        _monitor_send->set_can_pan (false);
+                       break;
+               case AfterFaderListen:
+                       switch (Config->get_afl_position ()) {
+                       case AFLFromBeforeProcessors:
+                               new_processors.insert (after_amp, _monitor_send);
+                               break;
+                       case AFLFromAfterProcessors:
+                               new_processors.insert (new_processors.end(), _monitor_send);
+                               break;
+                       }
+                       _monitor_send->set_can_pan (true);
+                       break;
                }
        }
 
@@ -4460,13 +4494,20 @@ Route::setup_invisible_processors ()
 
        if (_monitor_control && is_monitor ()) {
                assert (!_monitor_control->display_to_user ());
-               new_processors.push_front (_monitor_control);
+               new_processors.insert (amp, _monitor_control);
+       }
+
+       /* TRIM CONTROL */
+
+       if (_trim && _trim->active()) {
+               assert (!_trim->display_to_user ());
+               new_processors.push_front (_trim);
        }
 
        /* INTERNAL RETURN */
 
-       /* doing this here means that any monitor control will come just after
-          the return.
+       /* doing this here means that any monitor control will come after
+          the return and trim.
        */
 
        if (_intreturn) {
@@ -4474,10 +4515,6 @@ Route::setup_invisible_processors ()
                new_processors.push_front (_intreturn);
        }
 
-       if (_trim && _trim->active()) {
-               assert (!_trim->display_to_user ());
-               new_processors.push_front (_trim);
-       }
        /* EXPORT PROCESSOR */
 
        if (_capturing_processor) {
@@ -4487,6 +4524,12 @@ Route::setup_invisible_processors ()
 
        _processors = new_processors;
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!(*i)->display_to_user () && !(*i)->enabled () && (*i) != _monitor_send) {
+                       (*i)->enable (true);
+               }
+       }
+
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
@@ -4522,7 +4565,7 @@ Route::maybe_note_meter_position ()
        if (_meter_point != MeterCustom) {
                return;
        }
-       
+
        _custom_meter_position_noted = true;
        /* custom meter points range from after trim to before panner/main_outs
         * this is a limitation by the current processor UI
@@ -4591,7 +4634,7 @@ Route::has_external_redirects () const
                /* ignore inactive processors and obviously ignore the main
                 * outs since everything has them and we don't care.
                 */
-                
+
                if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
                        return true;;
                }
@@ -4611,11 +4654,9 @@ boost::shared_ptr<Processor>
 Route::the_instrument_unlocked () const
 {
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
-                       if ((*i)->input_streams().n_midi() > 0 &&
-                           (*i)->output_streams().n_audio() > 0) {
-                               return (*i);
-                       }
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+               if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+                       return (*i);
                }
        }
        return boost::shared_ptr<Processor>();
@@ -4637,11 +4678,12 @@ Route::non_realtime_locate (framepos_t pos)
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
                }
        }
+       _roll_delay = _initial_delay;
 }
 
 void
@@ -4649,9 +4691,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 {
        size_t n_buffers;
        size_t i;
-       
-       /* MIDI 
-        *  
+
+       /* MIDI
+        *
         * We don't currently mix MIDI input together, so we don't need the
         * complex logic of the audio case.
         */
@@ -4662,7 +4704,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 
                boost::shared_ptr<MidiPort> source_port = io->midi (i);
                MidiBuffer& buf (bufs.get_midi (i));
-               
+
                if (source_port) {
                        buf.copy (source_port->get_midi_buffer(nframes));
                } else {
@@ -4680,19 +4722,19 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
        if (n_ports > n_buffers) {
                scaling = ((float) n_buffers) / n_ports;
        }
-       
+
        for (i = 0; i < n_ports; ++i) {
-               
+
                /* if there are more ports than buffers, map them onto buffers
                 * in a round-robin fashion
                 */
 
                boost::shared_ptr<AudioPort> source_port = io->audio (i);
                AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-                       
+
 
                if (i < n_buffers) {
-                       
+
                        /* first time through just copy a channel into
                           the output buffer.
                        */
@@ -4702,11 +4744,11 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                        if (scaling != 1.0f) {
                                buf.apply_gain (scaling, nframes);
                        }
-                       
+
                } else {
-                       
+
                        /* on subsequent times around, merge data from
-                        * the port with what is already there 
+                        * the port with what is already there
                         */
 
                        if (scaling != 1.0f) {
@@ -4733,3 +4775,496 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                bufs.set_count (io->n_ports());
        }
 }
+
+boost::shared_ptr<AutomationControl>
+Route::pan_azimuth_control() const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan)));
+#else
+       if (!_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return _pannable->pan_azimuth_control;
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::pan_elevation_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanElevationAutomation) != c.end()) {
+               return _pannable->pan_elevation_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_width_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanWidthAutomation) != c.end()) {
+               return _pannable->pan_width_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_frontback_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanFrontBackAutomation) != c.end()) {
+               return _pannable->pan_frontback_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+boost::shared_ptr<AutomationControl>
+Route::pan_lfe_control() const
+{
+       if (Profile->get_mixbus() || !_pannable || !panner()) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       set<Evoral::Parameter> c = panner()->what_can_be_automated ();
+
+       if (c.find (PanLFEAutomation) != c.end()) {
+               return _pannable->pan_lfe_control;
+       } else {
+               return boost::shared_ptr<AutomationControl>();
+       }
+}
+
+uint32_t
+Route::eq_band_cnt () const
+{
+       if (Profile->get_mixbus()) {
+               return 3;
+       } else {
+               /* Ardour has no well-known EQ object */
+               return 0;
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_gain_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               if (is_master() || mixbus()) {
+                       port_number = 4;
+               } else {
+                       port_number = 8;
+               }
+               break;
+       case 1:
+               if (is_master() || mixbus()) {
+                       port_number = 3;
+               } else {
+                       port_number = 6;
+               }
+               break;
+       case 2:
+               if (is_master() || mixbus()) {
+                       port_number = 2;
+               } else {
+                       port_number = 4;
+               }
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::eq_freq_controllable (uint32_t band) const
+{
+#ifdef MIXBUS
+
+       if (mixbus() || is_master()) {
+               /* no frequency controls for mixbusses or master */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       uint32_t port_number;
+       switch (band) {
+       case 0:
+               port_number = 7;
+               break;
+       case 1:
+               port_number = 5;
+               break;
+       case 2:
+               port_number = 3;
+               break;
+       default:
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_q_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_shape_controllable (uint32_t band) const
+{
+       return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_hpf_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+       if (!eq) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::eq_band_name (uint32_t band) const
+{
+       if (Profile->get_mixbus()) {
+               switch (band) {
+               case 0:
+                       return _("lo");
+               case 1:
+                       return _("mid");
+               case 2:
+                       return _("hi");
+               default:
+                       return string();
+               }
+       } else {
+               return string ();
+       }
+}
+
+boost::shared_ptr<AutomationControl>
+Route::comp_enable_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_threshold_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_speed_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_mode_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_makeup_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+boost::shared_ptr<AutomationControl>
+Route::comp_redux_controllable () const
+{
+#ifdef MIXBUS
+       boost::shared_ptr<PluginInsert> comp = ch_comp();
+
+       if (!comp) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::comp_mode_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Leveler");
+       case 1:
+               return _("Compressor");
+       case 2:
+               return _("Limiter");
+       case 3:
+               return mixbus() ? _("Sidechain") : _("Limiter");
+       }
+
+       return _("???");
+#else
+       return _("???");
+#endif
+}
+
+string
+Route::comp_speed_name (uint32_t mode) const
+{
+#ifdef MIXBUS
+       switch (mode) {
+       case 0:
+               return _("Attk");
+       case 1:
+               return _("Ratio");
+       case 2:
+       case 3:
+               return _("Rels");
+       }
+       return _("???");
+#else
+       return _("???");
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::send_level_controllable (uint32_t n) const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       if (n >= 8) {
+               /* no such bus */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+       boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(nth_send (n));
+       if (!s) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return s->gain_control ();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::send_enable_controllable (uint32_t n) const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       if (n >= 8) {
+               /* no such bus */
+               return boost::shared_ptr<AutomationControl>();
+       }
+
+       const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+       /* although Ardour sends have enable/disable as part of the Processor
+          API, it is not exposed as a controllable.
+
+          XXX: we should fix this.
+       */
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::send_name (uint32_t n) const
+{
+#ifdef MIXBUS
+       if (n >= 8) {
+               return string();
+       }
+       boost::shared_ptr<Route> r = _session.get_mixbus (n);
+       assert (r);
+       return r->name();
+#else
+       boost::shared_ptr<Processor> p = nth_send (n);
+       if (p) {
+               return p->name();
+       } else {
+               return string();
+       }
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::master_send_enable_controllable () const
+{
+#ifdef  MIXBUS
+       boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+       if (!plug) {
+               return boost::shared_ptr<AutomationControl>();
+       }
+       return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign)));
+#else
+       return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+bool
+Route::slaved () const
+{
+       if (!_gain_control) {
+               return false;
+       }
+       /* just test one particular control, not all of them */
+       return _gain_control->slaved ();
+}
+
+bool
+Route::slaved_to (boost::shared_ptr<VCA> vca) const
+{
+       if (!vca || !_gain_control) {
+               return false;
+       }
+
+       /* just test one particular control, not all of them */
+
+       return _gain_control->slaved_to (vca->gain_control());
+}
+
+bool
+Route::muted_by_others_soloing () const
+{
+       if (!can_be_muted_by_others ()) {
+               return false;
+       }
+
+       return  _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
+}
+
+void
+Route::clear_all_solo_state ()
+{
+       double v = _solo_safe_control->get_value ();
+
+       _solo_control->clear_all_solo_state ();
+
+       if (v != 0.0) {
+               _solo_safe_control->set_value (v, Controllable::NoGroup);
+       }
+}