merge 3.0-panexp (pan experiments) branch, revisions 8534-8585 into 3.0, thus ending...
[ardour.git] / libs / ardour / route.cc
index 29184ab940fd563b28394a6cd4789e1d983470af..b732fd09e8b6c08cff0b59fbfd6b0fff85d62782 100644 (file)
@@ -46,7 +46,9 @@
 #include "ardour/meter.h"
 #include "ardour/mix.h"
 #include "ardour/monitor_processor.h"
+#include "ardour/pannable.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
@@ -58,6 +60,9 @@
 #include "ardour/session.h"
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
+#include "ardour/capturing_processor.h"
 
 #include "i18n.h"
 
@@ -71,14 +76,14 @@ PBD::Signal0<void> Route::RemoteControlIDChange;
 
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
-       , AutomatableControls (sess)
+       , Automatable (sess)
+       , GraphNode( sess.route_graph )
         , _active (true)
         , _initial_delay (0)
         , _roll_delay (0)
        , _flags (flg)
         , _pending_declick (true)
         , _meter_point (MeterPostFader)
-        , _phase_invert (0)
         , _self_solo (false)
         , _soloed_by_others_upstream (0)
         , _soloed_by_others_downstream (0)
@@ -90,10 +95,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _solo_control (new SoloControllable (X_("solo"), *this))
        , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, name))
-        , _mute_points (MuteMaster::AllPoints)
         , _have_internal_generator (false)
-        , _physically_connected (false)
-        , _graph_level (-1)
         , _solo_safe (false)
        , _default_type (default_type)
         , _remote_control_id (0)
@@ -106,14 +108,18 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 int
 Route::init ()
 {
-       /* add standard controls */
+        /* add standard controls */
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       
+       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
+
        add_control (_solo_control);
        add_control (_mute_control);
 
+        /* panning */
+        
+        _pannable.reset (new Pannable (_session));
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
@@ -122,9 +128,11 @@ Route::init ()
        _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));
 
+       _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
+
        /* add amp processor  */
 
-       _amp.reset (new Amp (_session, _mute_master));
+       _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
        /* add standard processors: meter, main outs, monitor out */
@@ -134,7 +142,7 @@ Route::init ()
 
        add_processor (_meter, PostFader);
 
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+       _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
 
         add_processor (_main_outs, PostFader);
 
@@ -161,7 +169,9 @@ Route::init ()
 
                 /* no panning on the monitor main outs */
 
+#ifdef PANNER_HACKS
                 _main_outs->panner()->set_bypassed (true);
+#endif
        }
 
         if (is_master() || is_monitor() || is_hidden()) {
@@ -214,7 +224,7 @@ Route::remote_control_id() const
        return _remote_control_id;
 }
 
-long
+int32_t
 Route::order_key (std::string const & name) const
 {
        OrderKeys::const_iterator i = order_keys.find (name);
@@ -226,17 +236,34 @@ Route::order_key (std::string const & name) const
 }
 
 void
-Route::set_order_key (std::string const & name, long n)
+Route::set_order_key (std::string const & name, int32_t n)
 {
-       order_keys[name] = n;
+       bool changed = false;
+
+       /* This method looks more complicated than it should, but
+          it's important that we don't emit order_key_changed unless
+          it actually has, as expensive things happen on receipt of that
+          signal.
+       */
+
+       if (order_keys.find(name) == order_keys.end() || order_keys[name] != n) {
+               order_keys[name] = n;
+               changed = true;
+       }
 
        if (Config->get_sync_all_route_ordering()) {
                for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
-                       x->second = n;
+                       if (x->second != n) {
+                               x->second = n;
+                               changed = true;
+                       }
                }
        }
 
-       _session.set_dirty ();
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
+               _session.set_dirty ();
+       }
 }
 
 /** Set all order keys to be the same as that for `base', if such a key
@@ -251,7 +278,7 @@ Route::sync_order_keys (std::string const & base)
        }
 
        OrderKeys::iterator i;
-       uint32_t key;
+       int32_t key;
 
        if ((i = order_keys.find (base)) == order_keys.end()) {
                /* key doesn't exist, use the first existing key (during session initialization) */
@@ -264,8 +291,17 @@ Route::sync_order_keys (std::string const & base)
                i = order_keys.begin();
        }
 
+       bool changed = false;
+       
        for (; i != order_keys.end(); ++i) {
-               i->second = key;
+               if (i->second != key) {
+                       i->second = key;
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               order_key_changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -275,7 +311,7 @@ Route::ensure_track_or_route_name(string name, Session &session)
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
-               newname = bump_name_once (newname);
+               newname = bump_name_once (newname, '.');
        }
 
        return newname;
@@ -326,11 +362,11 @@ Route::set_gain (gain_t val, void *src)
                                }
                        }
 
