make i18n build work ; add mackie dir back to build ; token work on amp for MIDI...
[ardour.git] / libs / ardour / route.cc
index db06368371031cc124b5043e6124abb0fc7166f4..56311c975db1063cb0fe929bbfd11cab0ec59c83 100644 (file)
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/delivery.h"
 #include "ardour/dB.h"
+#include "ardour/internal_send.h"
+#include "ardour/internal_return.h"
 #include "ardour/ladspa_plugin.h"
 #include "ardour/meter.h"
 #include "ardour/mix.h"
@@ -61,92 +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;
 
-       _mute_affects_pre_fader = Config->get_mute_affects_pre_fader();
-       _mute_affects_post_fader = Config->get_mute_affects_post_fader();
-       _mute_affects_control_outs = Config->get_mute_affects_control_outs();
-       _mute_affects_main_outs = Config->get_mute_affects_main_outs();
-       
-       solo_gain = 1.0;
-       desired_solo_gain = 1.0;
-       mute_gain = 1.0;
-       desired_mute_gain = 1.0;
+       _phase_invert = 0;
+       _denormal_protection = false;
 
-       input_changed.connect (mem_fun (this, &Route::input_change_handler));
-       output_changed.connect (mem_fun (this, &Route::output_change_handler));
+       /* add standard controls */
+
+       add_control (_solo_control);
+       add_control (_mute_master);
        
-       /* add standard processors: amp, meter, main outs */
+       /* input and output objects */
 
-       /* amp & meter belong to IO but need to be added to our processor list */
+       _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);
-       add_processor (_meter);
-       
-       _main_outs.reset (new Delivery (_session, this, _name, Delivery::Main));
-       ProcessorList::iterator i = _processors.end();
-       add_processor (_main_outs, 0, &i);
+       _input->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
@@ -165,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) {
@@ -192,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;
@@ -230,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;
                        }
@@ -261,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)
@@ -304,136 +321,95 @@ Route::set_gain (gain_t val, void *src)
 void
 Route::process_output_buffers (BufferSet& bufs,
                               sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
-                              bool with_processors, int declick)
+                              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
-          ----------------------------------------------------------------------------------------- */
-
-       boost::shared_ptr<Delivery> co = _control_outs;
-       if (co) {
-               // deliver control outputs unless we're ...
-               bool self_mute = ((dmg == 0 && _mute_affects_control_outs) || // or muted by mute of this track
-                                 !recording_without_monitoring); // or rec-enabled w/o s/w monitoring 
-               bool other_mute = (dsg == 0); // muted by solo of another track
-               
-               co->set_self_mute (self_mute);
-               co->set_nonself_mute (other_mute);
-       }
-
-       /* -------------------------------------------------------------------------------------------
-          SET UP MAIN OUTPUT STAGE
-          ----------------------------------------------------------------------------------------- */
-
-       bool solo_audible = dsg > 0;
-       bool mute_audible = dmg > 0 || !_mute_affects_main_outs;
+       /* tell main outs what to do about monitoring */
+       _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
-       bool silent_anyway = (_gain == 0 && !_amp->apply_gain_automation());
-       bool muted_by_other_solo = (!solo_audible && (Config->get_solo_model() != SoloBus));
-       bool muted_by_self = !mute_audible;
 
-       _main_outs->set_nonself_mute (recording_without_monitoring || muted_by_other_solo || silent_anyway);
-       _main_outs->set_self_mute (muted_by_self);
-       
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0, false);
-               _pending_declick = 0;
+               Amp::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];
+                                       }
+                               } 
                        }
                }
