revert bd5da84928 - separate i/o-map & config reset.
[ardour.git] / libs / ardour / route.cc
index 6409fb5bf0e1e034da30f549ad3d7d1529fab59c..6519980b684c6e53b1a11dde447cd9bbea2f1c19 100644 (file)
@@ -22,7 +22,6 @@
 #endif
 
 #include <cmath>
 #endif
 
 #include <cmath>
-#include <fstream>
 #include <cassert>
 #include <algorithm>
 
 #include <cassert>
 #include <algorithm>
 
@@ -35,6 +34,7 @@
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/boost_debug.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/boost_debug.h"
+#include "pbd/unwind.h"
 
 #include "ardour/amp.h"
 #include "ardour/audio_buffer.h"
 
 #include "ardour/amp.h"
 #include "ardour/audio_buffer.h"
@@ -46,6 +46,7 @@
 #include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
 #include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
+#include "ardour/gain_control.h"
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
 #include "ardour/pannable.h"
 #include "ardour/panner.h"
 #include "ardour/panner_shell.h"
 #include "ardour/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"
 #include "ardour/processor.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
 #include "ardour/processor.h"
+#include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
@@ -87,6 +90,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _initial_delay (0)
        , _roll_delay (0)
        , _pending_process_reorder (0)
        , _initial_delay (0)
        , _roll_delay (0)
        , _pending_process_reorder (0)
+       , _pending_signals (0)
        , _flags (flg)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
        , _flags (flg)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
@@ -95,7 +99,8 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
        , _soloed_by_others_downstream (0)
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
        , _soloed_by_others_downstream (0)
-       , _solo_isolated (0)
+       , _solo_isolated (false)
+       , _solo_isolated_by_upstream (0)
        , _denormal_protection (false)
        , _recordable (true)
        , _silent (false)
        , _denormal_protection (false)
        , _recordable (true)
        , _silent (false)
@@ -110,28 +115,41 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
        , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
+       , _strict_io (false)
        , _custom_meter_position_noted (false)
        , _custom_meter_position_noted (false)
-       , _last_custom_meter_was_at_end (false)
 {
 {
-       if (is_master()) {
-               _meter_type = MeterK20;
-       }
        processor_max_streams.reset();
 }
 
 int
 Route::init ()
 {
        processor_max_streams.reset();
 }
 
 int
 Route::init ()
 {
+       /* set default meter type */
+       if (is_master()) {
+               _meter_type = Config->get_meter_type_master ();
+       }
+       else if (dynamic_cast<Track*>(this)) {
+               _meter_type = Config->get_meter_type_track ();
+       } else {
+               _meter_type = Config->get_meter_type_bus ();
+       }
+
        /* add standard controls */
 
        _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
        _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
        /* add standard controls */
 
        _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 ()));
+
+       _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));
 
        _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));
 
        add_control (_solo_control);
        add_control (_mute_control);
 
        add_control (_solo_control);
        add_control (_mute_control);
+       add_control (_phase_control);
 
        /* panning */
 
 
        /* panning */
 
@@ -159,11 +177,22 @@ Route::init ()
 
        /* add amp processor  */
 
 
        /* add amp processor  */
 
-       _amp.reset (new Amp (_session));
+       _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);
 
        add_processor (_amp, PostFader);
 
+       if (is_monitor ()) {
+               _amp->set_display_name (_("Monitor"));
+       }
+
        /* and input trim */
        /* and input trim */
-       _trim.reset (new Amp (_session, "trim"));
+
+       _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
+       add_control (_trim_control);
+
+       _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
        _trim->set_display_to_user (false);
 
        if (dynamic_cast<AudioTrack*>(this)) {
        _trim->set_display_to_user (false);
 
        if (dynamic_cast<AudioTrack*>(this)) {
@@ -207,8 +236,6 @@ Route::init ()
 
        /* now that we have _meter, its safe to connect to this */
 
 
        /* now that we have _meter, its safe to connect to this */
 
-       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
-
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                configure_processors (0);
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                configure_processors (0);
@@ -251,7 +278,7 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 void
 Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 {
 void
 Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 {
-       /* force IDs for master/monitor busses and prevent 
+       /* force IDs for master/monitor busses and prevent
           any other route from accidentally getting these IDs
           (i.e. legacy sessions)
        */
           any other route from accidentally getting these IDs
           (i.e. legacy sessions)
        */
@@ -270,7 +297,7 @@ Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 
        /* don't allow it to collide */
 
 
        /* don't allow it to collide */
 
-       if (!is_master () && !is_monitor() && 
+       if (!is_master () && !is_monitor() &&
            (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
                id += MonitorBusRemoteControlID;
        }
            (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
                id += MonitorBusRemoteControlID;
        }
@@ -290,7 +317,7 @@ Route::remote_control_id() const
 {
        if (is_master()) {
                return MasterBusRemoteControlID;
 {
        if (is_master()) {
                return MasterBusRemoteControlID;
-       } 
+       }
 
        if (is_monitor()) {
                return MonitorBusRemoteControlID;
 
        if (is_monitor()) {
                return MonitorBusRemoteControlID;
@@ -358,26 +385,37 @@ Route::ensure_track_or_route_name(string name, Session &session)
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
-               newname = bump_name_once (newname, '.');
+               newname = bump_name_once (newname, ' ');
        }
 
        return newname;
 }
 
 void
        }
 
        return newname;
 }
 
 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
 }
 
 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()) {
 
 
                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;
                        }
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
@@ -408,34 +446,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 {
 
 
                } 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;
        }
 
                }
 
                return;
        }
 
-       if (val == _amp->gain()) {
+       if (val == _gain_control->get_value()) {
                return;
        }
 
                return;
        }
 
-       _amp->set_gain (val, src);
-}
-
-void
-Route::inc_trim (gain_t fraction, void *src)
-{
-       _trim->inc_gain (fraction, src);
+       _gain_control->route_set_value (val);
 }
 
 void
 }
 
 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()
 {
        // TODO route group, see set_gain()
-       _trim->set_gain (val, 0);
+       _trim_control->route_set_value (val);
 }
 
 void
 }
 
 void
