add virtual Delivery::pan_outs() so that internal sends correctly configure their...
[ardour.git] / libs / ardour / route.cc
index 1d8f3ca18e671611d9cfa33cccd2c067c8c95372..8d1c1c8397764513d28f67def554df53b1090c51 100644 (file)
 
 */
 
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
 #include <cmath>
 #include <fstream>
 #include <cassert>
@@ -27,6 +31,7 @@
 #include "pbd/memento_command.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
+#include "pbd/boost_debug.h"
 
 #include "evoral/Curve.hpp"
 
@@ -46,7 +51,9 @@
 #include "ardour/meter.h"
 #include "ardour/mix.h"
 #include "ardour/monitor_processor.h"
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
@@ -59,6 +66,8 @@
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
 #include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
+#include "ardour/capturing_processor.h"
 
 #include "i18n.h"
 
@@ -74,29 +83,27 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , Automatable (sess)
        , GraphNode( sess.route_graph )
-        , _active (true)
-        , _initial_delay (0)
-        , _roll_delay (0)
+       , _active (true)
+       , _signal_latency (0)
+       , _initial_delay (0)
+       , _roll_delay (0)
        , _flags (flg)
-        , _pending_declick (true)
-        , _meter_point (MeterPostFader)
-        , _phase_invert (0)
-        , _self_solo (false)
-        , _soloed_by_others_upstream (0)
-        , _soloed_by_others_downstream (0)
-        , _solo_isolated (0)
-        , _denormal_protection (false)
-        , _recordable (true)
-        , _silent (false)
-        , _declickable (false)
-       , _solo_control (new SoloControllable (X_("solo"), *this))
-       , _mute_control (new MuteControllable (X_("mute"), *this))
+       , _pending_declick (true)
+       , _meter_point (MeterPostFader)
+       , _self_solo (false)
+       , _soloed_by_others_upstream (0)
+       , _soloed_by_others_downstream (0)
+       , _solo_isolated (0)
+       , _denormal_protection (false)
+       , _recordable (true)
+       , _silent (false)
+       , _declickable (false)
        , _mute_master (new MuteMaster (sess, name))
-        , _have_internal_generator (false)
-        , _solo_safe (false)
+       , _have_internal_generator (false)
+       , _solo_safe (false)
        , _default_type (default_type)
-        , _remote_control_id (0)
-        , _in_configure_processors (false)
+       , _remote_control_id (0)
+       , _in_configure_processors (false)
 {
        processor_max_streams.reset();
        order_keys[N_("signal")] = order_key_cnt++;
@@ -107,84 +114,89 @@ Route::init ()
 {
        /* add standard controls */
 
+       _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
+       _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
+
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
 
        add_control (_solo_control);
        add_control (_mute_control);
 
+       /* panning */
+
+       if (!(_flags & Route::MonitorOut)) {
+               Pannable* p = new Pannable (_session);
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+               boost_debug_shared_ptr_mark_interesting (p, "Pannable");
+#endif
+               _pannable.reset (p);
+       }
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
        _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
        _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
-       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+       _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
 
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
-       /* add standard processors: meter, main outs, monitor out */
+       /* create standard processors: meter, main outs, monitor out;
+          they will be added to _processors by setup_invisible_processors ()
+       */
 
        _meter.reset (new PeakMeter (_session));
        _meter->set_display_to_user (false);
+       _meter->activate ();
 
-       add_processor (_meter, PostFader);
-
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-
-        add_processor (_main_outs, PostFader);
+       _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
+       _main_outs->activate ();
 
        if (is_monitor()) {
                /* where we listen to tracks */
                _intreturn.reset (new InternalReturn (_session));
-               add_processor (_intreturn, PreFader);
-
-                ProcessorList::iterator i;
+               _intreturn->activate ();
 
-                for (i = _processors.begin(); i != _processors.end(); ++i) {
-                        if (*i == _intreturn) {
-                                ++i;
-                                break;
-                        }
-                }
-
-                /* the thing that provides proper control over a control/monitor/listen bus 
-                   (such as per-channel cut, dim, solo, invert, etc).
-                   It always goes right after the internal return;
-                 */
-                _monitor_control.reset (new MonitorProcessor (_session));
-                add_processor (_monitor_control, i);
-
-                /* no panning on the monitor main outs */
-
-                _main_outs->panner()->set_bypassed (true);
+               /* the thing that provides proper control over a control/monitor/listen bus
+                  (such as per-channel cut, dim, solo, invert, etc).
+               */
+               _monitor_control.reset (new MonitorProcessor (_session));
+               _monitor_control->activate ();
        }
 
-        if (is_master() || is_monitor() || is_hidden()) {
-                _mute_master->set_solo_ignore (true);
-        }
+       if (is_master() || is_monitor() || is_hidden()) {
+               _mute_master->set_solo_ignore (true);
+       }
 
        /* now that we have _meter, its safe to connect to this */
 
        Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 
-        return 0;
+       {
+               /* run a configure so that the invisible processors get set up */
+               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               configure_processors (0);
+       }
+
+       return 0;
 }
 
 Route::~Route ()
 {
        DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
 
-       /* do this early so that we don't get incoming signals as we are going through destruction 
+       /* do this early so that we don't get incoming signals as we are going through destruction
         */
 
        drop_connections ();
 
        /* don't use clear_processors here, as it depends on the session which may
-          be half-destroyed by now 
+          be half-destroyed by now
        */
 
        Glib::RWLock::WriterLock lm (_processor_lock);
@@ -281,7 +293,7 @@ Route::sync_order_keys (std::string const & base)
        }
 
        bool changed = false;
-       
+
        for (; i != order_keys.end(); ++i) {
                if (i->second != key) {
                        i->second = key;
@@ -351,11 +363,11 @@ Route::set_gain (gain_t val, void *src)
                                }
                        }
 
-                       _route_group->apply (&Route::inc_gain, factor, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
 
                } else {
 
-                       _route_group->apply (&Route::set_gain, val, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
                }
 
                return;
@@ -368,6 +380,14 @@ Route::set_gain (gain_t val, void *src)
        _amp->set_gain (val, src);
 }
 
+void
+Route::maybe_declick (BufferSet&, framecnt_t, int)
+{
+       /* this is the "bus" implementation and they never declick.
+        */
+       return;
+}
+
 /** Process this route for one (sub) cycle (process thread)
  *
  * @param bufs Scratch buffers to use for the signal path
@@ -380,29 +400,20 @@ Route::set_gain (gain_t val, void *src)
  */
 void
 Route::process_output_buffers (BufferSet& bufs,
-                              sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
-                              bool /*with_processors*/, int declick)
+                              framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+                              bool /*with_processors*/, int declick,
+                               bool gain_automation_ok)
 {
-       bool monitor;
+       bool monitor = should_monitor ();
 
        bufs.is_silent (false);
 
-       switch (Config->get_monitoring_model()) {
-       case HardwareMonitoring:
-       case ExternalMonitoring:
-               monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
-               break;
-       default:
-               monitor = true;
-       }
-
-       if (!declick) {
-               declick = _pending_declick;
-       }
-
        /* figure out if we're going to use gain automation */
-       _amp->setup_gain_automation (start_frame, end_frame, nframes);
-
+       if (gain_automation_ok) {
+               _amp->setup_gain_automation (start_frame, end_frame, nframes);
+       } else {
+               _amp->apply_gain_automation (false);
+       }
 
        /* tell main outs what to do about monitoring */
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
@@ -412,19 +423,14 @@ Route::process_output_buffers (BufferSet& bufs,
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
 
-       if (declick > 0) {
-               Amp::declick (bufs, nframes, 1);
-       } else if (declick < 0) {
-               Amp::declick (bufs, nframes, -1);
-       }
-
+       maybe_declick (bufs, nframes, declick);
        _pending_declick = 0;
 
        /* -------------------------------------------------------------------------------------------
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (_phase_invert) {
+       if (_phase_invert.any ()) {
 
                int chn = 0;
 
@@ -433,13 +439,13 @@ Route::process_output_buffers (BufferSet& bufs,
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
 
-                               if (_phase_invert & chn) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
                                        }
                                } else {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] += 1.0e-27f;
                                        }
                                }
@@ -450,8 +456,8 @@ Route::process_output_buffers (BufferSet& bufs,
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
                                Sample* const sp = i->data();
 
-                               if (_phase_invert & (1<<chn)) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
                                }
@@ -464,7 +470,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
                                Sample* const sp = i->data();
-                               for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               for (pframes_t nx = 0; nx < nframes; ++nx) {
                                        sp[nx] += 1.0e-27f;
                                }
                        }
@@ -478,13 +484,25 @@ Route::process_output_buffers (BufferSet& bufs,
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-               if (bufs.count() != (*i)->input_streams()) {
-                       cerr << _name << " bufs = " << bufs.count()
-                            << " input for " << (*i)->name() << " = " << (*i)->input_streams()
-                            << endl;
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
+                       break;
+               }
+
+#ifndef NDEBUG
+               /* if it has any inputs, make sure they match */
+               if ((*i)->input_streams() != ChanCount::ZERO) {
+                       if (bufs.count() != (*i)->input_streams()) {
+                               cerr << _name << " bufs = " << bufs.count()
+                                    << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+                                    << endl;
+                               abort ();
+                       }
                }
-               assert (bufs.count() == (*i)->input_streams());
-               
+#endif
+               /* should we NOT run plugins here if the route is inactive?
+                  do we catch route != active somewhere higher?
+               */
+
                (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
                bufs.set_count ((*i)->output_streams());
        }
@@ -497,7 +515,7 @@ Route::n_process_buffers ()
 }
 
 void
-Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
@@ -533,33 +551,38 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        }
 
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
 }
 
 void
-Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
        bufs.set_count (_input->n_ports());
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
 }
 
 void
 Route::set_listen (bool yn, void* src)
 {
-        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));
+               return;
+       }
 
        if (_monitor_send) {
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
-                                _mute_master->set_soloed (true);
-                        } else {
+                               _mute_master->set_soloed (true);
+                       } else {
                                _monitor_send->deactivate ();
-                                _mute_master->set_soloed (false);
+                               _mute_master->set_soloed (false);
                        }
 
                        listen_changed (src); /* EMIT SIGNAL */
@@ -568,7 +591,7 @@ Route::set_listen (bool yn, void* src)
 }
 
 bool
-Route::listening () const
+Route::listening_via_monitor () const
 {
        if (_monitor_send) {
                return _monitor_send->active ();
@@ -583,7 +606,7 @@ Route::set_solo_safe (bool yn, void *src)
        if (_solo_safe != yn) {
                _solo_safe = yn;
                solo_safe_changed (src);
-       } 
+       }
 }
 
 bool
@@ -600,13 +623,13 @@ Route::set_solo (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
                return;
        }
 
        if (self_soloed() != yn) {
                set_self_solo (yn);
-                set_mute_master_solo ();
+               set_mute_master_solo ();
                solo_changed (true, src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
@@ -615,17 +638,17 @@ Route::set_solo (bool yn, void *src)
 void
 Route::set_self_solo (bool yn)
 {
-        _self_solo = yn;
+       _self_solo = yn;
 }
 
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
-        if (_solo_safe) {
-                return;
-        }
+       if (_solo_safe) {
+               return;
+       }
 
-        uint32_t old_sbu = _soloed_by_others_upstream;
+       uint32_t old_sbu = _soloed_by_others_upstream;
 
        if (delta < 0) {
                if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
@@ -637,50 +660,51 @@ Route::mod_solo_by_others_upstream (int32_t delta)
                _soloed_by_others_upstream += delta;
        }
 
-        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
-                                                  name(), delta, _soloed_by_others_upstream, old_sbu, 
-                                                  _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
-
-        /* push the inverse solo change to everything that feeds us. 
-           
-           This is important for solo-within-group. When we solo 1 track out of N that
-           feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
-           on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
-           tracks that feed it. This will silence them if they were audible because
-           of a bus solo, but the newly soloed track will still be audible (because 
-           it is self-soloed).
-           
-           but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
-           not in reverse.
-         */
-
-        if ((_self_solo || _soloed_by_others_downstream) &&
-            ((old_sbu == 0 && _soloed_by_others_upstream > 0) || 
-             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
-                
-                if (delta > 0 || !Config->get_exclusive_solo()) {
-                        DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
-                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
-                                boost::shared_ptr<Route> sr = i->r.lock();
-                                if (sr) {
-                                        sr->mod_solo_by_others_downstream (-delta);
-                                }
-                        }
-                } 
-        }
-
-        set_mute_master_solo ();
-        solo_changed (false, this);
+       DEBUG_TRACE (DEBUG::Solo, string_compose (
+                            "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
+                            name(), delta, _soloed_by_others_upstream, old_sbu,
+                            _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
+
+       /* push the inverse solo change to everything that feeds us.
+
+          This is important for solo-within-group. When we solo 1 track out of N that
+          feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
+          on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
+          tracks that feed it. This will silence them if they were audible because
+          of a bus solo, but the newly soloed track will still be audible (because
+          it is self-soloed).
+
+          but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
+          not in reverse.
+       */
+
+       if ((_self_solo || _soloed_by_others_downstream) &&
+           ((old_sbu == 0 && _soloed_by_others_upstream > 0) ||
+            (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
+
+               if (delta > 0 || !Config->get_exclusive_solo()) {
+                       DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                       for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                               boost::shared_ptr<Route> sr = i->r.lock();
+                               if (sr) {
+                                       sr->mod_solo_by_others_downstream (-delta);
+                               }
+                       }
+               }
+       }
+
+       set_mute_master_solo ();
+       solo_changed (false, this);
 }
 
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
-        if (_solo_safe) {
-                return;
-        }
+       if (_solo_safe) {
+               return;
+       }
 
-        if (delta < 0) {
+       if (delta < 0) {
                if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
                        _soloed_by_others_downstream += delta;
                } else {
@@ -690,16 +714,16 @@ Route::mod_solo_by_others_downstream (int32_t delta)
                _soloed_by_others_downstream += delta;
        }
 
-        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
+       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);
+       set_mute_master_solo ();
+       solo_changed (false, this);
 }
 
 void
 Route::set_mute_master_solo ()
 {
-        _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
+       _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
 }
 
 void
@@ -710,50 +734,50 @@ Route::set_solo_isolated (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
                return;
        }
-       
+
        /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
        boost::shared_ptr<RouteList> routes = _session.get_routes ();
        for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
 
                if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
-                        continue;
-                }
+                       continue;
+               }
 
                bool sends_only;
                bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
-               
+
                if (does_feed && !sends_only) {
                        (*i)->set_solo_isolated (yn, (*i)->route_group());
                }
        }
 
-        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
+       /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-        bool changed = false;
+       bool changed = false;
 
        if (yn) {
-                if (_solo_isolated == 0) {
-                        _mute_master->set_solo_ignore (true);
-                        changed = true;
-                }
+               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 (_solo_isolated == 0) {
+                               _mute_master->set_solo_ignore (false);
+                               changed = true;
+                       }
                }
        }
 
-        if (changed) {
-                solo_isolated_changed (src);
-        }
+       if (changed) {
+               solo_isolated_changed (src);
+       }
 }
 
 bool
