change Session::convert_to_frames_at() to Session::convert_to_frames() to reflect...
[ardour.git] / libs / ardour / route.cc
index db06368371031cc124b5043e6124abb0fc7166f4..6f9780d4b955c7781798e2bd56d579e6d22efa6c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000 Paul Davis 
+    Copyright (C) 2000 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <cassert>
 #include <algorithm>
 
-#include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
-#include "pbd/stacktrace.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
+#include "pbd/convert.h"
 
 #include "evoral/Curve.hpp"
 
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/debug.h"
+#include "ardour/delivery.h"
 #include "ardour/dB.h"
+#include "ardour/internal_send.h"
+#include "ardour/internal_return.h"
 #include "ardour/ladspa_plugin.h"
 #include "ardour/meter.h"
 #include "ardour/mix.h"
+#include "ardour/monitor_processor.h"
 #include "ardour/panner.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
@@ -53,6 +58,8 @@
 #include "ardour/session.h"
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
 
 #include "i18n.h"
 
@@ -61,100 +68,144 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
-
-Route::Route (Session& sess, string name, Flag flg,
-             DataType default_type, ChanCount in, ChanCount out)
-       : IO (sess, name, default_type, in, ChanCount::INFINITE, out, ChanCount::INFINITE)
+PBD::Signal1<void,string const&> Route::SyncOrderKeys;
+PBD::Signal0<void> Route::RemoteControlIDChange;
+
+Route::Route (Session& sess, string name, Flag flg, DataType default_type)
+       : SessionObject (sess, name)
+       , Automatable (sess)
+       , GraphNode( sess.route_graph )
+        , _active (true)
+        , _initial_delay (0)
+        , _roll_delay (0)
        , _flags (flg)
-       , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl))
-       , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
-{
-       _configured_inputs = in;
-       _configured_outputs = out;
-       init ();
-
-}
-
-Route::Route (Session& sess, const XMLNode& node, DataType default_type)
-       : IO (sess, *node.child ("IO"), default_type)
-       , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl))
-       , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
+        , _pending_declick (true)
+        , _meter_point (MeterPostFader)
+        , _self_solo (false)
+        , _soloed_by_others_upstream (0)
+        , _soloed_by_others_downstream (0)
+        , _solo_isolated (0)
+        , _denormal_protection (false)
+        , _recordable (true)
+        , _silent (false)
+        , _declickable (false)
+       , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *this))
+       , _mute_master (new MuteMaster (sess, name))
+        , _have_internal_generator (false)
+        , _solo_safe (false)
+       , _default_type (default_type)
+        , _remote_control_id (0)
+        , _in_configure_processors (false)
 {
-       init ();
-       _set_state (node, false);
+       processor_max_streams.reset();
+       order_keys[N_("signal")] = order_key_cnt++;
 }
 
-void
+int
 Route::init ()
 {
-       processor_max_streams.reset();
-       _muted = false;
-       _soloed = false;
-       _solo_safe = false;
-       _recordable = true;
-       _active = true;
-       _phase_invert = false;
-       _denormal_protection = false;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
-       _silent = false;
-       _meter_point = MeterPostFader;
-       _initial_delay = 0;
-       _roll_delay = 0;
-       _own_latency = 0;
-       _user_latency = 0;
-       _have_internal_generator = false;
-       _declickable = false;
-       _pending_declick = true;
-       _remote_control_id = 0;
-       _in_configure_processors = false;
-       
-       _edit_group = 0;
-       _mix_group = 0;
+       /* add standard controls */
 
-       _mute_affects_pre_fader = Config->get_mute_affects_pre_fader();
-       _mute_affects_post_fader = Config->get_mute_affects_post_fader();
-       _mute_affects_control_outs = Config->get_mute_affects_control_outs();
-       _mute_affects_main_outs = Config->get_mute_affects_main_outs();
-       
-       solo_gain = 1.0;
-       desired_solo_gain = 1.0;
-       mute_gain = 1.0;
-       desired_mute_gain = 1.0;
+       _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
 
-       input_changed.connect (mem_fun (this, &Route::input_change_handler));
-       output_changed.connect (mem_fun (this, &Route::output_change_handler));
-       
-       /* add standard processors: amp, meter, main outs */
+       add_control (_solo_control);
+       add_control (_mute_control);
 
-       /* amp & meter belong to IO but need to be added to our processor list */
+       /* input and output objects */
 
-       _amp->set_sort_key (0);
-       _meter->set_sort_key (1);
-       add_processor (_amp);
-       add_processor (_meter);
-       
-       _main_outs.reset (new Delivery (_session, this, _name, Delivery::Main));
-       ProcessorList::iterator i = _processors.end();
-       add_processor (_main_outs, 0, &i);
+       _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 */
+
+       _meter.reset (new PeakMeter (_session));
+       _meter->set_display_to_user (false);
+
+       add_processor (_meter, PostFader);
+
+       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+
+        add_processor (_main_outs, PostFader);
+
+       if (is_monitor()) {
+               /* where we listen to tracks */
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+
+                ProcessorList::iterator i;
+
+                for (i = _processors.begin(); i != _processors.end(); ++i) {
+                        if (*i == _intreturn) {
+                                ++i;
+                                break;
+                        }
+                }
+
+                /* the thing that provides proper control over a control/monitor/listen bus 
+                   (such as per-channel cut, dim, solo, invert, etc).
+                   It always goes right after the internal return;
+                 */
+                _monitor_control.reset (new MonitorProcessor (_session));
+                add_processor (_monitor_control, i);
+
+                /* no panning on the monitor main outs */
+
+                _main_outs->panner()->set_bypassed (true);
+       }
+
+        if (is_master() || is_monitor() || is_hidden()) {
+                _mute_master->set_solo_ignore (true);
+        }
+
+       /* now that we have _meter, its safe to connect to this */
+
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
+
+        return 0;
 }
 
 Route::~Route ()
 {
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       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 
+        */
 
-       for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
-               free ((void*)(i->first));
+       drop_connections ();
+
+       /* don't use clear_processors here, as it depends on the session which may
+          be half-destroyed by now 
+       */
+
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->drop_references ();
        }
+
+       _processors.clear ();
 }
 
 void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
+               if (notify_class_listeners) {
+                       RemoteControlIDChange ();
+               }
        }
 }
 
@@ -164,43 +215,61 @@ Route::remote_control_id() const
        return _remote_control_id;
 }
 
-long
-Route::order_key (const char* name) const
+int32_t
+Route::order_key (std::string const & name) const
 {
-       OrderKeys::const_iterator i;
-
-       for (i = order_keys.begin(); i != order_keys.end(); ++i) {
-               if (!strcmp (name, i->first)) {
-                       return i->second;
-               }
+       OrderKeys::const_iterator i = order_keys.find (name);
+       if (i == order_keys.end()) {
+               return -1;
        }
 
-       return -1;
+       return i->second;
 }
 
 void
-Route::set_order_key (const char* name, long n)
+Route::set_order_key (std::string const & name, int32_t n)
 {
-       order_keys[strdup(name)] = n;
+       bool changed = false;
+
+       /* This method looks more complicated than it should, but
+          it's important that we don't emit order_key_changed unless
+          it actually has, as expensive things happen on receipt of that
+          signal.
+       */
+
+       if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) {
+               order_keys[name] = n;
+               changed = true;
+       }
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
-                       x->second = n;
+                       if (x->second != n) {
+                               x->second = n;
+                               changed = true;
+                       }
                }
-       } 
+       }
 
-       _session.set_dirty ();
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
 }
 
+/** Set all order keys to be the same as that for `base', if such a key
+ *  exists in this route.
+ *  @param base Base key.
+ */
 void
-Route::sync_order_keys (const char* base)
+Route::sync_order_keys (std::string const & base)
 {
        if (order_keys.empty()) {
                return;
        }
 
        OrderKeys::iterator i;
-       uint32_t key;
+       int32_t key;
 
        if ((i = order_keys.find (base)) == order_keys.end()) {
                /* key doesn't exist, use the first existing key (during session initialization) */
@@ -213,8 +282,17 @@ Route::sync_order_keys (const char* base)
                i = order_keys.begin();
        }
 
+       bool changed = false;
+       
        for (; i != order_keys.end(); ++i) {
-               i->second = key;
+               if (i->second != key) {
+                       i->second = key;
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -223,31 +301,32 @@ Route::ensure_track_or_route_name(string name, Session &session)
 {
        string newname = name;
 
-       while (session.route_by_name (newname) != NULL) {
-               newname = bump_name_once (newname);
+       while (!session.io_name_is_legal (newname)) {
+               newname = bump_name_once (newname, '.');
        }
 
        return newname;
 }
 
+
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
-       IO::inc_gain (fraction, src);
+       _amp->inc_gain (fraction, src);
 }
 
 void
 Route::set_gain (gain_t val, void *src)
 {
-       if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
-               
-               if (_mix_group->is_relative()) {
-                       
-                       gain_t usable_gain = gain();
+       if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) {
+
+               if (_route_group->is_relative()) {
+
+                       gain_t usable_gain = _amp->gain();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
-                                               
+
                        gain_t delta = val;
                        if (delta < 0.000001f) {
                                delta = 0.000001f;
@@ -261,40 +340,40 @@ Route::set_gain (gain_t val, void *src)
                        gain_t factor = delta / usable_gain;
 
                        if (factor > 0.0f) {
-                               factor = _mix_group->get_max_factor(factor);
+                               factor = _route_group->get_max_factor(factor);
                                if (factor == 0.0f) {
-                                       _gain_control->Changed(); /* EMIT SIGNAL */
+                                       _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        } else {
-                               factor = _mix_group->get_min_factor(factor);
+                               factor = _route_group->get_min_factor(factor);
                                if (factor == 0.0f) {
-                                       _gain_control->Changed(); /* EMIT SIGNAL */
+                                       _amp->gain_control()->Changed(); /* EMIT SIGNAL */
                                        return;
                                }
                        }
-                                       
-                       _mix_group->apply (&Route::inc_gain, factor, _mix_group);
+
+                       _route_group->apply (&Route::inc_gain, factor, _route_group);
 
                } else {
-                       
-                       _mix_group->apply (&Route::set_gain, val, _mix_group);
+
+                       _route_group->apply (&Route::set_gain, val, _route_group);
                }
 
                return;
-       } 
+       }
 
-       if (val == gain()) {
+       if (val == _amp->gain()) {
                return;
        }
 
-       IO::set_gain (val, src);
+       _amp->set_gain (val, src);
 }
 
 /** Process this route for one (sub) cycle (process thread)
  *
  * @param bufs Scratch buffers to use for the signal path
- * @param start_frame Initial transport frame 
+ * @param start_frame Initial transport frame
  * @param end_frame Final transport frame
  * @param nframes Number of frames to output (to ports)
  *
@@ -303,205 +382,228 @@ 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)
 {
-       ProcessorList::iterator i;
-       bool mute_declick_applied = false;
-       gain_t dmg, dsg, dg;
-       bool no_monitor;
+       bool monitor;
 
-       bufs.is_silent(false);
+       bufs.is_silent (false);
 
        switch (Config->get_monitoring_model()) {
        case HardwareMonitoring:
        case ExternalMonitoring:
-               no_monitor = true;
+               monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
                break;
        default:
-               no_monitor = false;
-       }
-
-       declick = _pending_declick;
-       
-       const bool recording_without_monitoring = no_monitor && record_enabled()
-                       && (!Config->get_auto_input() || _session.actively_recording());
-       
-
-       /* -------------------------------------------------------------------------------------------
-          SET UP GAIN (FADER)
-          ----------------------------------------------------------------------------------------- */
-
-       { 
-               Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
-               
-               if (dm.locked()) {
-                       dmg = desired_mute_gain;
-                       dsg = desired_solo_gain;
-                       dg = _gain_control->user_float();
-               } else {
-                       dmg = mute_gain;
-                       dsg = solo_gain;
-                       dg = _gain;
-               }
+               monitor = true;
        }
-       
-       // apply gain at the amp if...
-       _amp->apply_gain(
-                       // we're not recording
-                       !(record_enabled() && _session.actively_recording())
-                       // or (we are recording, and) software monitoring is required
-                       || Config->get_monitoring_model() == SoftwareMonitoring);
-       
-       // mute at the amp if...
-       _amp->apply_mute (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader,
-                         mute_gain, dmg);
-
-       _amp->set_gain (_gain, dg);
-       
 
-       /* -------------------------------------------------------------------------------------------
-          SET UP CONTROL OUTPUTS
-          ----------------------------------------------------------------------------------------- */
-
-       boost::shared_ptr<Delivery> co = _control_outs;
-       if (co) {
-               // deliver control outputs unless we're ...
-               bool self_mute = ((dmg == 0 && _mute_affects_control_outs) || // or muted by mute of this track
-                                 !recording_without_monitoring); // or rec-enabled w/o s/w monitoring 
-               bool other_mute = (dsg == 0); // muted by solo of another track
-               
-               co->set_self_mute (self_mute);
-               co->set_nonself_mute (other_mute);
+       if (!declick) {
+               declick = _pending_declick;
        }
 
-       /* -------------------------------------------------------------------------------------------
-          SET UP MAIN OUTPUT STAGE
-          ----------------------------------------------------------------------------------------- */
+       /* figure out if we're going to use gain automation */
+        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);
 
-       bool solo_audible = dsg > 0;
-       bool mute_audible = dmg > 0 || !_mute_affects_main_outs;
 
-       bool silent_anyway = (_gain == 0 && !_amp->apply_gain_automation());
-       bool muted_by_other_solo = (!solo_audible && (Config->get_solo_model() != SoloBus));
-       bool muted_by_self = !mute_audible;
-
-       _main_outs->set_nonself_mute (recording_without_monitoring || muted_by_other_solo || silent_anyway);
-       _main_outs->set_self_mute (muted_by_self);
-       
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0, false);
-               _pending_declick = 0;
+               Amp::declick (bufs, nframes, 1);
        } else if (declick < 0) {
-               Amp::apply_gain (bufs, nframes, 1.0, 0.0, false);
-               _pending_declick = 0;
-       } else { // no global declick
-               if (solo_gain != dsg) {
-                       Amp::apply_gain (bufs, nframes, solo_gain, dsg, false);
-                       solo_gain = dsg;
-               }
+               Amp::declick (bufs, nframes, -1);
        }
 
+       _pending_declick = 0;
 
        /* -------------------------------------------------------------------------------------------
-          PRE-FADER MUTING
+          DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) {
-               Amp::apply_gain (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
-       if (mute_gain == 0.0f && dmg == 0.0f) {
-               bufs.is_silent(true);
-       }
+       if (_phase_invert.any ()) {
 
+               int chn = 0;
 
-       /* -------------------------------------------------------------------------------------------
-          DENORMAL CONTROL
-          ----------------------------------------------------------------------------------------- */
+               if (_denormal_protection || Config->get_denormal_protection()) {
 
-       if (_denormal_protection || Config->get_denormal_protection()) {
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
+                               Sample* const sp = i->data();
 
-               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) {
-                               sp[nx] += 1.0e-27f;
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx]  = -sp[nx];
+                                               sp[nx] += 1.0e-27f;
+                                       }
+                               } else {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx] += 1.0e-27f;
+                                       }
+                               }
                        }
