More tinkering with State<>. Use some StateDiffCommands instead of
[ardour.git] / libs / ardour / route.cc
index 575cad8e0b5063e2be71d5546ec1cb32fc9e1b5c..02005fc269e4ab67aa572108af0e10cfc3302ba1 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000 Paul Davis 
+    Copyright (C) 2000 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <cassert>
 #include <algorithm>
 
-#include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
-#include "pbd/stacktrace.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
+#include "pbd/convert.h"
 
 #include "evoral/Curve.hpp"
 
@@ -37,6 +37,7 @@
 #include "ardour/buffer_set.h"
 #include "ardour/configuration.h"
 #include "ardour/cycle_timer.h"
+#include "ardour/debug.h"
 #include "ardour/delivery.h"
 #include "ardour/dB.h"
 #include "ardour/internal_send.h"
@@ -64,64 +65,69 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
+PBD::Signal1<void,string const&> Route::SyncOrderKeys;
+PBD::Signal0<void> Route::RemoteControlIDChange;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , AutomatableControls (sess)
        , _flags (flg)
        , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *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);
+       _meter->set_display_to_user (false);
+       add_processor (_meter, PostFader);
 
        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));
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 }
 
 Route::Route (Session& sess, const XMLNode& node, DataType default_type)
        : SessionObject (sess, "toBeReset")
        , AutomatableControls (sess)
        , _solo_control (new SoloControllable (X_("solo"), *this))
+       , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, "toBeReset"))
        , _default_type (default_type)
 {
        init ();
 
-       _set_state (node, false);
+       _set_state (node, Stateful::loading_state_version, false);
 
        /* now that we have _meter, its safe to connect to this */
-       
-       _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+
+       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 }
 
 void
 Route::init ()
 {
-       _solo_level = 0;
-       _solo_isolated = false;
+       _self_solo = false;
+       _soloed_by_others = 0;
+       _solo_isolated = 0;
+       _solo_safe = false;
        _active = true;
        processor_max_streams.reset();
-       _solo_safe = false;
        _recordable = true;
-       order_keys[strdup (N_("signal"))] = order_key_cnt++;
+       order_keys[N_("signal")] = order_key_cnt++;
        _silent = false;
        _meter_point = MeterPostFader;
        _initial_delay = 0;
@@ -131,24 +137,26 @@ Route::init ()
        _pending_declick = true;
        _remote_control_id = 0;
        _in_configure_processors = false;
-       
-       _route_group = 0;
+       _mute_points = MuteMaster::AllPoints;
 
        _phase_invert = 0;
        _denormal_protection = false;
 
        /* add standard controls */
 
-       add_control (_solo_control);
-       add_control (_mute_master);
+       _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        
+       add_control (_solo_control);
+       add_control (_mute_control);
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
        _output.reset (new IO (_session, _name, IO::Output, _default_type));
 
-       _input->changed.connect (mem_fun (this, &Route::input_change_handler));
-       _output->changed.connect (mem_fun (this, &Route::output_change_handler));
+       _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
+       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
 
        /* add amp processor  */
 
@@ -158,22 +166,34 @@ Route::init ()
 
 Route::~Route ()
 {
-       Metering::disconnect (_meter_connection);
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
+
+       /* do this early so that we don't get incoming signals as we are going through destruction 
+        */
 
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       drop_connections ();
 
-       for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
-               free ((void*)(i->first));
+       /* don't use clear_processors here, as it depends on the session which may
+          be half-destroyed by now 
+       */
+
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->drop_references ();
        }
+
+       _processors.clear ();
 }
 
 void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
+               if (notify_class_listeners) {
+                       RemoteControlIDChange ();
+               }
        }
 }
 
@@ -184,35 +204,36 @@ 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) {
                        x->second = 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;
@@ -260,14 +281,14 @@ void
 Route::set_gain (gain_t val, void *src)
 {
        if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) {
-               
+
                if (_route_group->is_relative()) {
 
                        gain_t usable_gain = _amp->gain();
                        if (usable_gain < 0.000001f) {
                                usable_gain = 0.000001f;
                        }
-                                               
+
                        gain_t delta = val;
                        if (delta < 0.000001f) {
                                delta = 0.000001f;
@@ -293,16 +314,16 @@ Route::set_gain (gain_t val, void *src)
                                        return;
                                }
                        }
-                                       
+
                        _route_group->apply (&Route::inc_gain, factor, _route_group);
 
                } else {
-                       
+
                        _route_group->apply (&Route::set_gain, val, _route_group);
                }
 
                return;
-       } 
+       }
 
        if (val == _amp->gain()) {
                return;
@@ -314,7 +335,7 @@ Route::set_gain (gain_t val, void *src)
 /** Process this route for one (sub) cycle (process thread)
  *
  * @param bufs Scratch buffers to use for the signal path
- * @param start_frame Initial transport frame 
+ * @param start_frame Initial transport frame
  * @param end_frame Final transport frame
  * @param nframes Number of frames to output (to ports)
  *
@@ -324,7 +345,7 @@ 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)
 {
        bool monitor;
 
@@ -345,7 +366,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
        /* figure out if we're going to use gain automation */
        _amp->setup_gain_automation (start_frame, end_frame, nframes);
-       
+
 
        /* tell main outs what to do about monitoring */
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
@@ -359,20 +380,20 @@ Route::process_output_buffers (BufferSet& bufs,
                Amp::apply_gain (bufs, nframes, 0.0, 1.0);
        } else if (declick < 0) {
                Amp::apply_gain (bufs, nframes, 1.0, 0.0);
-       } 
+       }
 
        _pending_declick = 0;
