start shaping up VCA assign process
[ardour.git] / libs / ardour / route.cc
index 48661794903985b953f9bc3d114cd47cdbe12978..8b1f47ec4ec22a6409472fe16266dba077523ac5 100644 (file)
@@ -33,7 +33,6 @@
 #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_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"
@@ -56,6 +57,7 @@
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
+#include "ardour/parameter_descriptor.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
@@ -67,6 +69,7 @@
 #include "ardour/session.h"
 #include "ardour/unknown_processor.h"
 #include "ardour/utils.h"
+#include "ardour/vca.h"
 
 #include "i18n.h"
 
@@ -76,7 +79,9 @@ 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;
 
+/** Base class for all routable/mixable objects (tracks and busses) */
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , Automatable (sess)
@@ -113,7 +118,10 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _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)
 {
        processor_max_streams.reset();
 }
@@ -136,17 +144,17 @@ Route::init ()
        _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
        _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
        _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ()));
-       _group_gain_control.reset (new GroupGainControllable (X_("groupgain"), shared_from_this ()));
+
+       _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ()));
+       _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ()));
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
        _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle));
-       _group_gain_control->set_flags (Controllable::Flag (_group_gain_control->flags() | Controllable::GainLike));
 
        add_control (_solo_control);
        add_control (_mute_control);
        add_control (_phase_control);
-       add_control (_group_gain_control);
 
        /* panning */
 
@@ -165,6 +173,20 @@ 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.
+        */
+
+       _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
+       add_control (_gain_control);
+
+       _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));
@@ -172,13 +194,12 @@ Route::init ()
        }
 #endif
 
-       /* add amp processor  */
+       /* and input trim */
 
-       _amp.reset (new Amp (_session));
-       add_processor (_amp, PostFader);
+       _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
+       add_control (_trim_control);
 
-       /* 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)) {
@@ -378,19 +399,30 @@ Route::ensure_track_or_route_name(string name, Session &session)
 }
 
 void
-Route::inc_gain (gain_t fraction, void *src)
+Route::inc_gain (gain_t factor)
 {
-       _amp->inc_gain (fraction, src);
+       /* To be used ONLY when doing group-relative gain adjustment, from
+        * ::set_gain()
+        */
+
+       float desired_gain = _gain_control->user_double();
+
+       if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
+               // really?! what's the idea here?
+               _gain_control->route_set_value (0.000001f + (0.000001f * factor));
+       } else {
+               _gain_control->route_set_value (desired_gain + (desired_gain * factor));
+       }
 }
 
 void
-Route::set_gain (gain_t val, void *src)
+Route::set_gain (gain_t val, Controllable::GroupControlDisposition group_override)
 {
-       if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) {
+       if (use_group (group_override, &RouteGroup::is_gain)) {
 
                if (_route_group->is_relative()) {
 
-                       gain_t usable_gain = _amp->gain();
+                       gain_t usable_gain = _gain_control->get_value();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
@@ -421,34 +453,28 @@ Route::set_gain (gain_t val, void *src)
                                }
                        }
 
-                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
+                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
 
                } else {
 
-                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
+                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
                }
 
                return;
        }
 
-       if (val == _amp->gain()) {
+       if (val == _gain_control->get_value()) {
                return;
        }
 
-       _amp->set_gain (val, src);
+       _gain_control->route_set_value (val);
 }
 
 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
@@ -645,7 +671,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        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;
                }
@@ -653,8 +679,20 @@ 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, 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);
@@ -770,19 +808,14 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
 }
 
 void
-Route::set_listen (bool yn, void* src, bool group_override)
+Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (_solo_safe) {
                return;
        }
 
-       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
-       if (group_override && _route_group) {
-               group_active = !group_active;
-       }
-
-       if (_route_group && src != _route_group && group_active) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group, group_override));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -797,7 +830,7 @@ Route::set_listen (bool yn, void* src, bool group_override)
                        }
                        _mute_master->set_soloed_by_others (false);
 