-               }
-       }
 
-       /* -------------------------------------------------------------------------------------------
-          and go ....
-          ----------------------------------------------------------------------------------------- */
+               } else {
 
-       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
+                               Sample* const sp = i->data();
 
-       if (rm.locked()) {
-               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->input_streams()));
-                       (*i)->run_in_place (bufs, start_frame, end_frame, nframes);
-                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams()));
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx] = -sp[nx];
+                                       }
+                               }
+                       }
                }
 
-               if (!_processors.empty()) {
-                       bufs.set_count(ChanCount::max(bufs.count(), _processors.back()->output_streams()));
+       } else {
+
+               if (_denormal_protection || Config->get_denormal_protection()) {
+
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+                               Sample* const sp = i->data();
+                               for (pframes_t nx = 0; nx < nframes; ++nx) {
+                                       sp[nx] += 1.0e-27f;
+                               }
+                       }
+
                }
        }
 
        /* -------------------------------------------------------------------------------------------
-          POST-FADER MUTING
+          and go ....
           ----------------------------------------------------------------------------------------- */
 
-       if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) {
-               Amp::apply_gain (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
+                       break;
+               }
+               
+               if (bufs.count() != (*i)->input_streams()) {
+                       cerr << _name << " bufs = " << bufs.count()
+                            << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+                            << endl;
+               }
+               assert (bufs.count() == (*i)->input_streams());
+                
+                /* should we NOT run plugins here if the route is inactive?
+                   do we catch route != active somewhere higher?
+                */
 
-       if (mute_gain == 0.0f && dmg == 0.0f) {
-               bufs.is_silent(true);
+               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               bufs.set_count ((*i)->output_streams());
        }
-       
-       // at this point we've reached the desired mute gain regardless
-       mute_gain = dmg;
 }
 
 ChanCount
 Route::n_process_buffers ()
 {
-       return max (n_inputs(), processor_max_streams);
+       return max (_input->n_ports(), processor_max_streams);
 }
 
 void
-Route::setup_peak_meters()
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
-       ChanCount max_streams = std::max (_inputs.count(), _outputs.count());
-       max_streams = std::max (max_streams, processor_max_streams);
-       _meter->configure_io (max_streams, max_streams);
+       BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+       _silent = false;
+
+       assert (bufs.available() >= input_streams());
+
+       if (_input->n_ports() == ChanCount::ZERO) {
+               silence_unlocked (nframes);
+       }
+
+       bufs.set_count (input_streams());
+
+       if (is_monitor() && _session.listening() && !_session.is_auditioning()) {
+
+               /* control/monitor bus ignores input ports when something is
+                  feeding the listen "stream". data will "arrive" into the
+                  route from the intreturn processor element.
+               */
+               bufs.silence (nframes, 0);
+
+       } else {
+
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
+
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
+                       }
+               }
+       }
+
+       write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
 }
 
 void
-Route::passthru (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_scratch_buffers(n_process_buffers());
+       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, false);
+}
 
-       _silent = false;
+void
+Route::set_listen (bool yn, void* src)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+       if (_monitor_send) {
+               if (yn != _monitor_send->active()) {
+                       if (yn) {
+                               _monitor_send->activate ();
+                                _mute_master->set_soloed (true);
+                        } else {
+                               _monitor_send->deactivate ();
+                                _mute_master->set_soloed (false);
+                       }
 
-       collect_input (bufs, nframes);
+                       listen_changed (src); /* EMIT SIGNAL */
+               }
+       }
+}
 
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+bool
+Route::listening () const
+{
+       if (_monitor_send) {
+               return _monitor_send->active ();
+       } else {
+               return false;
+       }
 }
 
 void
-Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::set_solo_safe (bool yn, void *src)
 {
-       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+       if (_solo_safe != yn) {
+               _solo_safe = yn;
+               solo_safe_changed (src);
+       } 
+}
+
+bool
+Route::solo_safe() const
+{
+       return _solo_safe;
 }
 
 void
@@ -511,118 +613,243 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
+               _route_group->apply (&Route::set_solo, yn, _route_group);
                return;
        }
 
-       if (_soloed != yn) {
-               _soloed = yn;
-               solo_changed (src); /* EMIT SIGNAL */
+       if (self_soloed() != yn) {
+               set_self_solo (yn);
+                set_mute_master_solo ();
+               solo_changed (true, src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
-       }       
-       
-       catch_up_on_solo_mute_override ();
+       }
+}
+
+void
+Route::set_self_solo (bool yn)
+{
+        _self_solo = yn;
+}
+
+void
+Route::mod_solo_by_others_upstream (int32_t delta)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+        uint32_t old_sbu = _soloed_by_others_upstream;
+
+       if (delta < 0) {
+               if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_upstream += delta;
+               } else {
+                       _soloed_by_others_upstream = 0;
+               }
+       } else {
+               _soloed_by_others_upstream += delta;
+       }
+
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
+                                                  name(), delta, _soloed_by_others_upstream, old_sbu, 
+                                                  _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
+
+        /* push the inverse solo change to everything that feeds us. 
+           
+           This is important for solo-within-group. When we solo 1 track out of N that
+           feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
+           on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
+           tracks that feed it. This will silence them if they were audible because
+           of a bus solo, but the newly soloed track will still be audible (because 
+           it is self-soloed).
+           
+           but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
+           not in reverse.
+         */
+
+        if ((_self_solo || _soloed_by_others_downstream) &&
+            ((old_sbu == 0 && _soloed_by_others_upstream > 0) || 
+             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
+                
+                if (delta > 0 || !Config->get_exclusive_solo()) {
+                        DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                                boost::shared_ptr<Route> sr = i->r.lock();
+                                if (sr) {
+                                        sr->mod_solo_by_others_downstream (-delta);
+                                }
+                        }
+                } 
+        }
+
+        set_mute_master_solo ();
+        solo_changed (false, this);
+}
+
+void
+Route::mod_solo_by_others_downstream (int32_t delta)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+        if (delta < 0) {
+               if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_downstream += delta;
+               } else {
+                       _soloed_by_others_downstream = 0;
+               }
+       } else {
+               _soloed_by_others_downstream += delta;
+       }
+
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
+
+        set_mute_master_solo ();
+        solo_changed (false, this);
+}
+
+void
+Route::set_mute_master_solo ()
+{
+        _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
 }
 
 void
-Route::catch_up_on_solo_mute_override ()
+Route::set_solo_isolated (bool yn, void *src)
 {
-       if (Config->get_solo_model() != InverseMute) {
+       if (is_master() || is_monitor() || is_hidden()) {
+               return;
+       }
+
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
+               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
                return;
        }
        
-       {
-               Glib::Mutex::Lock lm (declick_lock);
+       /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
+
+       boost::shared_ptr<RouteList> routes = _session.get_routes ();
+       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+
+               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
+                        continue;
+                }
+
+               bool sends_only;
+               bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
                
-               if (_muted) {
-                       if (Config->get_solo_mute_override()) {
-                               desired_mute_gain = (_soloed?1.0:0.0);
-                       } else {
-                               desired_mute_gain = 0.0;
-                       }
-               } else {
-                       desired_mute_gain = 1.0;
+               if (does_feed && !sends_only) {
+                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+               }
+       }
+
+        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
+
+        bool changed = false;
+
+       if (yn) {
+                if (_solo_isolated == 0) {
+                        _mute_master->set_solo_ignore (true);
+                        changed = true;
+                }
+               _solo_isolated++;
+       } else {
+               if (_solo_isolated > 0) {
+                       _solo_isolated--;
+                        if (_solo_isolated == 0) {
+                                _mute_master->set_solo_ignore (false);
+                                changed = true;
+                        }
                }
        }
+
+        if (changed) {
+                solo_isolated_changed (src);
+        }
 }
 
-void
-Route::set_solo_mute (bool yn)
+bool
+Route::solo_isolated () const
 {
-       Glib::Mutex::Lock lm (declick_lock);
-
-       /* Called by Session in response to another Route being soloed.
-        */
-          
-       desired_solo_gain = (yn?0.0:1.0);
+       return _solo_isolated > 0;
 }
 
 void
-Route::set_solo_safe (bool yn, void *src)
+Route::set_mute_points (MuteMaster::MutePoint mp)
 {
-       if (_solo_safe != yn) {
-               _solo_safe = yn;
-                solo_safe_changed (src); /* 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 (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_mute, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
+               _route_group->apply (&Route::set_mute, yn, _route_group);
                return;
        }
 
-       if (_muted != yn) {
-               _muted = yn;
+       if (muted() != yn) {
+                _mute_master->set_muted_by_self (yn);
                mute_changed (src); /* EMIT SIGNAL */
-               
                _mute_control->Changed (); /* EMIT SIGNAL */
-               
-               Glib::Mutex::Lock lm (declick_lock);
-               
-               if (_soloed && Config->get_solo_mute_override()) {
-                       desired_mute_gain = 1.0f;
-               } else {
-                       desired_mute_gain = (yn?0.0f:1.0f);
-               }
        }
 }
 
+bool
+Route::muted () const
+{
+        return _mute_master->muted_by_self();
+}
+
+#if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 {
        cerr << name << " {" << endl;
        for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
                        p != procs.end(); ++p) {
-               cerr << "\t" << (*p)->sort_key() << ": " << (*p)->name() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
        }
        cerr << "}" << endl;
 }