+
+       } else {
+
+               if (_denormal_protection || Config->get_denormal_protection()) {
+                       
+                       for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+                               Sample* const sp = i->data();
+                               for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       sp[nx] += 1.0e-27f;
+                               }
+                       }
+
+               } 
        }
 
        /* -------------------------------------------------------------------------------------------
@@ -443,57 +419,60 @@ Route::process_output_buffers (BufferSet& bufs,
        Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
 
        if (rm.locked()) {
-               for (i = _processors.begin(); i != _processors.end(); ++i) {
-                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->input_streams()));
-                       (*i)->run_in_place (bufs, start_frame, end_frame, nframes);
-                       bufs.set_count(ChanCount::max(bufs.count(), (*i)->output_streams()));
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->input_streams()));
+                       (*i)->run (bufs, start_frame, end_frame, nframes);
+                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
                }
 
                if (!_processors.empty()) {
-                       bufs.set_count(ChanCount::max(bufs.count(), _processors.back()->output_streams()));
+                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
                }
        }
-
-       /* -------------------------------------------------------------------------------------------
-          POST-FADER MUTING
-          ----------------------------------------------------------------------------------------- */
-
-       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);
-       }
-       
-       // 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);
-}
-
-void
-Route::setup_peak_meters()
-{
-       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);
+       return max (_input->n_ports(), processor_max_streams);
 }
 
 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());
+       BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
        _silent = false;
 
-       collect_input (bufs, nframes);
+       assert (bufs.available() >= _input->n_ports());
+       
+       if (_input->n_ports() == ChanCount::ZERO) {
+               silence (nframes);
+       }
+       
+       bufs.set_count (_input->n_ports());
+
+       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.
+               */
+                       
+               bufs.silence (nframes, 0);
+
+       } else {
+       
+               for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                       
+                       BufferSet::iterator o = bufs.begin(*t);
+                       PortSet& ports (_input->ports());
+                       
+                       for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+                               o->read_from (i->get_buffer(nframes), nframes);
+                       }
+               }
+       }
 
        process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
 }
@@ -504,125 +483,159 @@ Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t n
        process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
 }
 
+void
+Route::set_listen (bool yn, void* src)
+{
+       if (_control_outs) { 
+               if (yn != _control_outs->active()) {
+                       if (yn) {
+                               _control_outs->activate ();
+                       } else {
+                               _control_outs->deactivate ();
+                       }
+
+                       listen_changed (src); /* EMIT SIGNAL */
+               }
+       }
+}
+
+bool
+Route::listening () const
+{
+       if (_control_outs) {
+               return _control_outs->active ();
+       } else {
+               return false;
+       }
+}
+
 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.
+       */
 
-struct ProcessorSortByKey {
-    bool operator() (boost::shared_ptr<Processor> a, boost::shared_ptr<Processor> b) {
-           return a->sort_key() < b->sort_key();
-    }
-};
+       if (placement == PreFader) {
+               /* generic pre-fader: insert immediately before the amp */
+               loc = find (_processors.begin(), _processors.end(), _amp);
+       } else {
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
 
-uint32_t
-Route::fader_sort_key() const
-{
-       return _amp->sort_key();
+       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
  * processor will be inserted immediately before this location.  Otherwise,
  * @a position is used.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, ProcessorList::iterator* iter)
+Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err)
 {
        ChanCount old_pms = processor_max_streams;
 
@@ -630,10 +643,6 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                return 1;
        }
 
-       cerr << "Adding a processor called " << processor->name() << " sk = " << processor->sort_key() 
-            << ((iter == 0) ? " NO given position " : " with given position")
-            << endl;
-
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
 
@@ -645,15 +654,10 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                if (processor == _amp || processor == _meter || processor == _main_outs) {
                        // Ensure only one of these are in the list at any time
                        if (loc != _processors.end()) {
-                               if (iter) {
-                                       if (*iter == loc) { // Already in place, do nothing
-                                               return 0;
-                                       } else { // New position given, relocate
-                                               _processors.erase(loc);
-                                       }
-                               } else { // Insert at end
-                                       _processors.erase(loc);
-                                       loc = _processors.end();
+                               if (iter == loc) { // Already in place, do nothing
+                                       return 0;
+                               } else { // New position given, relocate
+                                       _processors.erase (loc);
                                }
                        }
 
@@ -662,46 +666,22 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams*
                                cerr << "ERROR: Processor added to route twice!" << endl;
                                return 1;
                        }
-               }
 