@@ -765,32 +789,34 @@ Route::solo_isolated () const
 void
 Route::set_mute_points (MuteMaster::MutePoint mp)
 {
-        _mute_master->set_mute_points (mp);
-        mute_points_changed (); /* EMIT SIGNAL */
-        
-        if (_mute_master->muted_by_self()) {
-                mute_changed (this); /* EMIT SIGNAL */
-        }
+       _mute_master->set_mute_points (mp);
+       mute_points_changed (); /* EMIT SIGNAL */
+
+       if (_mute_master->muted_by_self()) {
+               mute_changed (this); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
+       }
 }
 
 void
 Route::set_mute (bool yn, void *src)
 {
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
-               _route_group->apply (&Route::set_mute, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
                return;
        }
 
        if (muted() != yn) {
-                _mute_master->set_muted_by_self (yn);
+               _mute_master->set_muted_by_self (yn);
                mute_changed (src); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
 bool
 Route::muted () const
 {
-        return _mute_master->muted_by_self();
+       return _mute_master->muted_by_self();
 }
 
 #if 0
@@ -800,14 +826,14 @@ dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& p
        cerr << name << " {" << endl;
        for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
                        p != procs.end(); ++p) {
-               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << " @ " << (*p) << endl;
        }
        cerr << "}" << endl;
 }
 #endif
 
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
 {
        ProcessorList::iterator loc;
 
@@ -823,19 +849,21 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
                loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
-       return add_processor (processor, loc, err);
+       return add_processor (processor, loc, err, activation_allowed);
 }
 
 
 /** Add a processor to the route.
- * @a iter must point to an iterator in _processors and the new
- * processor will be inserted immediately before this location.  Otherwise,
- * @a position is used.
+ *  @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
  */
 int
 Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
 {
-       ChanCount old_pms = processor_max_streams;
+       assert (processor != _meter);
+       assert (processor != _main_outs);
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose (
+                            "%1 adding processor %2\n", name(), processor->name()));
 
        if (!_session.engine().connected() || !processor) {
                return 1;
@@ -843,14 +871,15 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
 
                boost::shared_ptr<PluginInsert> pi;
                boost::shared_ptr<PortInsert> porti;
 
                ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
 
-               if (processor == _amp || processor == _meter || processor == _main_outs) {
-                       // Ensure only one of these are in the list at any time
+               if (processor == _amp) {
+                       // Ensure only one amp is in the list at any time
                        if (loc != _processors.end()) {
                                if (iter == loc) { // Already in place, do nothing
                                        return 0;
@@ -873,37 +902,30 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
 
-               if (configure_processors_unlocked (err)) {
-                       ProcessorList::iterator ploc = loc;
-                       --ploc;
-                       _processors.erase(ploc);
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       cerr << "configure failed\n";
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return -1;
+                       }
                }
 
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
 
-                       if (pi->natural_input_streams() == ChanCount::ZERO) {
+                       if (pi->has_no_inputs ()) {
                                /* generator plugin */
                                _have_internal_generator = true;
                        }
 
                }
 
-                /* is this the monitor send ? if so, make sure we keep track of it */
-
-                boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
-
-                if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) {
-                        _monitor_send = isend;
-                }
-
-               if (activation_allowed && (processor != _monitor_send)) {
+               if (activation_allowed) {
                        processor->activate ();
                }
 
-               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
 
                _output->set_user_latency (0);
        }
@@ -915,18 +937,37 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 }
 
 bool
-Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
+Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
        const XMLProperty *prop;
 
        try {
                boost::shared_ptr<Processor> processor;
 
+               /* bit of a hack: get the `placement' property from the <Redirect> tag here
+                  so that we can add the processor in the right place (pre/post-fader)
+               */
+
+               XMLNodeList const & children = node.children ();
+               XMLNodeList::const_iterator i = children.begin ();
+
+               while (i != children.end() && (*i)->name() != X_("Redirect")) {
+                       ++i;
+               }
+
+               Placement placement = PreFader;
+
+               if (i != children.end()) {
+                       if ((prop = (*i)->property (X_("placement"))) != 0) {
+                               placement = Placement (string_2_enum (prop->value(), placement));
+                       }
+               }
+
                if (node.name() == "Insert") {
 
                        if ((prop = node.property ("type")) != 0) {
 
-                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
+                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                                prop->value() == "lv2" ||
                                                prop->value() == "vst" ||
                                                prop->value() == "audiounit") {
@@ -935,14 +976,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
 
                                } else {
 
-                                       processor.reset (new PortInsert (_session, _mute_master));
+                                       processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                }
 
                        }
 
                } else if (node.name() == "Send") {
 
-                       processor.reset (new Send (_session, _mute_master));
+                       processor.reset (new Send (_session, _pannable, _mute_master));
 
                } else {
 
@@ -950,23 +991,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
                        return false;
                }
 
-                if (processor->set_state (node, version)) {
-                        return false;
-                }
-
-               if (iter == _processors.end() && processor->display_to_user() && !_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       while (!(*p)->display_to_user() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       iter = p;
+               if (processor->set_state (node, version)) {
+                       return false;
                }
 
-               return (add_processor (processor, iter) == 0);
+               return (add_processor (processor, placement) == 0);
        }
 
        catch (failed_constructor &err) {
@@ -978,28 +1007,20 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
 int
 Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
+       /* NOTE: this is intended to be used ONLY when copying
+          processors from another Route. Hence the subtle
+          differences between this and ::add_processor()
+       */
+
        ProcessorList::iterator loc;
 
        if (before) {
                loc = find(_processors.begin(), _processors.end(), before);
        } else {
-               /* nothing specified - at end but before main outs */
-               loc = find (_processors.begin(), _processors.end(), _main_outs);
+               /* nothing specified - at end */
+               loc = _processors.end ();
        }
 
-       return add_processors (others, loc, err);
-}
-
-int
-Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
-{
-       /* NOTE: this is intended to be used ONLY when copying
-          processors from another Route. Hence the subtle
-          differences between this and ::add_processor()
-       */
-
-       ChanCount old_pms = processor_max_streams;
-
        if (!_session.engine().connected()) {
                return 1;
        }
@@ -1010,44 +1031,47 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-
-               ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
+               ProcessorState pstate (this);
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
 
-                       // Ensure meter only appears in the list once
                        if (*i == _meter) {
-                               ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
-                               if (m != _processors.end()) {
-                                       _processors.erase(m);
-                               }
+                               continue;
                        }
 
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
-
-                               ChanCount m = max (pi->input_streams(), pi->output_streams());
-
-                               if (m > potential_max_streams) {
-                                       potential_max_streams = m;
-                               }
                        }
 
-                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+                       _processors.insert (loc, *i);
 
                        if ((*i)->active()) {
                                (*i)->activate ();
                        }
 
-                       if (configure_processors_unlocked (err)) {
-                               _processors.erase (inserted);
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (configure_processors_unlocked (err)) {
+                                       pstate.restore ();
+                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       return -1;
+                               }
                        }
 
-                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+               }
+
+               for (ProcessorList::const_iterator 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;
+                               }
+                       }
                }
 
                _output->set_user_latency (0);
@@ -1193,8 +1217,6 @@ Route::ab_plugins (bool forward)
 void
 Route::clear_processors (Placement p)
 {
-       const ChanCount old_pms = processor_max_streams;
-
        if (!_session.engine().connected()) {
                return;
        }
@@ -1249,7 +1271,11 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (&err); // this can't fail
+               }
        }
 
        processor_max_streams.reset();
@@ -1271,8 +1297,6 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                return 0;
        }
 
-       ChanCount old_pms = processor_max_streams;
-
        if (!_session.engine().connected()) {
                return 1;
        }
@@ -1281,6 +1305,8 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
                ProcessorList::iterator i;
                bool removed = false;
 
@@ -1323,12 +1349,15 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
@@ -1337,7 +1366,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               if (pi->is_generator()) {
+                               if (pi->has_no_inputs ()) {
                                        _have_internal_generator = true;
                                        break;
                                }
@@ -1365,11 +1394,11 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+
                ProcessorList::iterator i;
                boost::shared_ptr<Processor> processor;
 
-               ProcessorList as_we_were = _processors;
-
                for (i = _processors.begin(); i != _processors.end(); ) {
 
                        processor = *i;
@@ -1410,12 +1439,15 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _output->set_user_latency (0);
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors = as_we_were;
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
@@ -1424,7 +1456,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               if (pi->is_generator()) {
+                               if (pi->has_no_inputs ()) {
                                        _have_internal_generator = true;
                                        break;
                                }
@@ -1444,52 +1476,54 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
        return 0;
 }
 
-
+/** Caller must hold process lock */
 int
 Route::configure_processors (ProcessorStreams* err)
 {
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
        if (!_in_configure_processors) {
                Glib::RWLock::WriterLock lm (_processor_lock);
                return configure_processors_unlocked (err);
        }
+
        return 0;
 }
 
 ChanCount
 Route::input_streams () const
 {
-        return _input->n_ports ();
+       return _input->n_ports ();
 }
 
-/** Configure the input/output configuration of each processor in the processors list.
- * Return 0 on success, otherwise configuration is impossible.
- */
-int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
 {
-       if (_in_configure_processors) {
-          return 0;
-       }
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       _in_configure_processors = true;
+       return try_configure_processors_unlocked (in, err);
+}
 
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
        // Check each processor in order to see if we can configure as requested
-       ChanCount in = input_streams ();
        ChanCount out;
-       list< pair<ChanCount,ChanCount> > configuration;
+       list<pair<ChanCount, ChanCount> > configuration;
        uint32_t index = 0;
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
        DEBUG_TRACE (DEBUG::Processors, "{\n");
-       for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
-               DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
-       }
-       DEBUG_TRACE (DEBUG::Processors, "}\n");
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+                       break;
+               }
+
                if ((*p)->can_support_io_configuration(in, out)) {
-                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 in = %2 out = %3\n",(*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1497,14 +1531,52 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                                err->index = index;
                                err->count = in;
                        }
-                       _in_configure_processors = false;
-                       return -1;
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, "}\n");
+                       return list<pair<ChanCount, ChanCount> > ();
                }
        }
 
-       // We can, so configure everything
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+
+       return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ *  Caller must hold process lock.
+ *  Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       if (_in_configure_processors) {
+               return 0;
+       }
+
+       /* put invisible processors where they should be */
+       setup_invisible_processors ();
+
+       _in_configure_processors = true;
+
+       list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+       if (configuration.empty ()) {
+               _in_configure_processors = false;
+               return -1;
+       }
+
+       ChanCount out;
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       break;
+               }
+
                (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
@@ -1516,11 +1588,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
-          configuration */
-       {
-               Glib::Mutex::Lock em (_session.engine().process_lock ());
-               _session.ensure_buffers (n_process_buffers ());
-       }
+          configuration 
+       */
+       _session.ensure_buffers (n_process_buffers ());
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
 
@@ -1619,10 +1689,10 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ChanCount old_pms = processor_max_streams;
+               ProcessorState pstate (this);
+
                ProcessorList::iterator oiter;
                ProcessorList::const_iterator niter;
-               ProcessorList as_it_was_before = _processors;
                ProcessorList as_it_will_be;
 
                oiter = _processors.begin();
@@ -1668,7 +1738,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                                        }
                                }
 