+#endif
 
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
+{
+       ProcessorList::iterator loc;
 
-struct ProcessorSortByKey {
-    bool operator() (boost::shared_ptr<Processor> a, boost::shared_ptr<Processor> b) {
-           return a->sort_key() < b->sort_key();
-    }
-};
+       /* XXX this is not thread safe - we don't hold the lock across determining the iter
+          to add before and actually doing the insertion. dammit.
+       */
 
-uint32_t
-Route::fader_sort_key() const
-{
-       return _amp->sort_key();
+       if (placement == PreFader) {
+               /* generic pre-fader: insert immediately before the amp */
+               loc = find (_processors.begin(), _processors.end(), _amp);
+       } else {
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
+
+       return add_processor (processor, loc, err);
 }
 
+
 /** Add a processor to the route.
- * If @a iter is not NULL, it must point to an iterator in _processors and the new
+ * @a iter must point to an iterator in _processors and the new
  * processor will be inserted immediately before this location.  Otherwise,
  * @a position is used.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, ProcessorList::iterator* iter)
+Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
 {
        ChanCount old_pms = processor_max_streams;
 
@@ -630,10 +857,6 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                return 1;
        }
 
-       cerr << "Adding a processor called " << processor->name() << " sk = " << processor->sort_key() 
-            << ((iter == 0) ? " NO given position " : " with given position")
-            << endl;
-
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
 
@@ -645,15 +868,10 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                if (processor == _amp || processor == _meter || processor == _main_outs) {
                        // Ensure only one of these are in the list at any time
                        if (loc != _processors.end()) {
-                               if (iter) {
-                                       if (*iter == loc) { // Already in place, do nothing
-                                               return 0;
-                                       } else { // New position given, relocate
-                                               _processors.erase(loc);
-                                       }
-                               } else { // Insert at end
-                                       _processors.erase(loc);
-                                       loc = _processors.end();
+                               if (iter == loc) { // Already in place, do nothing
+                                       return 0;
+                               } else { // New position given, relocate
+                                       _processors.erase (loc);
                                }
                        }
 
@@ -662,82 +880,145 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                                cerr << "ERROR: Processor added to route twice!" << endl;
                                return 1;
                        }
+
+                       loc = iter;
                }
 
-               if (iter) {
-                       // Use position given by user
-                       loc = *iter;
-               } else {
-                       if (processor->sort_key() == 0) {
-                               /* generic pre-fader: insert immediately before the amp */
-                               loc = find(_processors.begin(), _processors.end(), _amp);
-                       } else if (processor->sort_key() > _processors.size()) {
-                               /* generic post-fader: insert at end */
-                               loc = _processors.end();
-                       } else {
-                               /* find insert point */
-                               ProcessorSortByKey cmp;
-                               loc = upper_bound (_processors.begin(), _processors.end(), processor, cmp);
-                       }
-               }
-               
-               // Update sort keys
-               if (loc == _processors.end()) {
-                       processor->set_sort_key(_processors.size());
-               } else {
-                       processor->set_sort_key((*loc)->sort_key());
-                       for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) {
-                               (*p)->set_sort_key((*p)->sort_key() + 1);
-                       }
-               }
-
-               _processors.insert(loc, processor);
+               _processors.insert (loc, processor);
 
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
-               if (configure_processors_unlocked (err)) {
-                       dump_processors(_name + "bad config", _processors);
-                       ProcessorList::iterator ploc = loc;
-                       --ploc;
-                       _processors.erase(ploc);
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       cerr << "Bad IO config\n";
-                       return -1;
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               ProcessorList::iterator ploc = loc;
+                               --ploc;
+                               _processors.erase(ploc);
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               cerr << "configure failed\n";
+                               return -1;
+                       }
                }
-       
+
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       
+
                        if (pi->natural_input_streams() == ChanCount::ZERO) {
                                /* generator plugin */
                                _have_internal_generator = true;
                        }
-                       
+
+               }
+
+                /* is this the monitor send ? if so, make sure we keep track of it */
+
+                boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+
+                if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) {
+                        _monitor_send = isend;
+                }
+
+               if (activation_allowed && (processor != _monitor_send)) {
+                       processor->activate ();
                }
+
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
+
+               _output->set_user_latency (0);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
+
+       return 0;
+}
+
+bool
+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 ();
                
-               // Ensure peak vector sizes before the plugin is activated
-               ChanCount potential_max_streams = ChanCount::max (processor->input_streams(), processor->output_streams());
+               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) {
 
-               _meter->configure_io (potential_max_streams, potential_max_streams);
+                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
+                                               prop->value() == "lv2" ||
+                                               prop->value() == "vst" ||
+                                               prop->value() == "audiounit") {
+
+                                       processor.reset (new PluginInsert (_session));
+
+                               } else {
+
+                                       processor.reset (new PortInsert (_session, _mute_master));
+                               }
+
+                       }
 
-               // XXX: do we want to emit the signal here ? change call order.
-               processor->activate ();
-               processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+               } else if (node.name() == "Send") {
 
-               _user_latency = 0;
+                       processor.reset (new Send (_session, _mute_master));
+
+               } else {
+
+                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), node.name()) << endmsg;
+                       return false;
+               }
+
+                if (processor->set_state (node, version)) {
+                        return false;
+                }
+
+               return (add_processor (processor, placement) == 0);
        }
-       
-       if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) {
-               reset_panner ();
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
        }
+}
 
-       dump_processors (_name + " added one", _processors);
-       processors_changed (); /* EMIT SIGNAL */
-       
-       return 0;
+int
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
+{
+       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);
+       }
+
+       return add_processors (others, loc, err);
 }
 
 int
-Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint32_t first_sort_key)
+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
@@ -750,32 +1031,17 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint3
                return 1;
        }
 
+       if (others.empty()) {
+               return 0;
+       }
+
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
 
-               ProcessorList::iterator loc;
-               ProcessorList::iterator existing_end = _processors.end();
-               --existing_end;
-
-               ChanCount potential_max_streams = ChanCount::max(input_minimum(), output_minimum());
-               
-               if (first_sort_key == 0) {
-                       /* generic pre-fader: insert immediately before the amp */
-                       cerr << "Add new procs at amp, sk = " << first_sort_key << endl;
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-               } else if (first_sort_key > _processors.size()) {
-                       /* generic post-fader: insert at end */
-                       cerr << "Add new procs at end, sk = " << first_sort_key << endl;
-                       loc = _processors.end();
-               } else {
-                       /* find insert point */
-                       ProcessorSortByKey cmp;
-                       cerr << "Add new procs at sk = " << first_sort_key << endl;
-                       loc = upper_bound (_processors.begin(), _processors.end(), others.front(), cmp);
-               }
+               ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
-                       
+
                        // Ensure meter only appears in the list once
                        if (*i == _meter) {
                                ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
@@ -783,41 +1049,42 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint3
                                        _processors.erase(m);
                                }
                        }
-                       
+
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
-                               
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
+
+                               ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+                               if (m > potential_max_streams) {
                                        potential_max_streams = m;
+                               }
                        }
 
-                       // Ensure peak vector sizes before the plugin is activated
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
-                       
-                       _processors.insert (loc, *i);
-                       
-                       if (configure_processors_unlocked (err)) {
-                               ++existing_end;
-                               _processors.erase (existing_end, _processors.end());
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
+                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+                       if ((*i)->active()) {
+                               (*i)->activate ();
                        }
-                       
-                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (configure_processors_unlocked (err)) {
+                                       _processors.erase (inserted);
+                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       return -1;
+                               }
+                       }
+
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
                }
 
-               _user_latency = 0;
-       }
-       
-       if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) {
-               reset_panner ();
+               _output->set_user_latency (0);
        }
-       
-       dump_processors (_name + " added several", _processors);
-       processors_changed (); /* EMIT SIGNAL */
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -842,10 +1109,10 @@ void
 Route::disable_processors (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                (*i)->deactivate ();
        }
@@ -853,17 +1120,17 @@ Route::disable_processors (Placement p)
        _session.set_dirty ();
 }
 
-/** Turn off all redirects 
+/** Turn off all redirects
  */
 void
 Route::disable_processors ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->deactivate ();
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -874,16 +1141,16 @@ void
 Route::disable_plugins (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -893,13 +1160,13 @@ void
 Route::disable_plugins ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -908,7 +1175,7 @@ void
 Route::ab_plugins (bool forward)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-                       
+
        if (forward) {
 
                /* forward = turn off all active redirects, and mark them so that the next time
@@ -945,33 +1212,8 @@ Route::ab_plugins (bool forward)
                        }
                }
        }
-       
-       _session.set_dirty ();
-}
-       
-       
-/* Figure out the streams that will feed into PreFader */
-ChanCount
-Route::pre_fader_streams() const
-{
-       boost::shared_ptr<Processor> processor;
 
-       /* Find the last pre-fader redirect that isn't a send; sends don't affect the number
-        * of streams. */
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i) == _amp) {
-                       break;
-               }
-               if (boost::dynamic_pointer_cast<Send> (*i) == 0) {
-                       processor = *i;
-               }
-       }
-       
-       if (processor) {
-               return processor->output_streams();
-       } else {
-               return n_inputs ();
-       }
+       _session.set_dirty ();
 }
 
 
@@ -986,7 +1228,7 @@ Route::clear_processors (Placement p)
        if (!_session.engine().connected()) {
                return;
        }
-       
+
        bool already_deleting = _session.deletion_in_progress();
        if (!already_deleting) {
                _session.set_deletion_in_progress();
@@ -996,40 +1238,58 @@ Route::clear_processors (Placement p)
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
                ProcessorStreams err;
+               bool seen_amp = false;
 
-               ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
-               if (p == PreFader) {
-                       // Get rid of PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
-                               (*i)->drop_references ();
-                       }
-                       // Keep the rest
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               new_list.push_back (*i);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+                       if (*i == _amp) {
+                               seen_amp = true;
                        }
-               } else {
-                       // Keep PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
-                       }
-                       new_list.push_back (_amp);
-                       // Get rid of PostFader processors
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               (*i)->drop_references ();
+
+                       } else {
+                               if (seen_amp) {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       case PostFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       }
+
+                               } else {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       case PostFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       }
+                               }
                        }
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
-       }
 
-       if (processor_max_streams != old_pms) {
-               reset_panner ();
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (&err); // this can't fail
+               }
        }
-       
+
        processor_max_streams.reset();
        _have_internal_generator = false;
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
@@ -1062,7 +1322,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        if (*i == processor) {
 
                                /* move along, see failure case for configure_processors()
-                                  where we may need to reprocessor the processor.
+                                  where we may need to reconfigure the processor.
                                */
 
                                /* stop redirects that send signals to JACK ports
@@ -1070,13 +1330,17 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                   run.
                                */
 
-                               boost::shared_ptr<IOProcessor> redirect;
-                               
-                               if ((redirect = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
-                                       redirect->io()->disconnect_inputs (this);
-                                       redirect->io()->disconnect_outputs (this);
+                               boost::shared_ptr<IOProcessor> iop;
+
+                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                                       if (iop->input()) {
+                                               iop->input()->disconnect (this);
+                                       }
+                                       if (iop->output()) {
+                                               iop->output()->disconnect (this);
+                                       }
                                }
-                               
+
                                i = _processors.erase (i);
                                removed = true;
                                break;
@@ -1085,7 +1349,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                ++i;
                        }
 
-                       _user_latency = 0;
+                       _output->set_user_latency (0);
                }
 
                if (!removed) {
@@ -1093,19 +1357,23 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors.insert (i, processor);
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
 
                for (i = _processors.begin(); i != _processors.end(); ++i) {
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                if (pi->is_generator()) {
                                        _have_internal_generator = true;
@@ -1115,90 +1383,237 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                }
        }
 
-       if (old_pms != processor_max_streams) {
-               reset_panner ();
-       }
-
        processor->drop_references ();
-       
-       dump_processors (_name + " removed one", _processors);
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
 
 int
-Route::configure_processors (ProcessorStreams* err)
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
 {
-       if (!_in_configure_processors) {
-               Glib::RWLock::WriterLock lm (_processor_lock);
-               return configure_processors_unlocked (err);
-       }
-       return 0;
-}
+       ProcessorList deleted;
 
-/** 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)
-{
-       if (_in_configure_processors) {
-          return 0;
+       if (!_session.engine().connected()) {
+               return 1;
        }
 
-       _in_configure_processors = true;
+       processor_max_streams.reset();
 
-       // Check each processor in order to see if we can configure as requested
-       ChanCount in = _configured_inputs;
-       ChanCount out;
-       list< pair<ChanCount,ChanCount> > configuration;
-       uint32_t index = 0;
-       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
-               (*p)->set_sort_key(index);
-               if ((*p)->can_support_io_configuration(in, out)) {
-                       configuration.push_back(make_pair(in, out));
-                       in = out;
-               } else {
-                       if (err) {
-                               err->index = index;
-                               err->count = in;
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
+
+               ProcessorList as_we_were = _processors;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+
+                       processor = *i;
+
+                       /* these can never be removed */
+
+                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                               ++i;
+                               continue;
                        }