-               
+
        /* -------------------------------------------------------------------------------------------
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
        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();
 
@@ -392,19 +413,19 @@ Route::process_output_buffers (BufferSet& bufs,
 
                        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) {
@@ -412,7 +433,7 @@ Route::process_output_buffers (BufferSet& bufs,
                                }
                        }
 
-               } 
+               }
        }
 
        /* -------------------------------------------------------------------------------------------
@@ -423,13 +444,16 @@ Route::process_output_buffers (BufferSet& bufs,
 
        if (rm.locked()) {
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->input_streams()));
-                       (*i)->run (bufs, start_frame, end_frame, nframes);
-                       bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
-               }
 
-               if (!_processors.empty()) {
-                       bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+                       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, *i != _processors.back());
+                       bufs.set_count ((*i)->output_streams());
                }
        }
 }
@@ -448,36 +472,93 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        _silent = false;
 
        assert (bufs.available() >= _input->n_ports());
-       
+
        if (_input->n_ports() == ChanCount::ZERO) {
                silence (nframes);
        }
-       
+
        bufs.set_count (_input->n_ports());
-       
-       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);
+       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);
+                       }
                }
        }
 
+       write_out_of_band_data (bufs, start_frame, end_frame, nframes);
        process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
 }
 
 void
 Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
 {
-       process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+       BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
+       bufs.set_count (_input->n_ports());
+       write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+}
+
+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_safe (bool yn, void *src)
+{
+       if (_solo_safe != yn) {
+               _solo_safe = yn;
+               solo_safe_changed (src);
+       } 
+}
+
+bool
+Route::solo_safe() const
+{
+       return _solo_safe;
 }
 
 void
 Route::set_solo (bool yn, void *src)
 {
-       if (_solo_safe || _solo_isolated) {
+       if (_solo_safe) {
                return;
        }
 
@@ -486,97 +567,115 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       if (soloed() != yn) {
-               mod_solo_level (yn ? 1 : -1);
+       if (self_soloed() != yn) {
+               set_self_solo (yn);
+               set_delivery_solo ();
                solo_changed (src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
-       }       
+       }
+}
+
+void
+Route::set_self_solo (bool yn)
+{
+       _self_solo = yn;
 }
 
 void
-Route::mod_solo_level (int32_t delta)
+Route::mod_solo_by_others (int32_t delta)
 {
        if (delta < 0) {
-               if (_solo_level >= (uint32_t) delta) {
-                       _solo_level += delta;
+               if (_soloed_by_others >= (uint32_t) delta) {
+                       _soloed_by_others += delta;
                } else {
-                       _solo_level = 0;
+                       _soloed_by_others = 0;
                }
        } else {
-               _solo_level += delta;
+               _soloed_by_others += delta;
        }
 
-       /* tell "special" delivery units what the solo situation is
-        */
+       set_delivery_solo ();
+}
 
-       switch (Config->get_solo_model()) {
-       case SoloInPlace:
-               /* main outs are used for soloing */
-               _main_outs->set_solo_level (_solo_level);
-               _main_outs->set_solo_isolated (_solo_isolated);
-               if (_control_outs) {
-                       /* control outs just keep on playing */
-                       _control_outs->set_solo_level (0);
-                       _control_outs->set_solo_isolated (true);
-               }
-               break;
+void
+Route::set_delivery_solo ()
+{
+       /* tell all delivery processors what the solo situation is, so that they keep
+          delivering even though Session::soloing() is true and they were not
+          explicitly soloed.
+       */
 
-       case SoloAFL:
-       case SoloPFL:
-               /* control outs are used for soloing */
-               if (_control_outs) {
-                       _control_outs->set_solo_level (_solo_level);
-                       _control_outs->set_solo_isolated (_solo_isolated);
+       Glib::RWLock::ReaderLock rm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               boost::shared_ptr<Delivery> d;
+               
+               if ((d = boost::dynamic_pointer_cast<Delivery> (*i)) != 0) {
+                       d->set_solo_level (soloed ());
+                       d->set_solo_isolated (solo_isolated());
                }
-               /* main outs just keep on playing */
-               _main_outs->set_solo_level (0);
-               _main_outs->set_solo_isolated (true);
-               break;
        }
 }
 
 void
 Route::set_solo_isolated (bool yn, void *src)
 {
+       if (is_master() || is_control() || is_hidden()) {
+               return;
+       }
+
        if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
                _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
                return;
        }
+       
+       /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
-       if (yn != _solo_isolated) {
-               _solo_isolated = yn;
-
-               /* tell "special" delivery units what the solo situation is
-                */
+       boost::shared_ptr<RouteList> routes = _session.get_routes ();
+       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+               bool sends_only;
+               bool does_feed = feeds (*i, &sends_only);
                
-               switch (Config->get_solo_model()) {
-               case SoloInPlace:
-                       _main_outs->set_solo_level (_solo_level);
-                       _main_outs->set_solo_isolated (_solo_isolated);
-                       if (_control_outs) {
-                               _main_outs->set_solo_level (1);
-                               _main_outs->set_solo_isolated (false);
-                       }
-                       break;
-               case SoloAFL:
-               case SoloPFL:
-                       if (_control_outs) {
-                               _control_outs->set_solo_level (_solo_level);
-                               _control_outs->set_solo_isolated (_solo_isolated);
-                       }
-                       _main_outs->set_solo_level (1);
-                       _main_outs->set_solo_isolated (false);
-                       break;
+               if (does_feed && !sends_only) {
+                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+               }
+       }
+
+       bool changed = false;
+
+       if (yn) {
+               if (_solo_isolated == 0) {
+                       changed = true;
+               }
+               _solo_isolated++;
+       } else {
+               changed = (_solo_isolated == 1);
+               if (_solo_isolated > 0) {
+                       _solo_isolated--;
                }
+       }
 
+       if (changed) {
+               set_delivery_solo ();
                solo_isolated_changed (src);
        }
 }
 
 bool
-Route::solo_isolated () const 
+Route::solo_isolated () const
+{
+       return _solo_isolated > 0;
+}
+
+void
+Route::set_mute_points (MuteMaster::MutePoint mp)
 {
-       return _solo_isolated;
+       _mute_points = mp;
+       mute_points_changed (); /* EMIT SIGNAL */
+
+       if (_mute_master->muted()) {
+               _mute_master->mute_at (_mute_points);
+               mute_changed (this); /* EMIT SIGNAL */
+       }
 }
 
 void
@@ -588,13 +687,18 @@ Route::set_mute (bool yn, void *src)
        }
 
        if (muted() != yn) {
-               _mute_master->mute (yn);
-               mute_changed (src);
+               if (yn) {
+                       _mute_master->mute_at (_mute_points);
+               } else {
+                       _mute_master->clear_mute ();
+               }
+
+               mute_changed (src); /* EMIT SIGNAL */
        }
-}      
+}
 
 bool
-Route::muted() const 
+Route::muted() const
 {
        return _mute_master->muted ();
 }
@@ -623,22 +727,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;
-                       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,7 +782,6 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
-               
 
                if (configure_processors_unlocked (err)) {
                        ProcessorList::iterator ploc = loc;
@@ -700,42 +791,29 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        cerr << "configure failed\n";
                        return -1;
                }
-       
+
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       
+
                        if (pi->natural_input_streams() == ChanCount::ZERO) {
                                /* generator plugin */
                                _have_internal_generator = true;
                        }