@@ -492,13 +524,12 @@ Route::process_output_buffers (BufferSet& bufs,
 
        /* Tell main outs what to do about monitoring.  We do this so that
           on a transition between monitoring states we get a de-clicking gain
 
        /* Tell main outs what to do about monitoring.  We do this so that
           on a transition between monitoring states we get a de-clicking gain
-          change in the _main_outs delivery.
-       */
-       bool silence = monitoring_state () == MonitoringSilence;
+          change in the _main_outs delivery, if config.get_use_monitor_fades()
+          is true.
 
 
-       //but we override this in the case where we have an internal generator
-       if ( _have_internal_generator )
-               silence = false;
+          We override this in the case where we have an internal generator.
+       */
+       bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
 
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
 
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
@@ -633,7 +664,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        break;
                }
 
                        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;
                }
                if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
                        break;
                }
@@ -641,8 +672,20 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                        break;
                }
 
                        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);
                 */
                if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
                        (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
@@ -758,14 +801,14 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
 }
 
 void
 }
 
 void
-Route::set_listen (bool yn, void* src)
+Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
 {
        if (_solo_safe) {
                return;
        }
 
 {
        if (_solo_safe) {
                return;
        }
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
                return;
        }
 
                return;
        }
 
@@ -773,13 +816,14 @@ Route::set_listen (bool yn, void* src)
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
-                               _mute_master->set_soloed (true);
+                               _mute_master->set_soloed_by_self (true);
                        } else {
                                _monitor_send->deactivate ();
                        } else {
                                _monitor_send->deactivate ();
-                               _mute_master->set_soloed (false);
+                               _mute_master->set_soloed_by_self (false);
                        }
                        }
+                       _mute_master->set_soloed_by_others (false);
 
 
-                       listen_changed (src); /* EMIT SIGNAL */
+                       listen_changed (group_override); /* EMIT SIGNAL */
                }
        }
 }
                }
        }
 }
@@ -795,11 +839,12 @@ Route::listening_via_monitor () const
 }
 
 void
 }
 
 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;
 {
        if (_solo_safe != yn) {
                _solo_safe = yn;
-               solo_safe_changed (src);
+               solo_safe_changed (); /* EMIT SIGNAL */
+               _solo_safe_control->Changed(); /* EMIT SIGNAL */
        }
 }
 
        }
 }
 
@@ -810,8 +855,47 @@ Route::solo_safe() const
 }
 
 void
 }
 
 void
-Route::set_solo (bool yn, void *src)
+Route::clear_all_solo_state ()
+{
+       // ideally this function will never do anything, it only exists to forestall Murphy
+       bool emit_changed = false;
+
+#ifndef NDEBUG
+       // these are really debug messages, but of possible interest.
+       if (_self_solo) {
+               PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
+       }
+       if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
+               PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
+                               name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
+       }
+#endif
+
+       if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
+               // if self-soled, set_solo() will do signal emission
+               emit_changed = true;
+       }
+
+       _soloed_by_others_upstream = 0;
+       _soloed_by_others_downstream = 0;
+
+       {
+               PBD::Unwinder<bool> uw (_solo_safe, false);
+               set_solo (false, Controllable::NoGroup);
+       }
+
+       if (emit_changed) {
+               set_mute_master_solo ();
+               solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+       }
+}
+
+void
+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;
        if (_solo_safe) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
                return;
@@ -822,20 +906,26 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
                return;
        }
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
+       if (use_group (group_override, &RouteGroup::is_solo)) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
                return;
        }
 
                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);
        if (self_soloed() != yn) {
                set_self_solo (yn);
-               set_mute_master_solo ();
-               solo_changed (true, src); /* EMIT SIGNAL */
+               solo_changed (true, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
+
+       assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
+
+       /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
+          Config->get_solo_mute_overrride().
+       */
+
+       if (yn && Profile->get_trx()) {
+               set_mute (false, Controllable::UseGroup);
+       }
 }
 
 void
 }
 
 void
@@ -843,17 +933,13 @@ Route::set_self_solo (bool yn)
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
+       set_mute_master_solo ();
 }
 
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
 }
 
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
-               return;
-       }
-
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        uint32_t old_sbu = _soloed_by_others_upstream;
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        uint32_t old_sbu = _soloed_by_others_upstream;
@@ -891,8 +977,11 @@ Route::mod_solo_by_others_upstream (int32_t delta)
             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
 
                if (delta > 0 || !Config->get_exclusive_solo()) {
             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
 
                if (delta > 0 || !Config->get_exclusive_solo()) {
-                       DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                       DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                               if (i->sends_only) {
+                                       continue;
+                               }
                                boost::shared_ptr<Route> sr = i->r.lock();
                                if (sr) {
                                        sr->mod_solo_by_others_downstream (-delta);
                                boost::shared_ptr<Route> sr = i->r.lock();
                                if (sr) {
                                        sr->mod_solo_by_others_downstream (-delta);
@@ -902,18 +991,13 @@ Route::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
        }
 
        set_mute_master_solo ();
-       solo_changed (false, this);
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
 }
 
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
-               return;
-       }
-
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        if (delta < 0) {
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        if (delta < 0) {
@@ -929,24 +1013,70 @@ 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 ();
        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);
+       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
 void
 Route::set_mute_master_solo ()
 {
 }
 
 void
 Route::set_mute_master_solo ()
 {
-       _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
+       _mute_master->set_soloed_by_self (self_soloed());
+       _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
+}
+
+void
+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",
+                               name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
+
+       if (!yn) {
+               if (_solo_isolated_by_upstream >= 1) {
+                       _solo_isolated_by_upstream--;
+               } else {
+                       _solo_isolated_by_upstream = 0;
+               }
+       } else {
+               _solo_isolated_by_upstream++;
+       }
+
+       if (solo_isolated() != old) {
+               /* solo isolated status changed */
+               _mute_master->set_solo_ignore (solo_isolated());
+               solo_isolated_changed (); /* EMIT SIGNAL */
+       }
 }
 
 void
 }
 
 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 (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;
+       }
+
+       bool changed = false;
+
+       if (yn) {
+               if (_solo_isolated == false) {
+                       _mute_master->set_solo_ignore (true);
+                       changed = true;
+               }
+               _solo_isolated = true;
+       } else {
+               if (_solo_isolated == true) {
+                       _solo_isolated = false;
+                       _mute_master->set_solo_ignore (false);
+                       changed = true;
+               }
+       }
+
+
+       if (!changed) {
                return;
        }
 
                return;
        }
 