-                                /* now remove from old order - its taken care of no matter what */
+                               /* now remove from old order - its taken care of no matter what */
                                oiter = _processors.erase (oiter);
                        }
 
@@ -1676,17 +1746,18 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
 
-               if (configure_processors_unlocked (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_streams = old_pms;
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               return -1;
+                       }
                }
        }
 
-        if (true) {
-                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
-               set_processor_positions ();
-        }
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -1720,7 +1791,9 @@ Route::state(bool full_state)
        }
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("phase-invert", _phase_invert?"yes":"no");
+       string p;
+       boost::to_string (_phase_invert, p);
+       node->add_property("phase-invert", p);
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
@@ -1751,10 +1824,13 @@ Route::state(bool full_state)
        node->add_property ("soloed-by-upstream", buf);
        snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
        node->add_property ("soloed-by-downstream", buf);
+       node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
+       node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
+       node->add_child_nocopy (_mute_control->get_state ());
        node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
@@ -1767,6 +1843,10 @@ Route::state(bool full_state)
                cmt->add_content (_comment);
        }
 
+       if (_pannable) {
+               node->add_child_nocopy (_pannable->state (full_state));
+       }
+
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                node->add_child_nocopy((*i)->state (full_state));
        }
@@ -1794,7 +1874,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       XMLPropertyList plist;
        const XMLProperty *prop;
 
        if (node.name() != "Route"){
@@ -1816,15 +1895,25 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                _flags = Flag (0);
        }
 
-        if (is_master() || is_monitor() || is_hidden()) {
-                _mute_master->set_solo_ignore (true);
-        }
+       if (is_master() || is_monitor() || is_hidden()) {
+               _mute_master->set_solo_ignore (true);
+       }
+
+       if (is_monitor()) {
+               /* monitor bus does not get a panner, but if (re)created
+                  via XML, it will already have one by the time we
+                  call ::set_state(). so ... remove it.
+               */
+               unpan ();
+       }
 
        /* add all processors (except amp, which is always present) */
 
        nlist = node.children();
        XMLNode processor_state (X_("processor_state"));
 
+       Stateful::save_extra_xml (node);
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
 
                child = *niter;
@@ -1844,6 +1933,15 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
                }
+
+
+               if (child->name() == X_("Pannable")) {
+                       if (_pannable) {
+                               _pannable->set_state (*child, version);
+                       } else {
+                               warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg;
+                       }
+               }
        }
 
        set_processor_state (processor_state);
@@ -1866,8 +1964,12 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                set_solo_isolated (string_is_affirmative (prop->value()), this);
        }
 
+       if ((prop = node.property ("solo-safe")) != 0) {
+               set_solo_safe (string_is_affirmative (prop->value()), this);
+       }
+
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -1877,12 +1979,12 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
        if ((prop = node.property (X_("active"))) != 0) {
                bool yn = string_is_affirmative (prop->value());
                _active = !yn; // force switch
-               set_active (yn);
+               set_active (yn, this);
        }
 
        if ((prop = node.property (X_("meter-point"))) != 0) {
                MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
-                set_meter_point (mp);
+               set_meter_point (mp, true);
                if (_meter) {
                        _meter->set_display_to_user (_meter_point == MeterCustom);
                }
@@ -1929,15 +2031,9 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
-
-                       _extra_xml = new XMLNode (*child);
-
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
                        if (prop->value() == "solo") {
                                _solo_control->set_state (*child, version);
-                               _session.add_controllable (_solo_control);
                        }
 
                } else if (child->name() == X_("RemoteControl")) {
@@ -1961,7 +2057,6 @@ Route::_set_state_2X (const XMLNode& node, int version)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       XMLPropertyList plist;
        const XMLProperty *prop;
 
        /* 2X things which still remain to be handled:
@@ -1980,9 +2075,13 @@ Route::_set_state_2X (const XMLNode& node, int version)
        } else {
                _flags = Flag (0);
        }
-       
+
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
+               if (string_is_affirmative (prop->value ())) {
+                       p.set ();
+               }
+               set_phase_invert (p);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -1998,61 +2097,62 @@ Route::_set_state_2X (const XMLNode& node, int version)
        }
 
        if ((prop = node.property (X_("muted"))) != 0) {
-               
+
                bool first = true;
                bool muted = string_is_affirmative (prop->value());
-               
-               if (muted){
-                 
+
+               if (muted) {
+
                        string mute_point;
-                       
+
                        if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
-                         
+
                                if (string_is_affirmative (prop->value())){
                                        mute_point = mute_point + "PreFader";
                                        first = false;
                                }
                        }
-                       
+
                        if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
-                         
+
                                if (string_is_affirmative (prop->value())){
-                                 
+
                                        if (!first) {
                                                mute_point = mute_point + ",";
                                        }
-                                       
+
                                        mute_point = mute_point + "PostFader";
                                        first = false;
                                }
                        }
 
                        if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
-                         
+
                                if (string_is_affirmative (prop->value())){
-                                 
+
                                        if (!first) {
                                                mute_point = mute_point + ",";
                                        }
-                                       
+
                                        mute_point = mute_point + "Listen";
                                        first = false;
                                }
                        }
 
                        if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
-                         
+
                                if (string_is_affirmative (prop->value())){
-                                 
+
                                        if (!first) {
                                                mute_point = mute_point + ",";
                                        }
-                                       
+
                                        mute_point = mute_point + "Main";
                                }
                        }
-                       
+
                        _mute_master->set_mute_points (mute_point);
+                       _mute_master->set_muted_by_self (true);
                }
        }
 
@@ -2095,24 +2195,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                }
        }
 
-       /* add standard processors */
-
-       //_meter.reset (new PeakMeter (_session));
-       //add_processor (_meter, PreFader);
-
-       if (is_monitor()) {
-               /* where we listen to tracks */
-               _intreturn.reset (new InternalReturn (_session));
-               add_processor (_intreturn, PreFader);
-
-                _monitor_control.reset (new MonitorProcessor (_session));
-                add_processor (_monitor_control, PostFader);
-       }
-
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-       add_processor (_main_outs, PostFader);
-
-       /* IOs */
+       /* IOs */
 
        nlist = node.children ();
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
@@ -2139,9 +2222,9 @@ Route::_set_state_2X (const XMLNode& node, int version)
                        if ((prop = child->property (X_("active"))) != 0) {
                                bool yn = string_is_affirmative (prop->value());
                                _active = !yn; // force switch
-                               set_active (yn);
+                               set_active (yn, this);
                        }
-                       
+
                        if ((prop = child->property (X_("gain"))) != 0) {
                                gain_t val;
 
@@ -2149,19 +2232,22 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                        _amp->gain_control()->set_value (val);
                                }
                        }
-                       
+
                        /* Set up Panners in the IO */
                        XMLNodeList io_nlist = child->children ();
-                       
+
                        XMLNodeConstIterator io_niter;
                        XMLNode *io_child;
-                       
+
                        for (io_niter = io_nlist.begin(); io_niter != io_nlist.end(); ++io_niter) {
 
                                io_child = *io_niter;
-                               
+
                                if (io_child->name() == X_("Panner")) {
-                                       _main_outs->panner()->set_state(*io_child, version);
+                                       _main_outs->panner_shell()->set_state(*io_child, version);
+                               } else if (io_child->name() == X_("Automation")) {
+                                       /* IO's automation is for the fader */
+                                       _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
                                }
                        }
                }
@@ -2181,6 +2267,8 @@ Route::_set_state_2X (const XMLNode& node, int version)
 
        set_processor_state_2X (redirect_nodes, version);
 
+       Stateful::save_extra_xml (node);
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -2191,15 +2279,11 @@ Route::_set_state_2X (const XMLNode& node, int version)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
-
-                       _extra_xml = new XMLNode (*child);
-
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
-                       if (prop->value() == "solo") {
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == X_("solo")) {
                                _solo_control->set_state (*child, version);
-                               _session.add_controllable (_solo_control);
+                       } else if (prop->value() == X_("mute")) {
+                               _mute_control->set_state (*child, version);
                        }
 
                } else if (child->name() == X_("RemoteControl")) {
@@ -2209,7 +2293,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                set_remote_control_id (x);
                        }
 