-                       
+
                }
-               
-               // 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));
 
-               _output->set_user_latency (0);
-       }
-       
-       processors_changed (); /* EMIT SIGNAL */
-       
-       return 0;
-}
+               if (_control_outs != processor) {
+                       // XXX: do we want to emit the signal here ? change call order.
+                       processor->activate ();
+               }
 
-bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
-{
-       ProcessorList::iterator loc;
+               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
 
-       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();
+               _output->set_user_latency (0);
        }
 
-       return add_processor_from_xml (node, loc);
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+
+       return 0;
 }
 
 bool
@@ -743,165 +821,195 @@ 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, _mute_master, node));
-                       add_processor (send, iter); 
-                       return true;
-               } 
-               
-               catch (failed_constructor &err) {
-                       error << _("Send construction failed") << endmsg;
-                       return false;
-               }
-               
-       } else if (node.name() == "Processor") {
-               
-               try {
-                       if ((prop = node.property ("type")) != 0) {
+       if (node.name() != "Processor") {
+               return false;
+       }
 
-                               boost::shared_ptr<Processor> processor;
+       try {
+               if ((prop = node.property ("type")) != 0) {
 
-                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
-                                   prop->value() == "lv2" ||
-                                   prop->value() == "vst" ||
-                                   prop->value() == "audiounit") {
-                                       
-                                       processor.reset (new PluginInsert(_session, node));
-                                       
-                               } else if (prop->value() == "port") {
+                       boost::shared_ptr<Processor> processor;
 
-                                       processor.reset (new PortInsert (_session, _mute_master, node));
-                               
-                               } else if (prop->value() == "send") {
+                       if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+                           prop->value() == "lv2" ||
+                           prop->value() == "vst" ||
+                           prop->value() == "audiounit") {
 
-                                       processor.reset (new Send (_session, _mute_master, node));
+                               processor.reset (new PluginInsert(_session, node));
 
-                               } else if (prop->value() == "meter") {
+                       } else if (prop->value() == "port") {
 
-                                       if (_meter) {
-                                               if (_meter->set_state (node)) {
-                                                       return false;
-                                               } else {
-                                                       return true;
-                                               }
-                                       }
+                               processor.reset (new PortInsert (_session, _mute_master, node));
+
+                       } else if (prop->value() == "send") {
+
+                               processor.reset (new Send (_session, _mute_master, node));
 
-                                       _meter.reset (new PeakMeter (_session, node));                                          
-                                       processor = _meter;
-                               
-                               } else if (prop->value() == "amp") {
+                       } else if (prop->value() == "meter") {
 
-                                       /* amp always exists */
-                                       
-                                       processor = _amp;
-                                       if (processor->set_state (node)) {
+                               if (_meter) {
+                                       if (_meter->set_state (node, Stateful::loading_state_version)) {
                                                return false;
                                        } else {
-                                               /* never any reason to add it */
                                                return true;
                                        }
-                                       
-                               } else if (prop->value() == "listen" || prop->value() == "deliver") {
+                               }
 
-                                       /* XXX need to generalize */
+                               _meter.reset (new PeakMeter (_session, node));
+                               _meter->set_display_to_user (_meter_point == MeterCustom);
+                               processor = _meter;
 
-                               } else if (prop->value() == "intsend") {
+                       } else if (prop->value() == "amp") {
 
-                                       processor.reset (new InternalSend (_session, _mute_master, node));
+                               /* amp always exists */
 
-                               } else if (prop->value() == "intreturn") {
-                                       
-                                       if (_intreturn) {
-                                               if (_intreturn->set_state (node)) {
-                                                       return false;
-                                               } else {
-                                                       return true;
-                                               }
-                                       }
-                                       _intreturn.reset (new InternalReturn (_session, node));
-                                       processor = _intreturn;
-
-                               } else if (prop->value() == "main-outs") {
-                                       
-                                       if (_main_outs) {
-                                               if (_main_outs->set_state (node)) {
-                                                       return false;
-                                               } else {
-                                                       return true;
-                                               }
-                                       }
+                               processor = _amp;
+                               if (processor->set_state (node, Stateful::loading_state_version)) {
+                                       return false;
+                               } else {
+                                       /* never any reason to add it */
+                                       return true;
+                               }
 
-                                       _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
-                                       processor = _main_outs;
+                       } else if (prop->value() == "intsend") {
 
-                               } else {
-                                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+                               processor.reset (new InternalSend (_session, _mute_master, node));
+
+                       } else if (prop->value() == "intreturn") {
+
+                               if (_intreturn) {
+                                       if (_intreturn->set_state (node, Stateful::loading_state_version)) {
+                                               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;
-                                       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, Stateful::loading_state_version)) {
+                                               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->display_to_user() && !_processors.empty()) {
+                               /* check for invisible processors stacked at the end and leave them there */
+                               ProcessorList::iterator p;
+                               p = _processors.end();
+                               --p;
+                               while (!(*p)->display_to_user() && 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)
+
+bool
+Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
 {
-       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();
+       const XMLProperty *prop;
+
+       try {
+               boost::shared_ptr<Processor> processor;
+
+               if (node.name() == "Insert") {
+
+                       if ((prop = node.property ("type")) != 0) {
+
+                               if (prop->value() == "ladspa" || prop->value() == "Ladspa" || 
+                                               prop->value() == "lv2" ||
+                                               prop->value() == "vst" ||
+                                               prop->value() == "audiounit") {
+
+                                       processor.reset (new PluginInsert (_session, node));
+
+                               } else {
+
+                                       processor.reset (new PortInsert (_session, _mute_master, node));
+                               }
 
-               if (!_processors.empty()) {
+                       }
+
+               } else if (node.name() == "Send") {
+
+                       processor.reset (new Send (_session, _mute_master, node, version));
+
+               } else {
+
+                       error << string_compose(_("unknown Processor type \"%1\"; ignored"), node.name()) << endmsg;
+                       return false;
+               }
+
+               if (iter == _processors.end() && processor->display_to_user() && !_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()) {
+                       while (!(*p)->display_to_user() && p != _processors.begin()) {
                                --p;
                        }
                        ++p;
-                       loc = p;
+                       iter = p;
                }
+
+               return (add_processor (processor, iter) == 0);
        }
 
-       return add_processors (others, loc, err);
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
+       }
 }
 
 int
-Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
-       /* NOTE: this is intended to be used ONLY when copying
-          processors from another Route. Hence the subtle
-          differences between this and ::add_processor()
+       ProcessorList::iterator loc;
+
+       if (before) {
+               loc = find(_processors.begin(), _processors.end(), before);
+       } else {
+               /* nothing specified - at end but before main outs */
+               loc = find (_processors.begin(), _processors.end(), _main_outs);
+       }
+
+       return add_processors (others, loc, err);
+}
+
+int
+Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
+{
+       /* NOTE: this is intended to be used ONLY when copying
+          processors from another Route. Hence the subtle
+          differences between this and ::add_processor()
        */
 
        ChanCount old_pms = processor_max_streams;
@@ -916,13 +1024,11 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList::iterator existing_end = _processors.end();
-               --existing_end;
 
                ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
-                       
+
                        // Ensure meter only appears in the list once
                        if (*i == _meter) {
                                ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
@@ -930,33 +1036,38 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                        _processors.erase(m);
                                }
                        }
-                       
+
                        boost::shared_ptr<PluginInsert> pi;
-                       
+
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
-                               
-                               ChanCount m = max(pi->input_streams(), pi->output_streams());
-                               if (m > potential_max_streams)
+
+                               ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+                               if (m > potential_max_streams) {
                                        potential_max_streams = m;
+                               }
+                       }
+
+                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+                       if ((*i)->active()) {
+                               (*i)->activate ();
                        }
 
-                       _processors.insert (iter, *i);
-                       
                        if (configure_processors_unlocked (err)) {
-                               ++existing_end;
-                               _processors.erase (existing_end, _processors.end());
+                               _processors.erase (inserted);
                                configure_processors_unlocked (0); // it worked before we tried to add it ...
                                return -1;
                        }
-                       
-                       (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+                       (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
                }
 
                _output->set_user_latency (0);
        }
-       
-       processors_changed (); /* EMIT SIGNAL */
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -981,10 +1092,10 @@ void
 Route::disable_processors (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                (*i)->deactivate ();
        }
@@ -992,17 +1103,17 @@ Route::disable_processors (Placement p)
        _session.set_dirty ();
 }
 
-/** Turn off all redirects 
+/** Turn off all redirects
  */
 void
 Route::disable_processors ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->deactivate ();
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1013,16 +1124,16 @@ void
 Route::disable_plugins (Placement p)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator start, end;
        placement_range(p, start, end);
-       
+
        for (ProcessorList::iterator i = start; i != end; ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1032,13 +1143,13 @@ void
 Route::disable_plugins ()
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
                        (*i)->deactivate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1047,7 +1158,7 @@ void
 Route::ab_plugins (bool forward)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
-                       
+
        if (forward) {
 
                /* forward = turn off all active redirects, and mark them so that the next time
@@ -1084,11 +1195,11 @@ Route::ab_plugins (bool forward)
                        }
                }
        }
-       
+
        _session.set_dirty ();
 }
-       
-       
+
+
 /** Remove processors with a given placement.
  * @param p Placement of processors to remove.
  */
@@ -1100,7 +1211,7 @@ Route::clear_processors (Placement p)
        if (!_session.engine().connected()) {
                return;
        }
-       
+
        bool already_deleting = _session.deletion_in_progress();
        if (!already_deleting) {
                _session.set_deletion_in_progress();
@@ -1110,26 +1221,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;
+                                       }
+                               }
                        }
                }
 
@@ -1139,7 +1267,7 @@ Route::clear_processors (Placement p)
 
        processor_max_streams.reset();
        _have_internal_generator = false;
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
@@ -1219,7 +1347,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
                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;
@@ -1230,11 +1358,104 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        }
 
        processor->drop_references ();
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* 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 ();
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+
+       return 0;
+}
+
+
 int
 Route::configure_processors (ProcessorStreams* err)
 {
@@ -1262,9 +1483,20 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        ChanCount out;
        list< pair<ChanCount,ChanCount> > configuration;
        uint32_t index = 0;
-       
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
+#ifndef NDEBUG
+       DEBUG_TRACE (DEBUG::Processors, "{\n");
+       for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
+               DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
+       }
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+#endif
+
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
+
                if ((*p)->can_support_io_configuration(in, out)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1276,22 +1508,24 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                        return -1;
                }
        }
