first part of MIDI cut/copy/paste ; fix for input/output_streams of an IOProcessor...
[ardour.git] / libs / ardour / route.cc
index 7fb063eae012f558fe84090cdc47d53259b5a471..989cecdd6bcc1b98c0ade22629e4b16ae54dd827 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,86 +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)
-       : IO (sess, name, 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)
+         
 {
        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 */
+
+       _input.reset (new IO (_session, _name, IO::Input, _default_type));
+       _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       /* amp & meter belong to IO but need to be added to our processor list */
+       _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);
-       add_processor (_meter, PreFader);
-       
-       _main_outs.reset (new Delivery (_session, this, _name, Delivery::Main));
-       add_processor (_main_outs, 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
@@ -159,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) {
@@ -186,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;
@@ -224,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;
                        }
@@ -255,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)
@@ -298,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()
-                       && (!_session.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;
+                               }
+                       }
+
+               } 
        }
 
        /* -------------------------------------------------------------------------------------------
@@ -437,57 +419,65 @@ 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) {
+                       if (bufs.count() != (*i)->input_streams()) {
+                               cerr << _name << " bufs = " << bufs.count()
+                                    << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+                                    << endl;
+                       }
+                       assert (bufs.count() == (*i)->input_streams());
+                       (*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);
 }
@@ -498,111 +488,130 @@ 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)->name() << endl;
+               cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
        }
        cerr << "}" << endl;
 }
-
-Route::ProcessorList::iterator
-Route::prefader_iterator() 
-{
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-       return find (_processors.begin(), _processors.end(), _amp);
-}
+#endif
 
 int
 Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
@@ -615,23 +624,10 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
 
        if (placement == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
+               loc = find (_processors.begin(), _processors.end(), _amp);
        } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-
-               if (processor->visible() && !_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+               /* generic post-fader: insert right before the main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
        return add_processor (processor, loc, err);
@@ -690,6 +686,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        --ploc;
                        _processors.erase(ploc);
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       cerr << "configure failed\n";
                        return -1;
                }
        
@@ -702,160 +699,144 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        
                }
                
-               // 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 ();
+               if (_control_outs != processor) {
+                       // 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 ();
-       }
-
        processors_changed (); /* EMIT SIGNAL */
        
        return 0;
 }
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
-{
-       ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
-       }
-
-       return add_processor_from_xml (node, loc);
-}
-
 bool
 Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
 {
        const XMLProperty *prop;
 
-       // 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, iter); 
-                       return true;
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
-               }
-               
-       } else if (node.name() == "Processor") {
+       if (node.name() != "Processor") {
+               return false;
+       }
                
-               try {
-                       if ((prop = node.property ("type")) != 0) {
-
-                               boost::shared_ptr<Processor> processor;
-                               bool have_insert = 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") {
+                       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;
+                               processor.reset (new PluginInsert(_session, node));
                                        
-                               } else if (prop->value() == "port") {
+                       } else if (prop->value() == "port") {
 
-                                       processor.reset (new PortInsert (_session, node));
+                               processor.reset (new PortInsert (_session, _mute_master, node));
                                
-                               } else if (prop->value() == "send") {
+                       } else if (prop->value() == "send") {
 
-                                       processor.reset (new Send (_session, node));
-                                       have_insert = true;
-                               
-                               } else if (prop->value() == "meter") {
+                               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;
+                                       }
+                               }
 
-                                       processor = _meter;
+                               _meter.reset (new PeakMeter (_session, node));                                          
+                               processor = _meter;
                                
-                               } else if (prop->value() == "amp") {
+                       } else if (prop->value() == "amp") {
+
+                               /* amp always exists */
                                        
-                                       processor = _amp;
+                               processor = _amp;
+                               if (processor->set_state (node)) {
+                                       return false;
+                               } else {
+                                       /* never any reason to add it */
+                                       return true;
+                               }
                                        
-                               } else if (prop->value() == "listen" || prop->value() == "deliver") {
+                       } else if (prop->value() == "intsend") {
 
-                                       /* XXX need to generalize */
+                               processor.reset (new InternalSend (_session, _mute_master, node));
 
-                                       processor = _control_outs;
-                                       
-                               } else if (prop->value() == "main-outs") {
-                                       
-                                       processor = _main_outs;
+                       } else if (prop->value() == "intreturn") {
                                        
-                               } else {
-
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                               if (_intreturn) {
+                                       if (_intreturn->set_state (node)) {
+                                               return false;
+                                       } else {
+                                               return true;
+                                       }
                                }
-                               
-                               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;
-                                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                                       while (!(*p)->visible() && p != _processors.begin()) {
-                                               --p;
+                               _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;
                                        }
-                                       ++p;
-                                       iter = p;
                                }
 
