make i18n build work ; add mackie dir back to build ; token work on amp for MIDI...
[ardour.git] / libs / ardour / route.cc
index 931ae9a99625a42bb2fc4097e0e57d7a4b5f4872..56311c975db1063cb0fe929bbfd11cab0ec59c83 100644 (file)
@@ -20,6 +20,7 @@
 #include <cmath>
 #include <fstream>
 #include <cassert>
+#include <algorithm>
 
 #include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "ardour/buffer.h"
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
-#include "ardour/control_outputs.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"
@@ -61,83 +64,104 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
+sigc::signal<void, string const &> Route::SyncOrderKeys;
 
-Route::Route (Session& sess, string name, Flag flg,
-               DataType default_type, ChanCount in, ChanCount out)
-       : IO (sess, name, default_type, in, ChanCount::INFINITE, out, ChanCount::INFINITE)
+Route::Route (Session& sess, string name, Flag flg, DataType default_type)
+       : SessionObject (sess, name)
+       , AutomatableControls (sess)
        , _flags (flg)
-       , _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl))
-       , _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
+       , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_master (new MuteMaster (sess, name))
+       , _default_type (default_type)
+         
 {
-       _configured_inputs = in;
-       _configured_outputs = out;
        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 ()
 {
+       _solo_level = 0;
+       _solo_isolated = false;
+       _active = true;
        processor_max_streams.reset();
-       _muted = false;
-       _soloed = false;
        _solo_safe = false;
        _recordable = true;
-       _active = true;
-       _phase_invert = false;
-       _denormal_protection = false;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
+       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 */
 
-       input_changed.connect (mem_fun (this, &Route::input_change_handler));
-       output_changed.connect (mem_fun (this, &Route::output_change_handler));
+       _input.reset (new IO (_session, _name, IO::Input, _default_type));
+       _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       _amp->set_sort_key (0);
-       _meter->set_sort_key (1);
-       add_processor (_amp, NULL);
-       add_processor (_meter, NULL);
+       _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));
-       }
 }
 
 void
@@ -156,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) {
@@ -183,8 +204,12 @@ 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 (const char* base)
+Route::sync_order_keys (std::string const & base)
 {
        if (order_keys.empty()) {
                return;
@@ -221,20 +246,21 @@ Route::ensure_track_or_route_name(string name, Session &session)
        return newname;
 }
 
+
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
-       IO::inc_gain (fraction, src);
+       _amp->inc_gain (fraction, src);
 }
 
 void
 Route::set_gain (gain_t val, void *src)
 {
-       if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
+       if (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;
                        }
@@ -252,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)
@@ -294,354 +320,314 @@ Route::set_gain (gain_t val, void *src)
  */
 void
 Route::process_output_buffers (BufferSet& bufs,
-               nframes_t start_frame, nframes_t end_frame, nframes_t nframes,
-               bool with_processors, int declick)
+                              sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
+                              bool /*with_processors*/, int declick)
 {
-       ProcessorList::iterator i;
-       bool mute_declick_applied = false;
-       gain_t dmg, dsg, dg;
-       bool no_monitor;
+       bool monitor;
 
-       bufs.is_silent(false);
+       bufs.is_silent (false);
 
        switch (Config->get_monitoring_model()) {
        case HardwareMonitoring:
        case ExternalMonitoring:
-               no_monitor = true;
+               monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
                break;
        default:
-               no_monitor = false;
+               monitor = true;
        }
 
-       declick = _pending_declick;
-       
-       const bool recording_without_monitoring = no_monitor && record_enabled()
-                       && (!Config->get_auto_input() || _session.actively_recording());
-       
-
-       /* -------------------------------------------------------------------------------------------
-          SET UP GAIN (FADER)
-          ----------------------------------------------------------------------------------------- */
-
-       { 
-               Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
-               
-               if (dm.locked()) {
-                       dmg = desired_mute_gain;
-                       dsg = desired_solo_gain;
-                       dg = _gain_control->user_float();
-               } else {
-                       dmg = mute_gain;
-                       dsg = solo_gain;
-                       dg = _gain;
-               }
+       if (!declick) {
+               declick = _pending_declick;
        }
-       
-       // apply gain at the amp if...
-       _amp->apply_gain(
-                       // we're not recording
-                       !(record_enabled() && _session.actively_recording())
-                       // or (we are recording, and) software monitoring is required
-                       || Config->get_monitoring_model() == SoftwareMonitoring);
-       
-       // mute at the amp if...
-       _amp->apply_mute(
-               !_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader,
-               mute_gain, dmg);
 
-       _amp->set_gain (_gain, dg);
+       /* figure out if we're going to use gain automation */
+       _amp->setup_gain_automation (start_frame, end_frame, nframes);
        
 
-       /* -------------------------------------------------------------------------------------------
-          SET UP CONTROL OUTPUTS
-          ----------------------------------------------------------------------------------------- */
+       /* tell main outs what to do about monitoring */
+       _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
-       boost::shared_ptr<ControlOutputs> co = _control_outs;
-       if (co) {
-               // deliver control outputs unless we're ...
-               co->deliver (!(
-                               dsg == 0 || // muted by solo of another track
-                               (dmg == 0 && _mute_affects_control_outs) || // or muted by mute of this track
-                               !recording_without_monitoring )); // or rec-enabled w/o s/w monitoring 
-       }
-       
 
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0, false);
-               _pending_declick = 0;
+               Amp::apply_gain (bufs, nframes, 0.0, 1.0);
        } else if (declick < 0) {
-               Amp::apply_gain (bufs, nframes, 1.0, 0.0, false);
-               _pending_declick = 0;
-       } else { // no global declick
-               if (solo_gain != dsg) {
-                       Amp::apply_gain (bufs, nframes, solo_gain, dsg, false);
-                       solo_gain = dsg;
-               }
-       }
-
+               Amp::apply_gain (bufs, nframes, 1.0, 0.0);
+       } 
 
