make i18n build work ; add mackie dir back to build ; token work on amp for MIDI...
[ardour.git] / libs / ardour / route.cc
index 740e5949b481904c2b0513b5eb805769188231c5..56311c975db1063cb0fe929bbfd11cab0ec59c83 100644 (file)
 #include <cmath>
 #include <fstream>
 #include <cassert>
+#include <algorithm>
 
 #include <sigc++/bind.h>
-#include <pbd/xml++.h>
-#include <pbd/enumwriter.h>
-#include <pbd/stacktrace.h>
-
-#include <ardour/timestamps.h>
-#include <ardour/audioengine.h>
-#include <ardour/route.h>
-#include <ardour/buffer.h>
-#include <ardour/processor.h>
-#include <ardour/plugin_insert.h>
-#include <ardour/port_insert.h>
-#include <ardour/send.h>
-#include <ardour/session.h>
-#include <ardour/utils.h>
-#include <ardour/configuration.h>
-#include <ardour/cycle_timer.h>
-#include <ardour/route_group.h>
-#include <ardour/port.h>
-#include <ardour/audio_port.h>
-#include <ardour/ladspa_plugin.h>
-#include <ardour/panner.h>
-#include <ardour/dB.h>
-#include <ardour/amp.h>
-#include <ardour/meter.h>
-#include <ardour/buffer_set.h>
-#include <ardour/mix.h>
-#include <ardour/profile.h>
+#include "pbd/xml++.h"
+#include "pbd/enumwriter.h"
+#include "pbd/stacktrace.h"
+#include "pbd/memento_command.h"
+
+#include "evoral/Curve.hpp"
+
+#include "ardour/amp.h"
+#include "ardour/audio_port.h"
+#include "ardour/audioengine.h"
+#include "ardour/buffer.h"
+#include "ardour/buffer_set.h"
+#include "ardour/configuration.h"
+#include "ardour/cycle_timer.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/panner.h"
+#include "ardour/plugin_insert.h"
+#include "ardour/port.h"
+#include "ardour/port_insert.h"
+#include "ardour/processor.h"
+#include "ardour/profile.h"
+#include "ardour/route.h"
+#include "ardour/route_group.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
+#include "ardour/timestamps.h"
+#include "ardour/utils.h"
 
 #include "i18n.h"
 
@@ -57,80 +64,104 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void> Route::SyncOrderKeys;
+sigc::signal<void, string const &> Route::SyncOrderKeys;
 
-Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type)
-       : IO (sess, name, input_min, input_max, output_min, output_max, default_type),
-         _flags (flg),
-         _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)),
-         _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
+Route::Route (Session& sess, string name, Flag flg, DataType default_type)
+       : SessionObject (sess, name)
+       , AutomatableControls (sess)
+       , _flags (flg)
+       , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_master (new MuteMaster (sess, name))
+       , _default_type (default_type)
+         
 {
        init ();
+       
+       /* add standard processors other than amp (added by ::init()) */
+       
+       _meter.reset (new PeakMeter (_session));
+       add_processor (_meter, PreFader);
+
+       if (_flags & ControlOut) {
+               /* where we listen to tracks */
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+       
+       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+       add_processor (_main_outs, PostFader);
+
+       /* now that we have _meter, its safe to connect to this */
+
+       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
 }
 
 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))
+       : SessionObject (sess, "toBeReset")
+       , AutomatableControls (sess)
+       , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_master (new MuteMaster (sess, "toBeReset"))
+       , _default_type (default_type)
 {
        init ();
+
        _set_state (node, false);
+
+       /* now that we have _meter, its safe to connect to this */
+       
+       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
 }
 
 void
 Route::init ()
 {
-       processor_max_outs.reset();
-       _muted = false;
-       _soloed = false;
+       _solo_level = 0;
+       _solo_isolated = false;
+       _active = true;
+       processor_max_streams.reset();
        _solo_safe = false;
        _recordable = true;
-       _active = true;
-       _phase_invert = false;
-       _denormal_protection = false;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
+       order_keys[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;
+       _route_group = 0;
+
+       _phase_invert = 0;
+       _denormal_protection = false;
+
+       /* 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();
+       add_control (_solo_control);
+       add_control (_mute_master);
        
-       solo_gain = 1.0;
-       desired_solo_gain = 1.0;
-       mute_gain = 1.0;
-       desired_mute_gain = 1.0;
+       /* input and output objects */
 
-       _control_outs = 0;
+       _input.reset (new IO (_session, _name, IO::Input, _default_type));
+       _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       input_changed.connect (mem_fun (this, &Route::input_change_handler));
-       output_changed.connect (mem_fun (this, &Route::output_change_handler));
+       _input->changed.connect (mem_fun (this, &Route::input_change_handler));
+       _output->changed.connect (mem_fun (this, &Route::output_change_handler));
+
+       /* add amp processor  */
+
+       _amp.reset (new Amp (_session, _mute_master));
+       add_processor (_amp, PostFader);
 }
 
 Route::~Route ()
 {
+       Metering::disconnect (_meter_connection);
+
        clear_processors (PreFader);
        clear_processors (PostFader);
-
-       for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
-               free ((void*)(i->first));
-       }
-
-       if (_control_outs) {
-               delete _control_outs;
-       }
 }
 
 void
@@ -149,23 +180,20 @@ Route::remote_control_id() const
 }
 
 long
-Route::order_key (const char* name) const
+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, long n)
 {
-       order_keys[strdup(name)] = n;
+       order_keys[name] = n;
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
@@ -176,21 +204,33 @@ Route::set_order_key (const char* name, long n)
        _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 ()
+Route::sync_order_keys (std::string const & base)
 {
-       uint32_t key;
-       
        if (order_keys.empty()) {
                return;
        }
-       
-       OrderKeys::iterator x = order_keys.begin();
-       key = x->second;
-       ++x;
 
-       for (; x != order_keys.end(); ++x) {
-               x->second = key;
+       OrderKeys::iterator i;
+       uint32_t key;
+
+       if ((i = order_keys.find (base)) == order_keys.end()) {
+               /* key doesn't exist, use the first existing key (during session initialization) */
+               i = order_keys.begin();
+               key = i->second;
+               ++i;
+       } else {
+               /* key exists - use it and reset all others (actually, itself included) */
+               key = i->second;
+               i = order_keys.begin();
+       }
+
+       for (; i != order_keys.end(); ++i) {
+               i->second = key;
        }
 }
 
@@ -199,8 +239,7 @@ Route::ensure_track_or_route_name(string name, Session &session)
 {
        string newname = name;
 
-       while (session.route_by_name (newname)!=NULL)
-       {
+       while (session.route_by_name (newname) != NULL) {
                newname = bump_name_once (newname);
        }
 
@@ -211,17 +250,17 @@ Route::ensure_track_or_route_name(string name, Session &session)
 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 (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) {
                
-               if (_mix_group->is_relative()) {
-                       
-                       gain_t usable_gain = gain();
+               if (_route_group->is_relative()) {
+
+                       gain_t usable_gain = _amp->gain();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
@@ -239,34 +278,34 @@ 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)
@@ -275,978 +314,620 @@ Route::set_gain (gain_t val, void *src)
  * @param start_frame Initial transport frame 
  * @param end_frame Final transport frame
  * @param nframes Number of frames to output (to ports)
- * @param offset Output offset (of port buffers, for split cycles)
  *
  * Note that (end_frame - start_frame) may not be equal to nframes when the
  * transport speed isn't 1.0 (eg varispeed).
  */
 void
 Route::process_output_buffers (BufferSet& bufs,
-                              nframes_t start_frame, nframes_t end_frame, 
-                              nframes_t nframes, nframes_t offset, bool with_processors, int declick,
-                              bool meter)
+                              sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
+                              bool /*with_processors*/, int declick)
 {
-       // This is definitely very audio-only for now
-       assert(_default_type == DataType::AUDIO);
-       
-       ProcessorList::iterator i;
-       bool post_fader_work = false;
-       bool mute_declick_applied = false;
-       gain_t dmg, dsg, dg;
-       IO *co;
-       bool mute_audible;
-       bool solo_audible;
-       bool no_monitor;
-       gain_t* gab = _session.gain_automation_buffer();
+       bool monitor;
+
+       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;
+               monitor = true;
        }
 
-       declick = _pending_declick;
-
-       {
-               Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK);
-               
-               if (cm.locked()) {
-                       co = _control_outs;
-               } else {
-                       co = 0;
-               }
+       if (!declick) {
+               declick = _pending_declick;
        }
+
+       /* figure out if we're going to use gain automation */
+       _amp->setup_gain_automation (start_frame, end_frame, nframes);
        
-       { 
-               Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
-               
-               if (dm.locked()) {
-                       dmg = desired_mute_gain;
-                       dsg = desired_solo_gain;
-                       dg = _desired_gain;
-               } else {
-                       dmg = mute_gain;
-                       dsg = solo_gain;
-                       dg = _gain;
-               }
-       }
 
-       /* ----------------------------------------------------------------------------------------------------
+       /* tell main outs what to do about monitoring */
+       _main_outs->no_outs_cuz_we_no_monitor (!monitor);
+
+
+       /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
-          -------------------------------------------------------------------------------------------------- */
+          ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::run_in_place (bufs, nframes, 0.0, 1.0, false);
-               _pending_declick = 0;
+               Amp::apply_gain (bufs, nframes, 0.0, 1.0);
        } else if (declick < 0) {
-               Amp::run_in_place (bufs, nframes, 1.0, 0.0, false);
-               _pending_declick = 0;
-       } else {
-
-               /* no global declick */
-
-               if (solo_gain != dsg) {
-                       Amp::run_in_place (bufs, nframes, solo_gain, dsg, false);
-                       solo_gain = dsg;
-               }
-       }
-
-
-       /* ----------------------------------------------------------------------------------------------------
-          INPUT METERING & MONITORING
-          -------------------------------------------------------------------------------------------------- */
-
-       if (meter && (_meter_point == MeterInput)) {
-               _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
-       }
-
-       if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) {
-               Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
+               Amp::apply_gain (bufs, nframes, 1.0, 0.0);
+       } 
 
-       if ((_meter_point == MeterInput) && co) {
+       _pending_declick = 0;
                
-               solo_audible = dsg > 0;
-               mute_audible = dmg > 0;// || !_mute_affects_pre_fader;
-               
-               if (    // muted by solo of another track
-                       
-                       !solo_audible || 
-                       
-                       // muted by mute of this track 
-                       
-                       !mute_audible ||
-                       
-                       // rec-enabled but not s/w monitoring 
-                       
-                       // TODO: this is probably wrong
+       /* -------------------------------------------------------------------------------------------
+          DENORMAL CONTROL/PHASE INVERT
+          ----------------------------------------------------------------------------------------- */
 
-                       (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
-
-                       ) {
-                       
-                       co->silence (nframes, offset);
-                       
-               } else {
+       if (_phase_invert) {
+               
+               int chn = 0;
 
-                       co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
+               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();
 
-       /* -----------------------------------------------------------------------------------------------------
-          DENORMAL CONTROL
-          -------------------------------------------------------------------------------------------------- */
+                               if (_phase_invert & chn) {
+                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx]  = -sp[nx];
+                                               sp[nx] += 1.0e-27f;
+                                       }
+                               } else {
+                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx] += 1.0e-27f;
+                                       }
+                               }
+                       }
 
-       if (_denormal_protection || Config->get_denormal_protection()) {
+               } else {
 
-               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
-                       Sample* const sp = i->data();
-                       
-                       for (nframes_t nx = offset; nx < nframes + offset; ++nx) {
-                               sp[nx] += 1.0e-27f;
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
+                               Sample* const sp = i->data();
+                               
+                               if (_phase_invert & chn) {
+                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                               sp[nx] = -sp[nx];
+                                       }
+                               } 
                        }
                }
-       }
 