-                       _route_group->apply (&Route::inc_gain, factor, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
 
                } else {
 
-                       _route_group->apply (&Route::set_gain, val, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
                }
 
                return;
@@ -355,8 +391,9 @@ 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)
+                              framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+                              bool /*with_processors*/, int declick,
+                               bool gain_automation_ok)
 {
        bool monitor;
 
@@ -376,9 +413,12 @@ 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);
-
-
+        if (gain_automation_ok) {
+                _amp->setup_gain_automation (start_frame, end_frame, nframes);
+        } else {
+                _amp->apply_gain_automation (false);
+        }
+        
        /* tell main outs what to do about monitoring */
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
@@ -388,9 +428,9 @@ Route::process_output_buffers (BufferSet& bufs,
           ----------------------------------------------------------------------------------------- */
 
        if (declick > 0) {
-               Amp::apply_gain (bufs, nframes, 0.0, 1.0);
+               Amp::declick (bufs, nframes, 1);
        } else if (declick < 0) {
-               Amp::apply_gain (bufs, nframes, 1.0, 0.0);
+               Amp::declick (bufs, nframes, -1);
        }
 
        _pending_declick = 0;
@@ -399,7 +439,7 @@ Route::process_output_buffers (BufferSet& bufs,
           DENORMAL CONTROL/PHASE INVERT
           ----------------------------------------------------------------------------------------- */
 
-       if (_phase_invert) {
+       if (_phase_invert.any ()) {
 
                int chn = 0;
 
@@ -408,13 +448,13 @@ 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) {
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
                                        }
                                } else {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] += 1.0e-27f;
                                        }
                                }
@@ -425,8 +465,8 @@ 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 & (1<<chn)) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               if (_phase_invert[chn]) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
                                }
@@ -439,7 +479,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
                        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) {
+                               for (pframes_t nx = 0; nx < nframes; ++nx) {
                                        sp[nx] += 1.0e-27f;
                                }
                        }
@@ -451,25 +491,25 @@ Route::process_output_buffers (BufferSet& bufs,
           and go ....
           ----------------------------------------------------------------------------------------- */
 
-       Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
-
-       if (rm.locked()) {
-                //cerr << name() << " upstream solo " << _soloed_by_others_upstream
-                // << " downstream solo " << _soloed_by_others_downstream
-                // << " self " << _self_solo
-                //<< endl;
-               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-                       if (bufs.count() != (*i)->input_streams()) {
-                               cerr << _name << " bufs = " << bufs.count()
-                                    << " input for " << (*i)->name() << " = " << (*i)->input_streams()
-                                    << endl;
-                       }
-                       assert (bufs.count() == (*i)->input_streams());
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-                       (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
-                       bufs.set_count ((*i)->output_streams());
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
+                       break;
+               }
+               
+               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());
+                
+                /* should we NOT run plugins here if the route is inactive?
+                   do we catch route != active somewhere higher?
+                */
+
+               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               bufs.set_count ((*i)->output_streams());
        }
 }
 
@@ -480,7 +520,7 @@ Route::n_process_buffers ()
 }
 
 void
-Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
@@ -489,7 +529,7 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        assert (bufs.available() >= input_streams());
 
        if (_input->n_ports() == ChanCount::ZERO) {
-               silence (nframes);
+               silence_unlocked (nframes);
        }
 
        bufs.set_count (input_streams());
@@ -516,16 +556,16 @@ Route::passthru (sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
        }
 
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
 }
 
 void
-Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
+Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int 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);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
 }
 
 void
@@ -539,8 +579,10 @@ Route::set_listen (bool yn, void* src)
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
-                       } else {
+                                _mute_master->set_soloed (true);
+                        } else {
                                _monitor_send->deactivate ();
+                                _mute_master->set_soloed (false);
                        }
 
                        listen_changed (src); /* EMIT SIGNAL */
@@ -581,7 +623,7 @@ Route::set_solo (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
                return;
        }
 
@@ -606,6 +648,8 @@ Route::mod_solo_by_others_upstream (int32_t delta)
                 return;
         }
 
+        uint32_t old_sbu = _soloed_by_others_upstream;
+
        if (delta < 0) {
                if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
                        _soloed_by_others_upstream += delta;
@@ -616,6 +660,9 @@ Route::mod_solo_by_others_upstream (int32_t delta)
                _soloed_by_others_upstream += delta;
        }
 
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
+                                                  name(), delta, _soloed_by_others_upstream, old_sbu, 
+                                                  _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
 
         /* push the inverse solo change to everything that feeds us. 
            
@@ -630,13 +677,19 @@ Route::mod_solo_by_others_upstream (int32_t delta)
            not in reverse.
          */
 