-               if (iter) {
-                       // Use position given by user
-                       loc = *iter;
-               } else {
-                       if (processor->sort_key() == 0) {
-                               /* generic pre-fader: insert immediately before the amp */
-                               loc = find(_processors.begin(), _processors.end(), _amp);
-                       } else if (processor->sort_key() > _processors.size()) {
-                               /* generic post-fader: insert at end */
-                               loc = _processors.end();
-                       } else {
-                               /* find insert point */
-                               ProcessorSortByKey cmp;
-                               loc = upper_bound (_processors.begin(), _processors.end(), processor, cmp);
-                       }
-               }
-               
-               // Update sort keys
-               if (loc == _processors.end()) {
-                       processor->set_sort_key(_processors.size());
-               } else {
-                       processor->set_sort_key((*loc)->sort_key());
-                       for (ProcessorList::iterator p = loc; p != _processors.end(); ++p) {
-                               (*p)->set_sort_key((*p)->sort_key() + 1);
-                       }
+                       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 + "bad config", _processors);
                        ProcessorList::iterator ploc = loc;
                        --ploc;
                        _processors.erase(ploc);
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       cerr << "Bad IO config\n";
+                       cerr << "configure failed\n";
                        return -1;
                }
        
@@ -714,30 +694,149 @@ 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 + " added one", _processors);
        processors_changed (); /* EMIT SIGNAL */
        
        return 0;
 }
 
+bool
+Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
+{
+       const XMLProperty *prop;
+
+       if (node.name() != "Processor") {
+               return false;
+       }
+               
+       try {
+               if ((prop = node.property ("type")) != 0) {
+                       
+                       boost::shared_ptr<Processor> processor;
+
+                       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") {
+
+                               processor.reset (new PortInsert (_session, _mute_master, node));
+                               
+                       } else if (prop->value() == "send") {
+
+                               processor.reset (new Send (_session, _mute_master, node));
+
+                       } else if (prop->value() == "meter") {
+
+                               if (_meter) {
+                                       if (_meter->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
+                               }
+
+                               _meter.reset (new PeakMeter (_session, node));                                          
+                               processor = _meter;
+                               
+                       } else if (prop->value() == "amp") {
+
+                               /* 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") {
+
+                               processor.reset (new InternalSend (_session, _mute_master, node));
+
+                       } 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;
+               }
+       }
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
+       }
+}
+
+int
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
+{
+       ProcessorList::iterator loc;
+
+       if (before) {
+               loc = find(_processors.begin(), _processors.end(), before);
+       } else {
+               /* nothing specified - at end but before main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
+
+       return add_processors (others, loc, err);
+}
+
 int
-Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint32_t first_sort_key)
+Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
 {
        /* NOTE: this is intended to be used ONLY when copying
           processors from another Route. Hence the subtle
@@ -750,29 +849,16 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint3
                return 1;
        }
 
+       if (others.empty()) {
+               return 0;
+       }
+
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-
-               ProcessorList::iterator loc;
                ProcessorList::iterator existing_end = _processors.end();
                --existing_end;
 
-               ChanCount potential_max_streams = ChanCount::max(input_minimum(), output_minimum());
-               
-               if (first_sort_key == 0) {
-                       /* generic pre-fader: insert immediately before the amp */
-                       cerr << "Add new procs at amp, sk = " << first_sort_key << endl;
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-               } else if (first_sort_key > _processors.size()) {
-                       /* generic post-fader: insert at end */
-                       cerr << "Add new procs at end, sk = " << first_sort_key << endl;
-                       loc = _processors.end();
-               } else {
-                       /* find insert point */
-                       ProcessorSortByKey cmp;
-                       cerr << "Add new procs at sk = " << first_sort_key << endl;
-                       loc = upper_bound (_processors.begin(), _processors.end(), others.front(), cmp);
-               }
+               ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
                        
@@ -789,15 +875,14 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint3
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
                                
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
+                               ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+                               if (m > potential_max_streams) {
                                        potential_max_streams = m;
+                               }
                        }
 
-                       // Ensure peak vector sizes before the plugin is activated
-                       _meter->configure_io (potential_max_streams, potential_max_streams);
-                       
-                       _processors.insert (loc, *i);
+                       _processors.insert (iter, *i);
                        
                        if (configure_processors_unlocked (err)) {
                                ++existing_end;
@@ -809,14 +894,9 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err, uint3
                        (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
                }
 
-               _user_latency = 0;
-       }
-       
-       if (processor_max_streams != old_pms || old_pms == ChanCount::ZERO) {
-               reset_panner ();
+               _output->set_user_latency (0);
        }
        
-       dump_processors (_name + " added several", _processors);
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
@@ -950,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.
  */
@@ -996,26 +1051,43 @@ Route::clear_processors (Placement p)
                Glib::RWLock::WriterLock lm (_processor_lock);
                ProcessorList new_list;
                ProcessorStreams err;
+               bool seen_amp = false;
 
-               ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
-               if (p == PreFader) {
-                       // Get rid of PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
-                               (*i)->drop_references ();
-                       }
-                       // Keep the rest
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               new_list.push_back (*i);
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+                       if (*i == _amp) {
+                               seen_amp = true;
                        }
-               } else {
-                       // Keep PreFader processors
-                       for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                               
+                               /* you can't remove these */
+
                                new_list.push_back (*i);
-                       }
-                       new_list.push_back (_amp);
-                       // Get rid of PostFader processors
-                       for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
-                               (*i)->drop_references ();
+
+                       } else {
+                               if (seen_amp) {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       case PostFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       }
+
+                               } else {
+
+                                       switch (p) {
+                                       case PreFader:
+                                               (*i)->drop_references ();
+                                               break;
+                                       case PostFader:
+                                               new_list.push_back (*i);
+                                               break;
+                                       }
+                               }
                        }
                }
 