-       /* ----------------------------------------------------------------------------------------------------
-          PRE-FADER REDIRECTS
-          -------------------------------------------------------------------------------------------------- */
+       } else {
 
-       if (with_processors) {
-               Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-               if (rm.locked()) {
-                       if (mute_gain > 0 || !_mute_affects_pre_fader) {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       switch ((*i)->placement()) {
-                                       case PreFader:
-                                               (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
-                                               break;
-                                       case PostFader:
-                                               post_fader_work = true;
-                                               break;
-                                       }
-                               }
-                       } else {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       switch ((*i)->placement()) {
-                                       case PreFader:
-                                               (*i)->silence (nframes, offset);
-                                               break;
-                                       case PostFader:
-                                               post_fader_work = true;
-                                               break;
-                                       }
+               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 (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       sp[nx] += 1.0e-27f;
                                }
                        }
+
                } 
        }
 
-       // This really should already be true...
-       bufs.set_count(pre_fader_streams());
-       
-       if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) {
-               Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
+       /* -------------------------------------------------------------------------------------------
+          and go ....
+          ----------------------------------------------------------------------------------------- */
 
-       /* ----------------------------------------------------------------------------------------------------
-          PRE-FADER METERING & MONITORING
-          -------------------------------------------------------------------------------------------------- */
+       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
 
-       if (meter && (_meter_point == MeterPreFader)) {
-               _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
-       }
+       if (rm.locked()) {
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->input_streams()));
+                       (*i)->run (bufs, start_frame, end_frame, nframes);
+                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
+               }
 
-       
-       if ((_meter_point == MeterPreFader) && co) {
-               
-               solo_audible = dsg > 0;
-               mute_audible = dmg > 0 || !_mute_affects_pre_fader;
-               
-               if ( // muted by solo of another track
-                       
-                       !solo_audible || 
-                       
-                       // muted by mute of this track 
-                       
-                       !mute_audible ||
-                       
-                       // rec-enabled but not s/w monitoring 
-                       
-                       (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+               if (!_processors.empty()) {
+                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+               }
+       }
+}
 
-                       ) {
-                       
-                       co->silence (nframes, offset);
-                       
-               } else {
+ChanCount
+Route::n_process_buffers ()
+{
+       return max (_input->n_ports(), processor_max_streams);
+}
 
-                       co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
-               } 
-       } 
-       
-       /* ----------------------------------------------------------------------------------------------------
-          GAIN STAGE
-          -------------------------------------------------------------------------------------------------- */
+void
+Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+{
+       BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
-       /* if not recording or recording and requiring any monitor signal, then apply gain */
+       _silent = false;
 
-       if ( // not recording 
+       assert (bufs.available() >= _input->n_ports());
+       
+       if (_input->n_ports() == ChanCount::ZERO) {
+               silence (nframes);
+       }
+       
+       bufs.set_count (_input->n_ports());
 
-               !(record_enabled() && _session.actively_recording()) || 
+       if (is_control() && _session.listening()) {
                
-           // OR recording 
-               
-                // AND software monitoring required
+               /* 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);
 
-               Config->get_monitoring_model() == SoftwareMonitoring) { 
-               
-               if (apply_gain_automation) {
+       } else {
+       
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        
-                       if (_phase_invert) {
-                               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] *= -gab[nx];
-                                       }
-                               }
-                       } else {
-                               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] *= gab[nx];
-                                       }
-                               }
-                       }
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
                        
-                       if (apply_gain_automation && _session.transport_rolling() && nframes > 0) {
-                               _effective_gain = gab[nframes-1];
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
                        }
-                       
-               } else {
-                       
-                       /* manual (scalar) gain */
-                       
-                       if (_gain != dg) {
-                               
-                               Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert);
-                               _gain = dg;
-                               
-                       } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) {
-                               
-                               /* no need to interpolate current gain value,
-                                  but its non-unity, so apply it. if the gain
-                                  is zero, do nothing because we'll ship silence
-                                  below.
-                               */
+               }
+       }
 
-                               gain_t this_gain;
-                               
-                               if (_phase_invert) {
-                                       this_gain = -_gain;
-                               } else {
-                                       this_gain = _gain;
-                               }
-                               
-                               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
-                                       Sample* const sp = i->data();
-                                       apply_gain_to_buffer(sp,nframes,this_gain);
-                               }
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+}
 
-                       } else if (_gain == 0) {
-                               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
-                                       i->clear();
-                               }
+void
+Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+{
+       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+}
+
+void
+Route::set_listen (bool yn, void* src)
+{
+       if (_control_outs) { 
+               if (yn != _control_outs->active()) {
+                       if (yn) {
+                               _control_outs->activate ();
+                       } else {
+                               _control_outs->deactivate ();
                        }
+
+                       listen_changed (src); /* EMIT SIGNAL */
                }
+       }
+}
 
+bool
+Route::listening () const
+{
+       if (_control_outs) {
+               return _control_outs->active ();
        } else {
+               return false;
+       }
+}
 
-               /* actively recording, no monitoring required; leave buffers as-is to save CPU cycles */
+void
+Route::set_solo (bool yn, void *src)
+{
+       if (_solo_safe || _solo_isolated) {
+               return;
+       }
 
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo, yn, _route_group);
+               return;
        }
 
-       /* ----------------------------------------------------------------------------------------------------
-          POST-FADER REDIRECTS
-          -------------------------------------------------------------------------------------------------- */
+       if (soloed() != yn) {
+               mod_solo_level (yn ? 1 : -1);
+               solo_changed (src); /* EMIT SIGNAL */
+               _solo_control->Changed (); /* EMIT SIGNAL */
+       }       
+}
 
-       /* note that post_fader_work cannot be true unless with_processors was also true, so don't test both */
+void
+Route::mod_solo_level (int32_t delta)
+{
+       if (delta < 0) {
+               if (_solo_level >= (uint32_t) delta) {
+                       _solo_level += delta;
+               } else {
+                       _solo_level = 0;
+               }
+       } else {
+               _solo_level += delta;
+       }
 
-       if (post_fader_work) {
+       /* tell main outs what the solo situation is
+        */
 
-               Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-               if (rm.locked()) {
-                       if (mute_gain > 0 || !_mute_affects_post_fader) {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       switch ((*i)->placement()) {
-                                       case PreFader:
-                                               break;
-                                       case PostFader:
-                                               (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
-                                               break;
-                                       }
-                               }
-                       } else {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       switch ((*i)->placement()) {
-                                       case PreFader:
-                                               break;
-                                       case PostFader:
-                                               (*i)->silence (nframes, offset);
-                                               break;
-                                       }
-                               }
-                       }
-               } 
-       }
+       _main_outs->set_solo_level (_solo_level);
+       _main_outs->set_solo_isolated (_solo_isolated);
+}
 
-       if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) {
-               Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
+void
+Route::set_solo_isolated (bool yn, void *src)
+{
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               return;
        }
 
-       /* ----------------------------------------------------------------------------------------------------
-          CONTROL OUTPUT STAGE
-          -------------------------------------------------------------------------------------------------- */
+       if (yn != _solo_isolated) {
+               _solo_isolated = yn;
 
-       if ((_meter_point == MeterPostFader) && co) {
+               /* tell main outs what the solo situation is
+                */
                
-               solo_audible = solo_gain > 0;
-               mute_audible = dmg > 0 || !_mute_affects_control_outs;
+               _main_outs->set_solo_level (_solo_level);
+               _main_outs->set_solo_isolated (_solo_isolated);
 
-               if ( // silent anyway
+               solo_isolated_changed (src);
+       }
+}
 
-                       (_gain == 0 && !apply_gain_automation) || 
-                   
-                     // muted by solo of another track
+bool
+Route::solo_isolated () const 
+{
+       return _solo_isolated;
+}
 
-                       !solo_audible || 
-                   
-                     // muted by mute of this track 
+void
+Route::set_mute (bool yn, void *src)
+{
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) {
+               _route_group->apply (&Route::set_mute, yn, _route_group);
+               return;
+       }
 
-                       !mute_audible ||
+       if (muted() != yn) {
+               _mute_master->mute (yn);
+               mute_changed (src);
+       }
+}      
 
-                   // recording but not s/w monitoring 
-                       
-                       (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+bool
+Route::muted() const 
+{
+       return _mute_master->muted ();
+}
 
-                       ) {
-                       
-                       co->silence (nframes, offset);
-                       
-               } else {
+#if 0
+static void
+dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
+{
+       cerr << name << " {" << endl;
+       for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
+                       p != procs.end(); ++p) {
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
+       }
+       cerr << "}" << endl;
+}
+#endif
 
-                       co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
-               } 
-       } 
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
+{
+       ProcessorList::iterator loc;
 
-       /* ----------------------------------------------------------------------
-          GLOBAL MUTE 
-          ----------------------------------------------------------------------*/
+       /* 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.
+       */
 
-       if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) {
-               Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
+       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);
        }
-       
-       /* ----------------------------------------------------------------------------------------------------
-          MAIN OUTPUT STAGE
-          -------------------------------------------------------------------------------------------------- */
 
-       solo_audible = dsg > 0;
-       mute_audible = dmg > 0 || !_mute_affects_main_outs;
-       
-       if (n_outputs().get(_default_type) == 0) {
-           
-           /* relax */
+       return add_processor (processor, loc, err);
+}
 