-        if (delta > 0) {
-                for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
-                        boost::shared_ptr<Route> sr = i->r.lock();
-                        if (sr) {
-                                sr->mod_solo_by_others_downstream (-delta);
+        if ((_self_solo || _soloed_by_others_downstream) &&
+            ((old_sbu == 0 && _soloed_by_others_upstream > 0) || 
+             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
+                
+                if (delta > 0 || !Config->get_exclusive_solo()) {
+                        DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                                boost::shared_ptr<Route> sr = i->r.lock();
+                                if (sr) {
+                                        sr->mod_solo_by_others_downstream (-delta);
+                                }
                         }
-                }
+                } 
         }
 
         set_mute_master_solo ();
@@ -650,7 +703,7 @@ Route::mod_solo_by_others_downstream (int32_t delta)
                 return;
         }
 
-       if (delta < 0) {
+        if (delta < 0) {
                if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
                        _soloed_by_others_downstream += delta;
                } else {
@@ -660,6 +713,8 @@ Route::mod_solo_by_others_downstream (int32_t delta)
                _soloed_by_others_downstream += delta;
        }
 
+        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
+
         set_mute_master_solo ();
         solo_changed (false, this);
 }
@@ -678,7 +733,7 @@ Route::set_solo_isolated (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
                return;
        }
        
@@ -701,22 +756,27 @@ Route::set_solo_isolated (bool yn, void *src)
 
         /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
+        bool changed = false;
+
        if (yn) {
                 if (_solo_isolated == 0) {
                         _mute_master->set_solo_ignore (true);
+                        changed = true;
                 }
                _solo_isolated++;
-                solo_isolated_changed (src);
        } else {
                if (_solo_isolated > 0) {
                        _solo_isolated--;
                         if (_solo_isolated == 0) {
                                 _mute_master->set_solo_ignore (false);
+                                changed = true;
                         }
-                        solo_isolated_changed (src);
                }
        }
 
+        if (changed) {
+                solo_isolated_changed (src);
+        }
 }
 
 bool
@@ -728,12 +788,12 @@ Route::solo_isolated () const
 void
 Route::set_mute_points (MuteMaster::MutePoint mp)
 {
-        _mute_points = mp;
-        _mute_master->set_mute_points (MuteMaster::AllPoints);
+        _mute_master->set_mute_points (mp);
         mute_points_changed (); /* EMIT SIGNAL */
         
-        if (_mute_master->muted()) {
+        if (_mute_master->muted_by_self()) {
                 mute_changed (this); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
         }
 }
 
@@ -741,20 +801,21 @@ void
 Route::set_mute (bool yn, void *src)
 {
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
-               _route_group->apply (&Route::set_mute, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
                return;
        }
 
        if (muted() != yn) {
-                _mute_master->set_muted (yn);
+                _mute_master->set_muted_by_self (yn);
                mute_changed (src); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
 bool
 Route::muted () const
 {
-        return _mute_master->muted();
+        return _mute_master->muted_by_self();
 }
 
 #if 0
@@ -837,13 +898,17 @@ 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;
-                       --ploc;
-                       _processors.erase(ploc);
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       cerr << "configure failed\n";
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               ProcessorList::iterator ploc = loc;
+                               --ploc;
+                               _processors.erase(ploc);
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               cerr << "configure failed\n";
+                               return -1;
+                       }
                }
 
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
@@ -873,18 +938,38 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
 
 bool
-Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
+Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
        const XMLProperty *prop;
 
        try {
                boost::shared_ptr<Processor> processor;
 
+               /* bit of a hack: get the `placement' property from the <Redirect> tag here
+                  so that we can add the processor in the right place (pre/post-fader)
+               */
+
+               XMLNodeList const & children = node.children ();
+               XMLNodeList::const_iterator i = children.begin ();
+               
+               while (i != children.end() && (*i)->name() != X_("Redirect")) {
+                       ++i;
+               }
+
+               Placement placement = PreFader;
+
+               if (i != children.end()) {
+                       if ((prop = (*i)->property (X_("placement"))) != 0) {
+                               placement = Placement (string_2_enum (prop->value(), placement));
+                       }
+               }
+
                if (node.name() == "Insert") {
 
                        if ((prop = node.property ("type")) != 0) {
@@ -898,14 +983,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
 
                                } else {
 
-                                       processor.reset (new PortInsert (_session, _mute_master));
+                                       processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                }
 
                        }
 
                } else if (node.name() == "Send") {
 
-                       processor.reset (new Send (_session, _mute_master));
+                       processor.reset (new Send (_session, _pannable, _mute_master));
 
                } else {
 
@@ -917,19 +1002,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
                         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;
-               }
-
-               return (add_processor (processor, iter) == 0);
+               return (add_processor (processor, placement) == 0);
        }
 
        catch (failed_constructor &err) {
@@ -1004,10 +1077,13 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                (*i)->activate ();
                        }
 