@@ -1023,10 +1095,6 @@ Route::clear_processors (Placement p)
                configure_processors_unlocked (&err); // this can't fail
        }
 
-       if (processor_max_streams != old_pms) {
-               reset_panner ();
-       }
-       
        processor_max_streams.reset();
        _have_internal_generator = false;
        processors_changed (); /* EMIT SIGNAL */
@@ -1062,7 +1130,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        if (*i == processor) {
 
                                /* move along, see failure case for configure_processors()
-                                  where we may need to reprocessor the processor.
+                                  where we may need to reconfigure the processor.
                                */
 
                                /* stop redirects that send signals to JACK ports
@@ -1070,13 +1138,17 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                   run.
                                */
 
-                               boost::shared_ptr<IOProcessor> redirect;
-                               
-                               if ((redirect = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
-                                       redirect->io()->disconnect_inputs (this);
-                                       redirect->io()->disconnect_outputs (this);
+                               boost::shared_ptr<IOProcessor> iop;
+
+                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                                       if (iop->input()) {
+                                               iop->input()->disconnect (this);
+                                       }
+                                       if (iop->output()) {
+                                               iop->output()->disconnect (this);
+                                       }
                                }
-                               
+
                                i = _processors.erase (i);
                                removed = true;
                                break;
@@ -1085,7 +1157,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                ++i;
                        }
 
-                       _user_latency = 0;
+                       _output->set_user_latency (0);
                }
 
                if (!removed) {
@@ -1115,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 + " removed one", _processors);
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
 }
 
+
 int
 Route::configure_processors (ProcessorStreams* err)
 {
@@ -1150,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;
@@ -1173,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;
@@ -1243,22 +1402,99 @@ Route::all_processors_active (Placement p, bool state)
        _session.set_dirty ();
 }
 
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
+{
+       bool pre_fader = true;
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* semantic note: if p == amp, we want to return true, so test
+                  for equality before checking if this is the amp
+               */
+
+               if ((*i) == p) {
+                       break;
+               }
+
+               if ((*i) == _amp) {
+                       pre_fader = false;
+                       break;
+               }
+       }
+
+       return pre_fader;
+}
+
 int