+       _pending_declick = 0;
+               
        /* -------------------------------------------------------------------------------------------
-          PRE-FADER MUTING
+          DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) {
-               Amp::apply_gain (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
-       if (mute_gain == 0.0f && dmg == 0.0f) {
-               bufs.is_silent(true);
-       }
+       if (_phase_invert) {
+               
+               int chn = 0;
 
+               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 = 0; nx < nframes; ++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];
+                                       }
+                               } 
                        }
                }
-       }
-
 
-       /* -------------------------------------------------------------------------------------------
-          PROCESSORS (including Amp (fader) and Meter)
-          ----------------------------------------------------------------------------------------- */
+       } else {
 
-       if (with_processors) {
-               Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-               if (rm.locked()) {
-                       //if (!bufs.is_silent()) {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->input_streams()));
-                                       (*i)->run_in_place (bufs, start_frame, end_frame, nframes);
-                                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams()));
-                               }
-                       /*} else {
-                               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                                       (*i)->silence (nframes);
-                                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams()));
+               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;
                                }
-                       }*/
-               } 
+                       }
 
-               if (!_processors.empty()) {
-                       bufs.set_count(ChanCount::max(bufs.count(), _processors.back()->output_streams()));
-               }
+               } 
        }
-       
 
        /* -------------------------------------------------------------------------------------------
-          POST-FADER MUTING
+          and go ....
           ----------------------------------------------------------------------------------------- */
 
-       if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) {
-               Amp::apply_gain (bufs, nframes, mute_gain, dmg, false);
-               mute_gain = dmg;
-               mute_declick_applied = true;
-       }
-       if (mute_gain == 0.0f && dmg == 0.0f) {
-               bufs.is_silent(true);
-       }
-       
+       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
 
-       /* -------------------------------------------------------------------------------------------
-          MAIN OUTPUT STAGE
-          ----------------------------------------------------------------------------------------- */
+       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()));
+               }
 
-       bool solo_audible = dsg > 0;
-       bool mute_audible = dmg > 0 || !_mute_affects_main_outs;
-       
-       if (n_outputs().get(_default_type) == 0) {
-           
-           /* relax */
+               if (!_processors.empty()) {
+                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+               }
+       }
+}
+
+ChanCount
+Route::n_process_buffers ()
+{
+       return max (_input->n_ports(), processor_max_streams);
+}
 
-       } else if (recording_without_monitoring) {
+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());
 
-               IO::silence (nframes);
+       _silent = false;
 
-       } else {
+       assert (bufs.available() >= _input->n_ports());
+       
+       if (_input->n_ports() == ChanCount::ZERO) {
+               silence (nframes);
+       }
+       
+       bufs.set_count (_input->n_ports());
 
-               if ( // we're silent anyway
-                       (_gain == 0 && !_amp->apply_gain_automation()) ||
-                       
-                       // or muted by solo of another track, but not using control outs for solo
-                       (!solo_audible && (Config->get_solo_model() != SoloBus)) ||
+       if (is_control() && _session.listening()) {
+               
+               /* control/monitor bus ignores input ports when something is
+                  feeding the listen "stream". data will "arrive" into the
+                  route from the intreturn processor element.
+               */
                        
-                       // or muted by mute of this track
-                       !mute_audible
-                       ) {
+               bufs.silence (nframes, 0);
 
-                       /* don't use Route::silence() here, because that causes
-                          all outputs (sends, port processors, etc. to be silent).
-                       */
-                       IO::silence (nframes);
+       } else {
+       
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        
-               } else {
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
                        
-                       deliver_output(bufs, start_frame, end_frame, nframes);
-
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
+                       }
                }
-
        }
 
-       /* -------------------------------------------------------------------------------------------
-          POST-FADER METERING
-          ----------------------------------------------------------------------------------------- */
-
-       /* TODO: Processor-list-ification needs to go further for this to be cleanly possible...
-       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);
-               }
-       }*/
-
-       // at this point we've reached the desired mute gain regardless
-       mute_gain = dmg;
-}
-
-ChanCount
-Route::n_process_buffers ()
-{
-       return max (n_inputs(), processor_max_streams);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
 }
 
 void
-Route::setup_peak_meters()
+Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
 {
-       ChanCount max_streams = std::max (_inputs.count(), _outputs.count());
-       max_streams = std::max (max_streams, processor_max_streams);
-       _meter->configure_io (max_streams, max_streams);
+       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
 }
 
 void
-Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick)
+Route::set_listen (bool yn, void* src)
 {
-       BufferSet& bufs = _session.get_scratch_buffers(n_process_buffers());
-
-       _silent = false;
-
-       collect_input (bufs, nframes);
+       if (_control_outs) { 
+               if (yn != _control_outs->active()) {
+                       if (yn) {
+                               _control_outs->activate ();
+                       } else {
+                               _control_outs->deactivate ();
+                       }
 
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+                       listen_changed (src); /* EMIT SIGNAL */
+               }
+       }
 }
 
-void
-Route::passthru_silence (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, int declick)
+bool
+Route::listening () const
 {
-       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+       if (_control_outs) {
+               return _control_outs->active ();
+       } else {
+               return false;
+       }
 }
 
 void
 Route::set_solo (bool yn, void *src)
 {
-       if (_solo_safe) {
+       if (_solo_safe || _solo_isolated) {
                return;
        }
 
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_solo, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo, yn, _route_group);
                return;
        }
 