-               } 
+               }
        }
 
        return 0;
@@ -2236,7 +2320,7 @@ Route::set_processor_state_2X (XMLNodeList const & nList, int version)
        */
 
        for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
-               add_processor_from_xml_2X (**i, version, _processors.begin ());
+               add_processor_from_xml_2X (**i, version);
        }
 }
 
@@ -2245,94 +2329,124 @@ Route::set_processor_state (const XMLNode& node)
 {
        const XMLNodeList &nlist = node.children();
        XMLNodeConstIterator niter;
-        ProcessorList new_order;
-        bool must_configure = false;
+       ProcessorList new_order;
+       bool must_configure = false;
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
                XMLProperty* prop = (*niter)->property ("type");
 
                if (prop->value() == "amp") {
-                        _amp->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_amp);
-                } else if (prop->value() == "meter") {
-                        _meter->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_meter);
-                } else if (prop->value() == "main-outs") {
-                        _main_outs->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_main_outs);
-                } else if (prop->value() == "intreturn") {
-                        if (!_intreturn) {
-                                _intreturn.reset (new InternalReturn (_session));
-                                must_configure = true;
-                        }
-                        _intreturn->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_intreturn);
-                } else if (is_monitor() && prop->value() == "monitor") {
-                        if (!_monitor_control) {
-                                _monitor_control.reset (new MonitorProcessor (_session));
-                                must_configure = true;
-                        }
-                        _monitor_control->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_monitor_control);
-                } else {
-                        ProcessorList::iterator o;
+                       _amp->set_state (**niter, Stateful::current_state_version);
+                       new_order.push_back (_amp);
+               } else if (prop->value() == "meter") {
+                       _meter->set_state (**niter, Stateful::current_state_version);
+               } else if (prop->value() == "main-outs") {
+                       _main_outs->set_state (**niter, Stateful::current_state_version);
+               } else if (prop->value() == "intreturn") {
+                       if (!_intreturn) {
+                               _intreturn.reset (new InternalReturn (_session));
+                               must_configure = true;
+                       }
+                       _intreturn->set_state (**niter, Stateful::current_state_version);
+               } else if (is_monitor() && prop->value() == "monitor") {
+                       if (!_monitor_control) {
+                               _monitor_control.reset (new MonitorProcessor (_session));
+                               must_configure = true;
+                       }
+                       _monitor_control->set_state (**niter, Stateful::current_state_version);
+               } else if (prop->value() == "capture") {
+                       _capturing_processor.reset (new CapturingProcessor (_session));
+               } else {
+                       ProcessorList::iterator o;
 
                        for (o = _processors.begin(); o != _processors.end(); ++o) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
-                                        (*o)->set_state (**niter, Stateful::current_state_version);
-                                        new_order.push_back (*o);
+                                       (*o)->set_state (**niter, Stateful::current_state_version);
+                                       new_order.push_back (*o);
                                        break;
                                }
                        }
 
-                        // If the processor (*niter) is not on the route then create it 
-                        
-                        if (o == _processors.end()) {
-                                
-                                boost::shared_ptr<Processor> processor;
-
-                                if (prop->value() == "intsend") {
-                                        
-                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
-                                        
-                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
-                                           prop->value() == "lv2" ||
-                                           prop->value() == "vst" ||
-                                           prop->value() == "audiounit") {
-                                        
-                                        processor.reset (new PluginInsert(_session));
-                                        
-                                } else if (prop->value() == "port") {
-                                        
-                                        processor.reset (new PortInsert (_session, _mute_master));
-                                        
-                                } else if (prop->value() == "send") {
-                                        
-                                        processor.reset (new Send (_session, _mute_master));
-                                        
-                                } else {
-                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
-                                        continue;
-                                }
-
-                                processor->set_state (**niter, Stateful::current_state_version);
-                                new_order.push_back (processor);
-                                must_configure = true;
-                        }
-                }
-        }
-
-        { 
+                       // If the processor (*niter) is not on the route then create it
+
+                       if (o == _processors.end()) {
+
+                               boost::shared_ptr<Processor> processor;
+
+                               if (prop->value() == "intsend") {
+
+                                       processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+
+                               } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+                                          prop->value() == "lv2" ||
+                                          prop->value() == "vst" ||
+                                          prop->value() == "audiounit") {
+
+                                       processor.reset (new PluginInsert(_session));
+
+                               } else if (prop->value() == "port") {
+
+                                       processor.reset (new PortInsert (_session, _pannable, _mute_master));
+
+                               } else if (prop->value() == "send") {
+
+                                       processor.reset (new Send (_session, _pannable, _mute_master));
+
+                               } else {
+                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                                       continue;
+                               }
+
+                               if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+                                       /* This processor could not be configured.  Turn it into a UnknownProcessor */
+                                       processor.reset (new UnknownProcessor (_session, **niter));
+                               }
+
+                               /* we have to note the monitor send here, otherwise a new one will be created
+                                  and the state of this one will be lost.
+                               */
+                               boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+                               if (isend && isend->role() == Delivery::Listen) {
+                                       _monitor_send = isend;
+                               }
+
+                               /* it doesn't matter if invisible processors are added here, as they
+                                  will be sorted out by setup_invisible_processors () shortly.
+                               */
+
+                               new_order.push_back (processor);
+                               must_configure = true;
+                       }
+               }
+       }
+
+       {
                Glib::RWLock::WriterLock lm (_processor_lock);
-                _processors = new_order;
-                if (must_configure) {
-                        configure_processors_unlocked (0);
-                }
-        }
+               _processors = new_order;
+
+               if (must_configure) {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (0);
+               }
+
+               for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-        processors_changed (RouteProcessorChange ());
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+
+                       boost::shared_ptr<PluginInsert> pi;
+
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->has_no_inputs ()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       processors_changed (RouteProcessorChange ());
        set_processor_positions ();
 }
 
@@ -2344,7 +2458,7 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
        if (!lm.locked()) {
@@ -2355,25 +2469,25 @@ Route::silence (nframes_t nframes)
 }
 
 void
-Route::silence_unlocked (nframes_t nframes)
+Route::silence_unlocked (framecnt_t nframes)
 {
        /* Must be called with the processor lock held */
-       
+
        if (!_silent) {
 
                _output->silence (nframes);
 
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
                                // skip plugins, they don't need anything when we're not active
                                continue;
                        }
-                       
+
                        (*i)->silence (nframes);
                }
-               
+
                if (nframes == _session.get_block_size()) {
                        // _silent = true;
                }
@@ -2389,8 +2503,8 @@ Route::add_internal_return ()
        }
 }
 
-BufferSet*
-Route::get_return_buffer () const
+void
+Route::add_send_to_internal_return (InternalSend* send)
 {
        Glib::RWLock::ReaderLock rm (_processor_lock);
 
@@ -2398,16 +2512,13 @@ Route::get_return_buffer () const
                boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
                if (d) {
-                       BufferSet* bs = d->get_buffers ();
-                       return bs;
+                       return d->add_send (send);
                }
        }
-
-       return 0;
 }
 
 void
-Route::release_return_buffer () const
+Route::remove_send_from_internal_return (InternalSend* send)
 {
        Glib::RWLock::ReaderLock rm (_processor_lock);
 
@@ -2415,74 +2526,62 @@ Route::release_return_buffer () const
                boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
                if (d) {
-                       return d->release_buffers ();
+                       return d->remove_send (send);
                }
        }
 }
 
+/** Add a monitor send (if we don't already have one) but don't activate it */
+int
+Route::listen_via_monitor ()
+{
+       /* master never sends to control outs */
+       assert (!is_master ());
+
+       /* make sure we have one */
+       if (!_monitor_send) {
+               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+               _monitor_send->set_display_to_user (false);
+       }
+
+       /* set it up */
+       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+       configure_processors (0);
+
+       return 0;
+}
+
+/** Add an internal send to a route.
+ *  @param route route to send to.
+ *  @param placement placement for the send.
+ */
 int
-Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
 {
-       vector<string> ports;
-       vector<string>::const_iterator i;
+       assert (route != _session.monitor_out ());
 
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
 
                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) {
-
-                               /* if the target is the control outs, then make sure
-                                  we take note of which i-send is doing that.
-                               */
-
-                               if (route == _session.monitor_out()) {
-                                       _monitor_send = boost::dynamic_pointer_cast<Delivery>(d);
-                               }
-
                                /* already listening via the specified IO: do nothing */
-
                                return 0;
                        }
                }
        }
 