@@ -960,42 +1090,23 @@ Route::set_solo_isolated (bool yn, void *src)
                }
 
                bool sends_only;
                }
 
                bool sends_only;
-               bool does_feed = direct_feeds_according_to_graph (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
+               bool does_feed = feeds (*i, &sends_only);
 
                if (does_feed && !sends_only) {
 
                if (does_feed && !sends_only) {
-                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+                       (*i)->mod_solo_isolated_by_upstream (yn);
                }
        }
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
                }
        }
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       bool changed = false;
-
-       if (yn) {
-               if (_solo_isolated == 0) {
-                       _mute_master->set_solo_ignore (true);
-                       changed = true;
-               }
-               _solo_isolated++;
-       } else {
-               if (_solo_isolated > 0) {
-                       _solo_isolated--;
-                       if (_solo_isolated == 0) {
-                               _mute_master->set_solo_ignore (false);
-                               changed = true;
-                       }
-               }
-       }
-
-       if (changed) {
-               solo_isolated_changed (src);
-       }
+       solo_isolated_changed (); /* EMIT SIGNAL */
+       _solo_isolate_control->Changed(); /* EMIT SIGNAL */
 }
 
 bool
 Route::solo_isolated () const
 {
 }
 
 bool
 Route::solo_isolated () const
 {
-       return _solo_isolated > 0;
+       return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0);
 }
 
 void
 }
 
 void
@@ -1005,16 +1116,16 @@ Route::set_mute_points (MuteMaster::MutePoint mp)
        mute_points_changed (); /* EMIT SIGNAL */
 
        if (_mute_master->muted_by_self()) {
        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
                _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;
        }
 
                return;
        }
 
@@ -1025,7 +1136,7 @@ Route::set_mute (bool yn, void *src)
                */
                act_on_mute ();
                /* tell everyone else */
                */
                act_on_mute ();
                /* tell everyone else */
-               mute_changed (src); /* EMIT SIGNAL */
+               mute_changed (); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
@@ -1036,6 +1147,21 @@ Route::muted () const
        return _mute_master->muted_by_self();
 }
 
        return _mute_master->muted_by_self();
 }
 
+bool
+Route::muted_by_others () const
+{
+       // This method is only used by route_ui for display state.
+       // The real thing is MuteMaster::muted_by_others_at()
+
+       //master is never muted by others
+       if (is_master())
+               return false;
+
+       //now check to see if something is soloed (and I am not)
+       //see also MuteMaster::mute_gain_at()
+       return (_session.soloing() && !soloed() && !solo_isolated());
+}
+
 #if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 #if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
@@ -1058,7 +1184,7 @@ Route::before_processor_for_placement (Placement p)
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        ProcessorList::iterator loc;
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        ProcessorList::iterator loc;
-       
+
        if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
        if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
@@ -1081,14 +1207,14 @@ Route::before_processor_for_index (int index)
        }
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        }
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
        while (i != _processors.end() && j < index) {
                if ((*i)->display_to_user()) {
                        ++j;
                }
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
        while (i != _processors.end() && j < index) {
                if ((*i)->display_to_user()) {
                        ++j;
                }
-               
+
                ++i;
        }
 
                ++i;
        }
 
@@ -1132,6 +1258,13 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
                return 1;
        }
 
                return 1;
        }
 
+       if (_strict_io) {
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+                       pi->set_strict_io (true);
+               }
+       }
+
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
@@ -1191,7 +1324,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
 
                }
 
 
                }
 
-               if (activation_allowed && !_session.get_disable_all_loaded_plugins()) {
+               if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
                        processor->activate ();
                }
 
                        processor->activate ();
                }
 