-       if (_soloed != yn) {
-               _soloed = yn;
+       if (soloed() != yn) {
+               mod_solo_level (yn ? 1 : -1);
                solo_changed (src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }       
-       
-       catch_up_on_solo_mute_override ();
 }
 
 void
-Route::catch_up_on_solo_mute_override ()
+Route::mod_solo_level (int32_t delta)
 {
-       if (Config->get_solo_model() != InverseMute) {
-               return;
-       }
-       
-       {
-               Glib::Mutex::Lock lm (declick_lock);
-               
-               if (_muted) {
-                       if (Config->get_solo_mute_override()) {
-                               desired_mute_gain = (_soloed?1.0:0.0);
-                       } else {
-                               desired_mute_gain = 0.0;
-                       }
+       if (delta < 0) {
+               if (_solo_level >= (uint32_t) delta) {
+                       _solo_level += delta;
                } else {
-                       desired_mute_gain = 1.0;
+                       _solo_level = 0;
                }
+       } else {
+               _solo_level += delta;
        }
+
+       /* tell main outs what the solo situation is
+        */
+
+       _main_outs->set_solo_level (_solo_level);
+       _main_outs->set_solo_isolated (_solo_isolated);
 }
 
 void
-Route::set_solo_mute (bool yn)
+Route::set_solo_isolated (bool yn, void *src)
 {
-       Glib::Mutex::Lock lm (declick_lock);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               return;
+       }
 
-       /* Called by Session in response to another Route being soloed.
-        */
-          
-       desired_solo_gain = (yn?0.0:1.0);
+       if (yn != _solo_isolated) {
+               _solo_isolated = yn;
+
+               /* tell main outs what the solo situation is
+                */
+               
+               _main_outs->set_solo_level (_solo_level);
+               _main_outs->set_solo_isolated (_solo_isolated);
+
+               solo_isolated_changed (src);
+       }
 }
 
-void
-Route::set_solo_safe (bool yn, void *src)
+bool
+Route::solo_isolated () const 
 {
-       if (_solo_safe != yn) {
-               _solo_safe = yn;
-                solo_safe_changed (src); /* EMIT SIGNAL */
-       }
+       return _solo_isolated;
 }
 
 void
 Route::set_mute (bool yn, void *src)
-
 {
-       if (_mix_group && src != _mix_group && _mix_group->is_active()) {
-               _mix_group->apply (&Route::set_mute, yn, _mix_group);
+       if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) {
+               _route_group->apply (&Route::set_mute, yn, _route_group);
                return;
        }
 
-       if (_muted != yn) {
-               _muted = yn;
-               mute_changed (src); /* EMIT SIGNAL */
-               
-               _mute_control->Changed (); /* EMIT SIGNAL */
-               
-               Glib::Mutex::Lock lm (declick_lock);
-               
-               if (_soloed && Config->get_solo_mute_override()) {
-                       desired_mute_gain = 1.0f;
-               } else {
-                       desired_mute_gain = (yn?0.0f:1.0f);
-               }
+       if (muted() != yn) {
+               _mute_master->mute (yn);
+               mute_changed (src);
        }
+}      
+
+bool
+Route::muted() const 
+{
+       return _mute_master->muted ();
 }
 
+#if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
 {
        cerr << name << " {" << endl;
        for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
                        p != procs.end(); ++p) {
-               cerr << "\t" << (*p)->sort_key() << ": " << (*p)->name() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
        }
        cerr << "}" << endl;
 }
+#endif
+
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
+{
+       ProcessorList::iterator loc;
+
+       /* 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 (placement == PreFader) {
+               /* generic pre-fader: insert immediately before the amp */
+               loc = find (_processors.begin(), _processors.end(), _amp);
+       } else {
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
+
+       return add_processor (processor, loc, err);
+}
+
 
 /** Add a processor to the route.
  * If @a iter is not NULL, it must point to an iterator in _processors and the new
@@ -649,7 +635,7 @@ dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& p
  * @a position is used.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, ProcessorList::iterator* iter, Placement placement)
+Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err)
 {
        ChanCount old_pms = processor_max_streams;
 
@@ -665,18 +651,13 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
 
                ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
 
-               if (processor == _amp || processor == _meter) {
-                       // Ensure only one amp and one meter are in the list at any time
+               if (processor == _amp || processor == _meter || processor == _main_outs) {
+                       // Ensure only one of these are in the list at any time
                        if (loc != _processors.end()) {
-                               if (iter) {
-                                       if (*iter == loc) { // Already in place, do nothing
-                                               return 0;
-                                       } else { // New position given, relocate
-                                               _processors.erase(loc);
-                                       }
-                               } else { // Insert at end
-                                       _processors.erase(loc);
-                                       loc = _processors.end();
+                               if (iter == loc) { // Already in place, do nothing
+                                       return 0;
+                               } else { // New position given, relocate
+                                       _processors.erase (loc);
                                }
                        }
 
@@ -685,41 +666,22 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                                cerr << "ERROR: Processor added to route twice!" << endl;
                                return 1;
                        }
-               }
-
-               // Use position given by user
-               if (iter) {
-                       loc = *iter;
-
-               // Insert immediately before the amp
-               } else if (placement == PreFader) {
-                       loc = find(_processors.begin(), _processors.end(), _amp);
 
-               // Insert at end
-               } else {
-                       loc = _processors.end();
-               }
-               
-               // Update sort keys
-               if (loc == _processors.end()) {
-                       processor->set_sort_key(_processors.size());
-               } else {
-                       processor->set_sort_key((*loc)->sort_key());
-                       for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) {
-                               (*p)->set_sort_key((*p)->sort_key() + 1);
-                       }
+                       loc = iter;
                }
 
-               _processors.insert(loc, processor);
+               _processors.insert (loc, processor);
 
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
+               
+
                if (configure_processors_unlocked (err)) {
-                       dump_processors(_name, _processors);
                        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;
                }
        
@@ -732,99 +694,209 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                        
                }
                
-               // Ensure peak vector sizes before the plugin is activated
-               ChanCount potential_max_streams = ChanCount::max(
-                               processor->input_streams(), processor->output_streams());
-
-               _meter->configure_io (potential_max_streams, potential_max_streams);
-
                // 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));
 
-               _user_latency = 0;
+               _output->set_user_latency (0);
        }
        
-       if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) {
-               reset_panner ();
-       }
-
-       dump_processors (_name, _processors);
        processors_changed (); /* EMIT SIGNAL */
-
        
        return 0;
 }
 
-int
-Route::add_processors (const ProcessorList& others, ProcessorStreams* err, Placement placement)
+bool
+Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
 {
-       /* 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;
+       const XMLProperty *prop;
 
-       if (!_session.engine().connected()) {
-               return 1;
+       if (node.name() != "Processor") {
+               return false;
        }
+               
+       try {
+               if ((prop = node.property ("type")) != 0) {
+                       
+                       boost::shared_ptr<Processor> processor;
 
-       {
-               Glib::RWLock::WriterLock lm (_processor_lock);
+                       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") {
 
-               ProcessorList::iterator existing_end = _processors.end();
-               --existing_end;
+                               processor.reset (new PortInsert (_session, _mute_master, node));
+                               
+                       } else if (prop->value() == "send") {
 
-               ChanCount potential_max_streams = ChanCount::max(input_minimum(), output_minimum());
+                               processor.reset (new Send (_session, _mute_master, node));
 
-               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);
+                       } else if (prop->value() == "meter") {
+
+                               if (_meter) {
+                                       if (_meter->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
                                }
-                       }
-                       
-                       boost::shared_ptr<PluginInsert> pi;
-                       
-                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1);
+
+                               _meter.reset (new PeakMeter (_session, node));                                          
+                               processor = _meter;
                                
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
-                                       potential_max_streams = m;
-                       }
+                       } else if (prop->value() == "amp") {
 
-                       // Ensure peak vector sizes before the plugin is activated
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
+                               /* amp always exists */
+                                       
+                               processor = _amp;
+                               if (processor->set_state (node)) {
+                                       return false;
+                               } else {
+                                       /* never any reason to add it */
+                                       return true;
+                               }
+                                       
+                       } else if (prop->value() == "intsend") {
 
-                       ProcessorList::iterator loc = (placement == PreFader)
-                                       ? find(_processors.begin(), _processors.end(), _amp)
-                                       : _processors.end();
+                               processor.reset (new InternalSend (_session, _mute_master, node));
 
-                       _processors.insert (loc, *i);
-                       
-                       if (configure_processors_unlocked (err)) {
-                               ++existing_end;
-                               _processors.erase (existing_end, _processors.end());
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
-                       }
-                       
-                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+                       } 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;
+
+                       } 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;
+
+                       } 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;
+                       }
+
+                       return (add_processor (processor, iter) == 0);
+                               
+               } else {
+                       error << _("Processor XML node has no type property") << endmsg;
+                       return false;
                }