-Route::sort_processors (ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
+       /* "new_order" is an ordered list of processors to be positioned according to "placement".
+          NOTE: all processors in "new_order" MUST be marked as 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.
+       */
+
        {
-               ProcessorSortByKey comparator;
                Glib::RWLock::WriterLock lm (_processor_lock);
                ChanCount old_pms = processor_max_streams;
+               ProcessorList::iterator oiter;
+               ProcessorList::const_iterator niter;
+               ProcessorList as_it_was_before = _processors;
+               ProcessorList as_it_will_be;
 
-               /* the sweet power of C++ ... */
+               oiter = _processors.begin();
+               niter = new_order.begin(); 
 
-               ProcessorList as_it_was_before = _processors;
+               while (niter !=  new_order.end()) {
+                       
+                       /* 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. 
 
-               dump_processors (_name + " PRESORT", _processors);
+                          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;
@@ -1266,8 +1502,6 @@ Route::sort_processors (ProcessorStreams* err)
                } 
        } 
 
-       dump_processors (_name + " sorted", _processors);
-       reset_panner ();
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
@@ -1292,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;
@@ -1335,9 +1563,10 @@ 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);
@@ -1360,266 +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 if (prop->value() == "listen" || prop->value() == "deliver") {
-
-                                       /* XXX need to generalize */
-
-                                       processor = _control_outs;
-                                       
-                               } else if (prop->value() == "main-outs") {
-                                       
-                                       processor = _main_outs;
-                                       
-                               } 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);
                }
        }
 
@@ -1640,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);
                                }
                        }
 