@@ -1244,7 +1377,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                                                prop->value() == "lxvst" ||
                                                prop->value() == "audiounit") {
 
                                                prop->value() == "lxvst" ||
                                                prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert (_session));
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, node));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                       }
 
                                } else {
 
 
                                } else {
 
@@ -1267,11 +1404,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                if (processor->set_state (node, version)) {
                        return false;
                }
                if (processor->set_state (node, version)) {
                        return false;
                }
-               
+
                //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
                if (i != children.end()) {
                        if ((prop = (*i)->property (X_("active"))) != 0) {
                //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
                if (i != children.end()) {
                        if ((prop = (*i)->property (X_("active"))) != 0) {
-                               if ( string_is_affirmative (prop->value()) && !_session.get_disable_all_loaded_plugins() )
+                               if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
                                        processor->activate();
                                else
                                        processor->deactivate();
                                        processor->activate();
                                else
                                        processor->deactivate();
@@ -1326,7 +1463,8 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1);
+                               pi->set_count (1); // why? configure_processors_unlocked() will re-do this
+                               pi->set_strict_io (_strict_io);
                        }
 
                        _processors.insert (loc, *i);
                        }
 
                        _processors.insert (loc, *i);
@@ -1650,7 +1788,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
                if (!removed) {
                        /* what? */
                        return 1;
-               } 
+               }
 
                if (configure_processors_unlocked (err)) {
                        pstate.restore ();
 
                if (configure_processors_unlocked (err)) {
                        pstate.restore ();
@@ -1684,6 +1822,104 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        return 0;
 }
 
        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)) {
+                       pstate.restore ();
+                       configure_processors_unlocked (0);
+                       return -1;
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->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)
 {
 int
 Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
 {
@@ -1830,15 +2066,40 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
-                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
-                       DEBUG_TRACE (DEBUG::Processors, "}\n");
-                       return list<pair<ChanCount, ChanCount> > ();
-               }
-
                if ((*p)->can_support_io_configuration(in, out)) {
                        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 ((*p)->can_support_io_configuration(in, out)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
+
+                       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) {
                        in = out;
                } else {
                        if (err) {
@@ -1892,13 +2153,25 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
-                       break;
+               if (!(*p)->configure_io(c->first, c->second)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
+                       _in_configure_processors = false;
+                       return -1;
                }
                }
-
-               (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
+
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                       /* 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->input_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->output_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams() * pi->get_count());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_output_streams() * pi->get_count());
+               }
                out = c->second;
 
                if (boost::dynamic_pointer_cast<Delivery> (*p)
                out = c->second;
 
                if (boost::dynamic_pointer_cast<Delivery> (*p)
@@ -1916,11 +2189,11 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
 
        if (_meter) {
 
 
        if (_meter) {
-               _meter->reset_max_channels (processor_max_streams);
+               _meter->set_max_channels (processor_max_streams);
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
-          configuration 
+          configuration
        */
        _session.ensure_buffers (n_process_buffers ());
 
        */
        _session.ensure_buffers (n_process_buffers ());
 
@@ -1941,12 +2214,12 @@ Route::all_visible_processors_active (bool state)
        if (_processors.empty()) {
                return;
        }
        if (_processors.empty()) {
                return;
        }
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
-               
+
                if (state) {
                        (*i)->activate ();
                } else {
                if (state) {
                        (*i)->activate ();
                } else {
@@ -2130,35 +2403,143 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
        return 0;
 }
 
-XMLNode&
-Route::get_state()
+bool
+Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
 {
 {
-       return state(true);
+       ChanCount unused;
+       return customize_plugin_insert (proc, 0, unused);
 }
 
 }
 
-XMLNode&
-Route::get_template()
+bool
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
 {
 {
-       return state(false);
-}
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
 
 
-XMLNode&
-Route::state(bool full_state)
-{
-       XMLNode *node = new XMLNode("Route");
-       ProcessorList::iterator i;
-       char buf[32];
+       {
+               bool found = false;
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
+                       if (*p == proc) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found) {
+                       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());
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
 
 
-       if (_flags) {
-               node->add_property("flags", enum_2_string (_flags));
-       }
+               bool      old_cust = pi->custom_cfg ();
+               uint32_t  old_cnt  = pi->get_count ();
+               ChanCount old_chan = pi->output_streams ();
 
 
-       node->add_property("active", _active?"yes":"no");
+               if (count == 0) {
+                       pi->set_custom_cfg (false);
+               } else {
+                       pi->set_custom_cfg (true);
+                       pi->set_count (count);
+                       pi->set_outputs (outs);
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+
+                       pi->set_count (old_cnt);
+                       pi->set_outputs (old_chan);
+                       pi->set_custom_cfg (old_cust);
+
+                       return false;
+               }
+               configure_processors_unlocked (0);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::set_strict_io (const bool enable)
+{
+       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 ();
+
+               {
+                       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+                       configure_processors (0);
+               }
+               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)
+{
+       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);
        string p;
        boost::to_string (_phase_invert, p);
        node->add_property("phase-invert", p);
@@ -2207,7 +2588,7 @@ Route::state(bool full_state)
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (!full_state) {
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (!full_state) {
-                       /* template save: do not include internal sends functioning as 
+                       /* 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.
                           aux sends because the chance of the target ID
                           in the session where this template is used
                           is not very likely.
@@ -2237,8 +2618,10 @@ Route::state(bool full_state)
                        after->id().print (buf, sizeof (buf));
                        node->add_property (X_("processor-after-last-custom-meter"), buf);
                }
                        after->id().print (buf, sizeof (buf));
                        node->add_property (X_("processor-after-last-custom-meter"), buf);
                }
+       }
 
 
-               node->add_property (X_("last-custom-meter-was-at-end"), _last_custom_meter_was_at_end ? "yes" : "no");
+       if (!_session._template_state_dir.empty()) {
+               foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
        }
 
        return *node;
        }
 
        return *node;
@@ -2274,6 +2657,10 @@ Route::set_state (const XMLNode& node, int version)
                _flags = Flag (0);
        }
 
                _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);
        }
        if (is_master() || is_monitor() || is_auditioner()) {
                _mute_master->set_solo_ignore (true);
        }
@@ -2356,11 +2743,11 @@ Route::set_state (const XMLNode& node, int version)
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
        }
 
        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) {
        }
 
        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) {
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
@@ -2431,10 +2818,6 @@ Route::set_state (const XMLNode& node, int version)
                }
        }
 
                }
        }
 
-       if ((prop = node.property (X_("last-custom-meter-was-at-end"))) != 0) {
-               _last_custom_meter_was_at_end = string_is_affirmative (prop->value ());
-       }
-
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -2519,7 +2902,7 @@ Route::set_state_2X (const XMLNode& node, int version)
 
                /* XXX force reset of solo status */
 
 
                /* XXX force reset of solo status */
 
-               set_solo (yn, this);
+               set_solo (yn);
        }
 
        if ((prop = node.property (X_("muted"))) != 0) {
        }
 
        if ((prop = node.property (X_("muted"))) != 0) {
@@ -2658,7 +3041,7 @@ Route::set_state_2X (const XMLNode& node, int version)
                                gain_t val;
 
                                if (sscanf (prop->value().c_str(), "%f", &val) == 1) {
                                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);
                                }
                        }
 
                                }
                        }
 
@@ -2821,11 +3204,20 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
                                           prop->value() == "windows-vst" ||
                                } 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") {
 
                                           prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert(_session));
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, **niter));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                               if (_strict_io) {
+                                                       boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+                                                       pi->set_strict_io (true);
+                                               }
 
 
+                                       }
                                } else if (prop->value() == "port") {
 
                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                } else if (prop->value() == "port") {
 
                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
@@ -3054,19 +3446,26 @@ Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 
          again:
                for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
 
          again:
                for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       
+
                        boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
                        boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-                       
+
                        if (d && d->target_route() == route) {
                                rl.release ();
                        if (d && d->target_route() == route) {
                                rl.release ();
-                               remove_processor (*x, &err, false);
+                               if (remove_processor (*x, &err, false) > 0) {
+                                       rl.acquire ();
+                                       continue;
+                               }
                                rl.acquire ();
 
                                /* list could have been demolished while we dropped the lock
                                   so start over.
                                */
                                rl.acquire ();
 
                                /* list could have been demolished while we dropped the lock
                                   so start over.
                                */
-                               
-                               goto again;
+                               if (_session.engine().connected()) {
+                                       /* i/o processors cannot be removed if the engine is not running
+                                        * so don't live-loop in case the engine is N/A or dies
+                                        */
+                                       goto again;
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -3076,7 +3475,7 @@ void
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
 Route::set_comment (string cmt, void *src)
 {
        _comment = cmt;
-       comment_changed (src);
+       comment_changed ();
        _session.set_dirty ();
 }
 
        _session.set_dirty ();
 }
 
@@ -3198,23 +3597,80 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
-
        if ((change.type & IOChange::ConfigurationChanged)) {
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
                _phase_invert.resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
 
                configure_processors (0);
                _phase_invert.resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_input->connected() && _soloed_by_others_upstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), true);
-               } else {
-                       cancel_solo_after_disconnect (true);
+       if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
+               int sbou = 0;
+               int ibou = 0;
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               if (_input->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbou;
+                                       }
+                                       if ((*i)->solo_isolated()) {
+                                               ++ibou;
+                                       }
+                               }
+                       }
+               }
+
+               int delta  = sbou - _soloed_by_others_upstream;
+               int idelta = ibou - _solo_isolated_by_upstream;
+
+               if (idelta < -1) {
+                       PBD::warning << string_compose (
+                                       _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
+                                       _name, ibou, _solo_isolated_by_upstream, idelta)
+                                    << endmsg;
+
+               }
+
+               if (_soloed_by_others_upstream) {
+                       // ignore new connections (they're not propagated)
+                       if (delta <= 0) {
+                               mod_solo_by_others_upstream (delta);
+                       }
+               }
+
+               if (_solo_isolated_by_upstream) {
+                       // solo-isolate currently only propagates downstream
+                       if (idelta < 0) {
+                               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?
+                       //_solo_isolated_by_upstream = ibou;
+               }
+
+               // Session::route_solo_changed  does not propagate indirect solo-changes
+               // propagate downstream to tracks
+               for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                       if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                               continue;
+                       }
+                       bool sends_only;
+                       bool does_feed = feeds (*i, &sends_only);
+                       if (delta <= 0 && does_feed && !sends_only) {
+                               (*i)->mod_solo_by_others_upstream (delta);
+                       }
+
+                       if (idelta < 0 && does_feed && !sends_only) {
+                               (*i)->mod_solo_isolated_by_upstream (false);
+                       }
                }
        }
 }
                }
        }
 }