-       
+
        // We can, so configure everything
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
                (*p)->configure_io(c->first, c->second);
-               (*p)->activate();
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
                out = c->second;
        }
 
-       // Ensure route outputs match last processor's outputs
-       if (out != _output->n_ports ()) {
-               _output->ensure_io (out, false, this);
+       if (_meter) {
+               _meter->reset_max_channels (processor_max_streams);
        }
 
+       /* make sure we have sufficient scratch buffers to cope with the new processor
+          configuration */
+       _session.ensure_buffers (n_process_buffers ());
+
        _in_configure_processors = false;
        return 0;
 }
@@ -1306,7 +1540,7 @@ Route::all_processors_flip ()
        }
 
        bool first_is_on = _processors.front()->active();
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (first_is_on) {
                        (*i)->deactivate ();
@@ -1314,7 +1548,7 @@ Route::all_processors_flip ()
                        (*i)->activate ();
                }
        }
-       
+
        _session.set_dirty ();
 }
 
@@ -1347,15 +1581,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
+          NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
           processors in the current actual processor list that are hidden. Any visible processors
           in the current list but not in "new_order" will be assumed to be deleted.
        */
@@ -1367,41 +1626,36 @@ 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;
-               niter = new_order.begin(); 
+               oiter = _processors.begin();
+               niter = new_order.begin();
 
                while (niter !=  new_order.end()) {
-                       
+
                        /* if the next processor in the old list is invisible (i.e. should not be in the new order)
-                          then append it to the temp list. 
+                          then append it to the temp list.
 
                           Otherwise, see if the next processor in the old list is in the new list. if not,
                           its been deleted. If its there, append it to the temp list.
                        */
 
-                       if (oiter == end) {
+                       if (oiter == _processors.end()) {
 
-                               /* no more elements in the old list, so just stick the rest of 
+                               /* 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)->set_placement (placement);
                                        ++niter;
                                }
                                break;
 
                        } else {
-                               
-                               if (!(*oiter)->visible()) {
+
+                               if (!(*oiter)->display_to_user()) {
 
                                        as_it_will_be.push_back (*oiter);
-                                       (*oiter)->set_placement (placement);
 
                                } else {
 
@@ -1412,15 +1666,14 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                                        } else {
                                                /* ignore this one, and add the next item from the new order instead */
                                                as_it_will_be.push_back (*niter);
-                                               (*niter)->set_placement (placement);
                                                ++niter;
                                        }
                                }
-                               
+
                                 /* now remove from old order - its taken care of no matter what */
                                oiter = _processors.erase (oiter);
                        }