-                               return (add_processor (processor, iter) == 0);
-                               
+                               _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
+                               processor = _main_outs;
+
                        } else {
-                               error << _("Processor XML node has no type property") << endmsg;
+                               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;
                        }
-               }
 
-               catch (failed_constructor &err) {
-                       warning << _("processor could not be created. Ignored.") << endmsg;
+                       return (add_processor (processor, iter) == 0);
+                               
+               } else {
+                       error << _("Processor XML node has no type property") << endmsg;
                        return false;
                }
        }
-       return false;
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
+       }
 }
 
 int
-Route::add_processors (const ProcessorList& others, Placement placement, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
        ProcessorList::iterator loc;
-       if (placement == PreFader) {
-               /* generic pre-fader: insert immediately before the amp */
-               loc = find(_processors.begin(), _processors.end(), _amp);
-       } else {
-               /* generic post-fader: insert at end */
-               loc = _processors.end();
 
-               if (!_processors.empty()) {
-                       /* check for invisible processors stacked at the end and leave them there */
-                       ProcessorList::iterator p;
-                       p = _processors.end();
-                       --p;
-                       cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
-                       while (!(*p)->visible() && p != _processors.begin()) {
-                               --p;
-                       }
-                       ++p;
-                       loc = p;
-               }
+       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);
@@ -884,7 +865,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                ProcessorList::iterator existing_end = _processors.end();
                --existing_end;
 
-               ChanCount potential_max_streams = ChanCount::max (n_inputs(), n_outputs());
+               ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
                        
@@ -901,14 +882,13 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                        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 (iter, *i);
                        
                        if (configure_processors_unlocked (err)) {
@@ -921,11 +901,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                        (*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);
        }
        
        processors_changed (); /* EMIT SIGNAL */
@@ -1061,31 +1037,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.
  */
@@ -1107,26 +1058,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;
+                                       }
+                               }
                        }
                }
 
@@ -1134,10 +1102,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 */
@@ -1173,7 +1137,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
@@ -1181,13 +1145,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;
@@ -1196,7 +1164,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                ++i;
                        }
 
-                       _user_latency = 0;
+                       _output->set_user_latency (0);
                }
 
                if (!removed) {
@@ -1226,16 +1194,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 ();
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
 }
 
+
 int
 Route::configure_processors (ProcessorStreams* err)
 {
@@ -1259,10 +1316,11 @@ 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 = n_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) {
                if ((*p)->can_support_io_configuration(in, out)) {
                        configuration.push_back(make_pair(in, out));
@@ -1280,16 +1338,16 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        // We can, so configure everything
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+               cerr << _name << " Configure " << (*p)->name() << " for " << in << "  + " << out << endl;
                (*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 (n_inputs(), out, false, this);
+       if (out != _output->n_ports ()) {
+               _output->ensure_io (out, false, this);
        }
 
        _in_configure_processors = false;
@@ -1351,9 +1409,40 @@ 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::reorder_processors (const ProcessorList& new_order, Placement placement, 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.
+       */
+
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
                ChanCount old_pms = processor_max_streams;
@@ -1361,11 +1450,8 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                ProcessorList::const_iterator niter;
                ProcessorList as_it_was_before = _processors;
                ProcessorList as_it_will_be;
-               ProcessorList::iterator start, end;
 
-               placement_range (placement, start, end);
-
-               oiter = start;
+               oiter = _processors.begin();
                niter = new_order.begin(); 
 
                while (niter !=  new_order.end()) {
@@ -1377,13 +1463,16 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                           its been deleted. If its there, append it to the temp list.
                        */
 
-                       if (oiter == end) {
+                       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 {
@@ -1397,7 +1486,7 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                                        /* visible processor: check that its in the new order */
 
                                        if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) {
-                                               /* deleted: do nothing */
+                                               /* 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);
@@ -1420,8 +1509,6 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                } 
        } 
 
-       /* do we really need to do this every time? */
-       reset_panner ();
        processors_changed (); /* EMIT SIGNAL */
 
        return 0;
@@ -1446,28 +1533,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;
@@ -1489,9 +1570,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);
@@ -1514,92 +1596,6 @@ Route::state(bool full_state)
        return *node;
 }
 
-XMLNode&
-Route::get_processor_state ()
-{
-       XMLNode* root = new XMLNode (X_("redirects"));
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               root->add_child_nocopy ((*i)->state (true));
-       }
-
-       return *root;
-}
-
-int
-Route::set_processor_state (const XMLNode& root)
-{
-       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;
-                               
-                       }
-               }
-
-       }
-
-       return 0;
-}
-
-void
-Route::set_deferred_state ()
-{
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
-
-       if (!deferred_state) {
-               return;
-       }
-
-       nlist = deferred_state->children();
-
-       _set_processor_states (nlist);
-
-       delete deferred_state;
-       deferred_state = 0;
-}
-
 int
 Route::set_state (const XMLNode& node)
 {
@@ -1607,8 +1603,9 @@ Route::set_state (const XMLNode& node)
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, bool /*call_base*/)
 {
+
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
@@ -1620,76 +1617,89 @@ Route::_set_state (const XMLNode& node, bool call_base)
                return -1;
        }
 
+       if ((prop = node.property (X_("name"))) != 0) {
+               Route::set_name (prop->value());
+       } 
+
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
+       }
+
        if ((prop = node.property (X_("flags"))) != 0) {
                _flags = Flag (string_2_enum (prop->value(), _flags));
        } else {
                _flags = Flag (0);
        }