-                       _in_configure_processors = false;
-                       return -1;
-               }
-       }
-       
-       // We can, so configure everything
-       list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
-       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
-               (*p)->configure_io(c->first, c->second);
-               (*p)->activate();
-               processor_max_streams = ChanCount::max(processor_max_streams, c->first);
-               processor_max_streams = ChanCount::max(processor_max_streams, c->second);
-               out = c->second;
-       }
 
-       // Ensure route outputs match last processor's outputs
-       if (out != n_outputs()) {
-               ensure_io(_configured_inputs, out, false, this);
-       }
+                       /* see if its in the list of processors to delete */
 
-       _in_configure_processors = false;
-       return 0;
-}
+                       if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
+                               ++i;
+                               continue;
+                       }
 
-void
-Route::all_processors_flip ()
-{
-       Glib::RWLock::ReaderLock lm (_processor_lock);
+                       /* stop IOProcessors that send to JACK ports
+                          from causing noise as a result of no longer being
+                          run.
+                       */
 
-       if (_processors.empty()) {
-               return;
-       }
+                       boost::shared_ptr<IOProcessor> iop;
+
+                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                               iop->disconnect ();
+                       }
+
+                       deleted.push_back (processor);
+                       i = _processors.erase (i);
+               }
+
+               if (deleted.empty()) {
+                       /* none of those in the requested list were found */
+                       return 0;
+               }
+
+               _output->set_user_latency (0);
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors = as_we_were;
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->is_generator()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* now try to do what we need to so that those that were removed will be deleted */
+
+       for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+               (*i)->drop_references ();
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
+
+       return 0;
+}
+
+/** Caller must hold process lock */
+int
+Route::configure_processors (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+       
+       if (!_in_configure_processors) {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               return configure_processors_unlocked (err);
+       }
+       return 0;
+}
+
+ChanCount
+Route::input_streams () const
+{
+        return _input->n_ports ();
+}
+
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       return try_configure_processors_unlocked (in, err);
+}
+
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
+       // Check each processor in order to see if we can configure as requested
+       ChanCount out;
+       list<pair<ChanCount, ChanCount> > configuration;
+       uint32_t index = 0;
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
+       DEBUG_TRACE (DEBUG::Processors, "{\n");
+
+       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+                       break;
+               }
+               
+               if ((*p)->can_support_io_configuration(in, out)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
+                       configuration.push_back(make_pair(in, out));
+                       in = out;
+               } else {
+                       if (err) {
+                               err->index = index;
+                               err->count = in;
+                       }
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, "}\n");
+                       return list<pair<ChanCount, ChanCount> > ();
+               }
+       }
+
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+       
+       return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ *  Caller must hold process lock.
+ *  Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       if (_in_configure_processors) {
+               return 0;
+       }
+
+       _in_configure_processors = true;
+
+       list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+       if (configuration.empty ()) {
+               _in_configure_processors = false;
+               return -1;
+       }
+
+       ChanCount out;
+
+       list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
+       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       break;
+               }
+               
+               (*p)->configure_io(c->first, c->second);
+               processor_max_streams = ChanCount::max(processor_max_streams, c->first);
+               processor_max_streams = ChanCount::max(processor_max_streams, c->second);
+               out = c->second;
+       }
+
+       if (_meter) {
+               _meter->reset_max_channels (processor_max_streams);
+       }
+
+       /* make sure we have sufficient scratch buffers to cope with the new processor
+          configuration */
+       _session.ensure_buffers (n_process_buffers ());
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
+
+       _in_configure_processors = false;
+       return 0;
+}
+
+void
+Route::all_processors_flip ()
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       if (_processors.empty()) {
+               return;
+       }
+
+       bool first_is_on = _processors.front()->active();
 
-       bool first_is_on = _processors.front()->active();
-       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (first_is_on) {
                        (*i)->deactivate ();
@@ -1206,7 +1621,7 @@ Route::all_processors_flip ()
                        (*i)->activate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1239,36 +1654,118 @@ Route::all_processors_active (Placement p, bool state)
                        }
                }
        }
-       
+
        _session.set_dirty ();
 }
 
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
+{
+       bool pre_fader = true;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* semantic note: if p == amp, we want to return true, so test
+                  for equality before checking if this is the amp
+               */
+
+               if ((*i) == p) {
+                       break;
+               }
+
+               if ((*i) == _amp) {
+                       pre_fader = false;
+                       break;
+               }
+       }
+
+       return pre_fader;
+}
+
 int
-Route::sort_processors (ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
+       /* "new_order" is an ordered list of processors to be positioned according to "placement".
+          NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
+          processors in the current actual processor list that are hidden. Any visible processors
+          in the current list but not in "new_order" will be assumed to be deleted.
+       */
+
        {
-               ProcessorSortByKey comparator;
                Glib::RWLock::WriterLock lm (_processor_lock);
                ChanCount old_pms = processor_max_streams;
+               ProcessorList::iterator oiter;
+               ProcessorList::const_iterator niter;
+               ProcessorList as_it_was_before = _processors;
+               ProcessorList as_it_will_be;
 
-               /* the sweet power of C++ ... */
+               oiter = _processors.begin();
+               niter = new_order.begin();
 
-               ProcessorList as_it_was_before = _processors;
+               while (niter !=  new_order.end()) {
 
-               dump_processors (_name + " PRESORT", _processors);
+                       /* if the next processor in the old list is invisible (i.e. should not be in the new order)
+                          then append it to the temp list.
 
-               _processors.sort (comparator);
-       
-               if (configure_processors_unlocked (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_streams = old_pms;
-                       return -1;
-               } 
-       } 
+                          Otherwise, see if the next processor in the old list is in the new list. if not,
+                          its been deleted. If its there, append it to the temp list.
+                       */
+
+                       if (oiter == _processors.end()) {
+
+                               /* no more elements in the old list, so just stick the rest of
+                                  the new order onto the temp list.
+                               */
+
+                               as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
+                               while (niter != new_order.end()) {
+                                       ++niter;
+                               }
+                               break;
+
+                       } else {
+
+                               if (!(*oiter)->display_to_user()) {
+
+                                       as_it_will_be.push_back (*oiter);
+
+                               } else {
+
+                                       /* visible processor: check that its in the new order */
+
+                                       if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) {
+                                               /* deleted: do nothing, shared_ptr<> will clean up */
+                                       } else {
+                                               /* ignore this one, and add the next item from the new order instead */
+                                               as_it_will_be.push_back (*niter);
+                                               ++niter;
+                                       }
+                               }
 
-       dump_processors (_name + " sorted", _processors);
-       reset_panner ();
-       processors_changed (); /* EMIT SIGNAL */
+                                /* now remove from old order - its taken care of no matter what */
+                               oiter = _processors.erase (oiter);
+                       }
+
+               }
+
+               _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               _processors = as_it_was_before;
+                               processor_max_streams = old_pms;
+                               return -1;
+                       }
+               }
+       }
+
+        if (true) {
+                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+               set_processor_positions ();
+        }
 
        return 0;
 }
@@ -1292,39 +1789,35 @@ Route::state(bool full_state)
        ProcessorList::iterator i;
        char buf[32];
 
+       id().print (buf, sizeof (buf));
+       node->add_property("id", buf);
+       node->add_property ("name", _name);
+       node->add_property("default-type", _default_type.to_string());
+
        if (_flags) {
                node->add_property("flags", enum_2_string (_flags));
        }