-       boost::shared_ptr<InternalSend> listener;
-
        try {
-
-                if (is_master()) {
-                        
-                        if (route == _session.monitor_out()) {
-                                /* master never sends to control outs */
-                                return 0;
-                        } else {
-                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
-                        }
-
-                } else {
-                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
-                }
+               boost::shared_ptr<InternalSend> listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+               add_processor (listener, placement);
 
        } catch (failed_constructor& err) {
                return -1;
        }
 
-       if (route == _session.monitor_out()) {
-               _monitor_send = listener;
-       }
-
-        if (placement == PostFader) {
-                /* put it *really* at the end, not just after the panner (main outs)
-                 */
-                add_processor (listener, _processors.end());
-        } else {
-                add_processor (listener, PreFader);
-        }
-
        return 0;
 }
 
@@ -2505,7 +2604,7 @@ Route::drop_listen (boost::shared_ptr<Route> route)
                        remove_processor (*x, &err);
                        rl.acquire ();
 
-                        /* list could have been demolished while we dropped the lock
+                       /* list could have been demolished while we dropped the lock
                           so start over.
                        */
 
@@ -2531,47 +2630,47 @@ Route::set_comment (string cmt, void *src)
 bool
 Route::add_fed_by (boost::shared_ptr<Route> other, bool via_sends_only)
 {
-        FeedRecord fr (other, via_sends_only);
+       FeedRecord fr (other, via_sends_only);
 
-        pair<FedBy::iterator,bool> result =  _fed_by.insert (fr);
+       pair<FedBy::iterator,bool> result =  _fed_by.insert (fr);
 
-        if (!result.second) {
+       if (!result.second) {
 
-                /* already a record for "other" - make sure sends-only information is correct */
-                if (!via_sends_only && result.first->sends_only) {
-                        FeedRecord* frp = const_cast<FeedRecord*>(&(*result.first));
-                        frp->sends_only = false;
-                }
-        }
-        
-        return result.second;
+               /* already a record for "other" - make sure sends-only information is correct */
+               if (!via_sends_only && result.first->sends_only) {
+                       FeedRecord* frp = const_cast<FeedRecord*>(&(*result.first));
+                       frp->sends_only = false;
+               }
+       }
+
+       return result.second;
 }
 
 void
 Route::clear_fed_by ()
 {
-        _fed_by.clear ();
+       _fed_by.clear ();
 }
 
 bool
 Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
 {
-        const FedBy& fed_by (other->fed_by());
+       const FedBy& fed_by (other->fed_by());
 
-        for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
-                boost::shared_ptr<Route> sr = f->r.lock();
+       for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
+               boost::shared_ptr<Route> sr = f->r.lock();
 
-                if (sr && (sr.get() == this)) {
+               if (sr && (sr.get() == this)) {
 
-                        if (via_sends_only) {
-                                *via_sends_only = f->sends_only;
-                        }
+                       if (via_sends_only) {
+                               *via_sends_only = f->sends_only;
+                       }
 
-                        return true;
-                }
-        }
+                       return true;
+               }
+       }
 
-        return false;
+       return false;
 }
 
 bool
@@ -2588,7 +2687,7 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
                return true;
        }
 
-       
+
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
                boost::shared_ptr<IOProcessor> iop;
@@ -2606,17 +2705,18 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
                } else {
                        DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tPROC %1 is not an IOP\n", (*r)->name()));
                }
-                       
+
        }
 
        DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tdoes NOT feed %1\n", other->name()));
        return false;
 }
 
+/** Called from the (non-realtime) butler thread when the transport is stopped */
 void
-Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
+Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
-       nframes_t now = _session.transport_frame();
+       framepos_t now = _session.transport_frame();
 
        {
                Glib::RWLock::ReaderLock lm (_processor_lock);
@@ -2625,11 +2725,12 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
                        automation_snapshot (now, true);
                }
 
+               Automatable::transport_stopped (now);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-                       if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
-                               (*i)->deactivate ();
-                               (*i)->activate ();
+                       if (!_have_internal_generator && (Config->get_plugins_stop_with_transport() && can_flush_processors)) {
+                               (*i)->flush ();
                        }
 
                        (*i)->transport_stopped (now);
@@ -2639,22 +2740,14 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
        _roll_delay = _initial_delay;
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
                configure_processors (0);
-       }
-}
-
-void
-Route::output_change_handler (IOChange change, void * /*src*/)
-{
-       if ((change & ConfigurationChanged)) {
-
-               /* XXX resize all listeners to match _main_outs? */
-
-               // configure_processors (0);
+               _phase_invert.resize (_input->n_ports().n_audio ());
+               io_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -2669,7 +2762,7 @@ Route::pans_required () const
 }
 
 int
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
                bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
@@ -2689,7 +2782,7 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
                if (_session.transport_speed() != 0.0f) {
                        /* we're rolling but some state is changing (e.g. our diskstream contents)
                           so we cannot use them. Be silent till this is over.
-                          
+
                           XXX note the absurdity of ::no_roll() being called when we ARE rolling!
                        */
                        silence_unlocked (nframes);
@@ -2706,8 +2799,8 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
        return 0;
 }
 
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
@@ -2720,28 +2813,37 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 
                nframes -= _roll_delay;
                silence_unlocked (_roll_delay);
-               /* we've written _roll_delay of samples into the
-                  output ports, so make a note of that for
-                  future reference.
-               */
-               _main_outs->increment_output_offset (_roll_delay);
                transport_frame += _roll_delay;
 
+               /* shuffle all the port buffers for things that lead "out" of this Route
+                  to reflect that we just wrote _roll_delay frames of silence.
+               */
+
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
+                       if (iop) {
+                               iop->increment_port_buffer_offset (_roll_delay);
+                       }
+               }
+               _output->increment_port_buffer_offset (_roll_delay);
+
                _roll_delay = 0;
+
        }
 
        return nframes;
 }
 
 int
-Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
             bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
        if (!lm.locked()) {
                return 0;
        }
-       
+
        automation_snapshot (_session.transport_frame(), false);
 
        if (n_outputs().n_total() == 0) {
@@ -2753,7 +2855,7 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
                return 0;
        }
 
-       nframes_t unused = 0;
+       framecnt_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2767,7 +2869,7 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
 }
 
 int
-Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/,
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
                    bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
@@ -2820,45 +2922,36 @@ Route::flush_processors ()
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
-               (*i)->activate ();
+               (*i)->flush ();
        }
 }
 
 void
-Route::set_meter_point (MeterPoint p)
+Route::set_meter_point (MeterPoint p, bool force)
 {
        /* CAN BE CALLED FROM PROCESS CONTEXT */
 
-       if (_meter_point == p) {
+       if (_meter_point == p && !force) {
                return;
        }
 
+       _meter_point = p;
+
        bool meter_was_visible_to_user = _meter->display_to_user ();
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-       
-               if (p != MeterCustom) {
-                       // Move meter in the processors list to reflect the new position
+
+               if (_meter_point != MeterCustom) {
+
+                       _meter->set_display_to_user (false);
+
+                       setup_invisible_processors ();
+
                        ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-                       _processors.erase(loc);
-                       switch (p) {
-                       case MeterInput:
-                               loc = _processors.begin();
-                               break;
-                       case MeterPreFader:
-                               loc = find (_processors.begin(), _processors.end(), _amp);
-                               break;
-                       case MeterPostFader:
-                               loc = _processors.end();
-                               break;
-                       default:
-                               break;
-                       }
-                       
+
                        ChanCount m_in;
-                       
+
                        if (loc == _processors.begin()) {
                                m_in = _input->n_ports();
                        } else {
@@ -2866,66 +2959,45 @@ Route::set_meter_point (MeterPoint p)
                                --before;
                                m_in = (*before)->output_streams ();
                        }
-                       
+
                        _meter->reflect_inputs (m_in);
-                       
-                       _processors.insert (loc, _meter);
-                       
+
                        /* we do not need to reconfigure the processors, because the meter
                           (a) is always ready to handle processor_max_streams
                           (b) is always an N-in/N-out processor, and thus moving
                           it doesn't require any changes to the other processors.
                        */
-                       
-                       _meter->set_display_to_user (false);
-                       
+
                } else {
-                       
+
                        // just make it visible and let the user move it
-                       
+
                        _meter->set_display_to_user (true);
                }
        }
 
-       _meter_point = p;
        meter_change (); /* EMIT SIGNAL */
 
        bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
-       
+
        processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
 }
 
 void
-Route::put_monitor_send_at (Placement p)
+Route::listen_position_changed ()
 {
-       if (!_monitor_send) {
-               return;
-       }
-
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList as_it_was (_processors);
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send);
-               _processors.erase(loc);
-               
-               switch (p) {
-               case PreFader:
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-                       if (loc != _processors.begin()) {
-                               --loc;
-                       }
-                       break;
-               case PostFader:
-                       loc = _processors.end();
-                       break;
-               }
-               
-               _processors.insert (loc, _monitor_send);
+               ProcessorState pstate (this);
 
-               if (configure_processors_unlocked (0)) {
-                       _processors = as_it_was;
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       return;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (0)) {
+                               pstate.restore ();
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
                }
        }
 