@@ -1654,60 +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")) {
-
-                       /* ignore this - deprecated */
-
-               } else if (child->name() == X_("Comment")) {
+               if (child->name() == X_("Comment")) {
 
                        /* XXX this is a terrible API design in libxml++ */
 
@@ -1723,48 +1746,61 @@ 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;
-
        ProcessorList::iterator i, o;
 
-       // Iterate through existing processors, remove those which are not in the state list
-       for (i = _processors.begin(); i != _processors.end(); ) {
+       // 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;
-       
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
                        XMLProperty* id_prop = (*niter)->property(X_("id"));
+
                        if (id_prop && (*i)->id() == id_prop->value()) {
                                processorInStateList = true;
                                break;
@@ -1780,18 +1816,20 @@ Route::_set_processor_states(const XMLNodeList &nlist)
 
        // 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");
-               
-               o = i;
 
-               if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
+               o = i;
 
-                       // Check whether the next processor in the list 
-                       
+               // Check whether the next processor in the list is the right one,
+               // except for "amp" which is always there and may not have the
+               // old ID since it is always created anew in every Route
+               
+               if (prop->value() != "amp") {
                        while (o != _processors.end()) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
@@ -1804,29 +1842,38 @@ Route::_set_processor_states(const XMLNodeList &nlist)
 
                // 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, the processor already exists; just
-               // 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);
-                               cerr << "move proc from state\n";
                                _processors.erase (o); // remove the old copy
                                _processors.insert (i, tmp); // insert the processor at the correct location
                                --i; // move iterator to the correct processor
                        }
 
+                       // and make it (just) so
+
                        (*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
@@ -1841,7 +1888,7 @@ Route::silence (nframes_t nframes)
 {
        if (!_silent) {
 
-               IO::silence (nframes);
+               _output->silence (nframes);
                
                { 
                        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
@@ -1867,34 +1914,48 @@ Route::silence (nframes_t nframes)
        }
 }      
 
-boost::shared_ptr<Delivery>
-Route::add_listener (boost::shared_ptr<IO> io, const string& listen_name)
+void
+Route::add_internal_return ()
 {
-       string name = _name;
-       name += '[';
-       name += listen_name;
-       name += ']';
-       
-       boost::shared_ptr<Delivery> listener (new Delivery (_session, name, Delivery::Listen)); 
-
-       /* As an IO, our control outs need as many IO outputs as we have outputs
-        *   (we track the changes in ::output_change_handler()).
-        * As a processor, the listener is an identity processor
-        *   (i.e. it does not modify its input buffers whatsoever)
-        */
+       if (!_intreturn) {
+               _intreturn.reset (new InternalReturn (_session));
+               add_processor (_intreturn, PreFader);
+       }
+}
 
-       if (listener->io()->ensure_io (ChanCount::ZERO, n_outputs(), true, this)) {
-               return boost::shared_ptr<Delivery>();
+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;
+               }
        }
        
-       listener->set_sort_key (_meter->sort_key() + 1);
-       add_processor (listener, NULL);
+       return 0;
+}
 
-       return listener;
+void
+Route::release_return_buffer () const
+{
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       
+       for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+               boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+               
+               if (d) {
+                       return d->release_buffers ();
+               }
+       }
 }
 
 int
-Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
 {
        vector<string> ports;
        vector<string>::const_iterator i;
@@ -1902,128 +1963,104 @@ Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
                
-               for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       boost::shared_ptr<const Delivery> d = boost::dynamic_pointer_cast<const Delivery>(*x);
+               for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
+
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+
+                       if (d && d->target_route() == route) {
+                               
+                               /* if the target is the control outs, then make sure
+                                  we take note of which i-send is doing that.
+                               */
+
+                               if (route == _session.control_out()) {
+                                       _control_outs = boost::dynamic_pointer_cast<Delivery>(d);
+                               }
 
-                       if (d && d->io() == io) {
                                /* already listening via the specified IO: do nothing */
+                               
                                return 0;
                        }
                }
        }
-
-       uint32_t ni = io->n_inputs().n_total();
-
-       for (uint32_t n = 0; n < ni; ++n) {
-               ports.push_back (io->input(n)->name());
-       }
-
-       if (ports.empty()) {
-               return 0;
-       }
-       
-       boost::shared_ptr<Delivery> listen_point = add_listener (io, listen_name);
        
-       /* XXX hack for now .... until we can generalize listen points */
+       boost::shared_ptr<InternalSend> listener;
 
-       _control_outs = listen_point;
+       try {
+               listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
 
-       /* now connect to the named ports */
-       
-       ni = listen_point->io()->n_outputs().n_total();
-       size_t psize = ports.size();
+       } catch (failed_constructor& err) {
+               return -1;
+       }
 
-       for (size_t n = 0; n < ni; ++n) {
-               if (listen_point->io()->connect_output (listen_point->io()->output (n), ports[n % psize], this)) {
-                       error << string_compose (_("could not connect %1 to %2"),
-                                                listen_point->io()->output(n)->name(), ports[n % psize]) << endmsg;
-                       return -1;
-               }
+       if (route == _session.control_out()) {
+               _control_outs = listener;
        }
 
+       add_processor (listener, placement);
        
        return 0;
 }      
 
 void
-Route::drop_listen (boost::shared_ptr<IO> io)
+Route::drop_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rm (_processor_lock);
+       Glib::RWLock::ReaderLock rl(_processor_lock);
+       rl.acquire ();
        
+  again:
        for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
                
-               tmp = x;
-               ++tmp;
-               
-               boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*x);
+               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
                
-               if (d && d->io() == io) {
-                       /* already listening via the specified IO: do nothing */
+               if (d && d->target_route() == route) {
+                       rl.release ();
                        remove_processor (*x, &err);
-                       
-               } 
-               
-               x = tmp;
-       }
-}
+                       rl.acquire ();
 
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+                        /* list could have been demolished while we dropped the lock
+                          so start over.
+                       */
 
-{
-       if (eg == _edit_group) {
-               return;
+                       goto again; 
+               } 
        }
 
-       if (_edit_group) {
-               _edit_group->remove (this);
-       }
+       rl.release ();
 
-       if ((_edit_group = eg) != 0) {
-               _edit_group->add (this);
+       if (route == _session.control_out()) {
+               _control_outs.reset ();
        }
-
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_edit_group (void *src)
+Route::set_route_group (RouteGroup *rg, void *src)
 {
-       _edit_group = 0;
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::set_mix_group (RouteGroup *mg, 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
@@ -2037,95 +2074,33 @@ Route::set_comment (string cmt, void *src)
 bool
 Route::feeds (boost::shared_ptr<Route> other)
 {
-       uint32_t i, j;
+       // cerr << _name << endl;
 
-       IO& self = *this;
-       uint32_t no = self.n_outputs().n_total();
-       uint32_t ni = other->n_inputs ().n_total();
-
-       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> proc = boost::dynamic_pointer_cast<IOProcessor>(*r);
                
-               if (!proc) {
-                       continue;
-               }
+               boost::shared_ptr<IOProcessor> iop;
                
-               no = proc->io()->n_outputs().n_total();
-               
-               for (i = 0; i < no; ++i) {
-                       for (j = 0; j < ni; ++j) {
-                               if (proc->io()->output(i)->connected_to (other->input (j)->name())) {
-                                       return true;
-                               }
+               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
+                       if (iop->feeds (other)) {
+                               // 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();
 
@@ -2147,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);
@@ -2161,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);
        }
 }
 
@@ -2184,24 +2156,19 @@ Route::pans_required () const
 
 int 
 Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,  
-               bool session_state_changing, bool can_record, bool rec_monitors_input)
+               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;
 }
@@ -2224,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;
@@ -2235,18 +2202,23 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 
 int
 Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
-            bool can_record, bool rec_monitors_input)
+            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;
        }