-       } else if (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) {
-               
-               IO::silence (nframes, offset);
-               
-       } else {
 
-               if ( // silent anyway
+/** Add a processor to the route.
+ * If @a iter is not NULL, it 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, ProcessorList::iterator iter, ProcessorStreams* err)
+{
+       ChanCount old_pms = processor_max_streams;
 
-                   (_gain == 0 && !apply_gain_automation) ||
-                   
-                   // muted by solo of another track, but not using control outs for solo
+       if (!_session.engine().connected() || !processor) {
+               return 1;
+       }
 
-                   (!solo_audible && (Config->get_solo_model() != SoloBus)) ||
-                   
-                   // muted by mute of this track
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
 
-                   !mute_audible
+               boost::shared_ptr<PluginInsert> pi;
+               boost::shared_ptr<PortInsert> porti;
 
-                       ) {
+               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
 
-                       /* don't use Route::silence() here, because that causes
-                          all outputs (sends, port processors, etc. to be silent).
-                       */
-                       
-                       if (_meter_point == MeterPostFader) {
-                               peak_meter().reset();
+               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 == loc) { // Already in place, do nothing
+                                       return 0;
+                               } else { // New position given, relocate
+                                       _processors.erase (loc);
+                               }
                        }
 
-                       IO::silence (nframes, offset);
-                       
                } else {
-                       
-                       deliver_output(bufs, start_frame, end_frame, nframes, offset);
+                       if (loc != _processors.end()) {
+                               cerr << "ERROR: Processor added to route twice!" << endl;
+                               return 1;
+                       }
 
+                       loc = iter;
                }
 
-       }
+               _processors.insert (loc, processor);
 
-       /* ----------------------------------------------------------------------------------------------------
-          POST-FADER METERING
-          -------------------------------------------------------------------------------------------------- */
+               // Set up processor list channels.  This will set processor->[input|output]_streams(),
+               // configure redirect ports properly, etc.
+               
 
-       if (meter && (_meter_point == MeterPostFader)) {
-               if ((_gain == 0 && !apply_gain_automation) || dmg == 0) {
-                       _meter->reset();
-               } else {
-                       _meter->run_in_place(output_buffers(), start_frame, end_frame, nframes, offset);
+               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;
                }
-       }
-}
-
-#ifdef NEW_POB
-/** 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 end_frame Final transport frame
- * @param nframes Number of frames to output (to ports)
- * @param offset Output offset (of port buffers, for split cycles)
- *
- * Note that (end_frame - start_frame) may not be equal to nframes when the
- * transport speed isn't 1.0 (eg varispeed).
- */
-void
-Route::process_output_buffers (BufferSet& bufs,
-                              nframes_t start_frame, nframes_t end_frame, 
-                              nframes_t nframes, nframes_t offset, bool with_processors, int declick,
-                              bool meter)
-{
-       // This is definitely very audio-only for now
-       assert(_default_type == DataType::AUDIO);
        
-       ProcessorList::iterator i;
-       bool post_fader_work = false;
-       bool mute_declick_applied = false;
-       gain_t dmg, dsg, dg;
-       IO *co;
-       bool mute_audible;
-       bool solo_audible;
-       bool no_monitor;
-       gain_t* gab = _session.gain_automation_buffer();
-
-       switch (Config->get_monitoring_model()) {
-       case HardwareMonitoring:
-       case ExternalMonitoring:
-               no_monitor = true;
-               break;
-       default:
-               no_monitor = false;
-       }
-
-       declick = _pending_declick;
-
-       {
-               Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK);
-               
-               if (cm.locked()) {
-                       co = _control_outs;
-               } else {
-                       co = 0;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+                       
+                       if (pi->natural_input_streams() == ChanCount::ZERO) {
+                               /* generator plugin */
+                               _have_internal_generator = true;
+                       }
+                       
                }
-       }
-       
-       { 
-               Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
                
-               if (dm.locked()) {
-                       dmg = desired_mute_gain;
-                       dsg = desired_solo_gain;
-                       dg = _desired_gain;
-               } else {
-                       dmg = mute_gain;
-                       dsg = solo_gain;
-                       dg = _gain;
-               }
-       }
-
-       /* ----------------------------------------------------------------------------------------------------
-          GLOBAL DECLICK (for transport changes etc.)
-          input metering & monitoring (control outs)
-          denormal control
-          pre-fader redirects
-          pre-fader metering & monitoring (control outs)
-          gain stage
-          post-fader redirects
-          global mute
-          main output
-          post-fader metering & monitoring (control outs)
-       */
+               // 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));
 
-       {
-               Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-               for (i = processors.begin(); i != processors.end(); ++i) {
-                       (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
-               }
+               _output->set_user_latency (0);
        }
+       
+       processors_changed (); /* EMIT SIGNAL */
+       
+       return 0;
+}
 
-       /* ----------------------------------------------------------------------------------------------------
-          INPUT METERING & MONITORING
-          -------------------------------------------------------------------------------------------------- */
-
-       if (meter && (_meter_point == MeterInput)) {
-               _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
-       }
+bool
+Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
+{
+       const XMLProperty *prop;
 
-       if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) {
-               Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
+       if (node.name() != "Processor") {
+               return false;
        }
-
-       /* ----------------------------------------------------------------------------------------------------
-          PRE-FADER REDIRECTS
-          -------------------------------------------------------------------------------------------------- */
-
-       // This really should already be true...
-       bufs.set_count(pre_fader_streams());
-       
-
-       if ((_meter_point == MeterPreFader) && co) {
                
-               solo_audible = dsg > 0;
-               mute_audible = dmg > 0 || !_mute_affects_pre_fader;
-               
-               if ( // muted by solo of another track
-                       
-                       !solo_audible || 
-                       
-                       // muted by mute of this track 
-                       
-                       !mute_audible ||
+       try {
+               if ((prop = node.property ("type")) != 0) {
                        
-                       // rec-enabled but not s/w monitoring 
-                       
-                       (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+                       boost::shared_ptr<Processor> processor;
 
-                       ) {
-                       
-                       co->silence (nframes, offset);
-                       
-               } else {
+                       if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
+                           prop->value() == "lv2" ||
+                           prop->value() == "vst" ||
+                           prop->value() == "audiounit") {
+                                       
+                               processor.reset (new PluginInsert(_session, node));
+                                       
+                       } else if (prop->value() == "port") {
 
-                       co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
-               } 
-       } 
-       
-       /* ----------------------------------------------------------------------------------------------------
-          GAIN STAGE
-          -------------------------------------------------------------------------------------------------- */
+                               processor.reset (new PortInsert (_session, _mute_master, node));
+                               
+                       } else if (prop->value() == "send") {
 
-       /* if not recording or recording and requiring any monitor signal, then apply gain */
+                               processor.reset (new Send (_session, _mute_master, node));
 
-       if ( // not recording 
+                       } else if (prop->value() == "meter") {
 
-               !(record_enabled() && _session.actively_recording()) || 
-               
-           // OR recording 
-               
-                // AND software monitoring required
-
-               Config->get_monitoring_model() == SoftwareMonitoring) { 
-               
-               if (apply_gain_automation) {
-                       
-                       if (_phase_invert) {
-                               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] *= -gab[nx];
-                                       }
-                               }
-                       } else {
-                               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] *= gab[nx];
+                               if (_meter) {
+                                       if (_meter->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
                                        }
                                }
-                       }
-                       
-                       if (apply_gain_automation && _session.transport_rolling() && nframes > 0) {
-                               _effective_gain = gab[nframes-1];
-                       }
-                       
-               } else {
-                       
-                       /* manual (scalar) gain */
-                       
-                       if (_gain != dg) {
-                               
-                               Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert);
-                               _gain = dg;
-                               
-                       } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) {
-                               
-                               /* no need to interpolate current gain value,
-                                  but its non-unity, so apply it. if the gain
-                                  is zero, do nothing because we'll ship silence
-                                  below.
-                               */
 
-                               gain_t this_gain;
+                               _meter.reset (new PeakMeter (_session, node));                                          
+                               processor = _meter;
                                
-                               if (_phase_invert) {
-                                       this_gain = -_gain;
-                               } else {
-                                       this_gain = _gain;
-                               }
-                               
-                               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
-                                       Sample* const sp = i->data();
-                                       apply_gain_to_buffer(sp,nframes,this_gain);
-                               }
+                       } else if (prop->value() == "amp") {
 
-                       } else if (_gain == 0) {
-                               for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
-                                       i->clear();
+                               /* amp always exists */
+                                       
+                               processor = _amp;
+                               if (processor->set_state (node)) {
+                                       return false;
+                               } else {
+                                       /* never any reason to add it */
+                                       return true;
                                }
-                       }
-               }
-
-       } else {
-
-               /* actively recording, no monitoring required; leave buffers as-is to save CPU cycles */
-
-       }
-
-
-       /* ----------------------------------------------------------------------------------------------------
-          CONTROL OUTPUT STAGE
-          -------------------------------------------------------------------------------------------------- */
-
-       if ((_meter_point == MeterPostFader) && co) {
-               
-               solo_audible = solo_gain > 0;
-               mute_audible = dmg > 0 || !_mute_affects_control_outs;
-
-               if ( // silent anyway
-
-                       (_gain == 0 && !apply_gain_automation) || 
-                   
-                     // muted by solo of another track
-
-                       !solo_audible || 
-                   
-                     // muted by mute of this track 
-
-                       !mute_audible ||
-
-                   // recording but not s/w monitoring 
-                       
-                       (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
-
-                       ) {
-                       
-                       co->silence (nframes, offset);
-                       
-               } else {
-
-                       co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
-               } 
-       } 
-
-       /* ----------------------------------------------------------------------------------------------------
-          MAIN OUTPUT STAGE
-          -------------------------------------------------------------------------------------------------- */
-
-       solo_audible = dsg > 0;
-       mute_audible = dmg > 0 || !_mute_affects_main_outs;
-       
-       if (n_outputs().get(_default_type) == 0) {
-           
-           /* relax */
-
-       } else if (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) {
-               
-               IO::silence (nframes, offset);
-               
-       } else {
-
-               if ( // silent anyway
+                                       
+                       } else if (prop->value() == "intsend") {
 
-                   (_gain == 0 && !apply_gain_automation) ||
-                   
-                   // muted by solo of another track, but not using control outs for solo
+                               processor.reset (new InternalSend (_session, _mute_master, node));
 
-                   (!solo_audible && (Config->get_solo_model() != SoloBus)) ||
-                   
-                   // muted by mute of this track
+                       } else if (prop->value() == "intreturn") {
+                                       
+                               if (_intreturn) {
+                                       if (_intreturn->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
+                               _intreturn.reset (new InternalReturn (_session, node));
+                               processor = _intreturn;
 
-                   !mute_audible
+                       } else if (prop->value() == "main-outs") {
+                                       
+                               if (_main_outs) {
+                                       if (_main_outs->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
 
-                       ) {
+                               _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
+                               processor = _main_outs;
 
-                       /* don't use Route::silence() here, because that causes
-                          all outputs (sends, port processors, etc. to be silent).
-                       */
-                       
-                       if (_meter_point == MeterPostFader) {
-                               peak_meter().reset();
+                       } else {
+                               error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                               return false;
+                       }
+                               
+                       if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+                               /* check for invisible processors stacked at the end and leave them there */
+                               ProcessorList::iterator p;
+                               p = _processors.end();
+                               --p;
+                               while (!(*p)->visible() && p != _processors.begin()) {
+                                       --p;
+                               }
+                               ++p;
+                               iter = p;
                        }
 
-                       IO::silence (nframes, offset);
-                       
+                       return (add_processor (processor, iter) == 0);
+                               
                } else {
-                       
-                       deliver_output(bufs, start_frame, end_frame, nframes, offset);
-
+                       error << _("Processor XML node has no type property") << endmsg;
+                       return false;
                }
-
        }
-}
-
-#endif /* NEW_POB */
-
-ChanCount
-Route::n_process_buffers ()
-{
-       return max (n_inputs(), processor_max_outs);
-}
-
-void
-Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset, int declick, bool meter_first)
-{
-       BufferSet& bufs = _session.get_scratch_buffers(n_process_buffers());
 
-       _silent = false;
-
-       collect_input (bufs, nframes, offset);
-
-       if (meter_first) {
-               _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
-               meter_first = false;
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
        }
-               
-       process_output_buffers (bufs, start_frame, end_frame, nframes, offset, true, declick, meter_first);
-}
-
-void
-Route::passthru_silence (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset, int declick, bool meter)
-{
-       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, offset, true, declick, meter);
 }
 
-void
-Route::set_solo (bool yn, void *src)
-{
-       if (_solo_safe) {
-               return;
-       }
-
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo, yn, _mix_group);
-               return;
-       }
-
-       if (_soloed != yn) {
-               _soloed = yn;
-               solo_changed (src); /* EMIT SIGNAL */
-               _solo_control->Changed (); /* EMIT SIGNAL */
-       }
-}
-
-void
-Route::set_solo_mute (bool yn)
+int
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
-       Glib::Mutex::Lock lm (declick_lock);
-
-       /* Called by Session in response to another Route being soloed.
-        */
-          
-       desired_solo_gain = (yn?0.0:1.0);
-}
+       ProcessorList::iterator loc;
 