-                       listen_changed (src, group_override); /* EMIT SIGNAL */
+                       listen_changed (group_override); /* EMIT SIGNAL */
                }
        }
 }
@@ -813,11 +846,12 @@ Route::listening_via_monitor () const
 }
 
 void
-Route::set_solo_safe (bool yn, void *src)
+Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */)
 {
        if (_solo_safe != yn) {
                _solo_safe = yn;
-               solo_safe_changed (src);
+               solo_safe_changed (); /* EMIT SIGNAL */
+               _solo_safe_control->Changed(); /* EMIT SIGNAL */
        }
 }
 
@@ -854,18 +888,21 @@ Route::clear_all_solo_state ()
 
        {
                PBD::Unwinder<bool> uw (_solo_safe, false);
-               set_solo (false, this);
+               set_solo (false, Controllable::NoGroup);
        }
 
        if (emit_changed) {
                set_mute_master_solo ();
-               solo_changed (false, this, false); /* EMIT SIGNAL */
+               solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_solo (bool yn, void *src, bool group_override)
+Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
 {
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
+                                                 name(), yn, enum_2_string(group_override), self_soloed()));
+
        if (_solo_safe) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
                return;
@@ -876,22 +913,14 @@ Route::set_solo (bool yn, void *src, bool group_override)
                return;
        }
 
-       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
-       if (group_override && _route_group) {
-               group_active = !group_active;
-       }
-       if (_route_group && src != _route_group && group_active) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group, group_override));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
                return;
        }
 
-       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()));
-
        if (self_soloed() != yn) {
                set_self_solo (yn);
-               set_mute_master_solo ();
-               solo_changed (true, src, group_override); /* EMIT SIGNAL */
+               solo_changed (true, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 
@@ -902,7 +931,7 @@ Route::set_solo (bool yn, void *src, bool group_override)
        */
 
        if (yn && Profile->get_trx()) {
-               set_mute (false, src);
+               set_mute (false, Controllable::UseGroup);
        }
 }
 
@@ -911,6 +940,7 @@ Route::set_self_solo (bool yn)
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
+       set_mute_master_solo ();
 }
 
 void
@@ -968,7 +998,7 @@ Route::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
-       solo_changed (false, this, false); /* EMIT SIGNAL */
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
@@ -990,7 +1020,7 @@ Route::mod_solo_by_others_downstream (int32_t delta)
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
 
        set_mute_master_solo ();
-       solo_changed (false, this, false); /* EMIT SIGNAL */
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
@@ -1001,7 +1031,7 @@ Route::set_mute_master_solo ()
 }
 
 void
-Route::mod_solo_isolated_by_upstream (bool yn, void* src)
+Route::mod_solo_isolated_by_upstream (bool yn)
 {
        bool old = solo_isolated ();
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
@@ -1020,19 +1050,19 @@ Route::mod_solo_isolated_by_upstream (bool yn, void* src)
        if (solo_isolated() != old) {
                /* solo isolated status changed */
                _mute_master->set_solo_ignore (solo_isolated());
-               solo_isolated_changed (src); /* EMIT SIGNAL */
+               solo_isolated_changed (); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_solo_isolated (bool yn, void *src)
+Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
 {
        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));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -1070,13 +1100,14 @@ Route::set_solo_isolated (bool yn, void *src)
                bool does_feed = feeds (*i, &sends_only);
 
                if (does_feed && !sends_only) {
-                       (*i)->mod_solo_isolated_by_upstream (yn, src);
+                       (*i)->mod_solo_isolated_by_upstream (yn);
                }
        }
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       solo_isolated_changed (src); /* EMIT SIGNAL */
+       solo_isolated_changed (); /* EMIT SIGNAL */
+       _solo_isolate_control->Changed(); /* EMIT SIGNAL */
 }
 
 bool