+       }
 
-               _user_latency = 0;
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
        }
-       
-       if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) {
-               reset_panner ();
+}
+
+int
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
+{
+       ProcessorList::iterator loc;
+
+       if (before) {
+               loc = find(_processors.begin(), _processors.end(), before);
+       } else {
+               /* nothing specified - at end but before main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
+
+       return add_processors (others, loc, err);
+}
+
+int
+Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
+{
+       /* NOTE: this is intended to be used ONLY when copying
+          processors from another Route. Hence the subtle
+          differences between this and ::add_processor()
+       */
+
+       ChanCount old_pms = processor_max_streams;
+
+       if (!_session.engine().connected()) {
+               return 1;
+       }
+
+       if (others.empty()) {
+               return 0;
+       }
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList::iterator existing_end = _processors.end();
+               --existing_end;
+
+               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) {
+                                       potential_max_streams = m;
+                               }
+                       }
+
+                       _processors.insert (iter, *i);
+                       
+                       if (configure_processors_unlocked (err)) {
+                               ++existing_end;
+                               _processors.erase (existing_end, _processors.end());
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return -1;
+                       }
+                       
+                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+               }
+
+               _output->set_user_latency (0);
        }
        
-       dump_processors (_name, _processors);
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
@@ -958,31 +1030,6 @@ 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 that isn't a send; sends don't affect the number
-        * of streams. */
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if ((*i) == _amp) {
-                       break;
-               }
-               if (boost::dynamic_pointer_cast<Send> (*i) == 0) {
-                       processor = *i;
-               }
-       }
-       
-       if (processor) {
-               return processor->output_streams();
-       } else {
-               return n_inputs ();
-       }
-}
-
-
 /** Remove processors with a given placement.
  * @param p Placement of processors to remove.
  */
@@ -1003,37 +1050,51 @@ Route::clear_processors (Placement p)
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
-               
-               ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
-               if (p == PreFader) {
-                       // Get rid of PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
-                               (*i)->drop_references ();
-                       }
-                       // Keep the rest
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               new_list.push_back (*i);
+               ProcessorStreams err;
+               bool seen_amp = false;
+
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+                       if (*i == _amp) {
+                               seen_amp = true;
                        }
-               } else {
-                       // Keep PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                               
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
-                       }
-                       new_list.push_back (_amp);
-                       // Get rid of PostFader processors
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               (*i)->drop_references ();
+
+                       } else {
+                               if (seen_amp) {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       case PostFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       }
+
+                               } else {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       case PostFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       }
+                               }
                        }
                }
 
                _processors = new_list;
+               configure_processors_unlocked (&err); // this can't fail
        }
 
-       /* FIXME: can't see how this test can ever fire */
-       if (processor_max_streams != old_pms) {
-               reset_panner ();
-       }
-       
        processor_max_streams.reset();
        _have_internal_generator = false;
        processors_changed (); /* EMIT SIGNAL */
@@ -1046,7 +1107,9 @@ Route::clear_processors (Placement p)
 int
 Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
 {
-       if (processor == _amp || processor == _meter) {
+       /* these can never be removed */
+
+       if (processor == _amp || processor == _meter || processor == _main_outs) {
                return 0;
        }
 
@@ -1063,38 +1126,38 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                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 configure_processors()
-                                  where we may need to reprocessor the processor.
+                                  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);
-                               }
+                               boost::shared_ptr<IOProcessor> iop;
 