-                       if (configure_processors_unlocked (err)) {
-                               _processors.erase (inserted);
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (configure_processors_unlocked (err)) {
+                                       _processors.erase (inserted);
+                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       return -1;
+                               }
                        }
 
                        (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
@@ -1017,6 +1093,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -1211,12 +1288,17 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (&err); // this can't fail
+               }
        }
 
        processor_max_streams.reset();
        _have_internal_generator = false;
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        if (!already_deleting) {
                _session.clear_deletion_in_progress();
@@ -1284,12 +1366,16 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors.insert (i, processor);
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
@@ -1308,6 +1394,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        processor->drop_references ();
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -1370,12 +1457,16 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _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;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       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;
@@ -1399,14 +1490,17 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
 
-
+/** Caller must hold process lock */
 int
 Route::configure_processors (ProcessorStreams* err)
 {
+       assert (!AudioEngine::instance()->process_lock().trylock());
+       
        if (!_in_configure_processors) {
                Glib::RWLock::WriterLock lm (_processor_lock);
                return configure_processors_unlocked (err);
@@ -1420,37 +1514,34 @@ Route::input_streams () const
         return _input->n_ports ();
 }
 
-/** Configure the input/output configuration of each processor in the processors list.
- * Return 0 on success, otherwise configuration is impossible.
- */
-int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
 {
-       if (_in_configure_processors) {
-          return 0;
-       }
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       _in_configure_processors = true;
+       return try_configure_processors_unlocked (in, err);
+}
 
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
        // Check each processor in order to see if we can configure as requested
-       ChanCount in = input_streams ();
        ChanCount out;
-       list< pair<ChanCount,ChanCount> > configuration;
+       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 (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+                       break;
+               }
+               
                if ((*p)->can_support_io_configuration(in, out)) {
-                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1in = %2 out = %3\n",(*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1458,14 +1549,49 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                                err->index = index;
                                err->count = in;
                        }
-                       _in_configure_processors = false;
-                       return -1;
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, "}\n");
+                       return list<pair<ChanCount, ChanCount> > ();
                }
        }
 
-       // We can, so configure everything
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+       
+       return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ *  Caller must hold process lock.
+ *  Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       if (_in_configure_processors) {
+               return 0;
+       }
+
+       _in_configure_processors = true;
+
+       list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+       if (configuration.empty ()) {
+               _in_configure_processors = false;
+               return -1;
+       }
+
+       ChanCount out;
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       break;
+               }
+               
                (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
@@ -1478,10 +1604,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
           configuration */
-       {
-               Glib::Mutex::Lock em (_session.engine().process_lock ());
-               _session.ensure_buffers (n_process_buffers ());
-       }
+       _session.ensure_buffers (n_process_buffers ());
+
+       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
 
        _in_configure_processors = false;
        return 0;
@@ -1635,15 +1760,20 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
 
-               if (configure_processors_unlocked (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_streams = old_pms;
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               _processors = as_it_was_before;
+                               processor_max_streams = old_pms;
+                               return -1;
+                       }
                }
        }
 
         if (true) {
                 processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+               set_processor_positions ();
         }
 
        return 0;
@@ -1678,7 +1808,9 @@ Route::state(bool full_state)
        }
 
        node->add_property("active", _active?"yes":"no");
-       node->add_property("phase-invert", _phase_invert?"yes":"no");
+       string p;
+       boost::to_string (_phase_invert, p);
+       node->add_property("phase-invert", p);
        node->add_property("denormal-protection", _denormal_protection?"yes":"no");
        node->add_property("meter-point", enum_2_string (_meter_point));
 
@@ -1709,10 +1841,13 @@ Route::state(bool full_state)
        node->add_property ("soloed-by-upstream", buf);
        snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
        node->add_property ("soloed-by-downstream", buf);
+       node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
+       node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
+       node->add_child_nocopy (_mute_control->get_state ());
        node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
@@ -1725,6 +1860,8 @@ Route::state(bool full_state)
                cmt->add_content (_comment);
        }
 
+        node->add_child_nocopy (_pannable->state (full_state));
+
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                node->add_child_nocopy((*i)->state (full_state));
        }
@@ -1752,7 +1889,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       XMLPropertyList plist;
        const XMLProperty *prop;
 
        if (node.name() != "Route"){
@@ -1802,6 +1938,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
                }
+
+
+                if (child->name() == X_("Pannable")) {
+                        _pannable->set_state (*child, version);
+                }
        }
 
        set_processor_state (processor_state);
@@ -1824,8 +1965,12 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                set_solo_isolated (string_is_affirmative (prop->value()), this);
        }
 