-       
-       node->add_property("default-type", _default_type.to_string());
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("muted", _muted?"yes":"no");
-       node->add_property("soloed", _soloed?"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("mute-affects-pre-fader", _mute_affects_pre_fader?"yes":"no"); 
-       node->add_property("mute-affects-post-fader", _mute_affects_post_fader?"yes":"no"); 
-       node->add_property("mute-affects-control-outs", _mute_affects_control_outs?"yes":"no"); 
-       node->add_property("mute-affects-main-outs", _mute_affects_main_outs?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
-       if (_edit_group) {
-               node->add_property("edit-group", _edit_group->name());
-       }
-       if (_mix_group) {
-               node->add_property("mix-group", _mix_group->name());
+       if (_route_group) {
+               node->add_property("route-group", _route_group->name());
        }
 
        string order_string;
-       OrderKeys::iterator x = order_keys.begin(); 
+       OrderKeys::iterator x = order_keys.begin();
 
        while (x != order_keys.end()) {
                order_string += string ((*x).first);
                order_string += '=';
                snprintf (buf, sizeof(buf), "%ld", (*x).second);
                order_string += buf;
-               
+
                ++x;
 
                if (x == order_keys.end()) {
@@ -1334,10 +1827,19 @@ Route::state(bool full_state)
                order_string += ':';
        }
        node->add_property ("order-keys", order_string);
-
-       node->add_child_nocopy (IO::state (full_state));
+       node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
+       node->add_property ("soloed-by-upstream", buf);
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
+       node->add_property ("soloed-by-downstream", buf);
+       node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
+       node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
+
+       node->add_child_nocopy (_input->state (full_state));
+       node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
        node->add_child_nocopy (_mute_control->get_state ());
+       node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
        snprintf (buf, sizeof (buf), "%d", _remote_control_id);
@@ -1356,196 +1858,203 @@ Route::state(bool full_state)
        if (_extra_xml){
                node->add_child_copy (*_extra_xml);
        }
-       
+
        return *node;
 }
 
-XMLNode&
-Route::get_processor_state ()
+int
+Route::set_state (const XMLNode& node, int version)
 {
-       XMLNode* root = new XMLNode (X_("redirects"));
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               root->add_child_nocopy ((*i)->state (true));
-       }
-
-       return *root;
+       return _set_state (node, version, true);
 }
 
 int
-Route::set_processor_state (const XMLNode& root)
+Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 {
-       if (root.name() != X_("redirects")) {
-               return -1;
+       if (version < 3000) {
+               return _set_state_2X (node, version);
        }
 
        XMLNodeList nlist;
-       XMLNodeList nnlist;
-       XMLNodeConstIterator iter;
        XMLNodeConstIterator niter;
-       Glib::RWLock::ReaderLock lm (_processor_lock);
+       XMLNode *child;
+       const XMLProperty *prop;
 
-       nlist = root.children();
-       
-       for (iter = nlist.begin(); iter != nlist.end(); ++iter){
+       if (node.name() != "Route"){
+               error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
+               return -1;
+       }
 
-               /* iter now points to a IOProcessor state node */
-               
-               nnlist = (*iter)->children ();
+       if ((prop = node.property (X_("name"))) != 0) {
+               Route::set_name (prop->value());
+       }
+
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
+       }
+
+       if ((prop = node.property (X_("flags"))) != 0) {
+               _flags = Flag (string_2_enum (prop->value(), _flags));
+       } else {
+               _flags = Flag (0);
+       }
 
-               for (niter = nnlist.begin(); niter != nnlist.end(); ++niter) {
+        if (is_master() || is_monitor() || is_hidden()) {
+                _mute_master->set_solo_ignore (true);
+        }
 
-                       /* find the IO child node, since it contains the ID we need */
+       /* add all processors (except amp, which is always present) */
 
-                       /* XXX OOP encapsulation violation, ugh */
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
 
-                       if ((*niter)->name() == IO::state_node_name) {
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
 
-                               XMLProperty* prop = (*niter)->property (X_("id"));
-                               
-                               if (!prop) {
-                                       warning << _("IOProcessor node has no ID, ignored") << endmsg;
-                                       break;
-                               }
+               child = *niter;
 
-                               ID id = prop->value ();
+               if (child->name() == IO::state_node_name) {
+                       if ((prop = child->property (X_("direction"))) == 0) {
+                               continue;
+                       }
 
-                               /* now look for a processor with that ID */
-       
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       if ((*i)->id() == id) {
-                                               (*i)->set_state (**iter);
-                                               break;
-                                       }
-                               }
-                               
-                               break;
-                               
+                       if (prop->value() == "Input") {
+                               _input->set_state (*child, version);
+                       } else if (prop->value() == "Output") {
+                               _output->set_state (*child, version);
                        }
                }
 
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
+               }
        }
 
-       return 0;
-}
+       set_processor_state (processor_state);
 
-void
-Route::set_deferred_state ()
-{
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
+       if ((prop = node.property ("self-solo")) != 0) {
+               set_self_solo (string_is_affirmative (prop->value()));
+       }
 
-       if (!deferred_state) {
-               return;
+       if ((prop = node.property ("soloed-by-upstream")) != 0) {
+               _soloed_by_others_upstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_upstream (atoi (prop->value()));
        }
 
-       nlist = deferred_state->children();
+       if ((prop = node.property ("soloed-by-downstream")) != 0) {
+               _soloed_by_others_downstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_downstream (atoi (prop->value()));
+       }
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               add_processor_from_xml (**niter);
+       if ((prop = node.property ("solo-isolated")) != 0) {
+               set_solo_isolated (string_is_affirmative (prop->value()), this);
        }
 
-       delete deferred_state;
-       deferred_state = 0;
-}
+       if ((prop = node.property ("solo-safe")) != 0) {
+               set_solo_safe (string_is_affirmative (prop->value()), this);
+       }
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator* iter)
-{
-       const XMLProperty *prop;
+       if ((prop = node.property (X_("phase-invert"))) != 0) {
+               set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
+       }
 
-       // legacy sessions use a different node name for sends
-       if (node.name() == "Send") {
-       
-               try {
-                       boost::shared_ptr<Send> send (new Send (_session, node));
-                       add_processor (send, 0, iter);
-                       return true;
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
-               }
-               
-       } else if (node.name() == "Processor") {
-               
-               try {
-                       if ((prop = node.property ("type")) != 0) {
+       if ((prop = node.property (X_("denormal-protection"))) != 0) {
+               set_denormal_protection (string_is_affirmative (prop->value()));
+       }
 
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = false;
+       if ((prop = node.property (X_("active"))) != 0) {
+               bool yn = string_is_affirmative (prop->value());
+               _active = !yn; // force switch
+               set_active (yn);
+       }
 
-                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
-                                   prop->value() == "lv2" ||
-                                   prop->value() == "vst" ||
-                                   prop->value() == "audiounit") {
-                                       
-                                       processor.reset (new PluginInsert(_session, node));
-                                       have_insert = true;
-                                       
-                               } else if (prop->value() == "port") {
+       if ((prop = node.property (X_("meter-point"))) != 0) {
+               MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
+                set_meter_point (mp);
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
+               }
+       }
 
-                                       processor.reset (new PortInsert (_session, node));
-                               
-                               } else if (prop->value() == "send") {
+       if ((prop = node.property (X_("order-keys"))) != 0) {
 
-                                       processor.reset (new Send (_session, node));
-                                       have_insert = true;
-                               
-                               } else if (prop->value() == "meter") {
+               int32_t n;
 
-                                       processor = _meter;
-                               
-                               } else if (prop->value() == "amp") {
-                                       
-                                       processor = _amp;
-                                       
-                               } else if (prop->value() == "listen" || prop->value() == "deliver") {
+               string::size_type colon, equal;
+               string remaining = prop->value();
 
-                                       /* XXX need to generalize */
+               while (remaining.length()) {
 
-                                       processor = _control_outs;
-                                       
-                               } else if (prop->value() == "main-outs") {
-                                       
-                                       processor = _main_outs;
-                                       
+                       if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
+                               error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                     << endmsg;
+                       } else {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
+                                       error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                             << endmsg;
                                } else {
-
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
-                               
-                               return (add_processor (processor, 0, iter) == 0);
-                               
+                       }
+
+                       colon = remaining.find_first_of (':');
+
+                       if (colon != string::npos) {
+                               remaining = remaining.substr (colon+1);
                        } else {
-                               error << _("Processor XML node has no type property") << endmsg;
+                               break;
                        }
                }
+       }
 
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
-                       return false;
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               child = *niter;
+
+               if (child->name() == X_("Comment")) {
+
+                       /* XXX this is a terrible API design in libxml++ */
+
+                       XMLNode *cmt = *(child->children().begin());
+                       _comment = cmt->content();
+
+               } else if (child->name() == X_("Extra")) {
+
+                       _extra_xml = new XMLNode (*child);
+
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == "solo") {
+                               _solo_control->set_state (*child, version);
+                       }
+
+               } else if (child->name() == X_("RemoteControl")) {
+                       if ((prop = child->property (X_("id"))) != 0) {
+                               int32_t x;
+                               sscanf (prop->value().c_str(), "%d", &x);
+                               set_remote_control_id (x);
+                       }
+
+               } else if (child->name() == X_("MuteMaster")) {
+                       _mute_master->set_state (*child, version);
                }
        }
-       return false;
-}
 
-int
-Route::set_state (const XMLNode& node)
-{
-       return _set_state (node, true);
+       return 0;
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state_2X (const XMLNode& node, int version)
 {
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       XMLPropertyList plist;
        const XMLProperty *prop;
 
-       if (node.name() != "Route"){
+       /* 2X things which still remain to be handled:
+        * default-type
+        * automation
+        * controlouts
+        */
+
+       if (node.name() != "Route") {
                error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
                return -1;
        }
@@ -1556,76 +2065,96 @@ Route::_set_state (const XMLNode& node, bool call_base)
                _flags = Flag (0);
        }
        
-       if ((prop = node.property (X_("default-type"))) != 0) {
-               _default_type = DataType(prop->value());
-               assert(_default_type != DataType::NIL);
-       }
-
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (prop->value()=="yes"?true:false, this);
+               boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
+               if (string_is_affirmative (prop->value ())) {
+                       p.set ();
+               }                       
+               set_phase_invert (p);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
-               set_denormal_protection (prop->value()=="yes"?true:false, this);
-       }
-       
-       _active = true;
-       if ((prop = node.property (X_("active"))) != 0) {
-               set_active (prop->value() == "yes");
-       }
-
-       if ((prop = node.property (X_("muted"))) != 0) {
-               bool yn = prop->value()=="yes"?true:false; 
-
-               /* force reset of mute status */
-
-               _muted = !yn;
-               set_mute(yn, this);
-               mute_gain = desired_mute_gain;
+               set_denormal_protection (string_is_affirmative (prop->value()));
        }
 
        if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = prop->value()=="yes"?true:false; 
+               bool yn = string_is_affirmative (prop->value());
 
-               /* force reset of solo status */
+               /* XXX force reset of solo status */
 
-               _soloed = !yn;
                set_solo (yn, this);
-               solo_gain = desired_solo_gain;
        }
 
-       if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
-               _mute_affects_pre_fader = (prop->value()=="yes")?true:false;
-       }
-
-       if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
-               _mute_affects_post_fader = (prop->value()=="yes")?true:false;
-       }
+       if ((prop = node.property (X_("muted"))) != 0) {
+               
+               bool first = true;
+               bool muted = string_is_affirmative (prop->value());
+               
+               if (muted){
+                 
+                       string mute_point;
+                       
+                       if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                       mute_point = mute_point + "PreFader";
+                                       first = false;
+                               }
+                       }
+                       
+                       if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                 
+                                       if (!first) {
+                                               mute_point = mute_point + ",";
+                                       }
+                                       
+                                       mute_point = mute_point + "PostFader";
+                                       first = false;
+                               }
+                       }
 
-       if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
-               _mute_affects_control_outs = (prop->value()=="yes")?true: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) {
-               _mute_affects_main_outs = (prop->value()=="yes")?true:false;
+                       if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
+                         
+                               if (string_is_affirmative (prop->value())){
+                                 
+                                       if (!first) {
+                                               mute_point = mute_point + ",";
+                                       }
+                                       
+                                       mute_point = mute_point + "Main";
+                               }
+                       }
+                       
+                       _mute_master->set_mute_points (mute_point);
+               }
        }
 
        if ((prop = node.property (X_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
-       
-       if ((prop = node.property (X_("edit-group"))) != 0) {
-               RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
-               if(edit_group == 0) {
-                       error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_edit_group(edit_group, this);
-               }
-       }
+
+       /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
+          don't mean the same thing.
+       */
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -1634,13 +2163,13 @@ Route::_set_state (const XMLNode& node, bool call_base)
 
                        if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
                                error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                     << endmsg;
+                                       << endmsg;
                        } else {
-                               if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                             << endmsg;
+                                               << endmsg;
                                } else {
-                                       set_order_key (remaining.substr (0, equal).c_str(), n);
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
                        }
 
@@ -1654,179 +2183,236 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       nlist = node.children();
+       /* IOs */
 
-       delete deferred_state;
-       deferred_state = new XMLNode(X_("deferred state"));
+       nlist = node.children ();
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-       /* set parent class properties before anything else */
+               child = *niter;
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               if (child->name() == IO::state_node_name) {
 
-               child = *niter;
+                       /* there is a note in IO::set_state_2X() about why we have to call
+                          this directly.
+                          */
 
-               if (child->name() == IO::state_node_name && call_base) {
-                       IO::set_state (*child);
-                       break;
-               }
-       }
+                       _input->set_state_2X (*child, version, true);
+                       _output->set_state_2X (*child, version, false);
+
+                       if ((prop = child->property (X_("name"))) != 0) {
+                               Route::set_name (prop->value ());
+                       }
+
+                       if ((prop = child->property (X_("id"))) != 0) {
+                               _id = prop->value ();
+                       }
 
-       XMLNodeList processor_nodes;
-       bool has_meter_processor = false; // legacy sessions don't
+                       if ((prop = child->property (X_("active"))) != 0) {
+                               bool yn = string_is_affirmative (prop->value());
+                               _active = !yn; // force switch
+                               set_active (yn);
+                       }
                        
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+                       if ((prop = child->property (X_("gain"))) != 0) {
+                               gain_t val;
 
-               child = *niter;
+                               if (sscanf (prop->value().c_str(), "%f", &val) == 1) {
+                                       _amp->gain_control()->set_value (val);
+                               }
+                       }
                        
-               if (child->name() == X_("Send") || child->name() == X_("Processor")) {
-                       processor_nodes.push_back(child);
-                       if ((prop = child->property (X_("type"))) != 0 && prop->value() == "meter")  {
-                               has_meter_processor = true;
+                       /* Set up Panners in the IO */
+                       XMLNodeList io_nlist = child->children ();
+                       
+                       XMLNodeConstIterator io_niter;
+                       XMLNode *io_child;
+                       
+                       for (io_niter = io_nlist.begin(); io_niter != io_nlist.end(); ++io_niter) {
+
+                               io_child = *io_niter;
+                               
+                               if (io_child->name() == X_("Panner")) {
+                                       _main_outs->panner()->set_state(*io_child, version);
+                               } else if (io_child->name() == X_("Automation")) {
+                                       /* IO's automation is for the fader */
+                                       _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
+                               }
                        }
                }
-
        }
 
-       _set_processor_states(processor_nodes);
-       if (!has_meter_processor) {
-               set_meter_point(_meter_point, NULL);
-       }
-       processors_changed ();
+       XMLNodeList redirect_nodes;
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
                child = *niter;
-               // All processors have been applied already
 
-               if (child->name() == X_("Automation")) {
-                       
-                       if ((prop = child->property (X_("path"))) != 0)  {
-                               load_automation (prop->value());
-                       }
+               if (child->name() == X_("Send") || child->name() == X_("Insert")) {
+                       redirect_nodes.push_back(child);
+               }
+
+       }
 
-               } else if (child->name() == X_("ControlOuts")) {
+       set_processor_state_2X (redirect_nodes, version);
 
-                       /* ignore this - deprecated */
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               child = *niter;
 
-               } else if (child->name() == X_("Comment")) {
+               if (child->name() == X_("Comment")) {
 
                        /* XXX this is a terrible API design in libxml++ */
 
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
+               } else if (child->name() == X_("extra")) {
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-                       
-                       if (prop->value() == "solo") {
-                               _solo_control->set_state (*child);
-                               _session.add_controllable (_solo_control);
-                       } else if (prop->value() == "mute") {
-                               _mute_control->set_state (*child);
-                               _session.add_controllable (_mute_control);
-                       }
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == X_("solo")) {
+                               _solo_control->set_state (*child, version);
+                       } else if (prop->value() == X_("mute")) {
+                               _mute_control->set_state (*child, version);
+                        }
+                                
                } else if (child->name() == X_("RemoteControl")) {
                        if ((prop = child->property (X_("id"))) != 0) {
                                int32_t x;
                                sscanf (prop->value().c_str(), "%d", &x);
                                set_remote_control_id (x);
                        }
-               }
+
+               } 
        }
 