-                               _processors.erase (i);
+                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                                       if (iop->input()) {
+                                               iop->input()->disconnect (this);
+                                       }
+                                       if (iop->output()) {
+                                               iop->output()->disconnect (this);
+                                       }
+                               }
 
-                               i = tmp;
+                               i = _processors.erase (i);
                                removed = true;
                                break;
+
+                       } else {
+                               ++i;
                        }
 
-                       _user_latency = 0;
+                       _output->set_user_latency (0);
                }
 
                if (!removed) {
@@ -1124,18 +1187,105 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                }
        }
 
-       if (old_pms != processor_max_streams) {
-               reset_panner ();
+       processor->drop_references ();
+       processors_changed (); /* EMIT SIGNAL */
+
+       return 0;
+}
+
+int
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
+{
+       ProcessorList deleted;
+       ProcessorList as_we_were;
+
+       if (!_session.engine().connected()) {
+               return 1;
+       }
+
+       processor_max_streams.reset();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList::iterator i;
+               boost::shared_ptr<Processor> processor;
+
+               as_we_were = _processors;
+
+               for (i = _processors.begin(); i != _processors.end(); ) {
+                       
+                       processor = *i;
+
+                       /* these can never be removed */
+
+                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                               ++i;
+                               continue;
+                       }
+                       
+                       /* 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;
+                       }
+
+                       /* 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 ();
+                       }
+
+                       deleted.push_back (processor);
+                       i = _processors.erase (i);
+               }
+
+               if (deleted.empty()) {
+                       /* none of those in the requested list were found */
+                       return 0;
+               }
+
+               _output->set_user_latency (0);
+
+               if (configure_processors_unlocked (err)) {
+                       /* get back to where we where */
+                       _processors = as_we_were;
+                       /* we know this will work, because it worked before :) */
+                       configure_processors_unlocked (0);
+                       return -1;
+               }
+
+               _have_internal_generator = false;
+
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       
+                       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+                               if (pi->is_generator()) {
+                                       _have_internal_generator = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /* now try to do what we need to so that those that were removed will be deleted */
+
+       for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+               (*i)->drop_references ();
        }
 
-       processor->drop_references ();
-       
-       dump_processors (_name, _processors);
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
 }
 
+
 int
 Route::configure_processors (ProcessorStreams* err)
 {
@@ -1159,12 +1309,12 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        _in_configure_processors = true;
 
        // Check each processor in order to see if we can configure as requested
-       ChanCount in = _configured_inputs;
+       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) {
-               (*p)->set_sort_key(index);
                if ((*p)->can_support_io_configuration(in, out)) {
                        configuration.push_back(make_pair(in, out));
                        in = out;
@@ -1182,15 +1332,15 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
                (*p)->configure_io(c->first, c->second);
-               (*p)->activate();
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
                out = c->second;
        }
 
        // Ensure route outputs match last processor's outputs
-       if (out != n_outputs()) {
-               ensure_io(_configured_inputs, out, false, this);
+       if (out != _output->n_ports ()) {
+               cerr << "For " << _name << " out/last mismatch - out = " << out << " vs. " << _output->n_ports() << endl;
+               _output->ensure_io (out, false, this);
        }
 
        _in_configure_processors = false;
@@ -1252,26 +1402,99 @@ Route::all_processors_active (Placement p, bool state)
        _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();
-    }
-};
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
+{
+       bool pre_fader = true;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* semantic note: if p == amp, we want to return true, so test
+                  for equality before checking if this is the amp
+               */
+
+               if ((*i) == p) {
+                       break;
+               }
+
+               if ((*i) == _amp) {
+                       pre_fader = false;
+                       break;
+               }
+       }
+
+       return pre_fader;
+}
 
 int