-void
-Route::set_solo_safe (bool yn, void *src)
-{
-       if (_solo_safe != yn) {
-               _solo_safe = yn;
-                solo_safe_changed (src); /* 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);
-               return;
+       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);
        }
 
-       if (_muted != yn) {
-               _muted = yn;
-               mute_changed (src); /* EMIT SIGNAL */
-               
-               _mute_control->Changed (); /* EMIT SIGNAL */
-               
-               Glib::Mutex::Lock lm (declick_lock);
-               desired_mute_gain = (yn?0.0f:1.0f);
-       }
+       return add_processors (others, loc, err);
 }
 
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
 {
-       ChanCount old_pmo = processor_max_outs;
+       /* NOTE: this is intended to be used ONLY when copying
+          processors from another Route. Hence the subtle
+          differences between this and ::add_processor()
+       */
+
+       ChanCount old_pms = processor_max_streams;
 
        if (!_session.engine().connected()) {
                return 1;
        }
 
-       {
-               Glib::RWLock::WriterLock lm (_processor_lock);
-
-               boost::shared_ptr<PluginInsert> pi;
-               boost::shared_ptr<PortInsert> porti;
-
-               //processor->set_default_type(_default_type);
-
-               _processors.push_back (processor);
-
-               // Set up processor list channels.  This will set processor->[input|output]_streams(),
-               // configure redirect ports properly, etc.
-               if (_reset_processor_counts (err)) {
-                       _processors.pop_back ();
-                       _reset_processor_counts (0); // it worked before we tried to add it ...
-                       return -1;
-               }
-
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       
-                       if (pi->natural_input_streams() == ChanCount::ZERO) {
-                               /* generator plugin */
-                               _have_internal_generator = true;
-                       }
-                       
-               }
-               
-               // Ensure peak vector sizes before the plugin is activated
-
-               ChanCount potential_max_streams;
-
-               potential_max_streams.set (DataType::AUDIO, max (processor->input_streams().n_audio(), 
-                                                                processor->output_streams().n_audio()));
-               potential_max_streams.set (DataType::MIDI, max (processor->input_streams().n_midi(), 
-                                                               processor->output_streams().n_midi()));
-
-               _meter->configure_io (potential_max_streams, potential_max_streams);
-
-               processor->activate ();
-               processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
-
-               _user_latency = 0;
-       }
-       
-       if (processor_max_outs != old_pmo || old_pmo == ChanCount::ZERO) {
-               reset_panner ();
-       }
-
-       processors_changed (); /* EMIT SIGNAL */
-       
-       return 0;
-}
-
-int
-Route::add_processors (const ProcessorList& others, ProcessorStreams* err)
-{
-       ChanCount old_pmo = processor_max_outs;
-
-       if (!_session.engine().connected()) {
-               return 1;
+       if (others.empty()) {
+               return 0;
        }
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-
                ProcessorList::iterator existing_end = _processors.end();
                --existing_end;
 
-               ChanCount potential_max_streams;
+               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);
+                               if (m != _processors.end()) {
+                                       _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.push_back (*i);
+                       _processors.insert (iter, *i);
                        
-                       if (_reset_processor_counts (err)) {
+                       if (configure_processors_unlocked (err)) {
                                ++existing_end;
                                _processors.erase (existing_end, _processors.end());
-                               _reset_processor_counts (0); // it worked before we tried to add it ...
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
                                return -1;
                        }
                        
-                       (*i)->activate ();
                        (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
                }
 
-               _user_latency = 0;
+               _output->set_user_latency (0);
        }
        
-       if (processor_max_outs != old_pmo || old_pmo == ChanCount::ZERO) {
-               reset_panner ();
-       }
-
        processors_changed (); /* EMIT SIGNAL */
+
        return 0;
 }
 
+void
+Route::placement_range(Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end)
+{
+       if (p == PreFader) {
+               start = _processors.begin();
+               end = find(_processors.begin(), _processors.end(), _amp);
+       } else {
+               start = find(_processors.begin(), _processors.end(), _amp);
+               ++start;
+               end = _processors.end();
+       }
+}
+
 /** Turn off all processors with a given placement
  * @param p Placement of processors to disable
  */
-
 void
 Route::disable_processors (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
        
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i)->placement() == p) {
-                       (*i)->set_active (false);
-               }
+       ProcessorList::iterator start, end;
+       placement_range(p, start, end);
+       
+       for (ProcessorList::iterator i = start; i != end; ++i) {
+               (*i)->deactivate ();
        }
 
        _session.set_dirty ();
@@ -1254,14 +935,13 @@ Route::disable_processors (Placement p)
 
 /** 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)->set_active (false);
+               (*i)->deactivate ();
        }
        
        _session.set_dirty ();
@@ -1270,15 +950,17 @@ Route::disable_processors ()
 /** Turn off all redirects with a given placement
  * @param p Placement of redirects to disable
  */
-
 void
 Route::disable_plugins (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
        
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (boost::dynamic_pointer_cast<PluginInsert> (*i) && (*i)->placement() == p) {
-                       (*i)->set_active (false);
+       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 ();
                }
        }
        
@@ -1287,7 +969,6 @@ Route::disable_plugins (Placement p)
 
 /** Turn off all plugins
  */
-
 void
 Route::disable_plugins ()
 {
@@ -1295,7 +976,7 @@ Route::disable_plugins ()
        
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
-                       (*i)->set_active (false);
+                       (*i)->deactivate ();
                }
        }
        
@@ -1320,7 +1001,7 @@ Route::ab_plugins (bool forward)
                        }
 
                        if ((*i)->active()) {
-                               (*i)->set_active (false);
+                               (*i)->deactivate ();
                                (*i)->set_next_ab_is_active (true);
                        } else {
                                (*i)->set_next_ab_is_active (false);
@@ -1338,9 +1019,9 @@ Route::ab_plugins (bool forward)
                        }
 
                        if ((*i)->get_next_ab_is_active()) {
-                               (*i)->set_active (true);
+                               (*i)->activate ();
                        } else {
-                               (*i)->set_active (false);
+                               (*i)->deactivate ();
                        }
                }
        }
@@ -1349,447 +1030,320 @@ Route::ab_plugins (bool forward)
 }
        
        
-/* 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
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i)->placement() == PreFader) {
-                       processor = *i;
-               }
-       }
-       
-       if (processor) {
-               return processor->output_streams();
-       } else {
-               return n_inputs ();
-       }
-}
-
-
 /** Remove processors with a given placement.
  * @param p Placement of processors to remove.
  */
 void
 Route::clear_processors (Placement p)
 {
-       const ChanCount old_pmo = processor_max_outs;
+       const ChanCount old_pms = processor_max_streams;
 
        if (!_session.engine().connected()) {
                return;
        }
+       
+       bool already_deleting = _session.deletion_in_progress();
+       if (!already_deleting) {
+               _session.set_deletion_in_progress();
+       }
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
-               
+               ProcessorStreams err;
+               bool seen_amp = false;
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((*i)->placement() == p) {
-                               /* it's the placement we want to get rid of */
-                               (*i)->drop_references ();
-                       } else {
-                               /* it's a different placement, so keep it */
+
+                       if (*i == _amp) {
+                               seen_amp = true;
+                       }
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                               
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
+
+                       } 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
        }
 
-       /* FIXME: can't see how this test can ever fire */
-       if (processor_max_outs != old_pmo) {
-               reset_panner ();
-       }
-       
-       processor_max_outs.reset();
+       processor_max_streams.reset();
        _have_internal_generator = false;
        processors_changed (); /* EMIT SIGNAL */