@@ -1092,16 +1123,16 @@ Route::set_mute_points (MuteMaster::MutePoint mp)
        mute_points_changed (); /* EMIT SIGNAL */
 
        if (_mute_master->muted_by_self()) {
-               mute_changed (this); /* EMIT SIGNAL */
+               mute_changed (); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
 void
-Route::set_mute (bool yn, void *src)
+Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
 {
-       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));
+       if (use_group (group_override, &RouteGroup::is_mute)) {
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
                return;
        }
 
@@ -1112,7 +1143,7 @@ Route::set_mute (bool yn, void *src)
                */
                act_on_mute ();
                /* tell everyone else */
-               mute_changed (src); /* EMIT SIGNAL */
+               mute_changed (); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
@@ -1230,89 +1261,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_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
-                       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;
@@ -1350,6 +1329,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                                                processor.reset (new UnknownProcessor (_session, node));
                                        } else {
                                                processor.reset (new PluginInsert (_session));
+                                               processor->set_owner (this);
                                        }
 
                                } else {
@@ -1393,24 +1373,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;
        }
 
@@ -1418,6 +1405,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);
@@ -1428,30 +1468,54 @@ 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 ((*i)->active()) {
+                               (*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) {
@@ -1664,7 +1728,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();
@@ -1736,9 +1800,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 ();
                                }
 
@@ -1758,10 +1828,10 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        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;
                }
 
@@ -1790,6 +1860,104 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        return 0;
 }
 
+int
+Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Processor> sub, ProcessorStreams* err)
+{
+       /* 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;
+       }
+
+       /* I/Os are out, too */
+       if (boost::dynamic_pointer_cast<IOProcessor> (old) || boost::dynamic_pointer_cast<IOProcessor> (sub)) {
+               return 1;
+       }
+
+       /* 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;
+               bool replaced = false;
+               bool enable = old->active ();
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+                       if (*i == old) {
+                               i = _processors.erase (i);
+                               _processors.insert (i, sub);
+                               sub->set_owner (this);
+                               replaced = true;
+                               break;
+                       } else {
+                               ++i;
+                       }
+               }
+
+               if (!replaced) {
+                       return 1;
+               }
+
+               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->activate ();
+               }
+
+               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)
 {
@@ -1832,9 +2000,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 ();
                        }
 
@@ -1849,10 +2022,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();
@@ -1903,7 +2076,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;
@@ -1937,8 +2110,65 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
                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_monitor() || is_auditioner())
+                                       && ( _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 ()) {
+                                       /* ..but at least as many as there are master-inputs */
+                                       // 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) {
@@ -1962,7 +2192,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());
@@ -1989,21 +2219,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 (!(*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;
                }
                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;
 
@@ -2020,6 +2283,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
+       lr.release ();
+       lm->acquire ();
+
 
        if (_meter) {
                _meter->set_max_channels (processor_max_streams);
@@ -2213,7 +2479,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;
                }
@@ -2236,36 +2502,243 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
-XMLNode&
-Route::get_state()
-{
-       return state(true);
-}
-
-XMLNode&
-Route::get_template()
-{
-       return state(false);
-}
-
-XMLNode&
-Route::state(bool full_state)
+bool
+Route::add_remove_sidechain (boost::shared_ptr<Processor> proc, bool add)
 {
-       XMLNode *node = new XMLNode("Route");
-       ProcessorList::iterator i;
-       char buf[32];
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
 
-       id().print (buf, sizeof (buf));
-       node->add_property("id", buf);
-       node->add_property ("name", _name);
-       node->add_property("default-type", _default_type.to_string());
+       if (pi->has_sidechain () == add) {
+               return true; // ?? call failed, but result is as expected.
+       }
 
-       if (_flags) {
-               node->add_property("flags", enum_2_string (_flags));
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
        }
 
-       node->add_property("active", _active?"yes":"no");
-       string p;
+       {
+               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()
+{
+       return state(true);
+}
+
+XMLNode&
+Route::get_template()
+{
+       return state(false);
+}
+
+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];
+
+       id().print (buf, sizeof (buf));
+       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_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");
@@ -2311,26 +2784,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;
+       {
+               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;
+                               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) {
@@ -2345,6 +2821,10 @@ Route::state(bool full_state)
                }
        }
 
+       if (!_session._template_state_dir.empty()) {
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
+       }
+
        return *node;
 }
 