+
+       /* add all processors (except amp, which is always present) */
+
+       nlist = node.children();
+       XMLNode processor_state (X_("processor_state"));
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               
+               child = *niter;
+
+               if (child->name() == IO::state_node_name) {
+                       if ((prop = child->property (X_("direction"))) == 0) {
+                               continue;
+                       }
+                       
+                       if (prop->value() == "Input") {
+                               _input->set_state (*child);
+                       } else if (prop->value() == "Output") {
+                               _output->set_state (*child);
+                       }
+               }
+                       
+               if (child->name() == X_("Processor")) {
+                       processor_state.add_child_copy (*child);
+               }
+       }
+
+       set_processor_state (processor_state);
        
-       if ((prop = node.property (X_("default-type"))) != 0) {
-               _default_type = DataType(prop->value());
-               assert(_default_type != DataType::NIL);
+       if ((prop = node.property ("solo_level")) != 0) {
+               _solo_level = 0; // needed for mod_solo_level() to work
+               mod_solo_level (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("solo-isolated")) != 0) {
+               set_solo_isolated (prop->value() == "yes", this);
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (prop->value()=="yes"?true:false, this);
+               set_phase_invert (prop->value()=="yes"?true:false);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
-               set_denormal_protection (prop->value()=="yes"?true:false, this);
+               set_denormal_protection (prop->value()=="yes"?true:false);
        }
        
-       _active = true;
        if ((prop = node.property (X_("active"))) != 0) {
-               set_active (prop->value() == "yes");
-       }
-
-       if ((prop = node.property (X_("muted"))) != 0) {
-               bool yn = prop->value()=="yes"?true:false; 
-
-               /* force reset of mute status */
-
-               _muted = !yn;
-               set_mute(yn, this);
-               mute_gain = desired_mute_gain;
+               bool yn = (prop->value() == "yes");
+               _active = !yn; // force switch
+               set_active (yn);
        }
 
        if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = prop->value()=="yes"?true:false; 
+               bool yn = (prop->value()=="yes");
 
-               /* force reset of solo status */
+               /* XXX force reset of solo status */
 
-               _soloed = !yn;
                set_solo (yn, this);
-               solo_gain = desired_solo_gain;
-       }
-
-       if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
-               _mute_affects_pre_fader = (prop->value()=="yes")?true:false;
-       }
-
-       if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
-               _mute_affects_post_fader = (prop->value()=="yes")?true:false;
-       }
-
-       if ((prop = node.property (X_("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);
                }
        }
 
@@ -1710,7 +1720,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);
                                }
                        }
 
@@ -1724,52 +1734,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;
-               }
-       }
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               
-               child = *niter;
-                       
-               if (child->name() == X_("Send") || child->name() == X_("Processor")) {
-                       deferred_state->add_child_copy (*child);
-               }
-       }
-
-       if (ports_legal) {
-               _set_processor_states (deferred_state->children());
-               delete deferred_state;
-               deferred_state = 0;
-       }
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-               child = *niter;
-
-               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++ */
 