+
+       if (!already_deleting) {
+               _session.clear_deletion_in_progress();
+       }
 }
 
 int
 Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
 {
-       ChanCount old_pmo = processor_max_outs;
+       /* these can never be removed */
+
+       if (processor == _amp || processor == _meter || processor == _main_outs) {
+               return 0;
+       }
+
+       ChanCount old_pms = processor_max_streams;
 
        if (!_session.engine().connected()) {
                return 1;
        }
 
-       processor_max_outs.reset();
+       processor_max_streams.reset();
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList::iterator i;
                bool removed = false;
 
-               for (i = _processors.begin(); i != _processors.end(); ++i) {
+               for (i = _processors.begin(); i != _processors.end(); ) {
                        if (*i == processor) {
 
-                               ProcessorList::iterator tmp;
-
-                               /* move along, see failure case for reset_processor_counts()
-                                  where we may need to reprocessor the processor.
+                               /* move along, see failure case for configure_processors()
+                                  where we may need to reconfigure the processor.
                                */
 
-                               tmp = i;
-                               ++tmp;
-
                                /* stop redirects that send signals to JACK ports
                                   from causing noise as a result of no longer being
                                   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);
-                               }
-
-                               _processors.erase (i);
-
-                               i = tmp;
-                               removed = true;
-                               break;
-                       }
-
-                       _user_latency = 0;
-               }
-
-               if (!removed) {
-                       /* what? */
-                       return 1;
-               }
-
-               if (_reset_processor_counts (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       _reset_processor_counts (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;
-                               }
-                       }
-               }
-       }
-
-       if (old_pmo != processor_max_outs) {
-               reset_panner ();
-       }
-
-       processor->drop_references ();
-
-       processors_changed (); /* EMIT SIGNAL */
-       return 0;
-}
-
-int
-Route::reset_processor_counts (ProcessorStreams* err)
-{
-       Glib::RWLock::WriterLock lm (_processor_lock);
-       return _reset_processor_counts (err);
-}
-
-
-int
-Route::_reset_processor_counts (ProcessorStreams* err)
-{
-       ProcessorList::iterator r;
-       uint32_t insert_cnt = 0;
-       uint32_t send_cnt = 0;
-       map<Placement,list<ProcessorCount> > proc_map;
-       ProcessorList::iterator prev;
-       ChanCount initial_streams = n_inputs ();
-       ChanCount previous_initial_streams = n_inputs ();
-       int ret = -1;
-       uint32_t max_audio = 0;
-       uint32_t max_midi = 0;
-
-       processor_max_outs.reset ();
-       
-       /* Step 1: build a map that links each insert to an in/out channel count 
-
-          Divide inserts up by placement so we get the signal flow
-          properly modelled. we need to do this because the _processors
-          list is not sorted by placement, and because other reasons may 
-          exist now or in the future for this separate treatment.
-       */
-
-       /* ... but it should/will be... */
-       
-       for (r = _processors.begin(); r != _processors.end(); ++r) {
-
-               boost::shared_ptr<PluginInsert> plugin_insert;
-               boost::shared_ptr<PortInsert> port_insert;
-
-               if ((plugin_insert = boost::dynamic_pointer_cast<PluginInsert>(*r)) != 0) {
-
-                       ++insert_cnt;
-                       proc_map[(*r)->placement()].push_back (ProcessorCount (*r));
-
-                       /* reset plugin counts back to one for now so
-                          that we have a predictable, controlled
-                          state to try to configure.
-                       */
-
-                       plugin_insert->set_count (1);
-
-               } else if ((port_insert = boost::dynamic_pointer_cast<PortInsert>(*r)) != 0) {
-                       
-                       ++insert_cnt;
-                       proc_map[(*r)->placement()].push_back (ProcessorCount (*r));
-
-               } else if (boost::dynamic_pointer_cast<Send> (*r) != 0) {
-                       ++send_cnt;
-               }
-       }
-       
-       if (insert_cnt == 0) {
-               if (send_cnt) {
-                       goto recompute;
-               } else {
-                       ret = 0;
-                       goto streamcount;
-               }
-       }
-
-       /* Now process each placement in order, checking to see if we 
-          can really do what has been requested.
-       */
-
-       /* A: PreFader */
-       
-       if (check_some_processor_counts (proc_map[PreFader], n_inputs (), err)) {
-               goto streamcount;
-       }
-
-       if (!proc_map[PreFader].empty()) {
-               previous_initial_streams = n_inputs ();
-               for (list<ProcessorCount>::iterator i = proc_map[PreFader].begin(); i != proc_map[PreFader].end(); i++) {
-                       if (i->processor->can_support_io_configuration (previous_initial_streams, initial_streams) < 0) {
-                               goto streamcount;
-                       }
-                       previous_initial_streams = initial_streams;
-               }
-       }
-
-       /* B: PostFader */
-
-       if (check_some_processor_counts (proc_map[PostFader], initial_streams, err)) {
-               goto streamcount;
-       }
-
-       if (!proc_map[PostFader].empty()) {
-               for (list<ProcessorCount>::iterator i = proc_map[PostFader].begin(); i != proc_map[PostFader].end(); i++) {
-                       if (i->processor->can_support_io_configuration (previous_initial_streams, initial_streams) < 0) {
-                               goto streamcount;
-                       }
-                       previous_initial_streams = initial_streams;
-               }
-       }
-
-       /* OK, everything can be set up correctly, so lets do it */
-
-       apply_some_processor_counts (proc_map[PreFader]);
-       apply_some_processor_counts (proc_map[PostFader]);
-
-       /* recompute max outs of any processor */
-
-       ret = 0;
-
-  recompute:
-
-       processor_max_outs.reset ();
-       prev = _processors.end();
-
-       for (r = _processors.begin(); r != _processors.end(); prev = r, ++r) {
-               boost::shared_ptr<Send> s;
-
-               if ((s = boost::dynamic_pointer_cast<Send> (*r)) != 0) {
-                       if (r == _processors.begin()) {
-                               s->expect_inputs (n_inputs());
-                       } else {
-                               s->expect_inputs ((*prev)->output_streams());
-                       }
-
-               } else {
-                       
-                       /* don't pay any attention to send output configuration, since it doesn't
-                          affect the route.
-                        */
-                       
-                       max_audio = max ((*r)->output_streams ().n_audio(), max_audio);
-                       max_midi = max ((*r)->output_streams ().n_midi(), max_midi);
-               }
-       }
-
-       processor_max_outs.set (DataType::AUDIO, max_audio);
-       processor_max_outs.set (DataType::MIDI, max_midi);
-                       
-       /* we're done */
-       return 0;
-
-  streamcount:
-       for (r = _processors.begin(); r != _processors.end(); ++r) {
-               max_audio = max ((*r)->output_streams ().n_audio(), max_audio);
-               max_midi = max ((*r)->output_streams ().n_midi(), max_midi);
-       }
-
-       processor_max_outs.set (DataType::AUDIO, max_audio);
-       processor_max_outs.set (DataType::MIDI, max_midi);
-
-       return ret;
-}                                 
+                               boost::shared_ptr<IOProcessor> iop;
 
-int32_t
-Route::apply_some_processor_counts (list<ProcessorCount>& iclist)
-{
-       list<ProcessorCount>::iterator i;
-       
-       for (i = iclist.begin(); i != iclist.end(); ++i) {
+                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                                       if (iop->input()) {
+                                               iop->input()->disconnect (this);
+                                       }
+                                       if (iop->output()) {
+                                               iop->output()->disconnect (this);
+                                       }
+                               }
 
-               ProcessorCount& pc (*i);
+                               i = _processors.erase (i);
+                               removed = true;
+                               break;
 
-               cerr << "now applying for " << (*i).processor->name() << " in = " << pc.in.n_audio() << " out = " << pc.out.n_audio() << endl;
+                       } else {
+                               ++i;
+                       }
 
-               if (pc.processor->configure_io (pc.in, pc.out)) {
-                       return -1;
+                       _output->set_user_latency (0);
                }
 
-               /* make sure that however many we have, they are all active */
+               if (!removed) {
+                       /* what? */
+                       return 1;
+               }
 
-               pc.processor->activate ();
-       }
+               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;
+               }
 
-       return 0;
-}
+               _have_internal_generator = false;
 
-/** Returns whether \a iclist can be configured and run starting with
- * \a required_inputs at the first processor's inputs.
- * If false is returned, \a iclist can not be run with \a required_inputs, and \a err is set.
- * Otherwise, \a err is set to the output of the list.
- */
-bool
-Route::check_some_processor_counts (list<ProcessorCount>& iclist, ChanCount required_inputs, ProcessorStreams* err)
-{
-       list<ProcessorCount>::iterator i;
-       size_t index = 0;
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
                        
-       if (err) {
-               err->index = 0;
-               err->count = required_inputs;
-       }
-
-       for (i = iclist.begin(); i != iclist.end(); ++i, ++index) {
-
-               cerr << "Checking whether " << (*i).processor->name() << " can support " << required_inputs.n_audio() << " inputs\n";
-
-               if (!(*i).processor->can_support_io_configuration (required_inputs, (*i).out)) {
-                       if (err) {
-                               err->index = index;
-                               err->count = required_inputs;
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->is_generator()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
                        }
-                       return true;
                }
-               
-               (*i).in = required_inputs;
-               required_inputs = (*i).out;
        }
 
-       return false;
+       processor->drop_references ();
+       processors_changed (); /* EMIT SIGNAL */
+
+       return 0;
 }
 
 int
-Route::copy_processors (const Route& other, Placement placement, ProcessorStreams* err)
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
 {
-       ChanCount old_pmo = processor_max_outs;
+       ProcessorList deleted;
+       ProcessorList as_we_were;
+
+       if (!_session.engine().connected()) {
+               return 1;
+       }
 
-       ProcessorList to_be_deleted;
+       processor_max_streams.reset();
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList::iterator tmp;
-               ProcessorList the_copy;
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
 
-               the_copy = _processors;
-               
-               /* remove all relevant processors */
+               as_we_were = _processors;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+                       
+                       processor = *i;
+
+                       /* these can never be removed */
 
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) {
-                       tmp = i;
-                       ++tmp;
+                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                               ++i;
+                               continue;
+                       }
+                       
+                       /* see if its in the list of processors to delete */
+                       
+                       if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
+                               ++i;
+                               continue;
+                       }
 
-                       if ((*i)->placement() == placement) {
-                               to_be_deleted.push_back (*i);
-                               _processors.erase (i);
+                       /* stop IOProcessors that send to JACK ports
+                          from causing noise as a result of no longer being
+                          run.
+                       */
+                       
+                       boost::shared_ptr<IOProcessor> iop;
+                       
+                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                               iop->disconnect ();
                        }
 
-                       i = tmp;
+                       deleted.push_back (processor);
+                       i = _processors.erase (i);
                }
 
-               /* now copy the relevant ones from "other" */
-               
-               for (ProcessorList::const_iterator i = other._processors.begin(); i != other._processors.end(); ++i) {
-                       if ((*i)->placement() == placement) {
-                               _processors.push_back (IOProcessor::clone (*i));
-                       }
+               if (deleted.empty()) {
+                       /* none of those in the requested list were found */
+                       return 0;
                }
 
-               /* reset plugin stream handling */
+               _output->set_user_latency (0);
+
+               if (configure_processors_unlocked (err)) {
+                       /* get back to where we where */
+                       _processors = as_we_were;
+                       /* we know this will work, because it worked before :) */
+                       configure_processors_unlocked (0);
+                       return -1;
+               }
 
-               if (_reset_processor_counts (err)) {
+               _have_internal_generator = false;
 
-                       /* FAILED COPY ATTEMPT: we have to restore order */
+               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;
+                               }
+                       }
+               }
+       }
 
-                       /* delete all cloned processors */
+       /* now try to do what we need to so that those that were removed will be deleted */
 
-                       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) {
+       for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+               (*i)->drop_references ();
+       }
 
-                               tmp = i;
-                               ++tmp;
+       processors_changed (); /* EMIT SIGNAL */
 
-                               if ((*i)->placement() == placement) {
-                                       _processors.erase (i);
-                               }
-                               
-                               i = tmp;
-                       }
+       return 0;
+}
 