@@ -3222,16 +3678,14 @@ Route::input_change_handler (IOChange change, void * /*src*/)
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
        if (_initial_io_setup) {
                return;
        }
 
        if ((change.type & IOChange::ConfigurationChanged)) {
        if (_initial_io_setup) {
                return;
        }
 
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
 
                if (is_master()) {
                configure_processors (0);
 
                if (is_master()) {
@@ -3241,25 +3695,48 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                io_changed (); /* EMIT SIGNAL */
        }
 
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_output->connected() && _soloed_by_others_downstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), false);
-               } else {
-                       cancel_solo_after_disconnect (false);
+       if (_soloed_by_others_downstream) {
+               int sbod = 0;
+               /* checking all all downstream routes for
+                * explicit of implict solo is a rather drastic measure,
+                * ideally the input_change_handler() of the other route
+                * would propagate the change to us.
+                */
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               if (_output->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbod;
+                                               break;
+                                       }
+                               }
+                       }
                }
                }
-       }
-}
+               int delta = sbod - _soloed_by_others_downstream;
+               if (delta <= 0) {
+                       // do not allow new connections to change implicit solo (no propagation)
+                       mod_solo_by_others_downstream (delta);
+                       // Session::route_solo_changed() does not propagate indirect solo-changes
+                       // propagate upstream to tracks
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
+                               if (delta != 0 && does_feed && !sends_only) {
+                                       (*i)->mod_solo_by_others_downstream (delta);
+                               }
+                       }
 
 
-void
-Route::cancel_solo_after_disconnect (bool upstream)
-{
-       if (upstream) {
-               _soloed_by_others_upstream = 0;
-       } else {
-               _soloed_by_others_downstream = 0;
+               }
        }
        }
-       set_mute_master_solo ();
-       solo_changed (false, this);
 }
 
 uint32_t
 }
 
 uint32_t
@@ -3482,7 +3959,9 @@ Route::set_meter_point_unlocked ()
 
        bool meter_was_visible_to_user = _meter->display_to_user ();
 
 
        bool meter_was_visible_to_user = _meter->display_to_user ();
 
-       maybe_note_meter_position ();
+       if (!_custom_meter_position_noted) {
+               maybe_note_meter_position ();
+       }
 
        if (_meter_point != MeterCustom) {
 
 
        if (_meter_point != MeterCustom) {
 
@@ -3491,32 +3970,29 @@ Route::set_meter_point_unlocked ()
                setup_invisible_processors ();
 
        } else {
                setup_invisible_processors ();
 
        } else {
-
                _meter->set_display_to_user (true);
 
                /* If we have a previous position for the custom meter, try to put it there */
                _meter->set_display_to_user (true);
 
                /* If we have a previous position for the custom meter, try to put it there */
-               if (_custom_meter_position_noted) {
-                       boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
-                       
-                       if (after) {
-                               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after);
-                               if (i != _processors.end ()) {
-                                       _processors.remove (_meter);
-                                       _processors.insert (i, _meter);
-                               }
-                       } else if (_last_custom_meter_was_at_end) {
+               boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
+               if (after) {
+                       ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after);
+                       if (i != _processors.end ()) {
                                _processors.remove (_meter);
                                _processors.remove (_meter);
-                               _processors.push_back (_meter);
+                               _processors.insert (i, _meter);
                        }
                        }
+               } else {// at end, right before the mains_out/panner
+                       _processors.remove (_meter);
+                       ProcessorList::iterator main = _processors.end();
+                       _processors.insert (--main, _meter);
                }
        }
 
        /* Set up the meter for its new position */
 
        ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
                }
        }
 
        /* Set up the meter for its new position */
 
        ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-       