-Route::sort_processors (ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
+       /* "new_order" is an ordered list of processors to be positioned according to "placement".
+          NOTE: all processors in "new_order" MUST be marked as 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.
+       */
+
        {
-               ProcessorSorter comparator;
                Glib::RWLock::WriterLock lm (_processor_lock);
                ChanCount old_pms = processor_max_streams;
+               ProcessorList::iterator oiter;
+               ProcessorList::const_iterator niter;
+               ProcessorList as_it_was_before = _processors;
+               ProcessorList as_it_will_be;
 
-               /* the sweet power of C++ ... */
+               oiter = _processors.begin();
+               niter = new_order.begin(); 
 
-               ProcessorList as_it_was_before = _processors;
+               while (niter !=  new_order.end()) {
+                       
+                       /* 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. 
+
+                          Otherwise, see if the next processor in the old list is in the new list. if not,
+                          its been deleted. If its there, append it to the temp list.
+                       */
+
+                       if (oiter == _processors.end()) {
+
+                               /* no more elements in the old list, so just stick the rest of 
+                                  the new order onto the temp list.
+                               */
+
+                               as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
+                               while (niter != new_order.end()) {
+                                       ++niter;
+                               }
+                               break;
+
+                       } else {
+                               
+                               if (!(*oiter)->visible()) {
+
+                                       as_it_will_be.push_back (*oiter);
+
+                               } else {
+
+                                       /* visible processor: check that its in the new order */
+
+                                       if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) {
+                                               /* deleted: do nothing, shared_ptr<> will clean up */
+                                       } else {
+                                               /* ignore this one, and add the next item from the new order instead */
+                                               as_it_will_be.push_back (*niter);
+                                               ++niter;
+                                       }
+                               }
+                               
+                                /* 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());
 
-               _processors.sort (comparator);
-       
                if (configure_processors_unlocked (err)) {
                        _processors = as_it_was_before;
                        processor_max_streams = old_pms;
@@ -1279,7 +1502,6 @@ Route::sort_processors (ProcessorStreams* err)
                } 
        } 
 
-       reset_panner ();
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
@@ -1304,28 +1526,22 @@ Route::state(bool full_state)
        ProcessorList::iterator i;
        char buf[32];
 
+       id().print (buf, sizeof (buf));
+       node->add_property("id", buf);
+       node->add_property ("name", _name);
+       node->add_property("default-type", _default_type.to_string());
+
        if (_flags) {
                node->add_property("flags", enum_2_string (_flags));
        }
-       
-       node->add_property("default-type", _default_type.to_string());
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("muted", _muted?"yes":"no");
-       node->add_property("soloed", _soloed?"yes":"no");
        node->add_property("phase-invert", _phase_invert?"yes":"no");
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
-       node->add_property("mute-affects-pre-fader", _mute_affects_pre_fader?"yes":"no"); 
-       node->add_property("mute-affects-post-fader", _mute_affects_post_fader?"yes":"no"); 
-       node->add_property("mute-affects-control-outs", _mute_affects_control_outs?"yes":"no"); 
-       node->add_property("mute-affects-main-outs", _mute_affects_main_outs?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
-       if (_edit_group) {
-               node->add_property("edit-group", _edit_group->name());
-       }
-       if (_mix_group) {
-               node->add_property("mix-group", _mix_group->name());
+       if (_route_group) {
+               node->add_property("route-group", _route_group->name());
        }
 
        string order_string;
@@ -1347,21 +1563,16 @@ Route::state(bool full_state)
        }
        node->add_property ("order-keys", order_string);
 
-       node->add_child_nocopy (IO::state (full_state));
+       node->add_child_nocopy (_input->state (full_state));
+       node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
-       node->add_child_nocopy (_mute_control->get_state ());
+       node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
        snprintf (buf, sizeof (buf), "%d", _remote_control_id);
        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->io()->state (full_state));
-               node->add_child_nocopy (*cnode);
-       }
-
        if (_comment.length()) {
                XMLNode *cmt = node->add_child ("Comment");
                cmt->add_content (_comment);
@@ -1378,256 +1589,110 @@ Route::state(bool full_state)
        return *node;
 }
 
-XMLNode&
-Route::get_processor_state ()
+int
+Route::set_state (const XMLNode& node)
 {
-       XMLNode* root = new XMLNode (X_("redirects"));
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               root->add_child_nocopy ((*i)->state (true));
-       }
-
-       return *root;
+       return _set_state (node, true);
 }
 
 int
-Route::set_processor_state (const XMLNode& root)
+Route::_set_state (const XMLNode& node, bool /*call_base*/)
 {
-       if (root.name() != X_("redirects")) {
-               return -1;
-       }
 
        XMLNodeList nlist;
-       XMLNodeList nnlist;
-       XMLNodeConstIterator iter;
        XMLNodeConstIterator niter;
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-
-       nlist = root.children();
-       
-       for (iter = nlist.begin(); iter != nlist.end(); ++iter){
-
-               /* iter now points to a IOProcessor state node */
-               
-               nnlist = (*iter)->children ();
-
-               for (niter = nnlist.begin(); niter != nnlist.end(); ++niter) {
-
-                       /* find the IO child node, since it contains the ID we need */
-
-                       /* XXX OOP encapsulation violation, ugh */
-
-                       if ((*niter)->name() == IO::state_node_name) {
-
-                               XMLProperty* prop = (*niter)->property (X_("id"));
-                               
-                               if (!prop) {
-                                       warning << _("IOProcessor node has no ID, ignored") << endmsg;
-                                       break;
-                               }
-
-                               ID id = prop->value ();
-
-                               /* now look for a processor with that ID */
-       
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       if ((*i)->id() == id) {
-                                               (*i)->set_state (**iter);
-                                               break;
-                                       }
-                               }
-                               
-                               break;
-                               
-                       }
-               }
+       XMLNode *child;
+       XMLPropertyList plist;
+       const XMLProperty *prop;
 
+       if (node.name() != "Route"){
+               error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
+               return -1;
        }
 
-       return 0;
-}
-
-void
-Route::set_deferred_state ()
-{
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
+       if ((prop = node.property (X_("name"))) != 0) {
+               Route::set_name (prop->value());
+       } 
 
-       if (!deferred_state) {
-               return;
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
        }
 
-       nlist = deferred_state->children();
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               add_processor_from_xml (**niter);
+       if ((prop = node.property (X_("flags"))) != 0) {
+               _flags = Flag (string_2_enum (prop->value(), _flags));
+       } else {
+               _flags = Flag (0);
        }
 
-       delete deferred_state;
-       deferred_state = 0;
-}
+       /* add all processors (except amp, which is always present) */
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator* iter)
-{
-       const XMLProperty *prop;
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
 
-       // legacy sessions use a different node name for sends
-       if (node.name() == "Send") {
-       
-               try {
-                       boost::shared_ptr<Send> send (new Send (_session, node));
-                       add_processor (send, 0, iter);
-                       return true;
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
-               }
-               
-       } else if (node.name() == "Processor") {
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                
-               try {
-                       if ((prop = node.property ("type")) != 0) {
-
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = false;
-
-                               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") {
-
-                                       processor.reset (new PortInsert (_session, node));
-                               
-                               } else if (prop->value() == "send") {
-
-                                       processor.reset (new Send (_session, node));
-                                       have_insert = true;
-                               
-                               } else if (prop->value() == "meter") {
-
-                                       processor = _meter;
-                               
-                               } else if (prop->value() == "amp") {
-                                       
-                                       processor = _amp;
-
-                               } else {
+               child = *niter;
 
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
-                               }
-                               
-                               return (add_processor (processor, 0, iter) == 0);
-                               
-                       } else {
-                               error << _("Processor XML node has no type property") << endmsg;
+               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);
                        }
                }
-
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
-                       return false;
+                       
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
                }
        }