@@ -2933,62 +3005,68 @@ Route::put_monitor_send_at (Placement p)
        _session.set_dirty ();
 }
 
-nframes_t
-Route::update_total_latency ()
+boost::shared_ptr<CapturingProcessor>
+Route::add_export_point()
 {
-       nframes_t old = _output->effective_latency();
-       nframes_t own_latency = _output->user_latency();
+       if (!_capturing_processor) {
 
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i)->active ()) {
-                       own_latency += (*i)->signal_latency ();
-               }
-       }
+               _capturing_processor.reset (new CapturingProcessor (_session));
+               _capturing_processor->activate ();
 
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency));
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors (0);
+               }
 
-       _output->set_port_latency (own_latency);
+       }
 
-       if (_output->user_latency() == 0) {
+       return _capturing_processor;
+}
 
-               /* this (virtual) function is used for pure Routes,
-                  not derived classes like AudioTrack.  this means
-                  that the data processed here comes from an input
-                  port, not prerecorded material, and therefore we
-                  have to take into account any input latency.
-               */
+framecnt_t
+Route::update_signal_latency ()
+{
+       framecnt_t l = _output->user_latency();
 
-               own_latency += _input->signal_latency ();
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       l += (*i)->signal_latency ();
+               }
        }
 
-       if (old != own_latency) {
-               _output->set_latency_delay (own_latency);
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
+
+       if (_signal_latency != l) {
+               _signal_latency = l;
                signal_latency_changed (); /* EMIT SIGNAL */
        }
 
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency));
-
-       return _output->effective_latency ();
+       return _signal_latency;
 }
 
 void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
 {
        _output->set_user_latency (nframes);
-       _session.update_latency_compensation (false, false);
+       _session.update_latency_compensation ();
 }
 
 void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_compensation (framecnt_t longest_session_latency)
 {
-       nframes_t old = _initial_delay;
+       framecnt_t old = _initial_delay;
 
-       if (_output->effective_latency() < longest_session_latency) {
-               _initial_delay = longest_session_latency - _output->effective_latency();
+       if (_signal_latency < longest_session_latency) {
+               _initial_delay = longest_session_latency - _signal_latency;
        } else {
                _initial_delay = 0;
        }
 
+       DEBUG_TRACE (DEBUG::Latency, string_compose (
+                            "%1: compensate for maximum latency of %2,"
+                            "given own latency of %3, using initial delay of %4\n",
+                            name(), longest_session_latency, _signal_latency, _initial_delay));
+
        if (_initial_delay != old) {
                initial_delay_changed (); /* EMIT SIGNAL */
        }
@@ -2999,91 +3077,105 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 }
 
 void
-Route::automation_snapshot (nframes_t now, bool force)
+Route::automation_snapshot (framepos_t now, bool force)
 {
-       panner()->automation_snapshot (now, force);
-       
+       if (_pannable) {
+               _pannable->automation_snapshot (now, force);
+       }
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
 }
 
-Route::SoloControllable::SoloControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
+Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
                             boost::shared_ptr<AutomationList>(), name)
-       , route (r)
+       , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
        set_list (gl);
 }
 
 void
-Route::SoloControllable::set_value (float val)
+Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-# if 0
-       this is how it should be done 
 
        boost::shared_ptr<RouteList> rl (new RouteList);
-       rl->push_back (route);
+
+       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);
        }
-#else
-       route.set_solo (bval, this);
-#endif
 }
 
-float
-Route::SoloControllable::get_value (void) const
+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 route.listening() ? 1.0f : 0.0f;
+               return r->listening_via_monitor() ? 1.0f : 0.0f;
        } else {
-               return route.self_soloed() ? 1.0f : 0.0f;
+               return r->self_soloed() ? 1.0f : 0.0f;
        }
 }
 
-Route::MuteControllable::MuteControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
                             boost::shared_ptr<AutomationList>(), name)
-       , route (r)
+       , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
        set_list (gl);
 }
 
 void
-Route::MuteControllable::set_value (float val)
+Route::MuteControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-# if 0
-       this is how it should be done 
 
        boost::shared_ptr<RouteList> rl (new RouteList);
-       rl->push_back (route);
+
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return;
+       }
+
+       rl->push_back (r);
        _session.set_mute (rl, bval);
-#else
-       route.set_mute (bval, this);
-#endif
 }
 
-float
-Route::MuteControllable::get_value (void) const
+double
+Route::MuteControllable::get_value () const
 {
-       return route.muted() ? 1.0f : 0.0f;
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return 0;
+       }
+
+       return r->muted() ? 1.0f : 0.0f;
 }
 
 void
-Route::set_block_size (nframes_t nframes)
+Route::set_block_size (pframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
-       
+
        _session.ensure_buffers (n_process_buffers ());
 }
 
@@ -3117,44 +3209,53 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
+Route::shift (framepos_t pos, framecnt_t frames)
 {
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
        /* gain automation */
-       XMLNode &before = _gain_control->get_state ();
-       _gain_control->shift (pos, frames);
-       XMLNode &after = _gain_control->get_state ();
-       _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+       {
+               boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
+
+               XMLNode &before = gc->alist()->get_state ();
+               gc->alist()->shift (pos, frames);
+               XMLNode &after = gc->alist()->get_state ();
+               _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+       }
 
        /* pan automation */
-       for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
-               Curve & c = (*i)->automation ();
-               XMLNode &before = c.get_state ();
-               c.shift (pos, frames);
-               XMLNode &after = c.get_state ();
-               _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
+       if (_pannable) {
+               ControlSet::Controls& c (_pannable->controls());
+
+               for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+                       boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
+                       if (pc) {
+                               boost::shared_ptr<AutomationList> al = pc->alist();
+                               XMLNode& before = al->get_state ();
+                               al->shift (pos, frames);
+                               XMLNode& after = al->get_state ();
+                               _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                       }
+               }
        }
 
        /* redirect automation */
        {
-               Glib::RWLock::ReaderLock lm (redirect_lock);
-               for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-
-                       set<uint32_t> a;
-                       (*i)->what_has_automation (a);
-
-                       for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
-                               AutomationList & al = (*i)->automation_list (*j);
-                               XMLNode &before = al.get_state ();
-                               al.shift (pos, frames);
-                               XMLNode &after = al.get_state ();
-                               _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+                       set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
+
+                       for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+                               boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+                               if (ac) {
+                                       boost::shared_ptr<AutomationList> al = ac->alist();
+                                       XMLNode &before = al->get_state ();
+                                       al->shift (pos, frames);
+                                       XMLNode &after = al->get_state ();
+                                       _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                               }
                        }
                }
        }
-#endif
-
 }
 
 
@@ -3184,22 +3285,20 @@ Route::set_name (const string& str)
        ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
-
-               Glib::RWLock::ReaderLock lm (_processor_lock);
-
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-                       /* rename all I/O processors that have inputs or outputs */
-
-                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
-
-                       if (iop && (iop->output() || iop->input())) {
-                               if (!iop->set_name (name)) {
-                                       ret = false;
-                               }
+               /* rename the main outs. Leave other IO processors
+                * with whatever name they already have, because its
+                * just fine as it is (it will not contain the route
+                * name if its a port insert, port send or port return).
+                */
+
+               if (_main_outs) {
+                       if (_main_outs->set_name (name)) {
+                               /* XXX returning false here is stupid because
+                                  we already changed the route name.
+                               */
+                               return false;
                        }
                }
-
        }
 
        return ret;
@@ -3223,25 +3322,39 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
        return boost::shared_ptr<Send>();
 }
 
+/** @param c Audio channel index.
+ *  @param yn true to invert phase, otherwise false.
+ */
 void