+       if ((prop = node.property ("solo-safe")) != 0) {
+               set_solo_safe (string_is_affirmative (prop->value()), this);
+       }
+
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -1835,12 +1980,12 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
        if ((prop = node.property (X_("active"))) != 0) {
                bool yn = string_is_affirmative (prop->value());
                _active = !yn; // force switch
-               set_active (yn);
+               set_active (yn, this);
        }
 
        if ((prop = node.property (X_("meter-point"))) != 0) {
                MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
-                set_meter_point (mp);
+                set_meter_point (mp, true);
                if (_meter) {
                        _meter->set_display_to_user (_meter_point == MeterCustom);
                }
@@ -1848,7 +1993,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -1859,7 +2004,7 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                                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) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                              << endmsg;
                                } else {
@@ -1891,11 +2036,9 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
+               } else if (child->name() == Controllable::xml_node_name && (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")) {
@@ -1919,7 +2062,6 @@ 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:
@@ -1940,7 +2082,11 @@ Route::_set_state_2X (const XMLNode& node, int version)
        }
        
        if ((prop = node.property (X_("phase-invert"))) != 0) {
-               set_phase_invert (string_is_affirmative (prop->value()));
+               boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
+               if (string_is_affirmative (prop->value ())) {
+                       p.set ();
+               }                       
+               set_phase_invert (p);
        }
 
        if ((prop = node.property (X_("denormal-protection"))) != 0) {
@@ -2024,7 +2170,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
-               long n;
+               int32_t n;
 
                string::size_type colon, equal;
                string remaining = prop->value();
@@ -2035,7 +2181,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                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) {
+                               if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
                                        error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
                                                << endmsg;
                                } else {
@@ -2053,23 +2199,6 @@ Route::_set_state_2X (const XMLNode& node, int version)
                }
        }
 
-       /* add standard processors */
-
-       //_meter.reset (new PeakMeter (_session));
-       //add_processor (_meter, PreFader);
-
-       if (is_monitor()) {
-               /* where we listen to tracks */
-               _intreturn.reset (new InternalReturn (_session));
-               add_processor (_intreturn, PreFader);
-
-                _monitor_control.reset (new MonitorProcessor (_session));
-                add_processor (_monitor_control, PostFader);
-       }
-
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-       add_processor (_main_outs, PostFader);
-
        /* IOs */
 
        nlist = node.children ();
@@ -2097,7 +2226,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                        if ((prop = child->property (X_("active"))) != 0) {
                                bool yn = string_is_affirmative (prop->value());
                                _active = !yn; // force switch
-                               set_active (yn);
+                               set_active (yn, this);
                        }
                        
                        if ((prop = child->property (X_("gain"))) != 0) {
@@ -2119,7 +2248,10 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                io_child = *io_niter;
                                
                                if (io_child->name() == X_("Panner")) {
-                                       _main_outs->panner()->set_state(*io_child, version);
+                                       _main_outs->panner_shell()->set_state(*io_child, version);
+                               } else if (io_child->name() == X_("Automation")) {
+                                       /* IO's automation is for the fader */
+                                       _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
                                }
                        }
                }
@@ -2149,17 +2281,17 @@ Route::_set_state_2X (const XMLNode& node, int version)
                        XMLNode *cmt = *(child->children().begin());
                        _comment = cmt->content();
 
-               } else if (child->name() == X_("Extra")) {
+               } else if (child->name() == X_("extra")) {
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
-                       if (prop->value() == "solo") {
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == X_("solo")) {
                                _solo_control->set_state (*child, version);
-                               _session.add_controllable (_solo_control);
-                       }
-
+                       } else if (prop->value() == X_("mute")) {
+                               _mute_control->set_state (*child, version);
+                        }
+                                
                } else if (child->name() == X_("RemoteControl")) {
                        if ((prop = child->property (X_("id"))) != 0) {
                                int32_t x;
@@ -2194,7 +2326,7 @@ Route::set_processor_state_2X (XMLNodeList const & nList, int version)
        */
 
        for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
-               add_processor_from_xml_2X (**i, version, _processors.begin ());
+               add_processor_from_xml_2X (**i, version);
        }
 }
 
@@ -2253,7 +2385,7 @@ Route::set_processor_state (const XMLNode& node)
 
                                 if (prop->value() == "intsend") {
                                         
-                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+                                        processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
                                         
                                 } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                            prop->value() == "lv2" ||
@@ -2264,33 +2396,39 @@ Route::set_processor_state (const XMLNode& node)
                                         
                                 } else if (prop->value() == "port") {
                                         
-                                        processor.reset (new PortInsert (_session, _mute_master));
+                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                         
                                 } else if (prop->value() == "send") {
                                         
-                                        processor.reset (new Send (_session, _mute_master));
+                                        processor.reset (new Send (_session, _pannable, _mute_master));
                                         
                                 } else {
                                         error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
                                         continue;
                                 }
 
-                                processor->set_state (**niter, Stateful::current_state_version);
-                                new_order.push_back (processor);
-                                must_configure = true;
+                               if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+                                       /* This processor could not be configured.  Turn it into a UnknownProcessor */
+                                       processor.reset (new UnknownProcessor (_session, **niter));
+                               }
+                                       
+                               new_order.push_back (processor);
+                               must_configure = true;
                         }
                 }
         }
 
-        { 
+       {
                Glib::RWLock::WriterLock lm (_processor_lock);
                 _processors = new_order;
                 if (must_configure) {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                         configure_processors_unlocked (0);
                 }
         }
 
         processors_changed (RouteProcessorChange ());
+       set_processor_positions ();
 }
 
 void