-                       /* restore the natural order */
 
-                       _processors = the_copy;
-                       processor_max_outs = old_pmo;
+int
+Route::configure_processors (ProcessorStreams* err)
+{
+       if (!_in_configure_processors) {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               return configure_processors_unlocked (err);
+       }
+       return 0;
+}
 
-                       /* we failed, even though things are OK again */
+/** 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;
+       }
 
-                       return -1;
+       _in_configure_processors = true;
 
+       // Check each processor in order to see if we can configure as requested
+       ChanCount in = _input->n_ports ();
+       ChanCount out;
+       list< pair<ChanCount,ChanCount> > configuration;
+       uint32_t index = 0;
+       
+       for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
+               if ((*p)->can_support_io_configuration(in, out)) {
+                       configuration.push_back(make_pair(in, out));
+                       in = out;
                } else {
-                       
-                       /* SUCCESSFUL COPY ATTEMPT: delete the processors we removed pre-copy */
-                       to_be_deleted.clear ();
-                       _user_latency = 0;
+                       if (err) {
+                               err->index = index;
+                               err->count = in;
+                       }
+                       _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);
+               processor_max_streams = ChanCount::max(processor_max_streams, c->first);
+               processor_max_streams = ChanCount::max(processor_max_streams, c->second);
+               out = c->second;
+       }
 
-       if (processor_max_outs != old_pmo || old_pmo == ChanCount::ZERO) {
-               reset_panner ();
+       // Ensure route outputs match last processor's outputs
+       if (out != _output->n_ports ()) {
+               cerr << "For " << _name << " out/last mismatch - out = " << out << " vs. " << _output->n_ports() << endl;
+               _output->ensure_io (out, false, this);
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       _in_configure_processors = false;
        return 0;
 }
 
@@ -1805,7 +1359,11 @@ Route::all_processors_flip ()
        bool first_is_on = _processors.front()->active();
        
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->set_active (!first_is_on);
+               if (first_is_on) {
+                       (*i)->deactivate ();
+               } else {
+                       (*i)->activate ();
+               }
        }
        
        _session.set_dirty ();
@@ -1823,290 +1381,212 @@ Route::all_processors_active (Placement p, bool state)
        if (_processors.empty()) {
                return;
        }
+       ProcessorList::iterator start, end;
+       placement_range(p, start, end);
 
+       bool before_amp = true;
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i)->placement() == p) {
-                       (*i)->set_active (state);
+               if ((*i) == _amp) {
+                       before_amp = false;
+                       continue;
+               }
+               if (p == PreFader && before_amp) {
+                       if (state) {
+                               (*i)->activate ();
+                       } else {
+                               (*i)->deactivate ();
+                       }
                }
        }
        
        _session.set_dirty ();
 }
 
-struct ProcessorSorter {
-    bool operator() (boost::shared_ptr<const Processor> a, boost::shared_ptr<const Processor> b) {
-           return a->sort_key() < b->sort_key();
-    }
-};
-
-int
-Route::sort_processors (ProcessorStreams* err)
-{
-       {
-               ProcessorSorter comparator;
-               Glib::RWLock::WriterLock lm (_processor_lock);
-               ChanCount old_pmo = processor_max_outs;
-
-               /* the sweet power of C++ ... */
-
-               ProcessorList as_it_was_before = _processors;
-
-               _processors.sort (comparator);
-       
-               if (_reset_processor_counts (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_outs = old_pmo;
-                       return -1;
-               } 
-       } 
-
-       reset_panner ();
-       processors_changed (); /* EMIT SIGNAL */
-
-       return 0;
-}
-
-XMLNode&
-Route::get_state()
-{
-       return state(true);
-}
-
-XMLNode&
-Route::get_template()
-{
-       return state(false);
-}
-
-XMLNode&
-Route::state(bool full_state)
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
 {
-       XMLNode *node = new XMLNode("Route");
-       ProcessorList::iterator i;
-       char buf[32];
-
-       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");
-       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"); 
-
-       if (_edit_group) {
-               node->add_property("edit-group", _edit_group->name());
-       }
-       if (_mix_group) {
-               node->add_property("mix-group", _mix_group->name());
-       }
+       bool pre_fader = true;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       string order_string;
-       OrderKeys::iterator x = order_keys.begin(); 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-       while (x != order_keys.end()) {
-               order_string += string ((*x).first);
-               order_string += '=';
-               snprintf (buf, sizeof(buf), "%ld", (*x).second);
-               order_string += buf;
-               
-               ++x;
+               /* semantic note: if p == amp, we want to return true, so test
+                  for equality before checking if this is the amp
+               */
 
-               if (x == order_keys.end()) {
+               if ((*i) == p) {
                        break;
                }
 
-               order_string += ':';
-       }
-       node->add_property ("order-keys", order_string);
-
-       node->add_child_nocopy (IO::state (full_state));
-       node->add_child_nocopy (_solo_control->get_state ());
-       node->add_child_nocopy (_mute_control->get_state ());
-
-       XMLNode* remote_control_node = new XMLNode (X_("remote_control"));
-       snprintf (buf, sizeof (buf), "%d", _remote_control_id);
-       remote_control_node->add_property (X_("id"), buf);
-       node->add_child_nocopy (*remote_control_node);
-
-       if (_control_outs) {
-               XMLNode* cnode = new XMLNode (X_("ControlOuts"));
-               cnode->add_child_nocopy (_control_outs->state (full_state));
-               node->add_child_nocopy (*cnode);
-       }
-
-       if (_comment.length()) {
-               XMLNode *cmt = node->add_child ("Comment");
-               cmt->add_content (_comment);
-       }
-
-       for (i = _processors.begin(); i != _processors.end(); ++i) {
-               node->add_child_nocopy((*i)->state (full_state));
-       }
-
-       if (_extra_xml){
-               node->add_child_copy (*_extra_xml);
-       }
-       
-       return *node;
-}
-
-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));
+               if ((*i) == _amp) {
+                       pre_fader = false;
+                       break;
+               }
        }
 
-       return *root;
+       return pre_fader;
 }
 
 int
-Route::set_processor_state (const XMLNode& root)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
-       if (root.name() != X_("redirects")) {
-               return -1;
-       }
+       /* "new_order" is an ordered list of processors to be positioned according to "placement".
+          NOTE: all processors in "new_order" MUST be marked as visible. 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.
+       */
 
-       XMLNodeList nlist;
-       XMLNodeList nnlist;
-       XMLNodeConstIterator iter;
-       XMLNodeConstIterator niter;
-       Glib::RWLock::ReaderLock lm (_processor_lock);
+       {
+               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;
 
-       nlist = root.children();
-       
-       for (iter = nlist.begin(); iter != nlist.end(); ++iter){
+               oiter = _processors.begin();
+               niter = new_order.begin(); 
 
-               /* iter now points to a IOProcessor state node */
-               
-               nnlist = (*iter)->children ();
+               while (niter !=  new_order.end()) {
+                       
+                       /* 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. 
 
-               for (niter = nnlist.begin(); niter != nnlist.end(); ++niter) {
+                          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.
+                       */
 
-                       /* find the IO child node, since it contains the ID we need */
+                       if (oiter == _processors.end()) {
 
-                       /* XXX OOP encapsulation violation, ugh */
+                               /* no more elements in the old list, so just stick the rest of 
+                                  the new order onto the temp list.
+                               */
 
-                       if ((*niter)->name() == IO::state_node_name) {
+                               as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
+                               while (niter != new_order.end()) {
+                                       ++niter;
+                               }
+                               break;
 
-                               XMLProperty* prop = (*niter)->property (X_("id"));
+                       } else {
                                
-                               if (!prop) {
-                                       warning << _("IOProcessor node has no ID, ignored") << endmsg;
-                                       break;
-                               }
+                               if (!(*oiter)->visible()) {
 
-                               ID id = prop->value ();
+                                       as_it_will_be.push_back (*oiter);
 
-                               /* 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;
+                               } 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;
                                        }
                                }
                                
-                               break;
-                               
+                                /* 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());
+
+               if (configure_processors_unlocked (err)) {
+                       _processors = as_it_was_before;
+                       processor_max_streams = old_pms;
+                       return -1;
+               } 
+       } 
+
+       processors_changed (); /* EMIT SIGNAL */
 
        return 0;
 }
 
-void
-Route::set_deferred_state ()
+XMLNode&
+Route::get_state()
 {
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
+       return state(true);
+}
 
-       if (!deferred_state) {
-               return;
-       }
+XMLNode&
+Route::get_template()
+{
+       return state(false);
+}
 
-       nlist = deferred_state->children();
+XMLNode&
+Route::state(bool full_state)
+{
+       XMLNode *node = new XMLNode("Route");
+       ProcessorList::iterator i;
+       char buf[32];
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               add_processor_from_xml (**niter);
+       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));
        }
 
-       delete deferred_state;
-       deferred_state = 0;
-}
+       node->add_property("active", _active?"yes":"no");
+       node->add_property("phase-invert", _phase_invert?"yes":"no");
+       node->add_property("denormal-protection", _denormal_protection?"yes":"no");
+       node->add_property("meter-point", enum_2_string (_meter_point));
 
-void
-Route::add_processor_from_xml (const XMLNode& node)
-{
-       const XMLProperty *prop;
+       if (_route_group) {
+               node->add_property("route-group", _route_group->name());
+       }
 
-       // 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);
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return;
-               }
-               
-       // use "Processor" in XML?
-       } else if (node.name() == "Processor") {
+       string order_string;
+       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;
                
-               try {
-                       if ((prop = node.property ("type")) != 0) {
+               ++x;
 
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = false;
+               if (x == order_keys.end()) {
+                       break;
+               }
 
-                               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") {
+               order_string += ':';
+       }
+       node->add_property ("order-keys", order_string);
 
-                                       processor.reset (new PortInsert (_session, node));
-                               
-                               } else if (prop->value() == "send") {
+       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_master->get_state ());
 
-                                       processor.reset (new Send (_session, node));
-                                       have_insert = true;
+       XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
+       snprintf (buf, sizeof (buf), "%d", _remote_control_id);
+       remote_control_node->add_property (X_("id"), buf);
+       node->add_child_nocopy (*remote_control_node);
 
-                               } else {
+       if (_comment.length()) {
+               XMLNode *cmt = node->add_child ("Comment");
+               cmt->add_content (_comment);
+       }
 
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
-                               }
-                               
-                               add_processor (processor);
-                               
-                       } else {
-                               error << _("Processor XML node has no type property") << endmsg;
-                       }
-               }
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               node->add_child_nocopy((*i)->state (full_state));
+       }
 
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
-                       return;
-               }
+       if (_extra_xml){
+               node->add_child_copy (*_extra_xml);
        }
+       
+       return *node;
 }
 
 int