@@ -2358,7 +2838,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;
@@ -2378,6 +2858,10 @@ Route::set_state (const XMLNode& node, int version)
                _flags = Flag (0);
        }
 
+       if ((prop = node.property (X_("strict-io"))) != 0) {
+               _strict_io = string_is_affirmative (prop->value());
+       }
+
        if (is_master() || is_monitor() || is_auditioner()) {
                _mute_master->set_solo_ignore (true);
        }
@@ -2460,11 +2944,11 @@ Route::set_state (const XMLNode& node, int version)
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
-               set_solo_isolated (string_is_affirmative (prop->value()), this);
+               set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
        }
 
        if ((prop = node.property ("solo-safe")) != 0) {
-               set_solo_safe (string_is_affirmative (prop->value()), this);
+               set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
@@ -2477,7 +2961,6 @@ Route::set_state (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("active"))) != 0) {
                bool yn = string_is_affirmative (prop->value());
-               _active = !yn; // force switch
                set_active (yn, this);
        }
 
@@ -2573,11 +3056,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
@@ -2619,7 +3102,7 @@ Route::set_state_2X (const XMLNode& node, int version)
 
                /* XXX force reset of solo status */
 
-               set_solo (yn, this);
+               set_solo (yn);
        }
 
        if ((prop = node.property (X_("muted"))) != 0) {
@@ -2758,7 +3241,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);
                                }
                        }
 
@@ -2900,7 +3383,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);
@@ -2921,13 +3404,20 @@ 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") {
 
                                        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") {
 
@@ -2936,6 +3426,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;
@@ -2947,6 +3440,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.
                                */
@@ -2971,7 +3470,7 @@ Route::set_processor_state (const XMLNode& node)
                _processors = new_order;
 
                if (must_configure) {
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -3186,7 +3685,7 @@ void
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
-       comment_changed (src);
+       comment_changed ();
        _session.set_dirty ();
 }
 
@@ -3236,12 +3735,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;
@@ -3250,13 +3790,20 @@ 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 ((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;
@@ -3281,6 +3828,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)
@@ -3360,7 +3913,7 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                if (_solo_isolated_by_upstream) {
                        // solo-isolate currently only propagates downstream
                        if (idelta < 0) {
-                               mod_solo_isolated_by_upstream (false, this);
+                               mod_solo_isolated_by_upstream (false);
                        }
                        // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
                        // but idelta can't be smaller than -1, can it?
@@ -3380,7 +3933,7 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                        }
 
                        if (idelta < 0 && does_feed && !sends_only) {
-                               (*i)->mod_solo_isolated_by_upstream (false, this);
+                               (*i)->mod_solo_isolated_by_upstream (false);
                        }
                }
        }
@@ -3450,6 +4003,16 @@ Route::output_change_handler (IOChange change, void * /*src*/)
        }
 }
 
+void
+Route::sidechain_change_handler (IOChange change, void* src)
+{
+       if (_initial_io_setup || _in_sidechain_setup) {
+               return;
+       }
+
+       input_change_handler (change, src);
+}
+
 uint32_t
 Route::pans_required () const
 {
@@ -3607,13 +4170,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();
@@ -3627,6 +4189,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
@@ -3735,10 +4315,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;
                }
        }
@@ -3759,7 +4339,7 @@ Route::add_export_point()
                _capturing_processor.reset (new CapturingProcessor (_session));
                _capturing_processor->activate ();
 
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lw);
 
        }
 