-       if ((prop = node.property (X_("mix-group"))) != 0) {
-               RouteGroup* mix_group = _session.mix_group_by_name(prop->value());
-               if (mix_group == 0) {
-                       error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               }  else {
-                       set_mix_group(mix_group, this);
-               }
+       return 0;
+}
+
+XMLNode&
+Route::get_processor_state ()
+{
+       XMLNode* root = new XMLNode (X_("redirects"));
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               root->add_child_nocopy ((*i)->state (true));
+       }
+
+       return *root;
+}
+
+void
+Route::set_processor_state_2X (XMLNodeList const & nList, int version)
+{
+       /* We don't bother removing existing processors not in nList, as this
+          method will only be called when creating a Route from scratch, not
+          for undo purposes.  Just put processors in at the appropriate place
+          in the list.
+       */
+
+       for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
+               add_processor_from_xml_2X (**i, version);
        }
-
-       return 0;
 }
 
 void
-Route::_set_processor_states(const XMLNodeList &nlist)
+Route::set_processor_state (const XMLNode& node)
 {
+       const XMLNodeList &nlist = node.children();
        XMLNodeConstIterator niter;
+        ProcessorList new_order;
+        bool must_configure = false;
 
-       ProcessorList::iterator i, o;
-
-       // Iterate through existing processors, remove those which are not in the state list
-       for (i = _processors.begin(); i != _processors.end(); ) {
-               ProcessorList::iterator tmp = i;
-               ++tmp;
-
-               bool processorInStateList = false;
-       
-               for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-                       XMLProperty* id_prop = (*niter)->property(X_("id"));
-                       if (id_prop && (*i)->id() == id_prop->value()) {
-                               processorInStateList = true;
-                               break;
-                       }
-               }
-               
-               if (!processorInStateList) {
-                       remove_processor (*i);
-               }
-
-               i = tmp;
-       }
-
-       // Iterate through state list and make sure all processors are on the track and in the correct order,
-       // set the state of existing processors according to the new state on the same go
-       i = _processors.begin();
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
-               
                XMLProperty* prop = (*niter)->property ("type");
-               
-               o = i;
-
-               if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
 
-                       // Check whether the next processor in the list 
-                       
-                       while (o != _processors.end()) {
+               if (prop->value() == "amp") {
+                        _amp->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_amp);
+                } else if (prop->value() == "meter") {
+                        _meter->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_meter);
+                } else if (prop->value() == "main-outs") {
+                        _main_outs->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_main_outs);
+                } else if (prop->value() == "intreturn") {
+                        if (!_intreturn) {
+                                _intreturn.reset (new InternalReturn (_session));
+                                must_configure = true;
+                        }
+                        _intreturn->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_intreturn);
+                } else if (is_monitor() && prop->value() == "monitor") {
+                        if (!_monitor_control) {
+                                _monitor_control.reset (new MonitorProcessor (_session));
+                                must_configure = true;
+                        }
+                        _monitor_control->set_state (**niter, Stateful::current_state_version);
+                        new_order.push_back (_monitor_control);
+                } else {
+                        ProcessorList::iterator o;
+
+                       for (o = _processors.begin(); o != _processors.end(); ++o) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
+                                        (*o)->set_state (**niter, Stateful::current_state_version);
+                                        new_order.push_back (*o);
                                        break;
                                }
-                               
-                               ++o;
-                       }
-               }
-
-               // If the processor (*niter) is not on the route,
-               // create it and move it to the correct location
-               if (o == _processors.end()) {
-
-                       if (add_processor_from_xml (**niter, &i)) {
-                               --i; // move iterator to the newly inserted processor
-                       } else {
-                               cerr << "Error restoring route: unable to restore processor" << endl;
                        }
 
-               // Otherwise, the processor already exists; just
-               // ensure it is at the location provided in the XML state
-               } else {
+                        // If the processor (*niter) is not on the route then create it 
+                        
+                        if (o == _processors.end()) {
+                                
+                                boost::shared_ptr<Processor> processor;
+
+                                if (prop->value() == "intsend") {
+                                        
+                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+                                        
+                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+                                           prop->value() == "lv2" ||
+                                           prop->value() == "vst" ||
+                                           prop->value() == "audiounit") {
+                                        
+                                        processor.reset (new PluginInsert(_session));
+                                        
+                                } else if (prop->value() == "port") {
+                                        
+                                        processor.reset (new PortInsert (_session, _mute_master));
+                                        
+                                } else if (prop->value() == "send") {
+                                        
+                                        processor.reset (new Send (_session, _mute_master));
+                                        
+                                } else {
+                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                                        continue;
+                                }
+
+                               if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+                                       /* This processor could not be configured.  Turn it into a UnknownProcessor */
+                                       processor.reset (new UnknownProcessor (_session, **niter));
+                               }
+                                       
+                               new_order.push_back (processor);
+                               must_configure = true;
+                        }
+                }
+        }
 
-                       if (i != o) {
-                               boost::shared_ptr<Processor> tmp = (*o);
-                               cerr << "move proc from state\n";
-                               _processors.erase (o); // remove the old copy
-                               _processors.insert (i, tmp); // insert the processor at the correct location
-                               --i; // move iterator to the correct processor
-                       }
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+                _processors = new_order;
+                if (must_configure) {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                        configure_processors_unlocked (0);
+                }
+        }
 
-                       (*i)->set_state (**niter);
-               }
-       }
+        processors_changed (RouteProcessorChange ());
+       set_processor_positions ();
 }
 
 void
@@ -1837,297 +2423,280 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
 {
-       if (!_silent) {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return;
+       }
 
-               IO::silence (nframes);
-               
-               { 
-                       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-                       
-                       if (lm.locked()) {
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       boost::shared_ptr<PluginInsert> pi;
+       silence_unlocked (nframes);
+}
 
-                                       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);
-                               }
+void
+Route::silence_unlocked (framecnt_t nframes)
+{
+       /* Must be called with the processor lock held */
+       
+       if (!_silent) {
 
-                               if (nframes == _session.get_block_size()) {
-                                       // _silent = true;
-                               }
+               _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;
+               }
+       }
+}
+
+void
+Route::add_internal_return ()
+{
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
        }
-}      
+}
 
-boost::shared_ptr<Delivery>
-Route::add_listener (boost::shared_ptr<IO> io, const string& listen_name)
+BufferSet*
+Route::get_return_buffer () const
 {
-       string name = _name;
-       name += '[';
-       name += listen_name;
-       name += ']';
-       
-       boost::shared_ptr<Delivery> listener (new Delivery (_session, name, Delivery::Listen)); 
+       Glib::RWLock::ReaderLock rm (_processor_lock);
 
-       /* As an IO, our control outs need as many IO outputs as we have outputs
-        *   (we track the changes in ::output_change_handler()).
-        * As a processor, the listener is an identity processor
-        *   (i.e. it does not modify its input buffers whatsoever)
-        */
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
-       if (listener->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this)) {
-               return boost::shared_ptr<Delivery>();
+               if (d) {
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
+               }
        }
-       
-       listener->set_sort_key (_meter->sort_key() + 1);
-       add_processor (listener, NULL);
 
-       return listener;
+       return 0;
+}
+
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+
+               if (d) {
+                       return d->release_buffers ();
+               }
+       }
 }
 
 int
-Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
 {
        vector<string> ports;
        vector<string>::const_iterator i;
 
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
-               
-               for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       boost::shared_ptr<const Delivery> d = boost::dynamic_pointer_cast<const Delivery>(*x);
 
-                       if (d && d->io() == io) {
+               for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
+
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+                       if (d && d->target_route() == route) {
+
+                               /* if the target is the control outs, then make sure
+                                  we take note of which i-send is doing that.
+                               */
+
+                               if (route == _session.monitor_out()) {
+                                       _monitor_send = boost::dynamic_pointer_cast<Delivery>(d);
+                               }
+
                                /* already listening via the specified IO: do nothing */
+
                                return 0;
                        }
                }
        }
 
-       uint32_t ni = io->n_inputs().n_total();
+       boost::shared_ptr<InternalSend> listener;
 
-       for (uint32_t n = 0; n < ni; ++n) {
-               ports.push_back (io->input(n)->name());
-       }
+       try {
 
-       if (ports.empty()) {
-               return 0;
-       }
-       
-       boost::shared_ptr<Delivery> listen_point = add_listener (io, listen_name);
-       
-       /* XXX hack for now .... until we can generalize listen points */
+                if (is_master()) {
+                        
+                        if (route == _session.monitor_out()) {
+                                /* master never sends to control outs */
+                                return 0;
+                        } else {
+                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                        }
 
-       _control_outs = listen_point;
+                } else {
+                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                }
 
-       /* now connect to the named ports */
-       
-       ni = listen_point->io()->n_outputs().n_total();
-       size_t psize = ports.size();
+       } catch (failed_constructor& err) {
+               return -1;
+       }
 
-       for (size_t n = 0; n < ni; ++n) {
-               if (listen_point->io()->connect_output (listen_point->io()->output (n), ports[n % psize], this)) {
-                       error << string_compose (_("could not connect %1 to %2"),
-                                                listen_point->io()->output(n)->name(), ports[n % psize]) << endmsg;
-                       return -1;
-               }
+       if (route == _session.monitor_out()) {
+               _monitor_send = listener;
        }
 
-       
-       return 0;
-}      
+        if (placement == PostFader) {
+                /* put it *really* at the end, not just after the panner (main outs)
+                 */
+                add_processor (listener, _processors.end());
+        } else {
+                add_processor (listener, PreFader);
+        }
+
+       return 0;
+}
 
 void
-Route::drop_listen (boost::shared_ptr<IO> io)
+Route::drop_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rm (_processor_lock);
-       
+       Glib::RWLock::ReaderLock rl(_processor_lock);
+       rl.acquire ();
+
+  again:
        for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
-               
-               tmp = x;
-               ++tmp;
-               
-               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*x);
-               
-               if (d && d->io() == io) {
-                       /* already listening via the specified IO: do nothing */
+
+               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+               if (d && d->target_route() == route) {
+                       rl.release ();
                        remove_processor (*x, &err);
-                       
-               } 
-               
-               x = tmp;
-       }
-}
+                       rl.acquire ();
 
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+                        /* list could have been demolished while we dropped the lock
+                          so start over.
+                       */
 
-{
-       if (eg == _edit_group) {
-               return;
+                       goto again;
+               }
        }
 
-       if (_edit_group) {
-               _edit_group->remove (this);
-       }
+       rl.release ();
 