-                       
+
                }
 
                _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
@@ -1429,10 +1682,10 @@ Route::reorder_processors (const ProcessorList& new_order, Placement placement,
                        _processors = as_it_was_before;
                        processor_max_streams = old_pms;
                        return -1;
-               } 
-       } 
+               }
+       }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1475,14 +1728,14 @@ Route::state(bool full_state)
        }
 
        string order_string;
-       OrderKeys::iterator x = order_keys.begin(); 
+       OrderKeys::iterator x = order_keys.begin();
 
        while (x != order_keys.end()) {
                order_string += string ((*x).first);
                order_string += '=';
                snprintf (buf, sizeof(buf), "%ld", (*x).second);
                order_string += buf;
-               
+
                ++x;
 
                if (x == order_keys.end()) {
@@ -1492,6 +1745,9 @@ Route::state(bool full_state)
                order_string += ':';
        }
        node->add_property ("order-keys", order_string);
+       node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others);
+       node->add_property ("soloed-by-others", buf);
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
@@ -1515,19 +1771,22 @@ Route::state(bool full_state)
        if (_extra_xml){
                node->add_child_copy (*_extra_xml);
        }
-       
+
        return *node;
 }
 
 int
-Route::set_state (const XMLNode& node)
+Route::set_state (const XMLNode& node, int version)
 {
-       return _set_state (node, true);
+       return _set_state (node, version, true);
 }
 
 int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 {
+       if (version < 3000) {
+               return _set_state_2X (node, version);
+       }
 
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
@@ -1542,7 +1801,7 @@ Route::_set_state (const XMLNode& node, bool call_base)
 
        if ((prop = node.property (X_("name"))) != 0) {
                Route::set_name (prop->value());
-       } 
+       }
 
        if ((prop = node.property ("id")) != 0) {
                _id = prop->value ();
@@ -1560,53 +1819,218 @@ Route::_set_state (const XMLNode& node, bool call_base)
        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);
+                               _input->set_state (*child, version);
                        } else if (prop->value() == "Output") {
-                               _output->set_state (*child);
+                               _output->set_state (*child, version);
                        }
                }
-                       
+
                if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
                }
        }
 
        set_processor_state (processor_state);
-       
-       if ((prop = node.property ("solo_level")) != 0) {
-               _solo_level = 0; // needed for mod_solo_level() to work
-               mod_solo_level (atoi (prop->value()));
+
+       if ((prop = node.property ("self-solo")) != 0) {
+               set_self_solo (string_is_affirmative (prop->value()));
+       }
+
+       if ((prop = node.property ("soloed-by-others")) != 0) {
+               _soloed_by_others = 0; // needed for mod_solo_by_others () to work
+               mod_solo_by_others (atoi (prop->value()));
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
-               set_solo_isolated (prop->value() == "yes", this);
+               set_solo_isolated (string_is_affirmative (prop->value()), this);
        }
 
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (prop->value()=="yes"?true:false);
+               set_phase_invert (string_is_affirmative (prop->value()));
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
-               set_denormal_protection (prop->value()=="yes"?true:false);
+               set_denormal_protection (string_is_affirmative (prop->value()));
        }
-       
+
        if ((prop = node.property (X_("active"))) != 0) {
-               bool yn = (prop->value() == "yes");
+               bool yn = string_is_affirmative (prop->value());
                _active = !yn; // force switch
                set_active (yn);
        }
 
+       if ((prop = node.property (X_("meter-point"))) != 0) {
+               _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
+               }
+       }
+
+       if ((prop = node.property (X_("order-keys"))) != 0) {
+
+               long n;
+
+               string::size_type colon, equal;
+               string remaining = prop->value();
+
+               while (remaining.length()) {
+
+                       if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
+                               error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                     << endmsg;
+                       } else {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+                                       error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+                                             << endmsg;
+                               } else {
+                                       set_order_key (remaining.substr (0, equal), n);
+                               }
+                       }
+
+                       colon = remaining.find_first_of (':');
+
+                       if (colon != string::npos) {
+                               remaining = remaining.substr (colon+1);
+                       } else {
+                               break;
+                       }
+               }
+       }
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+               child = *niter;
+
+               if (child->name() == X_("Comment")) {
+
+                       /* XXX this is a terrible API design in libxml++ */
+
+                       XMLNode *cmt = *(child->children().begin());
+                       _comment = cmt->content();
+
+               } else if (child->name() == X_("Extra")) {
+
+                       _extra_xml = new XMLNode (*child);
+
+               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
+
+                       if (prop->value() == "solo") {
+                               _solo_control->set_state (*child, version);
+                               _session.add_controllable (_solo_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);
+                       }
+
+               } else if (child->name() == X_("MuteMaster")) {
+                       _mute_master->set_state (*child, version);
+               }
+       }
+
+       return 0;
+}
+
+int
+Route::_set_state_2X (const XMLNode& node, int version)
+{
+       XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+       XMLNode *child;
+       XMLPropertyList plist;
+       const XMLProperty *prop;
+
+       /* 2X things which still remain to be handled:
+        * default-type
+        * muted
+        * mute-affects-pre-fader
+        * mute-affects-post-fader
+        * mute-affects-control-outs
+        * mute-affects-main-outs
+        * automation
+        * controlouts
+        */
+
+       if (node.name() != "Route") {
+               error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
+               return -1;
+       }
+
+       if ((prop = node.property (X_("flags"))) != 0) {
+               _flags = Flag (string_2_enum (prop->value(), _flags));
+       } else {
+               _flags = Flag (0);
+       }
+
+       /* add standard processors */
+
+       _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);
+
+       /* IOs */
+
+       nlist = node.children ();
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+               child = *niter;
+
+               if (child->name() == IO::state_node_name) {
+
+                       /* there is a note in IO::set_state_2X() about why we have to call
+                          this directly.
+                          */
+
+                       _input->set_state_2X (*child, version, true);
+                       _output->set_state_2X (*child, version, false);
+
+                       if ((prop = child->property (X_("name"))) != 0) {
+                               set_name (prop->value ());
+                       }
+
+                       if ((prop = child->property (X_("id"))) != 0) {
+                               _id = prop->value ();
+                       }
+
+                       if ((prop = child->property (X_("active"))) != 0) {
+                               bool yn = string_is_affirmative (prop->value());
+                               _active = !yn; // force switch
+                               set_active (yn);
+                       }
+               }
+
+               /* XXX: panners? */
+       }
+
+       if ((prop = node.property (X_("phase-invert"))) != 0) {
+               set_phase_invert (string_is_affirmative (prop->value()));
+       }
+
+       if ((prop = node.property (X_("denormal-protection"))) != 0) {
+               set_denormal_protection (string_is_affirmative (prop->value()));
+       }
+
        if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = (prop->value()=="yes");