-       return false;
-}
 
-int
-Route::set_state (const XMLNode& node)
-{
-       return _set_state (node, true);
-}
-
-int
-Route::_set_state (const XMLNode& node, bool call_base)
-{
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
-       XMLNode *child;
-       XMLPropertyList plist;
-       const XMLProperty *prop;
-
-       if (node.name() != "Route"){
-               error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
-               return -1;
+       set_processor_state (processor_state);
+       
+       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 (X_("flags"))) != 0) {
-               _flags = Flag (string_2_enum (prop->value(), _flags));
-       } else {
-               _flags = Flag (0);
-       }
-       
-       if ((prop = node.property (X_("default-type"))) != 0) {
-               _default_type = DataType(prop->value());
-               assert(_default_type != DataType::NIL);
+       if ((prop = node.property ("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_("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_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
        
-       if ((prop = node.property (X_("edit-group"))) != 0) {
-               RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
-               if(edit_group == 0) {
-                       error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
+       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);
                }
        }
 
@@ -1648,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);
                                }
                        }
 
@@ -1662,79 +1727,10 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
-       nlist = node.children();
-
-       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;
-       bool has_meter_processor = false; // legacy sessions don't
-                       
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-
-               child = *niter;
-                       
-               if (child->name() == X_("Send") || child->name() == X_("Processor")) {
-                       processor_nodes.push_back(child);
-                       if ((prop = child->property (X_("type"))) != 0 && prop->value() == "meter")  {
-                               has_meter_processor = true;
-                       }
-               }
-
-       }
-
-       _set_processor_states(processor_nodes);
-       if (!has_meter_processor) {
-               set_meter_point(_meter_point, NULL);
-       }
-       processors_changed ();
-
        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 = boost::shared_ptr<ControlOutputs> (
-                                       new ControlOutputs (_session, new IO (_session, coutname)));
-                       
-                       /* fix up the control out name in the XML before setting it.
-                          Otherwise track templates don't work because the control
-                          outs end up with the stored template name, rather than
-                          the new name of the track based on the template.
-                       */
-                       
-                       XMLProperty* prop = (*child->children().begin())->property ("name");
-                       if (prop) {
-                               prop->set_value (coutname);
-                       }
-                       
-                       _control_outs->io()->set_state (**(child->children().begin()));
-                       _control_outs->set_sort_key (_meter->sort_key() + 1);
-                       add_processor (_control_outs, 0);
 
-               } else if (child->name() == X_("Comment")) {
+               if (child->name() == X_("Comment")) {
 
                        /* XXX this is a terrible API design in libxml++ */
 
@@ -1750,61 +1746,64 @@ Route::_set_state (const XMLNode& node, bool call_base)
                        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_("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)
-                       XMLNode* ioproc_node = (*niter)->child(X_("IOProcessor"));
-                       if (ioproc_node && strncmp(buf, ioproc_node->child(X_("IO"))->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;
-                       } else {
-                               XMLProperty* id_prop = (*niter)->property(X_("id"));
-                               if (id_prop && strncmp(buf, id_prop->value().c_str(), sizeof(buf)) == 0) {
-                                       processorInStateList = true;
-                               }
-                               break;
                        }
                }
                
@@ -1815,58 +1814,66 @@ Route::_set_processor_states(const XMLNodeList &nlist)
                i = tmp;
        }
 
-       Placement placement = PreFader;
-
        // 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));
-                       XMLNode* ioproc_node = (*niter)->child(X_("IOProcessor"));
-                       if (ioproc_node && strncmp(buf, ioproc_node->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
-                               break;
-                       } else {
+               // 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 && strncmp(buf, id_prop->value().c_str(), sizeof(buf)) == 0) {
+                               if (id_prop && (*o)->id() == id_prop->value()) {
                                        break;
                                }
+                               
+                               ++o;
                        }
-
-                       ++o;
                }
 
                // If the processor (*niter) is not on the route,
                // create it and move it to the correct location
+
                if (o == _processors.end()) {
-                       if (add_processor_from_xml (**niter, &i)) {
+
+                       if (add_processor_from_xml (**niter, i)) {
                                --i; // move iterator to the newly inserted processor
                        } else {
                                cerr << "Error restoring route: unable to restore processor" << endl;
                        }
 
-               // Otherwise, we found the processor (*niter) on the route,
-               // ensure it is at the location provided in the XML state
                } else {
 
+                       // 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
+                               _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
                        }
 
-                       (*i)->set_state((**niter));
-               }
+                       // and make it (just) so
 
-               if (*i == _amp) {
-                       placement = PostFader;
+                       (*i)->set_state (**niter);
                }
        }
+
+       /* note: there is no configure_processors() call because we figure that
+          the XML state represents a working signal route.
+       */
+
+       processors_changed ();
 }
 
 void
@@ -1881,135 +1888,179 @@ Route::silence (nframes_t nframes)
 {
        if (!_silent) {
 
-               IO::silence (nframes);
+               _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);
+                               }
 
-               if (_control_outs) {
-                       _control_outs->io()->silence (nframes);
+                               if (nframes == _session.get_block_size()) {
+                                       // _silent = true;
+                               }
+                       }
                }
+               
+       }
+}      
+
+void
+Route::add_internal_return ()
+{
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+}
+
+BufferSet*
+Route::get_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
+               }
+       }
+       
+       return 0;
+}
+
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       return d->release_buffers ();
+               }
+       }
+}
+
+int
+Route::listen_via (boost::shared_ptr<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);
 
-               { 
-                       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;
-                                       }
+                       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.
+                               */
 
-                                       (*i)->silence (nframes);
+                               if (route == _session.control_out()) {
+                                       _control_outs = boost::dynamic_pointer_cast<Delivery>(d);
                                }
 
-                               if (nframes == _session.get_block_size()) {
-                                       // _silent = true;
-                               }
+                               /* already listening via the specified IO: do nothing */
+                               
+                               return 0;
                        }
                }