+
        ChanCount m_in;
        ChanCount m_in;
-       
+
        if (loc == _processors.begin()) {
                m_in = _input->n_ports();
        } else {
        if (loc == _processors.begin()) {
                m_in = _input->n_ports();
        } else {
@@ -3549,6 +4025,7 @@ Route::listen_position_changed ()
                ProcessorState pstate (this);
 
                if (configure_processors_unlocked (0)) {
                ProcessorState pstate (this);
 
                if (configure_processors_unlocked (0)) {
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
                        pstate.restore ();
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
                        return;
                        pstate.restore ();
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
                        return;
@@ -3651,115 +4128,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
        }
 }
 
        }
 }
 
-Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (SoloAutomation),
-                            ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
-                            boost::shared_ptr<AutomationList>(), name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::SoloControllable::set_value (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<RouteList> rl (new RouteList);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       rl->push_back (r);
-
-       if (Config->get_solo_control_is_listen_control()) {
-               _session.set_listen (rl, bval);
-       } else {
-               _session.set_solo (rl, bval);
-       }
-}
-
-double
-Route::SoloControllable::get_value () const
-{
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return 0;
-       }
-
-       if (Config->get_solo_control_is_listen_control()) {
-               return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       } else {
-               return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-       }
-}
-
-Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(),
-                            Evoral::Parameter (MuteAutomation),
-                            ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
-                            boost::shared_ptr<AutomationList>(),
-                            name)
-       , _route (r)
-{
-       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
-       gl->set_interpolation(Evoral::ControlList::Discrete);
-       set_list (gl);
-}
-
-void
-Route::MuteControllable::set_superficial_value(bool muted)
-{
-       /* Note we can not use AutomationControl::set_value here since it will emit
-          Changed(), but the value will not be correct to the observer. */
-
-       bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
-
-       Control::set_double (muted, _session.transport_frame(), to_list);
-}
-
-void
-Route::MuteControllable::set_value (double val)
-{
-       const bool bval = ((val >= 0.5) ? true : false);
-
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return;
-       }
-
-       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
-               // Playing back automation, set route mute directly
-               r->set_mute (bval, this);
-       } else {
-               // Set from user, queue mute event
-               boost::shared_ptr<RouteList> rl (new RouteList);
-               rl->push_back (r);
-               _session.set_mute (rl, bval, Session::rt_cleanup);
-       }
-
-       // Set superficial/automation value to drive controller (and possibly record)
-       set_superficial_value(bval);
-}
-
-double
-Route::MuteControllable::get_value () const
-{
-       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
-               // Playing back automation, get the value from the list
-               return AutomationControl::get_value();
-       }
-
-       // Not playing back automation, get the actual route mute value
-       boost::shared_ptr<Route> r = _route.lock ();
-       return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-}
-
 void
 Route::set_block_size (pframes_t nframes)
 {
 void
 Route::set_block_size (pframes_t nframes)
 {
@@ -3862,17 +4230,33 @@ Route::shift (framepos_t pos, framecnt_t frames)
        }
 }
 
        }
 }
 
+void
+Route::set_plugin_state_dir (boost::weak_ptr<Processor> p, const std::string& d)
+{
+       boost::shared_ptr<Processor> processor (p.lock ());
+       boost::shared_ptr<PluginInsert> pi  = boost::dynamic_pointer_cast<PluginInsert> (processor);
+       if (!pi) {
+               return;
+       }
+       pi->set_state_dir (d);
+}
 
 int
 Route::save_as_template (const string& path, const string& name)
 {
 
 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));
        XMLNode& node (state (false));
+
        XMLTree tree;
 
        IO::set_name_in_state (*node.children().front(), name);
 
        tree.set_root (&node);
        XMLTree tree;
 
        IO::set_name_in_state (*node.children().front(), name);
 
        tree.set_root (&node);
-       return tree.write (path.c_str());
+
+       /* return zero on success, non-zero otherwise */
+       return !tree.write (path.c_str());
 }
 
 
 }
 
 
@@ -3913,13 +4297,13 @@ Route::set_name (const string& str)
  *  @param name New name.
  */
 void
  *  @param name New name.
  */
 void
-Route::set_name_in_state (XMLNode& node, string const & name)
+Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
 {
        node.add_property (X_("name"), name);
 
        XMLNodeList children = node.children();
        for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
 {
        node.add_property (X_("name"), name);
 
        XMLNodeList children = node.children();
        for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
-               
+
                if ((*i)->name() == X_("IO")) {
 
                        IO::set_name_in_state (**i, name);
                if ((*i)->name() == X_("IO")) {
 
                        IO::set_name_in_state (**i, name);
@@ -3930,12 +4314,14 @@ Route::set_name_in_state (XMLNode& node, string const & name)
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
-                       
+
                } else if ((*i)->name() == X_("Diskstream")) {
 
                } else if ((*i)->name() == X_("Diskstream")) {
 
-                       (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       if (rename_playlist) {
+                               (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+                       }
                        (*i)->add_property (X_("name"), name);
                        (*i)->add_property (X_("name"), name);
-                       
+
                }
        }
 }
                }
        }
 }
@@ -3967,6 +4353,7 @@ Route::set_phase_invert (uint32_t c, bool yn)
        if (_phase_invert[c] != yn) {
                _phase_invert[c] = yn;
                phase_invert_changed (); /* EMIT SIGNAL */
        if (_phase_invert[c] != yn) {
                _phase_invert[c] = yn;
                phase_invert_changed (); /* EMIT SIGNAL */
+               _phase_control->Changed(); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
 }
                _session.set_dirty ();
        }
 }