+               bool yn = string_is_affirmative (prop->value());
 
                /* XXX force reset of solo status */
 
@@ -1616,15 +2040,10 @@ Route::_set_state (const XMLNode& node, bool call_base)
        if ((prop = node.property (X_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
-       
-       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_route_group (route_group, this);
-               }
-       }
+
+       /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
+          don't mean the same thing.
+       */
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
@@ -1637,13 +2056,13 @@ Route::_set_state (const XMLNode& node, bool call_base)
 
                        if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
                                error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                     << endmsg;
+                                       << endmsg;
                        } else {
                                if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
-                                             << endmsg;
+                                               << endmsg;
                                } else {
-                                       set_order_key (remaining.substr (0, equal).c_str(), n);
+                                       set_order_key (remaining.substr (0, equal), n);
                                }
                        }
 
@@ -1657,6 +2076,20 @@ Route::_set_state (const XMLNode& node, bool call_base)
                }
        }
 
+       XMLNodeList redirect_nodes;
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+               child = *niter;
+
+               if (child->name() == X_("Send") || child->name() == X_("Insert")) {
+                       redirect_nodes.push_back(child);
+               }
+
+       }
+
+       set_processor_state_2X (redirect_nodes, version);
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -1672,11 +2105,11 @@ Route::_set_state (const XMLNode& node, bool call_base)
                        _extra_xml = new XMLNode (*child);
 
                } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-                       
+
                        if (prop->value() == "solo") {
-                               _solo_control->set_state (*child);
+                               _solo_control->set_state (*child, version);
                                _session.add_controllable (_solo_control);
-                       } 
+                       }
 
                } else if (child->name() == X_("RemoteControl")) {
                        if ((prop = child->property (X_("id"))) != 0) {
@@ -1685,9 +2118,7 @@ Route::_set_state (const XMLNode& node, bool call_base)
                                set_remote_control_id (x);
                        }
 
-               } else if (child->name() == X_("MuteMaster")) {
-                       _mute_master->set_state (*child);
-               }
+               } 
        }
 
        return 0;
@@ -1704,6 +2135,20 @@ Route::get_processor_state ()
        return *root;
 }
 
+void
+Route::set_processor_state_2X (XMLNodeList const & nList, int version)
+{
+       /* We don't bother removing existing processors not in nList, as this
+          method will only be called when creating a Route from scratch, not
+          for undo purposes.  Just put processors in at the appropriate place
+          in the list.
+       */
+
+       for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
+               add_processor_from_xml_2X (**i, version, _processors.begin ());
+       }
+}
+
 void
 Route::set_processor_state (const XMLNode& node)
 {
@@ -1736,7 +2181,7 @@ Route::set_processor_state (const XMLNode& node)
                                break;
                        }
                }
-               
+
                if (!processorInStateList) {
                        remove_processor (*i);
                }
@@ -1750,7 +2195,7 @@ Route::set_processor_state (const XMLNode& node)
        i = _processors.begin();
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
-               
+
                XMLProperty* prop = (*niter)->property ("type");
 
                o = i;
@@ -1758,14 +2203,14 @@ Route::set_processor_state (const XMLNode& node)
                // Check whether the next processor in the list is the right one,
                // except for "amp" which is always there and may not have the
                // old ID since it is always created anew in every Route
-               
+
                if (prop->value() != "amp") {
                        while (o != _processors.end()) {
                                XMLProperty* id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
                                        break;
                                }
-                               
+
                                ++o;
                        }
                }
@@ -1795,7 +2240,7 @@ Route::set_processor_state (const XMLNode& node)
 
                        // and make it (just) so
 
-                       (*i)->set_state (**niter);
+                       (*i)->set_state (**niter, Stateful::current_state_version);
                }
        }
 
@@ -1803,7 +2248,7 @@ Route::set_processor_state (const XMLNode& node)
           the XML state represents a working signal route.
        */
 
-       processors_changed ();
+       processors_changed (RouteProcessorChange ());
 }
 
 void
@@ -1819,10 +2264,10 @@ Route::silence (nframes_t nframes)
        if (!_silent) {
 
                _output->silence (nframes);
-               
-               { 
+
+               {
                        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-                       
+
                        if (lm.locked()) {
                                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                                        boost::shared_ptr<PluginInsert> pi;
@@ -1831,7 +2276,7 @@ Route::silence (nframes_t nframes)
                                                // skip plugins, they don't need anything when we're not active
                                                continue;
                                        }
-                                       
+
                                        (*i)->silence (nframes);
                                }
 
@@ -1840,9 +2285,9 @@ Route::silence (nframes_t nframes)
                                }
                        }
                }
-               
+
        }
-}      
+}
 
 void
 Route::add_internal_return ()
@@ -1857,15 +2302,16 @@ 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) {
-                       return d->get_buffers ();
+                       BufferSet* bs = d->get_buffers ();
+                       return bs;
                }
        }
-       
+
        return 0;
 }
 
@@ -1873,10 +2319,10 @@ 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 ();
                }
@@ -1884,19 +2330,20 @@ Route::release_return_buffer () const
 }
 
 int
-Route::listen_via (boost::shared_ptr<Route> route, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
 {
        vector<string> ports;
        vector<string>::const_iterator i;
 
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
-               
+
                for (ProcessorList::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.
                                */
@@ -1906,25 +2353,29 @@ Route::listen_via (boost::shared_ptr<Route> route, const string& listen_name)
                                }
 
                                /* already listening via the specified IO: do nothing */
-                               
+
                                return 0;
                        }
                }
        }
-       
+
        boost::shared_ptr<InternalSend> listener;
 
        try {
-               listener.reset (new InternalSend (_session, _mute_master, route));
+               listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
 
        } catch (failed_constructor& err) {
                return -1;
        }
 
-       add_processor (listener, PreFader);
-       
-       return 0;
-}      
+       if (route == _session.control_out()) {
+               _control_outs = listener;
+       }
+
+       add_processor (listener, placement);
+
+       return 0;
+}
 
 void
 Route::drop_listen (boost::shared_ptr<Route> route)