-               
        }
-}      
-
-int
-Route::set_control_outs (const vector<string>& ports)
-{
-       vector<string>::const_iterator i;
        
-       if (is_control() || is_master()) {
-               /* no control outs for these two special busses */
-               return 0;
-       }
-       
-       if (ports.empty()) {
-               return 0;
-       }
-       string coutname = _name;
-       coutname += _("[control]");
-       
-       IO* out_io = new IO (_session, coutname);
-       boost::shared_ptr<ControlOutputs> out_proc(new ControlOutputs (_session, out_io));
-
-       /* As an IO, our control outs need as many IO outputs as we have outputs
-        *   (we track the changes in ::output_change_handler()).
-        * As a processor, the control outs is an identity processor
-        *   (i.e. it does not modify its input buffers whatsoever)
-        */
-       if (out_io->ensure_io (ChanCount::ZERO, n_outputs(), true, this)) {
+       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;
        }
 
-       /* now connect to the named ports */
-       
-       for (size_t n = 0; n < n_outputs().n_total(); ++n) {
-               if (out_io->connect_output (out_io->output (n), ports[n % ports.size()], this)) {
-                       error << string_compose (_("could not connect %1 to %2"),
-                                       out_io->output(n)->name(), ports[n]) << endmsg;
-                       return -1;
-               }
+       if (route == _session.control_out()) {
+               _control_outs = listener;
        }
 
-       _control_outs = out_proc;
-       _control_outs->set_sort_key (_meter->sort_key() + 1);
-       add_processor (_control_outs, NULL);
+       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
@@ -2023,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->io()->n_outputs().n_total();
                
-               for (i = 0; i < no; ++i) {
-                       for (j = 0; j < ni; ++j) {
-                               if (_control_outs->io()->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();
 
@@ -2149,13 +2122,11 @@ 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 *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
        if ((change & ConfigurationChanged)) {
                configure_processors (0);
@@ -2163,14 +2134,13 @@ Route::input_change_handler (IOChange change, void *src)
 }
 
 void
-Route::output_change_handler (IOChange change, void *src)
+Route::output_change_handler (IOChange change, void * /*src*/)
 {
        if ((change & ConfigurationChanged)) {
-               if (_control_outs) {
-                       _control_outs->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this);
-               }
                
-               configure_processors (0);
+               /* XXX resize all listeners to match _main_outs? */
+               
+               // configure_processors (0);
        }
 }
 
@@ -2185,25 +2155,20 @@ Route::pans_required () const
 }
 
 int 
-Route::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,  
-               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)  {
+       if (session_state_changing || !_active || n_inputs() == ChanCount::ZERO)  {
                silence (nframes);
                return 0;
        }
 
-       _amp->apply_gain_automation(false);
-       
-       if (n_inputs() != ChanCount::ZERO) {
-               passthru (start_frame, end_frame, nframes, 0);
-       } else {
-               silence (nframes);
-       }
+       _amp->apply_gain_automation (false);
+       passthru (start_frame, end_frame, nframes, 0);
 
        return 0;
 }
@@ -2226,7 +2191,7 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
                   output ports, so make a note of that for
                   future reference.
                */
-               increment_output_offset (_roll_delay);
+               _main_outs->increment_output_offset (_roll_delay);
                transport_frame += _roll_delay;
 
                _roll_delay = 0;
@@ -2236,19 +2201,24 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 }
 
 int
-Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, 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) {
+       if (n_outputs().n_total() == 0) {
+               return 0;
+       }
+
+       if (!_active || n_inputs().n_total() == 0) {
                silence (nframes);
                return 0;
        }
@@ -2261,29 +2231,14 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, int
 
        _silent = false;
 
-       _amp->apply_gain_automation(false);
-
-       { 
-               Glib::Mutex::Lock am (data().control_lock(), Glib::TRY_LOCK);
-               
-               if (am.locked() && _session.transport_rolling()) {
-                       
-                       if (_gain_control->automation_playback()) {
-                               _amp->apply_gain_automation(
-                                               _gain_control->list()->curve().rt_safe_get_vector (
-                                                       start_frame, end_frame, _session.gain_automation_buffer(), nframes));
-                       }
-               }
-       }
-
        passthru (start_frame, end_frame, nframes, declick);
 
        return 0;
 }
 
 int
-Route::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame
-                   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);
        return 0;
@@ -2292,7 +2247,7 @@ Route::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_fram
 void
 Route::toggle_monitor_input ()
 {
-       for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+       for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) {
                i->ensure_monitor_input( ! i->monitoring_input());
        }
 }
@@ -2300,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(':'));
@@ -2362,74 +2317,90 @@ Route::set_meter_point (MeterPoint p, void *src)
                }
                _processors.insert(loc, _meter);
                
-               // Update sort key
-               if (loc == _processors.end()) {
-                       _meter->set_sort_key(_processors.size());
-               } else {
-                       _meter->set_sort_key((*loc)->sort_key());
-                       for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) {
-                               (*p)->set_sort_key((*p)->sort_key() + 1);
-                       }
-               }
-
                 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;
+       nframes_t old = _output->effective_latency();
+       nframes_t own_latency = _output->user_latency();
 
-       if (_user_latency) {
-               _own_latency = _user_latency;
-       } else {
-               _own_latency = 0;
-
-               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);
 }
 
@@ -2438,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;
        }
@@ -2456,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 
@@ -2521,8 +2467,6 @@ Route::set_block_size (nframes_t nframes)
 void
 Route::protect_automation ()
 {
-       Automatable::protect_automation();
-       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i)
                (*i)->protect_automation();
 }
@@ -2550,7 +2494,7 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
 {
 #ifdef THIS_NEEDS_FIXING_FOR_V3
 
@@ -2602,3 +2546,163 @@ Route::save_as_template (const string& path, const string& 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;
+}