@@ -2259,29 +2231,14 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_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, sframes_t start_frame, sframes_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;
@@ -2290,7 +2247,7 @@ Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_fram
 void
 Route::toggle_monitor_input ()
 {
-       for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+       for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) {
                i->ensure_monitor_input( ! i->monitoring_input());
        }
 }
@@ -2298,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(':'));
@@ -2360,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;
-
-       if (_user_latency) {
-               _own_latency = _user_latency;
-       } else {
-               _own_latency = 0;
+       nframes_t old = _output->effective_latency();
+       nframes_t own_latency = _output->user_latency();
 
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((*i)->active ()) {
-                               _own_latency += (*i)->signal_latency ();
-                       }
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       own_latency += (*i)->signal_latency ();
                }
        }
 
 #undef DEBUG_LATENCY
 #ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << _own_latency << endl;
+       cerr << _name << ": internal redirect latency = " << own_latency << endl;
 #endif
 
-       set_port_latency (_own_latency);
+       _output->set_port_latency (own_latency);
        
-       if (!_user_latency) {
+       if (_output->user_latency() == 0) {
+
                /* this (virtual) function is used for pure Routes,
                   not derived classes like AudioTrack.  this means
                   that the data processed here comes from an input
                   port, not prerecorded material, and therefore we
                   have to take into account any input latency.
                */
-
-
-               _own_latency += input_latency ();
+               
+               own_latency += _input->signal_latency ();
        }
 
-       if (old != _own_latency) {
+       if (old != own_latency) {
+               _output->set_latency_delay (own_latency);
                signal_latency_changed (); /* EMIT SIGNAL */
        }
        
 #ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << input_latency() << " total = "
-            << _own_latency << endl;
+       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
+            << own_latency << endl;
 #endif
 
-       return _own_latency;
+       return _output->effective_latency ();
 }
 
 void
 Route::set_user_latency (nframes_t nframes)
 {
-       Latent::set_user_latency (nframes);
+       _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
@@ -2436,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;
        }
@@ -2454,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 
@@ -2519,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();
 }
@@ -2548,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
 
@@ -2607,23 +2553,25 @@ Route::set_name (const string& str)
 {
        bool ret;
        string ioproc_name;
+       string name;
 
-       if ((ret = IO::set_name (str)) == true) {
+       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 delivery objects to reflect our new name */
+                       /* rename all I/O processors that have inputs or outputs */
 
-                       boost::shared_ptr<Delivery> dp = boost::dynamic_pointer_cast<Delivery> (*i);
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
 
-                       if (dp) {
-                               string dp_name = str;
-                               dp_name += '[';
-                               dp_name += "XXX FIX ME XXX";
-                               dp_name += ']';
-                               
-                               if (!dp->set_name (dp_name)) {
+                       if (iop && (iop->output() || iop->input())) {
+                               if (!iop->set_name (name)) {
                                        ret = false;
                                }
                        }
@@ -2633,3 +2581,128 @@ Route::set_name (const string& str)
 
        return ret;
 }
+
+boost::shared_ptr<Send>
+Route::internal_send_for (boost::shared_ptr<const Route> target) const
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<InternalSend> send;
+               
+               if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+                       if (send->target_route() == target) {
+                               return send;
+                       }
+               }
+       }
+       
+       return boost::shared_ptr<Send>();
+}
+
+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;
+}