@@ -1934,12 +2385,12 @@ Route::drop_listen (boost::shared_ptr<Route> route)
 
        Glib::RWLock::ReaderLock rl(_processor_lock);
        rl.acquire ();
-       
+
   again:
        for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
-               
+
                boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-               
+
                if (d && d->target_route() == route) {
                        rl.release ();
                        remove_processor (*x, &err);
@@ -1949,8 +2400,8 @@ Route::drop_listen (boost::shared_ptr<Route> route)
                           so start over.
                        */
 
-                       goto again; 
-               } 
+                       goto again;
+               }
        }
 
        rl.release ();
@@ -1960,33 +2411,6 @@ Route::drop_listen (boost::shared_ptr<Route> route)
        }
 }
 
-void
-Route::set_route_group (RouteGroup *rg, void *src)
-{
-       if (rg == _route_group) {
-               return;
-       }
-
-       if (_route_group) {
-               _route_group->remove (this);
-       }
-
-       if ((_route_group = rg) != 0) {
-               _route_group->add (this);
-       }
-
-       _session.set_dirty ();
-       route_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::drop_route_group (void *src)
-{
-       _route_group = 0;
-       _session.set_dirty ();
-       route_group_changed (src); /* EMIT SIGNAL */
-}
-
 void
 Route::set_comment (string cmt, void *src)
 {
@@ -1996,35 +2420,46 @@ Route::set_comment (string cmt, void *src)
 }
 
 bool
-Route::feeds (boost::shared_ptr<Route> other)
+Route::feeds (boost::shared_ptr<Route> other, bool* only_send)
 {
-       // cerr << _name << endl;
+       DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
 
        if (_output->connected_to (other->input())) {
-               // cerr << "\tdirect FEEDS " << other->name() << endl;
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
+               if (only_send) {
+                       *only_send = false;
+               }
+
                return true;
        }
 
+       
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
-               
+
                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;
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
+                               if (only_send) {
+                                       *only_send = true;
+                               }
                                return true;
                        } else {
-                               // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
+                               DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does NOT feed %2\n", iop->name(), other->name()));
                        }
+               } else {
+                       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tPROC %1 is not an IOP\n", (*r)->name()));
                }
+                       
        }
 
-       // cerr << "\tdoes NOT FEED " << other->name() << endl;
+       DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tdoes NOT feed %1\n", other->name()));
        return false;
 }
 
 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();
 
@@ -2036,12 +2471,12 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
                }
 
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
+
                        if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
                                (*i)->deactivate ();
                                (*i)->activate ();
                        }
-                       
+
                        (*i)->transport_stopped (now);
                }
        }
@@ -2050,7 +2485,7 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f
 }
 
 void
-Route::input_change_handler (IOChange change, void *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
 {
        if ((change & ConfigurationChanged)) {
                configure_processors (0);
@@ -2058,12 +2493,12 @@ 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)) {
-               
+
                /* XXX resize all listeners to match _main_outs? */
-               
+
                // configure_processors (0);
        }
 }
@@ -2074,22 +2509,36 @@ Route::pans_required () const
        if (n_outputs().n_audio() < 2) {
                return 0;
        }
-       
+
        return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
 }
 
-int 
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,  
-               bool session_state_changing, bool can_record, bool rec_monitors_input)
+int
+Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+               bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
-       if (session_state_changing || !_active || n_inputs() == ChanCount::ZERO)  {
+       if (!_active || n_inputs() == ChanCount::ZERO)  {
                silence (nframes);
                return 0;
        }
+       if (session_state_changing) {
+               if (_session.transport_speed() != 0.0f) {
+                       /* we're rolling but some state is changing (e.g. our diskstream contents)
+                          so we cannot use them. Be silent till this is over.
+                          
+                          XXX note the absurdity of ::no_roll() being called when we ARE rolling!
+                       */
+                       silence (nframes);
+                       return 0;
+               }
+               /* we're really not rolling, so we're either delivery silence or actually
+                  monitoring, both of which are safe to do while session_state_changing is true.
+               */
+       }
 
        _amp->apply_gain_automation (false);
        passthru (start_frame, end_frame, nframes, 0);
@@ -2111,7 +2560,7 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 
                nframes -= _roll_delay;
                silence (_roll_delay);
-               /* we've written _roll_delay of samples into the 
+               /* we've written _roll_delay of samples into the
                   output ports, so make a note of that for
                   future reference.
                */
@@ -2126,7 +2575,7 @@ 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
@@ -2146,7 +2595,7 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
                silence (nframes);
                return 0;
        }
-       
+
        nframes_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
@@ -2161,8 +2610,8 @@ Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int
 }
 
 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;
@@ -2182,18 +2631,18 @@ Route::has_external_redirects () const
        // 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->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
-                               
+
                                string port_name = port->name();
                                string client_name = port_name.substr (0, port_name.find(':'));
 
                                /* only say "yes" if the redirect is actually in use */
-                               
+
                                if (client_name != "ardour" && pi->active()) {
                                        return true;
                                }
@@ -2222,30 +2671,73 @@ Route::flush_processors ()
 void
 Route::set_meter_point (MeterPoint p, void *src)
 {
-       if (_meter_point != p) {
-               _meter_point = p;
+       /* CAN BE CALLED FROM PROCESS CONTEXT */
 
-               // Move meter in the processors list
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
-               _processors.erase(loc);
-               switch (p) {
-               case MeterInput:
-                       loc = _processors.begin();
-                       break;
-               case MeterPreFader:
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-                       break;
-               case MeterPostFader:
-                       loc = _processors.end();
-                       break;
+       if (_meter_point == p) {
+               return;
+       }
+
+       bool meter_was_visible_to_user = _meter->display_to_user ();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+       
+               if (p != MeterCustom) {
+                       // Move meter in the processors list to reflect the new position
+                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+                       _processors.erase(loc);
+                       switch (p) {
+                       case MeterInput:
+                               loc = _processors.begin();
+                               break;
+                       case MeterPreFader:
+                               loc = find (_processors.begin(), _processors.end(), _amp);
+                               break;
+                       case MeterPostFader:
+                               loc = _processors.end();
+                               break;
+                       default:
+                               break;
+                       }
+                       
+                       ChanCount m_in;
+                       
+                       if (loc == _processors.begin()) {
+                               m_in = _input->n_ports();
+                       } else {
+                               ProcessorList::iterator before = loc;
+                               --before;
+                               m_in = (*before)->output_streams ();
+                       }
+                       
+                       _meter->reflect_inputs (m_in);
+                       
+                       _processors.insert (loc, _meter);
+                       
+                       /* we do not need to reconfigure the processors, because the meter
+                          (a) is always ready to handle processor_max_streams
+                          (b) is always an N-in/N-out processor, and thus moving
+                          it doesn't require any changes to the other processors.
+                       */
+                       
+                       _meter->set_display_to_user (false);
+                       
+               } else {
+                       
+                       // just make it visible and let the user move it
+                       
+                       _meter->set_display_to_user (true);
                }
-               _processors.insert(loc, _meter);
-               
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
        }
+
+       _meter_point = p;
+       meter_change (src); /* EMIT SIGNAL */
+
+       bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+       
+       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
 }
+
 void
 Route::put_control_outs_at (Placement p)
 {
@@ -2253,27 +2745,37 @@ Route::put_control_outs_at (Placement p)
                return;
        }
 
-       // Move meter in the processors list
-       ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
-       _processors.erase(loc);
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorList as_it_was (_processors);
+               // 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);
 
-       switch (p) {
-       case PreFader:
-               loc = find(_processors.begin(), _processors.end(), _amp);
-               if (loc != _processors.begin()) {
-                       --loc;
+               if (configure_processors_unlocked (0)) {
+                       _processors = as_it_was;
+                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       return;
                }
-               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 */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        _session.set_dirty ();
 }
 
@@ -2289,13 +2791,10 @@ Route::update_total_latency ()
                }
        }
 