@@ -4029,28 +4416,6 @@ Route::set_active (bool yn, void* src)
        }
 }
 
        }
 }
 
-void
-Route::meter ()
-{
-       Glib::Threads::RWLock::ReaderLock rm (_processor_lock);
-
-       assert (_meter);
-
-       _meter->meter ();
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               boost::shared_ptr<Send> s;
-               boost::shared_ptr<Return> r;
-
-               if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
-                       s->meter()->meter();
-               } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
-                       r->meter()->meter ();
-               }
-       }
-}
-
 boost::shared_ptr<Pannable>
 Route::pannable() const
 {
 boost::shared_ptr<Pannable>
 Route::pannable() const
 {
@@ -4070,10 +4435,26 @@ Route::panner_shell() const
        return _main_outs->panner_shell();
 }
 
        return _main_outs->panner_shell();
 }
 
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
 Route::gain_control() const
 {
 Route::gain_control() const
 {
-       return _amp->gain_control();
+       return _gain_control;
+}
+
+boost::shared_ptr<GainControl>
+Route::trim_control() const
+{
+       return _trim_control;
+}
+
+boost::shared_ptr<Route::PhaseControllable>
+Route::phase_control() const
+{
+       if (phase_invert().size()) {
+               return _phase_control;
+       } else {
+               return boost::shared_ptr<PhaseControllable>();
+       }
 }
 
 boost::shared_ptr<AutomationControl>
 }
 
 boost::shared_ptr<AutomationControl>
@@ -4107,10 +4488,10 @@ Route::get_control (const Evoral::Parameter& param)
 }
 
 boost::shared_ptr<Processor>
 }
 
 boost::shared_ptr<Processor>
-Route::nth_plugin (uint32_t n)
+Route::nth_plugin (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 {
        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)) {
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
@@ -4124,13 +4505,21 @@ Route::nth_plugin (uint32_t n)
 }
 
 boost::shared_ptr<Processor>
 }
 
 boost::shared_ptr<Processor>
-Route::nth_send (uint32_t n)
+Route::nth_send (uint32_t n) const
 {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 {
        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)) {
 
        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;
                        }
                        if (n-- == 0) {
                                return *i;
                        }
@@ -4172,7 +4561,7 @@ Route::set_processor_positions ()
        bool had_amp = false;
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_pre_fader (!had_amp);
        bool had_amp = false;
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_pre_fader (!had_amp);
-               if (boost::dynamic_pointer_cast<Amp> (*i)) {
+               if (*i == _amp) {
                        had_amp = true;
                }
        }
                        had_amp = true;
                }
        }
@@ -4212,6 +4601,11 @@ Route::unknown_processors () const
 {
        list<string> p;
 
 {
        list<string> p;
 
+       if (_session.get_disable_all_loaded_plugins ()) {
+               // Do not list "missing plugins" if they are explicitly disabled
+               return p;
+       }
+
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
@@ -4238,17 +4632,17 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
        } else {
                all_connections.min = ~((pframes_t) 0);
                all_connections.max = 0;
        } else {
                all_connections.min = ~((pframes_t) 0);
                all_connections.max = 0;
-               
+
                /* iterate over all "from" ports and determine the latency range for all of their
                   connections to the "outside" (outside of this Route).
                */
                /* iterate over all "from" ports and determine the latency range for all of their
                   connections to the "outside" (outside of this Route).
                */
-               
+
                for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
                for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
-                       
+
                        LatencyRange range;
                        LatencyRange range;
-                       
+
                        p->get_connected_latency_range (range, playback);
                        p->get_connected_latency_range (range, playback);
-                       
+
                        all_connections.min = min (all_connections.min, range.min);
                        all_connections.max = max (all_connections.max, range.max);
                }
                        all_connections.min = min (all_connections.min, range.min);
                        all_connections.max = max (all_connections.max, range.max);
                }
@@ -4351,7 +4745,7 @@ Route::setup_invisible_processors ()
 
        /* we'll build this new list here and then use it
         *
 
        /* we'll build this new list here and then use it
         *
-        * TODO put the ProcessorList is on the stack for RT-safety. 
+        * TODO put the ProcessorList is on the stack for RT-safety.
         */
 
        ProcessorList new_processors;
         */
 
        ProcessorList new_processors;
@@ -4367,7 +4761,7 @@ Route::setup_invisible_processors ()
        /* find the amp */
 
        ProcessorList::iterator amp = new_processors.begin ();
        /* find the amp */
 
        ProcessorList::iterator amp = new_processors.begin ();
-       while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+       while (amp != new_processors.end() && *amp != _amp) {
                ++amp;
        }
 
                ++amp;
        }
 