@@ -2301,33 +2439,39 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
 {
-       if (!_silent) {
-
-               _output->silence (nframes);
-
-               {
-                       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return;
+       }
 
-                       if (lm.locked()) {
-                               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-                                       boost::shared_ptr<PluginInsert> pi;
+       silence_unlocked (nframes);
+}
 
-                                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
-                                               // skip plugins, they don't need anything when we're not active
-                                               continue;
-                                       }
+void
+Route::silence_unlocked (framecnt_t nframes)
+{
+       /* Must be called with the processor lock held */
+       
+       if (!_silent) {
 
-                                       (*i)->silence (nframes);
-                               }
+               _output->silence (nframes);
 
-                               if (nframes == _session.get_block_size()) {
-                                       // _silent = true;
-                               }
+               for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       boost::shared_ptr<PluginInsert> pi;
+                       
+                       if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
+                               // skip plugins, they don't need anything when we're not active
+                               continue;
                        }
+                       
+                       (*i)->silence (nframes);
+               }
+               
+               if (nframes == _session.get_block_size()) {
+                       // _silent = true;
                }
-
        }
 }
 
@@ -2411,11 +2555,11 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
                                 /* master never sends to control outs */
                                 return 0;
                         } else {
-                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                                listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
                         }
 
                 } else {
-                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+                        listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
                 }
 
        } catch (failed_constructor& err) {
@@ -2426,13 +2570,22 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
                _monitor_send = listener;
        }
 
-        if (placement == PostFader) {
-                /* put it *really* at the end, not just after the panner (main outs)
-                 */
-                add_processor (listener, _processors.end());
-        } else {
-                add_processor (listener, PreFader);
-        }
+
+       if (aux) {
+
+               add_processor (listener, placement);
+
+       } else {
+               
+               if (placement == PostFader) {
+                       /* put it *really* at the end, not just after the panner (main outs)
+                        */
+                       add_processor (listener, _processors.end());
+               } else {
+                       add_processor (listener, PreFader);
+               }
+               
+       }
 
        return 0;
 }
@@ -2564,16 +2717,11 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
        return false;
 }
 
+/** Called from the (non-realtime) butler thread when the transport is stopped */
 void
-Route::check_physical_connections ()
+Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
-        _physically_connected = _output->physically_connected ();
-}
-
-void
-Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
-{
-       nframes_t now = _session.transport_frame();
+       framepos_t now = _session.transport_frame();
 
        {
                Glib::RWLock::ReaderLock lm (_processor_lock);
@@ -2582,11 +2730,12 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
                        automation_snapshot (now, true);
                }
 
+                Automatable::transport_stopped (now);
+
                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)->flush ();
                        }
 
                        (*i)->transport_stopped (now);
@@ -2596,21 +2745,52 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
        _roll_delay = _initial_delay;
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
                configure_processors (0);
+               _phase_invert.resize (_input->n_ports().n_audio ());
+               io_changed (); /* EMIT SIGNAL */
        }
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       if ((change & ConfigurationChanged)) {
+       if ((change.type & IOChange::ConfigurationChanged)) {
 
                /* XXX resize all listeners to match _main_outs? */
 
+               /* Auto-connect newly-created outputs, unless we're auto-connecting to master
+                  and we are master (as an auto-connect in this situation would cause a
+                  feedback loop)
+               */
+               AutoConnectOption ac = Config->get_output_auto_connect ();
+               if (ac == AutoConnectPhysical || (ac == AutoConnectMaster && !is_master ())) {
+
+                       ChanCount start = change.before;
+                       
+                       for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+                               if (change.before.get(*i) < change.after.get(*i)) {
+                                       /* the existing ChanCounts don't matter for this call as they are only
+                                          to do with matching input and output indices, and we are only changing
+                                          outputs here.
+                                       */
+                                       ChanCount dummy;
+
+                                       /* only auto-connect the newly-created outputs, not the ones that were
+                                          already there
+                                       */
+                                       start.set (*i, start.get (*i) + 1);
+                                       
+                                       _session.auto_connect_route (this, dummy, dummy, false, ChanCount(), change.before);
+                               }
+                       }
+               }
+
                // configure_processors (0);
        }
 }
@@ -2626,15 +2806,20 @@ Route::pans_required () const
 }
 
 int
-Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
                bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