-#undef DEBUG_LATENCY
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": internal redirect latency = " << own_latency << endl;
-#endif
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal redirect latency = %2\n", _name, own_latency));
 
        _output->set_port_latency (own_latency);
-       
+
        if (_output->user_latency() == 0) {
 
                /* this (virtual) function is used for pure Routes,
@@ -2304,7 +2803,7 @@ Route::update_total_latency ()
                   port, not prerecorded material, and therefore we
                   have to take into account any input latency.
                */
-               
+
                own_latency += _input->signal_latency ();
        }
 
@@ -2312,11 +2811,8 @@ Route::update_total_latency ()
                _output->set_latency_delay (own_latency);
                signal_latency_changed (); /* EMIT SIGNAL */
        }
-       
-#ifdef DEBUG_LATENCY
-       cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
-            << own_latency << endl;
-#endif
+
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency));
 
        return _output->effective_latency ();
 }
@@ -2351,13 +2847,15 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 void
 Route::automation_snapshot (nframes_t now, bool force)
 {
+       panner()->automation_snapshot (now, force);
+       
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
 }
 
 Route::SoloControllable::SoloControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation), 
+       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
                             boost::shared_ptr<AutomationList>(), name)
        , route (r)
 {
@@ -2369,23 +2867,70 @@ void
 Route::SoloControllable::set_value (float val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-       
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+
+       if (Config->get_solo_control_is_listen_control()) {
+               _session.set_listen (rl, bval);
+       } else {
+               _session.set_solo (rl, bval);
+       }
+#else
        route.set_solo (bval, this);
+#endif
 }
 
 float
 Route::SoloControllable::get_value (void) const
 {
-       return route.soloed() ? 1.0f : 0.0f;
+       if (Config->get_solo_control_is_listen_control()) {
+               return route.listening() ? 1.0f : 0.0f;
+       } else {
+               return route.self_soloed() ? 1.0f : 0.0f;
+       }
+}
+
+Route::MuteControllable::MuteControllable (std::string name, Route& r)
+       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+                            boost::shared_ptr<AutomationList>(), name)
+       , route (r)
+{
+       boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+       set_list (gl);
+}
+
+void
+Route::MuteControllable::set_value (float val)
+{
+       bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
+
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+       _session.set_mute (rl, bval);
+#else
+       route.set_mute (bval, this);
+#endif
+}
+
+float
+Route::MuteControllable::get_value (void) const
+{
+       return route.muted() ? 1.0f : 0.0f;
 }
 
-void 
+void
 Route::set_block_size (nframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
        }
-       _session.ensure_buffers(processor_max_streams);
+       
+       _session.ensure_buffers (n_process_buffers ());
 }
 
 void
@@ -2418,7 +2963,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
 
@@ -2441,10 +2986,10 @@ Route::shift (nframes64_t pos, nframes64_t frames)
        {
                Glib::RWLock::ReaderLock lm (redirect_lock);
                for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-                       
+
                        set<uint32_t> a;
                        (*i)->what_has_automation (a);
-                       
+
                        for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
                                AutomationList & al = (*i)->automation_list (*j);
                                XMLNode &before = al.get_state ();
@@ -2464,9 +3009,9 @@ Route::save_as_template (const string& path, const string& name)
 {
        XMLNode& node (state (false));
        XMLTree tree;
-       
+
        IO::set_name_in_state (*node.children().front(), name);
-       
+
        tree.set_root (&node);
        return tree.write (path.c_str());
 }
@@ -2481,15 +3026,15 @@ Route::set_name (const string& str)
 
        name = Route::ensure_track_or_route_name (str, _session);
        SessionObject::set_name (name);
-       
+
        ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
-               
+
                Glib::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       
+
                        /* rename all I/O processors that have inputs or outputs */
 
                        boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
@@ -2513,14 +3058,14 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
 
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                boost::shared_ptr<InternalSend> send;
-               
+
                if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
                        if (send->target_route() == target) {
                                return send;
                        }
                }
        }
-       
+
        return boost::shared_ptr<Send>();
 }
 
@@ -2569,20 +3114,33 @@ 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();
 }
 
@@ -2591,20 +3149,20 @@ 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));
+       boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(control (param));
 
        if (!c) {
 
                /* maybe one of our processors does or ... */
-               
+
                Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                       if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->data().control (param))) != 0) {
+                       if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->control (param))) != 0) {
                                break;
                        }
                }
        }
-               
+
        if (!c) {
 
                /* nobody does so we'll make a new one */
@@ -2615,3 +3173,40 @@ Route::get_control (const Evoral::Parameter& param)
 
        return c;
 }
+
+boost::shared_ptr<Processor>
+Route::nth_plugin (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+boost::shared_ptr<Processor>
+Route::nth_send (uint32_t n)
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       ProcessorList::iterator i;
+
+       for (i = _processors.begin(); i != _processors.end(); ++i) {
+               cerr << "check " << (*i)->name() << endl;
+               if (boost::dynamic_pointer_cast<Send> (*i)) {
+                       if (n-- == 0) {
+                               return *i;
+                       }
+               } else {
+                       cerr << "\tnot a send\n";
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}