@@ -3839,210 +4419,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)
-{
-       if (writable()) {
-               set_value_unchecked (val);
-       }
-}
-
-void
-Route::SoloControllable::set_value_unchecked (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. */
-
-       const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
-       const double where = _session.audible_frame ();
-       if (to_list) {
-               /* Note that we really need this:
-                *  if (as == Touch && _list->in_new_write_pass ()) {
-                *       alist->start_write_pass (_session.audible_frame ());
-                *  }
-                * here in the case of the user calling from a GUI or whatever.
-                * Without the ability to distinguish between user and
-                * automation-initiated changes, we lose the "touch mute"
-                * behaviour we have in AutomationController::toggled ().
-                */
-               _list->set_in_write_pass (true, false, where);
-       }
-
-       Control::set_double (muted, where, to_list);
-}
-
-void
-Route::MuteControllable::set_value (double val)
-{
-       if (writable()) {
-               set_value_unchecked (val);
-       }
-}
-
-void
-Route::MuteControllable::set_value_unchecked (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()) {
-               // Set superficial/automation value to drive controller (and possibly record)
-               set_superficial_value (bval);
-               // 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);
-       }
-}
-
-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;
-}
-
-Route::GroupGainControllable::GroupGainControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                                               Evoral::Parameter (GainAutomation),
-                                               ParameterDescriptor (Evoral::Parameter (GainAutomation)),
-                                               boost::shared_ptr<AutomationList>(),
-                                               name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(GainAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::GroupGainControllable::set_value (double val)
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       // I am not sure why I need the * .5 to make this work
-       float normalized_position = r->gain_control()->interface_to_internal (val * 0.5);
-       r->set_gain ((gain_t)normalized_position, this);
-}
-
-Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                                               Evoral::Parameter (PhaseAutomation),
-                                               ParameterDescriptor (Evoral::Parameter (PhaseAutomation)),
-                                               boost::shared_ptr<AutomationList>(),
-                                               name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::PhaseControllable::set_value (double v)
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (r->phase_invert().size()) {
-               if (v == 0 || (v < 1 && v > 0.9) ) {
-                       r->set_phase_invert (_current_phase, false);
-               } else {
-                       r->set_phase_invert (_current_phase, true);
-               }
-       }
-}
-
-double
-Route::PhaseControllable::get_value () const
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       return (double) r->phase_invert (_current_phase);
-}
-
-void
-Route::PhaseControllable::set_channel (uint32_t c)
-{
-       _current_phase = c;
-}
-
-uint32_t
-Route::PhaseControllable::channel () const
-{
-       return _current_phase;
-}
-
 void
 Route::set_block_size (pframes_t nframes)
 {
@@ -4145,16 +4521,31 @@ 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 zero on success, non-zero otherwise */
        return !tree.write (path.c_str());
 }
@@ -4210,7 +4601,7 @@ Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playli
 
                } 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);
                        }
@@ -4253,6 +4644,7 @@ Route::set_phase_invert (uint32_t c, bool yn)
        if (_phase_invert[c] != yn) {
                _phase_invert[c] = yn;
                phase_invert_changed (); /* EMIT SIGNAL */
+               _phase_control->Changed(); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
 }
@@ -4334,16 +4726,26 @@ 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<AutomationControl>
+boost::shared_ptr<GainControl>
 Route::trim_control() const
 {
-       return _trim->gain_control();
+       return _trim_control;
+}
+
+boost::shared_ptr<Route::PhaseControllable>
+Route::phase_control() const
+{
+       if (phase_invert().size()) {
+               return _phase_control;
+       } else {
+               return boost::shared_ptr<PhaseControllable>();
+       }
 }
 
 boost::shared_ptr<AutomationControl>
@@ -4377,10 +4779,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)) {
@@ -4394,13 +4796,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;
                        }
@@ -4468,6 +4878,9 @@ Route::input_port_count_changing (ChanCount to)
 bool
 Route::output_port_count_changing (ChanCount to)
 {
+       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;
@@ -4641,12 +5054,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 */
 
@@ -4743,7 +5157,7 @@ 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);
        }
 
        /* INTERNAL RETURN */
@@ -4900,11 +5314,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>();
@@ -5023,3 +5435,462 @@ 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_to (boost::shared_ptr<VCA> vca) const
+{
+       if (!vca || !_gain_control) {
+               return false;
+       }
+
+       return _gain_control->slaved_to (vca->control());
+}