+       }
+
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
        if (!_active || n_inputs() == ChanCount::ZERO)  {
-               silence (nframes);
+               silence_unlocked (nframes);
                return 0;
        }
        if (session_state_changing) {
@@ -2644,7 +2829,7 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
                           
                           XXX note the absurdity of ::no_roll() being called when we ARE rolling!
                        */
-                       silence (nframes);
+                       silence_unlocked (nframes);
                        return 0;
                }
                /* we're really not rolling, so we're either delivery silence or actually
@@ -2658,20 +2843,20 @@ Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
        return 0;
 }
 
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
                _roll_delay -= nframes;
-               silence (nframes);
+               silence_unlocked (nframes);
                /* transport frame is not legal for caller to use */
                return 0;
 
        } else if (_roll_delay > 0) {
 
                nframes -= _roll_delay;
-               silence (_roll_delay);
+               silence_unlocked (_roll_delay);
                /* we've written _roll_delay of samples into the
                   output ports, so make a note of that for
                   future reference.
@@ -2686,29 +2871,26 @@ 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,
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
             bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
-       {
-               // automation snapshot can also be called from the non-rt context
-               // and it uses the processor list, so we try to acquire the lock here
-               Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-
-               if (lm.locked()) {
-                       automation_snapshot (_session.transport_frame(), false);
-               }
+       Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+       if (!lm.locked()) {
+               return 0;
        }
+       
+       automation_snapshot (_session.transport_frame(), false);
 
        if (n_outputs().n_total() == 0) {
                return 0;
        }
 
        if (!_active || n_inputs().n_total() == 0) {
-               silence (nframes);
+               silence_unlocked (nframes);
                return 0;
        }
 
-       nframes_t unused = 0;
+       framecnt_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2722,7 +2904,7 @@ 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*/,
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
                    bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
@@ -2775,17 +2957,16 @@ Route::flush_processors ()
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               (*i)->deactivate ();
-               (*i)->activate ();
+                (*i)->flush ();
        }
 }
 
 void
-Route::set_meter_point (MeterPoint p)
+Route::set_meter_point (MeterPoint p, bool force)
 {
        /* CAN BE CALLED FROM PROCESS CONTEXT */
 
-       if (_meter_point == p) {
+       if (_meter_point == p && !force) {
                return;
        }
 
@@ -2825,7 +3006,7 @@ Route::set_meter_point (MeterPoint p)
                        _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
@@ -2877,10 +3058,14 @@ Route::put_monitor_send_at (Placement p)
                
                _processors.insert (loc, _monitor_send);
 
-               if (configure_processors_unlocked (0)) {
-                       _processors = as_it_was;
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       return;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       
+                       if (configure_processors_unlocked (0)) {
+                               _processors = as_it_was;
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
                }
        }
 
@@ -2888,11 +3073,26 @@ Route::put_monitor_send_at (Placement p)
        _session.set_dirty ();
 }
 
-nframes_t
+boost::shared_ptr<CapturingProcessor>
+Route::add_export_point()
+{
+       // Check if it exists already
+       boost::shared_ptr<CapturingProcessor> processor;
+       if ((processor = boost::dynamic_pointer_cast<CapturingProcessor> (*_processors.begin()))) {
+               return processor;
+       }
+
+       // ...else add it
+       processor.reset (new CapturingProcessor (_session));
+       add_processor (processor, _processors.begin());
+       return processor;
+}
+
+framecnt_t
 Route::update_total_latency ()
 {
-       nframes_t old = _output->effective_latency();
-       nframes_t own_latency = _output->user_latency();
+       framecnt_t old = _output->effective_latency();
+       framecnt_t own_latency = _output->user_latency();
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
@@ -2927,16 +3127,16 @@ Route::update_total_latency ()
 }
 
 void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
 {
        _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
 void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_delay (framecnt_t longest_session_latency)
 {
-       nframes_t old = _initial_delay;
+       framecnt_t old = _initial_delay;
 
        if (_output->effective_latency() < longest_session_latency) {
                _initial_delay = longest_session_latency - _output->effective_latency();
@@ -2954,10 +3154,9 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 }
 
 void
-Route::automation_snapshot (nframes_t now, bool force)
+Route::automation_snapshot (framepos_t now, bool force)
 {
-       panner()->automation_snapshot (now, force);
-       
+        _pannable->automation_snapshot (now, force);
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
@@ -2973,7 +3172,7 @@ Route::SoloControllable::SoloControllable (std::string name, Route& r)
 }
 
 void
-Route::SoloControllable::set_value (float val)
+Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
 # if 0
@@ -2992,7 +3191,7 @@ Route::SoloControllable::set_value (float val)
 #endif
 }
 
-float
+double
 Route::SoloControllable::get_value (void) const
 {
        if (Config->get_solo_control_is_listen_control()) {
@@ -3012,7 +3211,7 @@ Route::MuteControllable::MuteControllable (std::string name, Route& r)
 }
 
 void
-Route::MuteControllable::set_value (float val)
+Route::MuteControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
 # if 0
@@ -3026,14 +3225,14 @@ Route::MuteControllable::set_value (float val)
 #endif
 }
 
-float
+double
 Route::MuteControllable::get_value (void) const
 {
        return route.muted() ? 1.0f : 0.0f;
 }
 
 void
-Route::set_block_size (nframes_t nframes)
+Route::set_block_size (pframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
@@ -3072,44 +3271,53 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
+Route::shift (framepos_t pos, framecnt_t frames)
 {
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
        /* gain automation */
-       XMLNode &before = _gain_control->get_state ();
-       _gain_control->shift (pos, frames);
-       XMLNode &after = _gain_control->get_state ();
-       _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+        {
+                boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
+                
+                XMLNode &before = gc->alist()->get_state ();
+                gc->alist()->shift (pos, frames);
+                XMLNode &after = gc->alist()->get_state ();
+                _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+        }
 
        /* pan automation */
-       for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
-               Curve & c = (*i)->automation ();
-               XMLNode &before = c.get_state ();
-               c.shift (pos, frames);
-               XMLNode &after = c.get_state ();
-               _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
-       }
+        {
+                ControlSet::Controls& c (_pannable->controls());
+                
+                for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+                        boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
+                        if (pc) {
+                                boost::shared_ptr<AutomationList> al = pc->alist();
+                                XMLNode& before = al->get_state ();
+                                al->shift (pos, frames);
+                                XMLNode& after = al->get_state ();
+                                _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                        }
+                }
+        }
 
        /* redirect automation */
        {
-               Glib::RWLock::ReaderLock lm (redirect_lock);
-               for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-
-                       set<uint32_t> a;
-                       (*i)->what_has_automation (a);
-
-                       for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
-                               AutomationList & al = (*i)->automation_list (*j);
-                               XMLNode &before = al.get_state ();
-                               al.shift (pos, frames);
-                               XMLNode &after = al.get_state ();
-                               _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
-                       }
-               }
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+                       set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
+
+                       for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+                                boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+                                if (ac) {
+                                        boost::shared_ptr<AutomationList> al = ac->alist();
+                                        XMLNode &before = al->get_state ();
+                                        al->shift (pos, frames);
+                                        XMLNode &after = al->get_state ();
+                                        _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                                }
+                        }
+                }
        }