-       if ((_edit_group = eg) != 0) {
-               _edit_group->add (this);
+       if (route == _session.monitor_out()) {
+               _monitor_send.reset ();
        }
-
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_edit_group (void *src)
+Route::set_comment (string cmt, void *src)
 {
-       _edit_group = 0;
+       _comment = cmt;
+       comment_changed (src);
        _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
-void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+bool
+Route::add_fed_by (boost::shared_ptr<Route> other, bool via_sends_only)
 {
-       if (mg == _mix_group) {
-               return;
-       }
+        FeedRecord fr (other, via_sends_only);
 
-       if (_mix_group) {
-               _mix_group->remove (this);
-       }
+        pair<FedBy::iterator,bool> result =  _fed_by.insert (fr);
 
-       if ((_mix_group = mg) != 0) {
-               _mix_group->add (this);
-       }
+        if (!result.second) {
 
-       _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+                /* already a record for "other" - make sure sends-only information is correct */
+                if (!via_sends_only && result.first->sends_only) {
+                        FeedRecord* frp = const_cast<FeedRecord*>(&(*result.first));
+                        frp->sends_only = false;
+                }
+        }
+        
+        return result.second;
 }
 
 void
-Route::drop_mix_group (void *src)
+Route::clear_fed_by ()
 {
-       _mix_group = 0;
-       _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+        _fed_by.clear ();
 }
 
-void
-Route::set_comment (string cmt, void *src)
+bool
+Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
 {
-       _comment = cmt;
-       comment_changed (src);
-       _session.set_dirty ();
+        const FedBy& fed_by (other->fed_by());
+
+        for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
+                boost::shared_ptr<Route> sr = f->r.lock();
+
+                if (sr && (sr.get() == this)) {
+
+                        if (via_sends_only) {
+                                *via_sends_only = f->sends_only;
+                        }
+
+                        return true;
+                }
+        }
+
+        return false;
 }
 
 bool
-Route::feeds (boost::shared_ptr<Route> other)
+Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
 {
-       uint32_t i, j;
-
-       IO& self = *this;
-       uint32_t no = self.n_outputs().n_total();
-       uint32_t ni = other->n_inputs ().n_total();
+       DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
 
-       for (i = 0; i < no; ++i) {
-               for (j = 0; j < ni; ++j) {
-                       if (self.output(i)->connected_to (other->input(j)->name())) {
-                               return true;
-                       }
+       if (_output->connected_to (other->input())) {
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
+               if (only_send) {
+                       *only_send = false;
                }
+
+               return true;
        }
 
-       /* check IOProcessors which may also interconnect Routes */
+       
+       for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
-       for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
+               boost::shared_ptr<IOProcessor> iop;
 
-               boost::shared_ptr<IOProcessor> proc = boost::dynamic_pointer_cast<IOProcessor>(*r);
-               
-               if (!proc) {
-                       continue;
-               }
-               
-               no = proc->io()->n_outputs().n_total();
-               
-               for (i = 0; i < no; ++i) {
-                       for (j = 0; j < ni; ++j) {
-                               if (proc->io()->output(i)->connected_to (other->input (j)->name())) {
-                                       return true;
+               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
+                       if (iop->feeds (other)) {
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
+                               if (only_send) {
+                                       *only_send = true;
                                }
+                               return true;
+                       } else {
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name()));
                        }
+               } else {
+                       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tPROC %1 is not an IOP\n", (*r)->name()));
                }
+                       
        }
 
+       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tdoes NOT feed %1\n", other->name()));
        return false;
 }
 
+/** Called from the (non-realtime) butler thread when the transport is stopped */
 void
-Route::set_mute_config (mute_type t, bool onoff, void *src)
-{
-       switch (t) {
-       case PRE_FADER:
-               _mute_affects_pre_fader = onoff;
-               pre_fader_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case POST_FADER:
-               _mute_affects_post_fader = onoff;
-               post_fader_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case CONTROL_OUTS:
-               _mute_affects_control_outs = onoff;
-               control_outs_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case MAIN_OUTS:
-               _mute_affects_main_outs = onoff;
-               main_outs_changed(src); /* EMIT SIGNAL */
-               break;
-       }
-}
-
-bool
-Route::get_mute_config (mute_type t)
-{
-       bool onoff = false;
-       
-       switch (t){
-       case PRE_FADER:
-               onoff = _mute_affects_pre_fader; 
-               break;
-       case POST_FADER:
-               onoff = _mute_affects_post_fader;
-               break;
-       case CONTROL_OUTS:
-               onoff = _mute_affects_control_outs;
-               break;
-       case MAIN_OUTS:
-               onoff = _mute_affects_main_outs;
-               break;
-       }
-       
-       return onoff;
-}
-
-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);
@@ -2136,39 +2705,68 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
                        automation_snapshot (now, true);
                }
 
+                Automatable::transport_stopped (now);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
+
                        if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
-                               (*i)->deactivate ();
-                               (*i)->activate ();
+                                (*i)->flush ();
                        }
-                       
+
                        (*i)->transport_stopped (now);
                }
        }
 
-       IO::transport_stopped (now);
        _roll_delay = _initial_delay;
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
-Route::input_change_handler (IOChange change, void *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
                configure_processors (0);
+               _phase_invert.resize (_input->n_ports().n_audio ());
+               io_changed (); /* EMIT SIGNAL */
        }
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
-Route::output_change_handler (IOChange change, void *src)
+Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
-               if (_control_outs) {
-                       _control_outs->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this);
+       if ((change.type & IOChange::ConfigurationChanged)) {
+
+               /* XXX resize all listeners to match _main_outs? */
+
+               /* Auto-connect newly-created outputs, unless we're auto-connecting to master
+                  and we are master (as an auto-connect in this situation would cause a
+                  feedback loop)
+               */
+               AutoConnectOption ac = Config->get_output_auto_connect ();
+               if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) {
+
+                       ChanCount start = change.before;
+                       
+                       for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+                               if (change.before.get(*i) < change.after.get(*i)) {
+                                       /* the existing ChanCounts don't matter for this call as they are only
+                                          to do with matching input and output indices, and we are only changing
+                                          outputs here.
+                                       */
+                                       ChanCount dummy;
+
+                                       /* only auto-connect the newly-created outputs, not the ones that were
+                                          already there
+                                       */
+                                       start.set (*i, start.get (*i) + 1);
+                                       
+                                       _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before);
+                               }
+                       }
                }
-               
-               configure_processors (0);
+
+               // configure_processors (0);
        }
 }
 
@@ -2178,53 +2776,67 @@ Route::pans_required () const
        if (n_outputs().n_audio() < 2) {
                return 0;
        }
-       
+
        return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
 }
 
-int 
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,  
-               bool session_state_changing, bool can_record, bool rec_monitors_input)
+int
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
+               bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
-       if (n_outputs().n_total() == 0) {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
                return 0;
        }
 
-       if (session_state_changing || !_active)  {
-               silence (nframes);
+       if (n_outputs().n_total() == 0) {
                return 0;
        }
 
-       _amp->apply_gain_automation(false);
-       
-       if (n_inputs() != ChanCount::ZERO) {
-               passthru (start_frame, end_frame, nframes, 0);
-       } else {
-               silence (nframes);
+       if (!_active || n_inputs() == ChanCount::ZERO)  {
+               silence_unlocked (nframes);
+               return 0;
+       }
+       if (session_state_changing) {
+               if (_session.transport_speed() != 0.0f) {
+                       /* we're rolling but some state is changing (e.g. our diskstream contents)
+                          so we cannot use them. Be silent till this is over.
+                          
+                          XXX note the absurdity of ::no_roll() being called when we ARE rolling!
+                       */
+                       silence_unlocked (nframes);
+                       return 0;
+               }
+               /* we're really not rolling, so we're either delivery silence or actually
+                  monitoring, both of which are safe to do while session_state_changing is true.
+               */
        }
 
+       _amp->apply_gain_automation (false);
+       passthru (start_frame, end_frame, nframes, 0);
+
        return 0;
 }
 
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
                _roll_delay -= nframes;
-               silence (nframes);
+               silence_unlocked (nframes);
                /* transport frame is not legal for caller to use */
                return 0;
 
        } else if (_roll_delay > 0) {
 
                nframes -= _roll_delay;
-               silence (_roll_delay);
-               /* we've written _roll_delay of samples into the 
+               silence_unlocked (_roll_delay);
+               /* we've written _roll_delay of samples into the
                   output ports, so make a note of that for
                   future reference.
                */
-               increment_output_offset (_roll_delay);
+               _main_outs->increment_output_offset (_roll_delay);
                transport_frame += _roll_delay;
 
                _roll_delay = 0;
@@ -2234,54 +2846,41 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 }
 
 int
-Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
-            bool can_record, bool rec_monitors_input)
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
+            bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
-       {
-               Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-               if (lm.locked()) {
-                       // automation snapshot can also be called from the non-rt context
-                       // and it uses the processor list, so we take the lock out here
-                       automation_snapshot (_session.transport_frame(), false);
-               }
-       }
-
-       if ((n_outputs().n_total() == 0 && _processors.empty()) || n_inputs().n_total() == 0 || !_active) {
-               silence (nframes);
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
                return 0;
        }
        
-       nframes_t unused = 0;
+       automation_snapshot (_session.transport_frame(), false);
 
-       if ((nframes = check_initial_delay (nframes, unused)) == 0) {
+       if (n_outputs().n_total() == 0) {
                return 0;
        }
 
-       _silent = false;
+       if (!_active || n_inputs().n_total() == 0) {
+               silence_unlocked (nframes);
+               return 0;
+       }
 
-       _amp->apply_gain_automation(false);
+       framecnt_t unused = 0;
 
-       { 
-               Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK);
-               
-               if (am.locked() && _session.transport_rolling()) {
-                       
-                       if (_gain_control->automation_playback()) {
-                               _amp->apply_gain_automation(
-                                               _gain_control->list()->curve().rt_safe_get_vector (
-                                                       start_frame, end_frame, _session.gain_automation_buffer(), nframes));
-                       }
-               }
+       if ((nframes = check_initial_delay (nframes, unused)) == 0) {
+               return 0;
        }
 
+       _silent = false;
+
        passthru (start_frame, end_frame, nframes, declick);
 
        return 0;
 }
 
 int
-Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, 
-                   bool can_record, bool rec_monitors_input)
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
+                   bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
        return 0;
@@ -2290,7 +2889,7 @@ Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_fram
 void
 Route::toggle_monitor_input ()
 {
-       for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+       for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) {
                i->ensure_monitor_input( ! i->monitoring_input());
        }
 }
@@ -2298,21 +2897,21 @@ Route::toggle_monitor_input ()
 bool
 Route::has_external_redirects () const
 {
-       // FIXME: what about sends?
+       // FIXME: what about sends? - they don't return a signal back to ardour?
 
        boost::shared_ptr<const PortInsert> pi;
-       
+
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
                if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
 
-                       for (PortSet::const_iterator port = pi->io()->outputs().begin();
-                                       port != pi->io()->outputs().end(); ++port) {
-                               
+                       for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
+
                                string port_name = port->name();
                                string client_name = port_name.substr (0, port_name.find(':'));
 
                                /* only say "yes" if the redirect is actually in use */
-                               
+
                                if (client_name != "ardour" && pi->active()) {
                                        return true;
                                }
@@ -2320,87 +2919,153 @@ Route::has_external_redirects () const
                }
        }
 
-       return false;
-}
-
-void
-Route::flush_processors ()
-{
-       /* XXX shouldn't really try to take this lock, since
-          this is called from the RT audio thread.
-       */
-
-       Glib::RWLock::ReaderLock lm (_processor_lock);
+       return false;
+}
+
+void
+Route::flush_processors ()
+{
+       /* XXX shouldn't really try to take this lock, since
+          this is called from the RT audio thread.
+       */
+
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                (*i)->flush ();
+       }
+}
+
+void
+Route::set_meter_point (MeterPoint p)
+{
+       /* CAN BE CALLED FROM PROCESS CONTEXT */
+
+       if (_meter_point == p) {
+               return;
+       }
+
+       bool meter_was_visible_to_user = _meter->display_to_user ();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+       
+               if (p != MeterCustom) {
+                       // Move meter in the processors list to reflect the new position
+                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+                       _processors.erase(loc);
+                       switch (p) {
+                       case MeterInput:
+                               loc = _processors.begin();
+                               break;
+                       case MeterPreFader:
+                               loc = find (_processors.begin(), _processors.end(), _amp);
+                               break;
+                       case MeterPostFader:
+                               loc = _processors.end();
+                               break;
+                       default:
+                               break;
+                       }
+                       
+                       ChanCount m_in;
+                       
+                       if (loc == _processors.begin()) {
+                               m_in = _input->n_ports();
+                       } else {
+                               ProcessorList::iterator before = loc;
+                               --before;
+                               m_in = (*before)->output_streams ();
+                       }
+                       
+                       _meter->reflect_inputs (m_in);
+                       
+                       _processors.insert (loc, _meter);
+                       
+                       /* we do not need to reconfigure the processors, because the meter
+                          (a) is always ready to handle processor_max_streams
+                          (b) is always an N-in/N-out processor, and thus moving
+                          it doesn't require any changes to the other processors.
+                       */
+                       
+                       _meter->set_display_to_user (false);
+                       
+               } else {
+                       
+                       // just make it visible and let the user move it
+                       
+                       _meter->set_display_to_user (true);
+               }
+       }
+
+       _meter_point = p;
+       meter_change (); /* EMIT SIGNAL */
 
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
-               (*i)->activate ();
-       }
+       bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+       
+       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
 }
 
 void
-Route::set_meter_point (MeterPoint p, void *src)
+Route::put_monitor_send_at (Placement p)
 {
-       if (_meter_point != p) {
-               _meter_point = p;
+       if (!_monitor_send) {
+               return;
+       }
 
-               // Move meter in the processors list
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList as_it_was (_processors);
+               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send);
                _processors.erase(loc);
+               
                switch (p) {
-               case MeterInput:
-                       loc = _processors.begin();
-                       break;
-               case MeterPreFader:
+               case PreFader:
                        loc = find(_processors.begin(), _processors.end(), _amp);
+                       if (loc != _processors.begin()) {
+                               --loc;
+                       }
                        break;
-               case MeterPostFader:
+               case PostFader:
                        loc = _processors.end();
                        break;
                }
-               _processors.insert(loc, _meter);
                
-               // Update sort key
-               if (loc == _processors.end()) {
-                       _meter->set_sort_key(_processors.size());
-               } else {
-                       _meter->set_sort_key((*loc)->sort_key());
-                       for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) {
-                               (*p)->set_sort_key((*p)->sort_key() + 1);
+               _processors.insert (loc, _monitor_send);
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       
+                       if (configure_processors_unlocked (0)) {
+                               _processors = as_it_was;
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
                        }
                }
-
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
        }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
 }
 