@@ -4432,34 +4826,29 @@ Route::setup_invisible_processors ()
 
        if (_monitor_send && !is_monitor ()) {
                assert (!_monitor_send->display_to_user ());
 
        if (_monitor_send && !is_monitor ()) {
                assert (!_monitor_send->display_to_user ());
-               if (Config->get_solo_control_is_listen_control()) {
-                       switch (Config->get_listen_position ()) {
-                       case PreFaderListen:
-                               switch (Config->get_pfl_position ()) {
-                               case PFLFromBeforeProcessors:
-                                       new_processors.push_front (_monitor_send);
-                                       break;
-                               case PFLFromAfterProcessors:
-                                       new_processors.insert (amp, _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (false);
+               switch (Config->get_listen_position ()) {
+               case PreFaderListen:
+                       switch (Config->get_pfl_position ()) {
+                       case PFLFromBeforeProcessors:
+                               new_processors.push_front (_monitor_send);
                                break;
                                break;
-                       case AfterFaderListen:
-                               switch (Config->get_afl_position ()) {
-                               case AFLFromBeforeProcessors:
-                                       new_processors.insert (after_amp, _monitor_send);
-                                       break;
-                               case AFLFromAfterProcessors:
-                                       new_processors.insert (new_processors.end(), _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (true);
+                       case PFLFromAfterProcessors:
+                               new_processors.insert (amp, _monitor_send);
                                break;
                        }
                                break;
                        }
-               }  else {
-                       new_processors.insert (new_processors.end(), _monitor_send);
                        _monitor_send->set_can_pan (false);
                        _monitor_send->set_can_pan (false);
+                       break;
+               case AfterFaderListen:
+                       switch (Config->get_afl_position ()) {
+                       case AFLFromBeforeProcessors:
+                               new_processors.insert (after_amp, _monitor_send);
+                               break;
+                       case AFLFromAfterProcessors:
+                               new_processors.insert (new_processors.end(), _monitor_send);
+                               break;
+                       }
+                       _monitor_send->set_can_pan (true);
+                       break;
                }
        }
 
                }
        }
 
@@ -4473,7 +4862,7 @@ Route::setup_invisible_processors ()
 
        if (_monitor_control && is_monitor ()) {
                assert (!_monitor_control->display_to_user ());
 
        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 */
        }
 
        /* INTERNAL RETURN */
@@ -4500,6 +4889,12 @@ Route::setup_invisible_processors ()
 
        _processors = new_processors;
 
 
        _processors = new_processors;
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
+                       (*i)->activate ();
+               }
+       }
+
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
@@ -4535,20 +4930,34 @@ Route::maybe_note_meter_position ()
        if (_meter_point != MeterCustom) {
                return;
        }
        if (_meter_point != MeterCustom) {
                return;
        }
-       
+
        _custom_meter_position_noted = true;
        _custom_meter_position_noted = true;
+       /* custom meter points range from after trim to before panner/main_outs
+        * this is a limitation by the current processor UI
+        */
+       bool seen_trim = false;
+       _processor_after_last_custom_meter.reset();
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i) == _trim) {
+                       seen_trim = true;
+               }
+               if ((*i) == _main_outs) {
+                       _processor_after_last_custom_meter = *i;
+                       break;
+               }
                if (boost::dynamic_pointer_cast<PeakMeter> (*i)) {
                if (boost::dynamic_pointer_cast<PeakMeter> (*i)) {
-                       ProcessorList::iterator j = i;
-                       ++j;
-                       if (j != _processors.end ()) {
-                               _processor_after_last_custom_meter = *j;
-                               _last_custom_meter_was_at_end = false;
+                       if (!seen_trim) {
+                               _processor_after_last_custom_meter = _trim;
                        } else {
                        } else {
-                               _last_custom_meter_was_at_end = true;
+                               ProcessorList::iterator j = i;
+                               ++j;
+                               assert(j != _processors.end ()); // main_outs should be before
+                               _processor_after_last_custom_meter = *j;
                        }
                        }
+                       break;
                }
        }
                }
        }
+       assert(_processor_after_last_custom_meter.lock());
 }
 
 boost::shared_ptr<Processor>
 }
 
 boost::shared_ptr<Processor>
@@ -4590,7 +4999,7 @@ Route::has_external_redirects () const
                /* ignore inactive processors and obviously ignore the main
                 * outs since everything has them and we don't care.
                 */
                /* ignore inactive processors and obviously ignore the main
                 * outs since everything has them and we don't care.
                 */
-                
+
                if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
                        return true;;
                }
                if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
                        return true;;
                }
@@ -4636,11 +5045,12 @@ Route::non_realtime_locate (framepos_t pos)
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
                }
        }
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
                }
        }
+       _roll_delay = _initial_delay;
 }
 
 void
 }
 
 void
@@ -4648,9 +5058,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 {
        size_t n_buffers;
        size_t i;
 {
        size_t n_buffers;
        size_t i;
-       
-       /* MIDI 
-        *  
+
+       /* MIDI
+        *
         * We don't currently mix MIDI input together, so we don't need the
         * complex logic of the audio case.
         */
         * We don't currently mix MIDI input together, so we don't need the
         * complex logic of the audio case.
         */
@@ -4661,7 +5071,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 
                boost::shared_ptr<MidiPort> source_port = io->midi (i);
                MidiBuffer& buf (bufs.get_midi (i));
 
                boost::shared_ptr<MidiPort> source_port = io->midi (i);
                MidiBuffer& buf (bufs.get_midi (i));
-               
+
                if (source_port) {
                        buf.copy (source_port->get_midi_buffer(nframes));
                } else {
                if (source_port) {
                        buf.copy (source_port->get_midi_buffer(nframes));
                } else {
@@ -4679,19 +5089,19 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
        if (n_ports > n_buffers) {
                scaling = ((float) n_buffers) / n_ports;
        }
        if (n_ports > n_buffers) {
                scaling = ((float) n_buffers) / n_ports;
        }
-       
+
        for (i = 0; i < n_ports; ++i) {
        for (i = 0; i < n_ports; ++i) {
-               
+
                /* if there are more ports than buffers, map them onto buffers
                 * in a round-robin fashion
                 */
 
                boost::shared_ptr<AudioPort> source_port = io->audio (i);
                AudioBuffer& buf (bufs.get_audio (i%n_buffers));
                /* if there are more ports than buffers, map them onto buffers
                 * in a round-robin fashion
                 */
 
                boost::shared_ptr<AudioPort> source_port = io->audio (i);
                AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-                       
+
 
                if (i < n_buffers) {
 
                if (i < n_buffers) {
-                       
+
                        /* first time through just copy a channel into
                           the output buffer.
                        */
                        /* first time through just copy a channel into
                           the output buffer.
                        */
@@ -4701,11 +5111,11 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                        if (scaling != 1.0f) {
                                buf.apply_gain (scaling, nframes);
                        }
                        if (scaling != 1.0f) {
                                buf.apply_gain (scaling, nframes);
                        }
-                       
+
                } else {
                } else {
-                       
+
                        /* on subsequent times around, merge data from
                        /* on subsequent times around, merge data from
-                        * the port with what is already there 
+                        * the port with what is already there
                         */
 
                        if (scaling != 1.0f) {
                         */
 
                        if (scaling != 1.0f) {
@@ -4732,3 +5142,452 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                bufs.set_count (io->n_ports());
        }
 }
                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
+}