-#endif
-
 }
 
 
@@ -3178,25 +3386,39 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const
        return boost::shared_ptr<Send>();
 }
 
+/** @param c Audio channel index.
+ *  @param yn true to invert phase, otherwise false.
+ */
 void
-Route::set_phase_invert (bool yn)
+Route::set_phase_invert (uint32_t c, bool yn)
 {
-       if (_phase_invert != yn) {
-                if (yn) {
-                        _phase_invert = 0xffff; // XXX all channels
-                } else {
-                        _phase_invert = 0; // XXX no channels
-                }
+       if (_phase_invert[c] != yn) {
+               _phase_invert[c] = yn;
+               phase_invert_changed (); /* EMIT SIGNAL */
+                _session.set_dirty ();
+       }
+}
 
+void
+Route::set_phase_invert (boost::dynamic_bitset<> p)
+{
+       if (_phase_invert != p) {
+               _phase_invert = p;
                phase_invert_changed (); /* EMIT SIGNAL */
                 _session.set_dirty ();
        }
 }
 
 bool
+Route::phase_invert (uint32_t c) const
+{
+       return _phase_invert[c];
+}
+
+boost::dynamic_bitset<>
 Route::phase_invert () const
 {
-       return _phase_invert != 0;
+       return _phase_invert;
 }
 
 void
@@ -3215,8 +3437,13 @@ Route::denormal_protection () const
 }
 
 void
-Route::set_active (bool yn)
+Route::set_active (bool yn, void* src)
 {
+       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
+               _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
+               return;
+       }
+       
        if (_active != yn) {
                _active = yn;
                _input->set_active (yn);
@@ -3247,10 +3474,23 @@ Route::meter ()
        }
 }
 
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+       return _pannable;
+}
+
 boost::shared_ptr<Panner>
 Route::panner() const
 {
-       return _main_outs->panner();
+        /* may be null ! */
+       return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+       return _main_outs->panner_shell();
 }
 
 boost::shared_ptr<AutomationControl>
@@ -3341,8 +3581,53 @@ Route::has_io_processor_named (const string& name)
         return false;
 }
 
+MuteMaster::MutePoint
+Route::mute_points () const
+{
+       return _mute_master->mute_points ();
+}
+
 void
-Route::set_graph_level (int32_t l)
+Route::set_processor_positions ()
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+
+       bool had_amp = false;
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->set_pre_fader (!had_amp);
+               if (boost::dynamic_pointer_cast<Amp> (*i)) {
+                       had_amp = true;
+               }
+       }
+}
+
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+       list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+       if (c.empty()) {
+               /* The processors cannot be configured with the new input arrangement, so
+                  block the change.
+               */
+               return true;
+       }
+
+       /* The change is ok */
+       return false;
+}
+
+list<string>
+Route::unknown_processors () const
 {
-        _graph_level = l;
+       list<string> p;
+       
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+                       p.push_back ((*i)->name ());
+               }
+       }
+
+       return p;
 }