@@ -1785,48 +1753,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;
-       bool has_meter_processor = false; // legacy sessions don't
        ProcessorList::iterator i, o;
 
        // Iterate through existing processors, remove those which are not in the state list
+
        for (i = _processors.begin(); i != _processors.end(); ) {
+
+               /* leave amp alone, always */
+
+               if ((*i) == _amp) {
+                       ++i;
+                       continue;
+               }
+
                ProcessorList::iterator tmp = i;
                ++tmp;
 
                bool processorInStateList = false;
-       
+
                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;
@@ -1842,22 +1823,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");
 
-               if (prop && prop->value() == "meter")  {
-                       has_meter_processor = true;
-               }
-
                o = i;
 
-               if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
-
-                       // 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()) {
@@ -1870,6 +1849,7 @@ 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)) {
@@ -1878,10 +1858,11 @@ Route::_set_processor_states(const XMLNodeList &nlist)
                                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);
                                _processors.erase (o); // remove the old copy
@@ -1889,6 +1870,8 @@ Route::_set_processor_states(const XMLNodeList &nlist)
                                --i; // move iterator to the correct processor
                        }
 
+                       // and make it (just) so
+
                        (*i)->set_state (**niter);
                }
        }
@@ -1897,14 +1880,7 @@ Route::_set_processor_states(const XMLNodeList &nlist)
           the XML state represents a working signal route.
        */
 
-       if (!has_meter_processor) {
-               set_meter_point (_meter_point, NULL);
-       }
-
        processors_changed ();
-
-       
-
 }
 
 void
@@ -1919,7 +1895,7 @@ Route::silence (nframes_t nframes)
 {
        if (!_silent) {
 
-               IO::silence (nframes);
+               _output->silence (nframes);
                
                { 
                        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
@@ -1945,33 +1921,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;
+               }
        }
        
-       add_processor (listener, PostFader);
+       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;
@@ -1979,128 +1970,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)
-{
-       _edit_group = 0;
-       _session.set_dirty ();
-       edit_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+Route::set_route_group (RouteGroup *rg, void *src)
 {
-       if (mg == _mix_group) {
+       if (rg == _route_group) {
                return;
        }
 
-       if (_mix_group) {
-               _mix_group->remove (this);
+       if (_route_group) {
+               _route_group->remove (this);
        }
 
-       if ((_mix_group = mg) != 0) {
-               _mix_group->add (this);
+       if ((_route_group = rg) != 0) {
+               _route_group->add (this);
        }
 
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
-Route::drop_mix_group (void *src)
+Route::drop_route_group (void *src)
 {
-       _mix_group = 0;
+       _route_group = 0;
        _session.set_dirty ();
-       mix_group_changed (src); /* EMIT SIGNAL */
+       route_group_changed (src); /* EMIT SIGNAL */
 }
 
 void
@@ -2112,79 +2079,35 @@ Route::set_comment (string cmt, void *src)
 }
 
 bool
-Route::feeds (boost::shared_ptr<IO> other)
+Route::feeds (boost::shared_ptr<Route> other)
 {
-       if (connected_to (other)) {
+       // cerr << _name << endl;
+
+       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;
-
-               if ((proc = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
-                       if (proc->io()->connected_to (other)) {
+               
+               boost::shared_ptr<IOProcessor> iop;
+               
+               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
+                       if (iop->feeds (other)) {
+                               // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl;
                                return true;
+                       } else {
+                               // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
                        }
                }
        }
 
+       // cerr << "\tdoes NOT FEED " << other->name() << endl;
        return false;
 }
 
 void
-Route::set_mute_config (mute_type t, bool onoff, void *src)
-{
-       switch (t) {
-       case PRE_FADER:
-               _mute_affects_pre_fader = onoff;
-               pre_fader_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case POST_FADER:
-               _mute_affects_post_fader = onoff;
-               post_fader_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case CONTROL_OUTS:
-               _mute_affects_control_outs = onoff;
-               control_outs_changed(src); /* EMIT SIGNAL */
-               break;
-
-       case MAIN_OUTS:
-               _mute_affects_main_outs = onoff;
-               main_outs_changed(src); /* EMIT SIGNAL */
-               break;
-       }
-}
-
-bool
-Route::get_mute_config (mute_type t)
-{
-       bool onoff = false;
-       
-       switch (t){
-       case PRE_FADER:
-               onoff = _mute_affects_pre_fader; 
-               break;
-       case POST_FADER:
-               onoff = _mute_affects_post_fader;
-               break;
-       case CONTROL_OUTS:
-               onoff = _mute_affects_control_outs;
-               break;
-       case MAIN_OUTS:
-               onoff = _mute_affects_main_outs;
-               break;
-       }
-       
-       return onoff;
-}
-
-void
-Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors)
+Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
        nframes_t now = _session.transport_frame();
 
@@ -2206,13 +2129,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);
@@ -2220,14 +2141,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);
        }
 }
 
@@ -2243,24 +2163,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;
 }