-Route::set_phase_invert (bool yn)
+Route::set_phase_invert (uint32_t c, bool yn)
 {
-       if (_phase_invert != yn) {
-                if (yn) {
-                        _phase_invert = 0xffff; // XXX all channels
-                } else {
-                        _phase_invert = 0; // XXX no channels
-                }
+       if (_phase_invert[c] != yn) {
+               _phase_invert[c] = yn;
+               phase_invert_changed (); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
+}
 
+void
+Route::set_phase_invert (boost::dynamic_bitset<> p)
+{
+       if (_phase_invert != p) {
+               _phase_invert = p;
                phase_invert_changed (); /* EMIT SIGNAL */
-                _session.set_dirty ();
+               _session.set_dirty ();
        }
 }
 
 bool
+Route::phase_invert (uint32_t c) const
+{
+       return _phase_invert[c];
+}
+
+boost::dynamic_bitset<>
 Route::phase_invert () const
 {
-       return _phase_invert != 0;
+       return _phase_invert;
 }
 
 void
@@ -3260,8 +3373,13 @@ Route::denormal_protection () const
 }
 
 void
-Route::set_active (bool yn)
+Route::set_active (bool yn, void* src)
 {
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
+               _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
+               return;
+       }
+
        if (_active != yn) {
                _active = yn;
                _input->set_active (yn);
@@ -3292,10 +3410,23 @@ Route::meter ()
        }
 }
 
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+       return _pannable;
+}
+
 boost::shared_ptr<Panner>
 Route::panner() const
 {
-       return _main_outs->panner();
+       /* may be null ! */
+       return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+       return _main_outs->panner_shell();
 }
 
 boost::shared_ptr<AutomationControl>
@@ -3362,7 +3493,7 @@ Route::nth_send (uint32_t n)
                        if (n-- == 0) {
                                return *i;
                        }
-               } 
+               }
        }
 
        return boost::shared_ptr<Processor> ();
@@ -3371,19 +3502,19 @@ Route::nth_send (uint32_t n)
 bool
 Route::has_io_processor_named (const string& name)
 {
-        Glib::RWLock::ReaderLock lm (_processor_lock);
-        ProcessorList::iterator i;
-        
-        for (i = _processors.begin(); i != _processors.end(); ++i) {
-                if (boost::dynamic_pointer_cast<Send> (*i) ||
-                    boost::dynamic_pointer_cast<PortInsert> (*i)) {
-                        if ((*i)->name() == name) {
-                                return true;
-                        }
-                }
-        }
-        
-        return false;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<Send> (*i) ||
+                   boost::dynamic_pointer_cast<PortInsert> (*i)) {
+                       if ((*i)->name() == name) {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
 }
 
 MuteMaster::MutePoint
@@ -3405,3 +3536,328 @@ Route::set_processor_positions ()
                }
        }
 }
+
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+       list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+       if (c.empty()) {
+               /* The processors cannot be configured with the new input arrangement, so
+                  block the change.
+               */
+               return true;
+       }
+
+       /* The change is ok */
+       return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+       list<string> p;
+
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+                       p.push_back ((*i)->name ());
+               }
+       }
+
+       return p;
+}
+
+
+framecnt_t
+Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecnt_t our_latency) const
+{
+       /* we assume that all our input ports feed all our output ports. its not
+          universally true, but the alternative is way too corner-case to worry about.
+       */
+
+       jack_latency_range_t all_connections;
+
+       all_connections.min = ~((jack_nframes_t) 0);
+       all_connections.max = 0;
+
+       /* iterate over all "from" ports and determine the latency range for all of their
+          connections to the "outside" (outside of this Route).
+       */
+
+       for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
+
+               jack_latency_range_t range;
+
+               p->get_connected_latency_range (range, playback);
+
+               all_connections.min = min (all_connections.min, range.min);
+               all_connections.max = max (all_connections.max, range.max);
+       }
+
+       /* set the "from" port latencies to the max/min range of all their connections */
+
+       for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
+               p->set_private_latency_range (all_connections, playback);
+       }
+
+       /* set the ports "in the direction of the flow" to the same value as above plus our own signal latency */
+
+       all_connections.min += our_latency;
+       all_connections.max += our_latency;
+
+       for (PortSet::iterator p = to.begin(); p != to.end(); ++p) {
+               p->set_private_latency_range (all_connections, playback);
+       }
+
+       return all_connections.max;
+}
+
+framecnt_t
+Route::set_private_port_latencies (bool playback) const
+{
+       framecnt_t own_latency = 0;
+
+       /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD
+          OR LATENCY CALLBACK.
+
+          This is called (early) from the latency callback. It computes the REAL
+          latency associated with each port and stores the result as the "private"
+          latency of the port. A later call to Route::set_public_port_latencies()
+          sets all ports to the same value to reflect the fact that we do latency
+          compensation and so all signals are delayed by the same amount as they
+          flow through ardour.
+       */
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       own_latency += (*i)->signal_latency ();
+               }
+       }
+
+       if (playback) {
+               /* playback: propagate latency from "outside the route" to outputs to inputs */
+               return update_port_latencies (_output->ports (), _input->ports (), true, own_latency);
+       } else {
+               /* capture: propagate latency from "outside the route" to inputs to outputs */
+               return update_port_latencies (_input->ports (), _output->ports (), false, own_latency);
+       }
+}
+
+void
+Route::set_public_port_latencies (framecnt_t value, bool playback) const
+{
+       /* this is called to set the JACK-visible port latencies, which take
+          latency compensation into account.
+       */
+
+       jack_latency_range_t range;
+
+       range.min = value;
+       range.max = value;
+
+       {
+               const PortSet& ports (_input->ports());
+               for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) {
+                       p->set_public_latency_range (range, playback);
+               }
+       }
+
+       {
+               const PortSet& ports (_output->ports());
+               for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) {
+                       p->set_public_latency_range (range, playback);
+               }
+       }
+}
+
+/** Put the invisible processors in the right place in _processors.
+ *  Must be called with a writer lock on _processor_lock held.
+ */
+void
+Route::setup_invisible_processors ()
+{
+#ifndef NDEBUG
+       Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK);
+       assert (!lm.locked ());
+#endif
+
+       if (!_main_outs) {
+               /* too early to be doing this stuff */
+               return;
+       }
+
+       /* we'll build this new list here and then use it */
+
+       ProcessorList new_processors;
+
+       /* find visible processors */
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->display_to_user ()) {
+                       new_processors.push_back (*i);
+               }
+       }
+
+       /* find the amp */
+
+       ProcessorList::iterator amp = new_processors.begin ();
+       while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+               ++amp;
+       }
+
+       assert (amp != _processors.end ());
+
+       /* and the processor after the amp */
+
+       ProcessorList::iterator after_amp = amp;
+       ++after_amp;
+
+       /* METER */
+
+       if (_meter) {
+               switch (_meter_point) {
+               case MeterInput:
+                       assert (!_meter->display_to_user ());
+                       new_processors.push_front (_meter);
+                       break;
+               case MeterPreFader:
+                       assert (!_meter->display_to_user ());
+                       new_processors.insert (amp, _meter);
+                       break;
+               case MeterPostFader:
+                       /* do nothing here */
+                       break;
+               case MeterOutput:
+                       /* do nothing here */
+                       break;
+               case MeterCustom:
+                       /* the meter is visible, so we don't touch it here */
+                       break;
+               }
+       }
+
+       /* MAIN OUTS */
+
+       assert (_main_outs);
+       assert (!_main_outs->display_to_user ());
+       new_processors.push_back (_main_outs);
+
+       /* iterator for the main outs */
+
+       ProcessorList::iterator main = new_processors.end();
+       --main;
+
+       /* OUTPUT METERING */
+
+       if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) {
+               assert (!_meter->display_to_user ());
+
+               /* add the processor just before or just after the main outs */
+
+               ProcessorList::iterator meter_point = main;
+
+               if (_meter_point == MeterOutput) {
+                       ++meter_point;
+               }
+               new_processors.insert (meter_point, _meter);
+       }
+
+       /* MONITOR SEND */
+
+       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);
+                               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;
+                       }
+               }  else {
+                       new_processors.insert (new_processors.end(), _monitor_send);
+                       _monitor_send->set_can_pan (false);
+               }
+       }
+
+       /* MONITOR CONTROL */
+
+       if (_monitor_control && is_monitor ()) {
+               assert (!_monitor_control->display_to_user ());
+               new_processors.push_front (_monitor_control);
+       }
+
+       /* INTERNAL RETURN */
+
+       /* doing this here means that any monitor control will come just after
+          the return.
+       */
+
+       if (_intreturn) {
+               assert (!_intreturn->display_to_user ());
+               new_processors.push_front (_intreturn);
+       }
+
+       /* EXPORT PROCESSOR */
+
+       if (_capturing_processor) {
+               assert (!_capturing_processor->display_to_user ());
+               new_processors.push_front (_capturing_processor);
+       }
+
+       _processors = new_processors;
+
+       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 ()));
+       }
+}
+
+bool
+Route::should_monitor () const
+{
+       switch (Config->get_monitoring_model()) {
+       case HardwareMonitoring:
+       case ExternalMonitoring:
+               return !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+void
+Route::unpan ()
+{
+       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+       Glib::RWLock::ReaderLock lp (_processor_lock);
+
+       _pannable.reset ();
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*i);
+               if (d) {
+                       d->unpan ();
+               }
+       }
+}