@@ -2116,8 +1596,9 @@ Route::set_state (const XMLNode& node)
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, bool /*call_base*/)
 {
+
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
@@ -2129,72 +1610,89 @@ Route::_set_state (const XMLNode& node, bool call_base)
                return -1;
        }
 
+       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);
        }
+
+       /* add all processors (except amp, which is always present) */
+
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               
+               child = *niter;
+
+               if (child->name() == IO::state_node_name) {
+                       if ((prop = child->property (X_("direction"))) == 0) {
+                               continue;
+                       }
+                       
+                       if (prop->value() == "Input") {
+                               _input->set_state (*child);
+                       } else if (prop->value() == "Output") {
+                               _output->set_state (*child);
+                       }
+               }
+                       
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
+               }
+       }
+
+       set_processor_state (processor_state);
        
-       if ((prop = node.property (X_("default-type"))) != 0) {
-               _default_type = DataType(prop->value());
-               assert(_default_type != DataType::NIL);
+       if ((prop = node.property ("solo_level")) != 0) {
+               _solo_level = 0; // needed for mod_solo_level() to work
+               mod_solo_level (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("solo-isolated")) != 0) {
+               set_solo_isolated (prop->value() == "yes", this);
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (prop->value()=="yes"?true:false, this);
+               set_phase_invert (prop->value()=="yes"?true:false);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
-               set_denormal_protection (prop->value()=="yes"?true:false, this);
+               set_denormal_protection (prop->value()=="yes"?true:false);
        }
        
-       _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;
+               bool yn = (prop->value() == "yes");
+               _active = !yn; // force switch
+               set_active (yn);
        }
 
        if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = prop->value()=="yes"?true:false; 
+               bool yn = (prop->value()=="yes");
 
-               /* 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_("meter-point"))) != 0) {
+               _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
-
-       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-main-outs"))) != 0) {
-               _mute_affects_main_outs = (prop->value()=="yes")?true:false;
-       }
-
-       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;
+       
+       if ((prop = node.property (X_("route-group"))) != 0) {
+               RouteGroup* route_group = _session.route_group_by_name(prop->value());
+               if (route_group == 0) {
+                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
                } else {
-                       set_edit_group(edit_group, this);
+                       set_route_group (route_group, this);
                }
        }
 
@@ -2215,7 +1713,7 @@ Route::_set_state (const XMLNode& node, bool call_base)
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                              << endmsg;
                                } else {
-                                       set_order_key (remaining.substr (0, equal).c_str(), n);
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
                        }
 
@@ -2229,128 +1727,81 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       nlist = node.children();
-
-       if (deferred_state) {
-               delete deferred_state;
-       }
-
-       deferred_state = new XMLNode(X_("deferred state"));
-
-       /* set parent class properties before anything else */
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-
-               child = *niter;
-
-               if (child->name() == IO::state_node_name && call_base) {
-
-                       IO::set_state (*child);
-                       break;
-               }
-       }
-
-       XMLNodeList processor_nodes;
-                       
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-
-               child = *niter;
-                       
-               if (child->name() == X_("Send") || child->name() == X_("Processor")) {
-                       processor_nodes.push_back(child);
-               }
-
-       }
-
-       _set_processor_states(processor_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());
-                       }
-
-               } else if (child->name() == X_("ControlOuts")) {
-                       
-                       string coutname = _name;
-                       coutname += _("[control]");
-
-                       _control_outs = new IO (_session, coutname);
-                       _control_outs->set_state (**(child->children().begin()));
-
-               } 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) {
+               } 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() == X_("remote_control")) {
+                       } 
+
+               } 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);
+               } else if (child->name() == X_("MuteMaster")) {
+                       _mute_master->set_state (*child);
                }
        }
 
        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_states(const XMLNodeList &nlist)
+Route::set_processor_state (const XMLNode& node)
 {
+       const XMLNodeList &nlist = node.children();
        XMLNodeConstIterator niter;
-       char buf[64];
-
        ProcessorList::iterator i, o;
 
        // Iterate through existing processors, remove those which are not in the state list
+
        for (i = _processors.begin(); i != _processors.end(); ) {
+
+               /* leave amp alone, always */
+
+               if ((*i) == _amp) {
+                       ++i;
+                       continue;
+               }
+
                ProcessorList::iterator tmp = i;
                ++tmp;
 
                bool processorInStateList = false;
-       
-               (*i)->id().print (buf, sizeof (buf));
-
 
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-                       // legacy sessions (IOProcessor as a child of Processor, both is-a IO)
-                       if (strncmp(buf,(*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
-                               processorInStateList = true;
-                               break;
-                       } else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
+                       XMLProperty* id_prop = (*niter)->property(X_("id"));
+
+                       if (id_prop && (*i)->id() == id_prop->value()) {
                                processorInStateList = true;
                                break;
                        }
@@ -2360,72 +1811,68 @@ Route::_set_processor_states(const XMLNodeList &nlist)
                        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, ++i) {
+               
+               XMLProperty* prop = (*niter)->property ("type");
 
-               // Check whether the next processor in the list 
                o = i;
 
-               while (o != _processors.end()) {
-                       (*o)->id().print (buf, sizeof (buf));
-                       if ( strncmp(buf, (*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0)
-                               break;
-                       else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0)
-                               break;
-                       
-                       ++o;
+               // Check whether the next processor in the list is the right one,
+               // except for "amp" which is always there and may not have the
+               // old ID since it is always created anew in every Route
+               
+               if (prop->value() != "amp") {
+                       while (o != _processors.end()) {
+                               XMLProperty* id_prop = (*niter)->property(X_("id"));
+                               if (id_prop && (*o)->id() == id_prop->value()) {
+                                       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 the processor (*niter) is not on the route, we need to create it
-                       // and move it to the correct location
 
-                       ProcessorList::iterator prev_last = _processors.end();
-                       --prev_last; // We need this to check whether adding succeeded
-                       
-                       add_processor_from_xml (**niter);
+                       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;
+                       }
 
-                       ProcessorList::iterator last = _processors.end();
-                       --last;
+               } else {
 
-                       if (prev_last == last) {
-                               cerr << "Could not fully restore state as some processors were not possible to create" << endl;
-                               continue;
+                       // Otherwise, the processor already exists; just
+                       // ensure it is at the location provided in the XML state
 
+                       if (i != o) {
+                               boost::shared_ptr<Processor> tmp = (*o);
+                               _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
                        }
 
-                       boost::shared_ptr<Processor> tmp = (*last);
-                       // remove the processor from the wrong location
-                       _processors.erase(last);
-                       // processor the new processor at the current location
-                       _processors.insert(i, tmp);
+                       // and make it (just) so
 
-                       --i; // move pointer to the newly processored processor
-                       continue;
+                       (*i)->set_state (**niter);
                }
+       }
 
-               // We found the processor (*niter) on the route, first we must make sure the processor
-               // is at the location provided in the XML state
-               if (i != o) {
-                       boost::shared_ptr<Processor> tmp = (*o);
-                       // remove the old copy
-                       _processors.erase(o);
-                       // processor the processor at the correct location
-                       _processors.insert(i, tmp);
-
-                       --i; // move pointer so it points to the right processor
-               }
+       /* note: there is no configure_processors() call because we figure that
+          the XML state represents a working signal route.
+       */
 
-               (*i)->set_state( (**niter) );
-       }
-       
        processors_changed ();
 }
 
@@ -2437,31 +1884,28 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes, nframes_t offset)
+Route::silence (nframes_t nframes)
 {
        if (!_silent) {
 
-               IO::silence (nframes, offset);
-
-               if (_control_outs) {
-                       _control_outs->silence (nframes, offset);
-               }
-
+               _output->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;
+
                                        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, offset);
+                                       
+                                       (*i)->silence (nframes);
                                }
 
-                               if (nframes == _session.get_block_size() && offset == 0) {
+                               if (nframes == _session.get_block_size()) {
                                        // _silent = true;
                                }
                        }
@@ -2470,110 +1914,153 @@ Route::silence (nframes_t nframes, nframes_t offset)
        }
 }      
 
-int
-Route::set_control_outs (const vector<string>& ports)
+void
+Route::add_internal_return ()
 {
-       Glib::Mutex::Lock lm (_control_outs_lock);
-       vector<string>::const_iterator i;
-       size_t limit;
-       
-       if (_control_outs) {
-               delete _control_outs;
-               _control_outs = 0;
-       }
-
-       if (is_control() || is_master()) {
-               /* no control outs for these two special busses */
-               return 0;
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
        }
-       
-       if (ports.empty()) {
-               return 0;
-       }
-       string coutname = _name;
-       coutname += _("[control]");
-       
-       _control_outs = new IO (_session, coutname);
-
-       /* our control outs need as many outputs as we
-          have audio outputs. we track the changes in ::output_change_handler().
-       */
-       
-       // XXX its stupid that we have to get this value twice
+}
 
-       limit = n_outputs().n_audio();
+BufferSet*
+Route::get_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
        
-       if (_control_outs->ensure_io (ChanCount::ZERO, ChanCount (DataType::AUDIO, n_outputs().get (DataType::AUDIO)), true, this)) {
-               return -1;
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
+               }
        }
        
-       /* now connect to the named ports */
+       return 0;
+}
+
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
        
-       for (size_t n = 0; n < limit; ++n) {
-               if (_control_outs->connect_output (_control_outs->output (n), ports[n % ports.size()], this)) {
-                       error << string_compose (_("could not connect %1 to %2"), _control_outs->output(n)->name(), ports[n]) << endmsg;
-                       return -1;
+       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<Route> route, Placement placement, bool /*active*/, bool aux)
+{
+       vector<string> ports;
+       vector<string>::const_iterator i;
+
+       {
+               Glib::RWLock::ReaderLock rm (_processor_lock);
+               
+               for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
+
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+                       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.control_out()) {
+                                       _control_outs = boost::dynamic_pointer_cast<Delivery>(d);
+                               }
+
+                               /* already listening via the specified IO: do nothing */
+                               
+                               return 0;
+                       }
                }
        }
+       
+       boost::shared_ptr<InternalSend> listener;
+
+       try {
+               listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+
+       } catch (failed_constructor& err) {
+               return -1;
+       }
+
+       if (route == _session.control_out()) {
+               _control_outs = listener;
+       }
+
+       add_processor (listener, placement);
+       
        return 0;
 }      
 
 void
-Route::set_edit_group (RouteGroup *eg, void *src)
-
+Route::drop_listen (boost::shared_ptr<Route> route)
 {
-       if (eg == _edit_group) {
-               return;
-       }
+       ProcessorStreams err;
+       ProcessorList::iterator tmp;
 
-       if (_edit_group) {
-               _edit_group->remove (this);
-       }
+       Glib::RWLock::ReaderLock rl(_processor_lock);
+       rl.acquire ();
+       
+  again:
+       for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
+               
+               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+               
+               if (d && d->target_route() == route) {
+                       rl.release ();
+                       remove_processor (*x, &err);
+                       rl.acquire ();
+
+                        /* list could have been demolished while we dropped the lock
+                          so start over.
+                       */
 
-       if ((_edit_group = eg) != 0) {
-               _edit_group->add (this);
+                       goto again; 
+               } 
        }
 
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
-}
+       rl.release ();
 