-nframes_t
+framecnt_t
 Route::update_total_latency ()
 {
-       nframes_t old = _own_latency;
-
-       if (_user_latency) {
-               _own_latency = _user_latency;
-       } else {
-               _own_latency = 0;
+       framecnt_t old = _output->effective_latency();
+       framecnt_t own_latency = _output->user_latency();
 
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((*i)->active ()) {
-                               _own_latency += (*i)->signal_latency ();
-                       }
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       own_latency += (*i)->signal_latency ();
                }
        }
 
-#undef DEBUG_LATENCY
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << _own_latency << endl;
-#endif
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency));
+
+       _output->set_port_latency (own_latency);
+
+       if (_output->user_latency() == 0) {
 
-       set_port_latency (_own_latency);
-       
-       if (!_user_latency) {
                /* this (virtual) function is used for pure Routes,
                   not derived classes like AudioTrack.  this means
                   that the data processed here comes from an input
@@ -2408,36 +3073,33 @@ Route::update_total_latency ()
                   have to take into account any input latency.
                */
 
-
-               _own_latency += input_latency ();
+               own_latency += _input->signal_latency ();
        }
 
-       if (old != _own_latency) {
+       if (old != own_latency) {
+               _output->set_latency_delay (own_latency);
                signal_latency_changed (); /* EMIT SIGNAL */
        }
-       
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << input_latency() << " total = "
-            << _own_latency << endl;
-#endif
 
-       return _own_latency;
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency));
+
+       return _output->effective_latency ();
 }
 
 void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
 {
-       Latent::set_user_latency (nframes);
+       _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
 void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_delay (framecnt_t longest_session_latency)
 {
-       nframes_t old = _initial_delay;
+       framecnt_t old = _initial_delay;
 
-       if (_own_latency < longest_session_latency) {
-               _initial_delay = longest_session_latency - _own_latency;
+       if (_output->effective_latency() < longest_session_latency) {
+               _initial_delay = longest_session_latency - _output->effective_latency();
        } else {
                _initial_delay = 0;
        }
@@ -2452,75 +3114,97 @@ 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)
 {
-       if (!force && !should_snapshot(now)) {
-               return;
-       }
-
-       IO::automation_snapshot (now, force);
-
+       panner()->automation_snapshot (now, force);
+       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
 }
 
-Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp)
-       : Controllable (name), route (s), type(tp)
+Route::SoloControllable::SoloControllable (std::string name, Route& r)
+       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
+                            boost::shared_ptr<AutomationList>(), name)
+       , route (r)
 {
-       
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
+       set_list (gl);
 }
 
 void
-Route::ToggleControllable::set_value (float val)
+Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-       
-       switch (type) {
-       case MuteControl:
-               route.set_mute (bval, this);
-               break;
-       case SoloControl:
-               route.set_solo (bval, this);
-               break;
-       default:
-               break;
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+
+       if (Config->get_solo_control_is_listen_control()) {
+               _session.set_listen (rl, bval);
+       } else {
+               _session.set_solo (rl, bval);
        }
+#else
+       route.set_solo (bval, this);
+#endif
 }
 
-float
-Route::ToggleControllable::get_value (void) const
+double
+Route::SoloControllable::get_value (void) const
 {
-       float val = 0.0f;
-       
-       switch (type) {
-       case MuteControl:
-               val = route.muted() ? 1.0f : 0.0f;
-               break;
-       case SoloControl:
-               val = route.soloed() ? 1.0f : 0.0f;
-               break;
-       default:
-               break;
+       if (Config->get_solo_control_is_listen_control()) {
+               return route.listening() ? 1.0f : 0.0f;
+       } else {
+               return route.self_soloed() ? 1.0f : 0.0f;
        }
+}
+
+Route::MuteControllable::MuteControllable (std::string name, Route& r)
+       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+                            boost::shared_ptr<AutomationList>(), name)
+       , route (r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+       set_list (gl);
+}
 
-       return val;
+void
+Route::MuteControllable::set_value (double val)
+{
+       bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+       _session.set_mute (rl, bval);
+#else
+       route.set_mute (bval, this);
+#endif
 }
 
-void 
-Route::set_block_size (nframes_t nframes)
+double
+Route::MuteControllable::get_value (void) const
+{
+       return route.muted() ? 1.0f : 0.0f;
+}
+
+void
+Route::set_block_size (pframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
-       _session.ensure_buffers(processor_max_streams);
+       
+       _session.ensure_buffers (n_process_buffers ());
 }
 
 void
 Route::protect_automation ()
 {
-       Automatable::protect_automation();
-       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i)
                (*i)->protect_automation();
 }
@@ -2548,44 +3232,54 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (framepos_t pos, framecnt_t frames)
 {
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
        /* gain automation */
-       XMLNode &before = _gain_control->get_state ();
-       _gain_control->shift (pos, frames);
-       XMLNode &after = _gain_control->get_state ();
-       _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+        {
+                boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
+                
+                XMLNode &before = gc->alist()->get_state ();
+                gc->alist()->shift (pos, frames);
+                XMLNode &after = gc->alist()->get_state ();
+                _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+        }
 
        /* pan automation */
-       for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
-               Curve & c = (*i)->automation ();
-               XMLNode &before = c.get_state ();
-               c.shift (pos, frames);
-               XMLNode &after = c.get_state ();
-               _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
-       }
+        {
+                boost::shared_ptr<AutomationControl> pc;
+                uint32_t npans = _main_outs->panner()->npanners();
+
+                for (uint32_t p = 0; p < npans; ++p) {
+                        pc = _main_outs->panner()->pan_control (0, p);
+                        if (pc) {
+                                boost::shared_ptr<AutomationList> al = pc->alist();
+                                XMLNode& before = al->get_state ();
+                                al->shift (pos, frames);
+                                XMLNode& after = al->get_state ();
+                                _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                        }
+                }
+        }
 
        /* redirect automation */
        {
-               Glib::RWLock::ReaderLock lm (redirect_lock);
-               for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-                       
-                       set<uint32_t> a;
-                       (*i)->what_has_automation (a);
-                       
-                       for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
-                               AutomationList & al = (*i)->automation_list (*j);
-                               XMLNode &before = al.get_state ();
-                               al.shift (pos, frames);
-                               XMLNode &after = al.get_state ();
-                               _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
-                       }
-               }
-       }
-#endif
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+                       set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
 
+                       for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+                                boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+                                if (ac) {
+                                        boost::shared_ptr<AutomationList> al = ac->alist();
+                                        XMLNode &before = al->get_state ();
+                                        al->shift (pos, frames);
+                                        XMLNode &after = al->get_state ();
+                                        _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                                }
+                        }
+                }
+       }
 }
 
 
@@ -2594,9 +3288,9 @@ Route::save_as_template (const string& path, const string& name)
 {
        XMLNode& node (state (false));
        XMLTree tree;
-       
+
        IO::set_name_in_state (*node.children().front(), name);
-       
+
        tree.set_root (&node);
        return tree.write (path.c_str());
 }
@@ -2607,23 +3301,25 @@ Route::set_name (const string& str)
 {
        bool ret;
        string ioproc_name;
+       string name;
+
+       name = Route::ensure_track_or_route_name (str, _session);
+       SessionObject::set_name (name);
+
+       ret = (_input->set_name(name) && _output->set_name(name));
+
+       if (ret) {
 
-       if ((ret = IO::set_name (str)) == true) {
                Glib::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
-                       /* rename all delivery objects to reflect our new name */
 
-                       boost::shared_ptr<Delivery> dp = boost::dynamic_pointer_cast<Delivery> (*i);
+                       /* rename all I/O processors that have inputs or outputs */
 
-                       if (dp) {
-                               string dp_name = str;
-                               dp_name += '[';
-                               dp_name += "XXX FIX ME XXX";
-                               dp_name += ']';
-                               
-                               if (!dp->set_name (dp_name)) {
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
+
+                       if (iop && (iop->output() || iop->input())) {
+                               if (!iop->set_name (name)) {
                                        ret = false;
                                }
                        }
@@ -2633,3 +3329,249 @@ Route::set_name (const string& str)
 
        return ret;
 }
+
+boost::shared_ptr<Send>
+Route::internal_send_for (boost::shared_ptr<const Route> target) const
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<InternalSend> send;
+
+               if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+                       if (send->target_route() == target) {
+                               return send;
+                       }
+               }
+       }
+
+       return boost::shared_ptr<Send>();
+}
+
+/** @param c Audio channel index.
+ *  @param yn true to invert phase, otherwise false.
+ */
+void
+Route::set_phase_invert (uint32_t c, bool yn)
+{
+       if (_phase_invert[c] != yn) {
+               _phase_invert[c] = yn;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
+       }
+}
+
+void
+Route::set_phase_invert (boost::dynamic_bitset<> p)
+{
+       if (_phase_invert != p) {
+               _phase_invert = p;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
+       }
+}
+
+bool
+Route::phase_invert (uint32_t c) const
+{
+       return _phase_invert[c];
+}
+
+boost::dynamic_bitset<>
+Route::phase_invert () const
+{
+       return _phase_invert;
+}
+
+void
+Route::set_denormal_protection (bool yn)
+{
+       if (_denormal_protection != yn) {
+               _denormal_protection = yn;
+               denormal_protection_changed (); /* EMIT SIGNAL */
+       }
+}
+
+bool
+Route::denormal_protection () const
+{
+       return _denormal_protection;
+}
+
+void
+Route::set_active (bool yn)
+{
+       if (_active != yn) {
+               _active = yn;
+               _input->set_active (yn);
+               _output->set_active (yn);
+               active_changed (); // EMIT SIGNAL
+       }
+}
+
+void
+Route::meter ()
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+
+       assert (_meter);
+
+       _meter->meter ();
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               boost::shared_ptr<Send> s;
+               boost::shared_ptr<Return> r;
+
+               if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
+                       s->meter()->meter();
+               } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
+                       r->meter()->meter ();
+               }
+       }
+}
+
+boost::shared_ptr<Panner>
+Route::panner() const
+{
+       return _main_outs->panner();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::gain_control() const
+{
+       return _amp->gain_control();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::get_control (const Evoral::Parameter& param)
+{
+       /* either we own the control or .... */
+
+       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(control (param));
+
+       if (!c) {
+
+               /* maybe one of our processors does or ... */
+
+               Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->control (param))) != 0) {
+                               break;
+                       }
+               }
+       }
+
+       if (!c) {
+
+               /* nobody does so we'll make a new one */
+
+               c = boost::dynamic_pointer_cast<AutomationControl>(control_factory(param));
+               add_control(c);
+       }
+
+       return c;
+}
+
+boost::shared_ptr<Processor>
+Route::nth_plugin (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+boost::shared_ptr<Processor>
+Route::nth_send (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<Send> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               } 
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+bool
+Route::has_io_processor_named (const string& name)
+{
+        Glib::RWLock::ReaderLock lm (_processor_lock);
+        ProcessorList::iterator i;
+        
+        for (i = _processors.begin(); i != _processors.end(); ++i) {
+                if (boost::dynamic_pointer_cast<Send> (*i) ||
+                    boost::dynamic_pointer_cast<PortInsert> (*i)) {
+                        if ((*i)->name() == name) {
+                                return true;
+                        }
+                }
+        }
+        
+        return false;
+}
+
+MuteMaster::MutePoint
+Route::mute_points () const
+{
+       return _mute_master->mute_points ();
+}
+
+void
+Route::set_processor_positions ()
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       bool had_amp = false;
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->set_pre_fader (!had_amp);
+               if (boost::dynamic_pointer_cast<Amp> (*i)) {
+                       had_amp = true;
+               }
+       }
+}
+
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+       list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+       if (c.empty()) {
+               /* The processors cannot be configured with the new input arrangement, so
+                  block the change.
+               */
+               return true;
+       }
+
+       /* The change is ok */
+       return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+       list<string> p;
+       
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+                       p.push_back ((*i)->name ());
+               }
+       }
+
+       return p;
+}