@@ -2283,7 +2198,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;
@@ -2294,18 +2209,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;
        }
@@ -2318,29 +2238,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;
@@ -2349,7 +2254,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());
        }
 }
@@ -2357,15 +2262,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(':'));
@@ -2424,59 +2329,85 @@ Route::set_meter_point (MeterPoint p, void *src)
                _session.set_dirty ();
        }
 }
+void
+Route::put_control_outs_at (Placement p)
+{
+       if (!_control_outs) {
+               return;
+       }
+
+       // Move meter in the processors list
+       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
+       _processors.erase(loc);
+
+       switch (p) {
+       case PreFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               if (loc != _processors.begin()) {
+                       --loc;
+               }
+               break;
+       case PostFader:
+               loc = find(_processors.begin(), _processors.end(), _amp);
+               assert (loc != _processors.end());
+               loc++;
+               break;
+       }
+
+       _processors.insert(loc, _control_outs);
+
+       processors_changed (); /* EMIT SIGNAL */
+       _session.set_dirty ();
+}
 
 nframes_t
 Route::update_total_latency ()
 {
-       nframes_t old = _own_latency;
+       nframes_t old = _output->effective_latency();
+       nframes_t own_latency = _output->user_latency();
 
-       if (_user_latency) {
-               _own_latency = _user_latency;
-       } else {
-               _own_latency = 0;
-
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((*i)->active ()) {
-                               _own_latency += (*i)->signal_latency ();
-                       }
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       own_latency += (*i)->signal_latency ();
                }
        }
 
 #undef DEBUG_LATENCY
 #ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << _own_latency << endl;
+       cerr << _name << ": internal redirect latency = " << own_latency << endl;
 #endif
 
-       set_port_latency (_own_latency);
+       _output->set_port_latency (own_latency);
        
-       if (!_user_latency) {
+       if (_output->user_latency() == 0) {
+
                /* this (virtual) function is used for pure Routes,
                   not derived classes like AudioTrack.  this means
                   that the data processed here comes from an input
                   port, not prerecorded material, and therefore we
                   have to take into account any input latency.
                */
-
-
-               _own_latency += input_latency ();
+               
+               own_latency += _input->signal_latency ();
        }
 
-       if (old != _own_latency) {
+       if (old != own_latency) {
+               _output->set_latency_delay (own_latency);
                signal_latency_changed (); /* EMIT SIGNAL */
        }
        
 #ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << input_latency() << " total = "
-            << _own_latency << endl;
+       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
+            << own_latency << endl;
 #endif
 
-       return _own_latency;
+       return _output->effective_latency ();
 }
 
 void
 Route::set_user_latency (nframes_t nframes)
 {
-       Latent::set_user_latency (nframes);
+       _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
@@ -2485,8 +2416,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;
        }
@@ -2503,57 +2434,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 
@@ -2568,8 +2474,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();
 }
@@ -2597,7 +2501,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
 
@@ -2656,23 +2560,25 @@ Route::set_name (const string& str)
 {
        bool ret;
        string ioproc_name;
+       string name;
+
+       name = Route::ensure_track_or_route_name (str, _session);
+       SessionObject::set_name (name);
+       
+       ret = (_input->set_name(name) && _output->set_name(name));
 
-       if ((ret = IO::set_name (str)) == true) {
+       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;
                                }
                        }
@@ -2683,20 +2589,127 @@ Route::set_name (const string& str)
        return ret;
 }
 
-boost::shared_ptr<IO>
-Route::send_io_for (boost::shared_ptr<const IO> target) const
+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<Send> send;
+               boost::shared_ptr<InternalSend> send;
                
-               if ((send = boost::dynamic_pointer_cast<Send>(*i)) != 0) {
-                       if (send->io()->connected_to (target)) {
-                               return send->io();
+               if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+                       if (send->target_route() == target) {
+                               return send;
                        }
                }
        }
        
-       return boost::shared_ptr<IO>();
+       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;
 }