-void
-Route::drop_edit_group (void *src)
-{
-       _edit_group = 0;
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
+       if (route == _session.control_out()) {
+               _control_outs.reset ();
+       }
 }
 
 void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+Route::set_route_group (RouteGroup *rg, void *src)
 {
-       if (mg == _mix_group) {
+       if (rg == _route_group) {
                return;
        }
 
-       if (_mix_group) {
-               _mix_group->remove (this);
+       if (_route_group) {
+               _route_group->remove (this);
        }
 
-       if ((_mix_group = mg) != 0) {
-               _mix_group->add (this);
+       if ((_route_group = rg) != 0) {
+               _route_group->add (this);
        }
 
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_mix_group (void *src)
+Route::drop_route_group (void *src)
 {
-       _mix_group = 0;
+       _route_group = 0;
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
@@ -2587,111 +2074,33 @@ Route::set_comment (string cmt, void *src)
 bool
 Route::feeds (boost::shared_ptr<Route> other)
 {
-       uint32_t i, j;
-
-       IO& self = *this;
-       uint32_t no = self.n_outputs().n_total();
-       uint32_t ni = other->n_inputs ().n_total();
+       // cerr << _name << endl;
 
-       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())) {
+               // cerr << "\tdirect FEEDS " << other->name() << endl;
+               return true;
        }
 
-       /* check IOProcessors which may also interconnect Routes */
-
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
-
-               boost::shared_ptr<IOProcessor> redirect = boost::dynamic_pointer_cast<IOProcessor>(*r);
-
-               if ( ! redirect)
-                       continue;
-
-               // TODO: support internal redirects here
-
-               no = redirect->io()->n_outputs().n_total();
-
-               for (i = 0; i < no; ++i) {
-                       for (j = 0; j < ni; ++j) {
-                               if (redirect->io()->output(i)->connected_to (other->input (j)->name())) {
-                                       return true;
-                               }
-                       }
-               }
-       }
-
-       /* check for control room outputs which may also interconnect Routes */
-
-       if (_control_outs) {
-
-               no = _control_outs->n_outputs().n_total();
                
-               for (i = 0; i < no; ++i) {
-                       for (j = 0; j < ni; ++j) {
-                               if (_control_outs->output(i)->connected_to (other->input (j)->name())) {
-                                       return true;
-                               }
+               boost::shared_ptr<IOProcessor> iop;
+               
+               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
+                       if (iop->feeds (other)) {
+                               // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl;
+                               return true;
+                       } else {
+                               // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
                        }
                }
        }
 
+       // cerr << "\tdoes NOT FEED " << other->name() << endl;
        return false;
 }
 
 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::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
        nframes_t now = _session.transport_frame();
 
@@ -2713,28 +2122,25 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
                }
        }
 
-       IO::transport_stopped (now);
        _roll_delay = _initial_delay;
 }
 
 void
-Route::input_change_handler (IOChange change, void *ignored)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if (change & ConfigurationChanged) {
-               reset_processor_counts (0);
+       if ((change & ConfigurationChanged)) {
+               configure_processors (0);
        }
 }
 
 void
-Route::output_change_handler (IOChange change, void *ignored)
+Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       if (change & ConfigurationChanged) {
-               if (_control_outs) {
-                       _control_outs->ensure_io (ChanCount::ZERO, ChanCount(DataType::AUDIO, n_outputs().n_audio()), true, this);
-               }
+       if ((change & ConfigurationChanged)) {
                
-               reset_processor_counts (0);
+               /* XXX resize all listeners to match _main_outs? */
+               
+               // configure_processors (0);
        }
 }
 
@@ -2745,50 +2151,47 @@ Route::pans_required () const
                return 0;
        }
        
-       return max (n_inputs ().n_audio(), processor_max_outs.n_audio());
+       return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
 }
 
 int 
-Route::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, 
-                  bool session_state_changing, bool can_record, bool rec_monitors_input)
+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*/)
 {
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
-       if (session_state_changing || !_active)  {
-               silence (nframes, offset);
+       if (session_state_changing || !_active || n_inputs() == ChanCount::ZERO)  {
+               silence (nframes);
                return 0;
        }
 
-       apply_gain_automation = false;
-       
-       if (n_inputs().n_total()) {
-               passthru (start_frame, end_frame, nframes, offset, 0, false);
-       } else {
-               silence (nframes, offset);
-       }
+       _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& offset, nframes_t& transport_frame)
+Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
                _roll_delay -= nframes;
-               silence (nframes, offset);
+               silence (nframes);
                /* transport frame is not legal for caller to use */
                return 0;
 
        } else if (_roll_delay > 0) {
 
                nframes -= _roll_delay;
-
-               silence (_roll_delay, offset);
-
-               offset += _roll_delay;
+               silence (_roll_delay);
+               /* we've written _roll_delay of samples into the 
+                  output ports, so make a note of that for
+                  future reference.
+               */
+               _main_outs->increment_output_offset (_roll_delay);
                transport_frame += _roll_delay;
 
                _roll_delay = 0;
@@ -2798,62 +2201,53 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& offset, nframes_t& tra
 }
 
 int
-Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick,
-            bool can_record, bool rec_monitors_input)
+Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
+            bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        {
+               // automation snapshot can also be called from the non-rt context
+               // and it uses the processor list, so we try to acquire the lock here
                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, offset);
+       if (n_outputs().n_total() == 0) {
+               return 0;
+       }
+
+       if (!_active || n_inputs().n_total() == 0) {
+               silence (nframes);
                return 0;
        }
        
        nframes_t unused = 0;
 
-       if ((nframes = check_initial_delay (nframes, offset, unused)) == 0) {
+       if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
        }
 
        _silent = false;
 
-       apply_gain_automation = false;
-
-       { 
-               Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK);
-               
-               if (am.locked() && _session.transport_rolling()) {
-                       
-                       if (_gain_control->alist()->automation_playback()) {
-                               apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
-                                               start_frame, end_frame, _session.gain_automation_buffer(), nframes);
-                       }
-               }
-       }
-
-       passthru (start_frame, end_frame, nframes, offset, declick, false);
+       passthru (start_frame, end_frame, nframes, declick);
 
        return 0;
 }
 
 int
-Route::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset
-                   bool can_record, bool rec_monitors_input)
+Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/
+                   bool /*can_record*/, bool /*rec_monitors_input*/)
 {
-       silence (nframes, offset);
+       silence (nframes);
        return 0;
 }
 
 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());
        }
 }
@@ -2861,15 +2255,15 @@ 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(':'));
@@ -2906,63 +2300,107 @@ Route::set_meter_point (MeterPoint p, void *src)
 {
        if (_meter_point != p) {
                _meter_point = p;
+
+               // Move meter in the processors list
+               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;
+               }
+               _processors.insert(loc, _meter);
+               
                 meter_change (src); /* EMIT SIGNAL */
+               processors_changed (); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
 }
+void
+Route::put_control_outs_at (Placement p)
+{
+       if (!_control_outs) {
+               return;
+       }
+
+       // Move meter in the processors list
+       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
+       _processors.erase(loc);
+
+       switch (p) {
+       case PreFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               if (loc != _processors.begin()) {
+                       --loc;
+               }
+               break;
+       case PostFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               assert (loc != _processors.end());
+               loc++;
+               break;
+       }
+
+       _processors.insert(loc, _control_outs);
+
+       processors_changed (); /* EMIT SIGNAL */
+       _session.set_dirty ();
+}
 
 nframes_t
 Route::update_total_latency ()
 {
-       nframes_t old = _own_latency;
-
-       if (_user_latency) {
-               _own_latency = _user_latency;
-       } else {
-               _own_latency = 0;
+       nframes_t old = _output->effective_latency();
+       nframes_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;
+       cerr << _name << ": internal redirect latency = " << own_latency << endl;
 #endif
 
-       set_port_latency (_own_latency);
+       _output->set_port_latency (own_latency);
        
-       if (!_user_latency) {
+       if (_output->user_latency() == 0) {
+
                /* this (virtual) function is used for pure Routes,
                   not derived classes like AudioTrack.  this means
                   that the data processed here comes from an input
                   port, not prerecorded material, and therefore we
                   have to take into account any input latency.
                */
-
-
-               _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;
+       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
+            << own_latency << endl;
 #endif
 
-       return _own_latency;
+       return _output->effective_latency ();
 }
 
 void
 Route::set_user_latency (nframes_t nframes)
 {
-       Latent::set_user_latency (nframes);
+       _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
@@ -2971,8 +2409,8 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 {
        nframes_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;
        }
@@ -2989,57 +2427,32 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 void
 Route::automation_snapshot (nframes_t now, bool force)
 {
-       if (!force && !should_snapshot(now)) {
-               return;
-       }
-
-       IO::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 (float 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;
-       }
+       route.set_solo (bval, this);
 }
 
 float
-Route::ToggleControllable::get_value (void) const
+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;
-       }
-
-       return val;
+       return route.soloed() ? 1.0f : 0.0f;
 }
 
 void 
@@ -3048,13 +2461,12 @@ Route::set_block_size (nframes_t nframes)
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
+       _session.ensure_buffers(processor_max_streams);
 }
 
 void
 Route::protect_automation ()
 {
-       Automatable::protect_automation();
-       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i)
                (*i)->protect_automation();
 }
@@ -3074,3 +2486,223 @@ Route::set_pending_declick (int declick)
 
 }
 
+/** Shift automation forwards from a particular place, thereby inserting time.
+ *  Adds undo commands for any shifts that are performed.
+ *
+ * @param pos Position to start shifting from.
+ * @param frames Amount to shift forwards by.
+ */
+
+void
+Route::shift (nframes64_t /*pos*/, nframes64_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));
+
+       /* 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));
+       }
+
+       /* 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
+
+}
+
+
+int
+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());
+}
+
+
+bool
+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) {
+               
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       
+                       /* rename all I/O processors that have inputs or outputs */
+
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
+
+                       if (iop && (iop->output() || iop->input())) {
+                               if (!iop->set_name (name)) {
+                                       ret = false;
+                               }
+                       }
+               }
+
+       }
+
+       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>();
+}
+
+void
+Route::set_phase_invert (bool yn)
+{
+       if (_phase_invert != yn) {
+               _phase_invert = 0xffff; // XXX all channels
+               phase_invert_changed (); /* EMIT SIGNAL */
+       }
+}
+
+bool
+Route::phase_invert () const
+{
+       return _phase_invert != 0;
+}
+
+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>(data().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)->data().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;
+}