restore excess calls to sync-order stuff (for now); allow MIDI controllers to use...
[ardour.git] / libs / ardour / route.cc
index 862dbdece73d483d5f135df77acfd257b579f701..c5d26f7b7c8e537aac31cedcd7c2c22769542b7d 100644 (file)
 #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 "evoral/Curve.hpp"
 
@@ -37,6 +36,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,7 +64,8 @@ using namespace ARDOUR;
 using namespace PBD;
 
 uint32_t Route::order_key_cnt = 0;
-sigc::signal<void, string const &> 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)
@@ -80,7 +81,8 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        /* 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 */
@@ -93,7 +95,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 
        /* 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)
@@ -105,21 +107,22 @@ Route::Route (Session& sess, const XMLNode& node, DataType 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[N_("signal")] = order_key_cnt++;
        _silent = false;
@@ -131,8 +134,7 @@ 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;
@@ -147,8 +149,8 @@ Route::init ()
        _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,18 +160,28 @@ Route::init ()
 
 Route::~Route ()
 {
-       Metering::disconnect (_meter_connection);
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
+
+       /* 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 ();
+       }
 
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       _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 ();
+               }
        }
 }
 
@@ -420,13 +432,15 @@ Route::process_output_buffers (BufferSet& bufs,
 
        if (rm.locked()) {
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
                        if (bufs.count() != (*i)->input_streams()) {
                                cerr << _name << " bufs = " << bufs.count()
                                     << " input for " << (*i)->name() << " = " << (*i)->input_streams()
                                     << endl;
                        }
                        assert (bufs.count() == (*i)->input_streams());
-                       (*i)->run (bufs, start_frame, end_frame, nframes);
+
+                       (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
                        bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
                }
 
@@ -487,6 +501,7 @@ void
 Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_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);
 }
@@ -517,10 +532,25 @@ Route::listening () const
        }
 }
 
+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;
        }
 
@@ -529,50 +559,95 @@ 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::mod_solo_level (int32_t delta)
+Route::set_self_solo (bool yn)
+{
+       _self_solo = yn;
+}
+
+void
+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 main outs what the solo situation is
-        */
+       set_delivery_solo ();
+}
+
+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.
+       */
 
-       _main_outs->set_solo_level (_solo_level);
-       _main_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());
+               }
+       }
 }
 
 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;
+       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);
+               
+               if (does_feed && !sends_only) {
+                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+               }
+       }
 
-               /* tell main outs what the solo situation is
-                */
+       bool changed = false;
 
-               _main_outs->set_solo_level (_solo_level);
-               _main_outs->set_solo_isolated (_solo_isolated);
+       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);
        }
 }
@@ -580,7 +655,19 @@ Route::set_solo_isolated (bool yn, void *src)
 bool
 Route::solo_isolated () const
 {
-       return _solo_isolated;
+       return _solo_isolated > 0;
+}
+
+void
+Route::set_mute_points (MuteMaster::MutePoint mp)
+{
+       _mute_points = mp;
+       mute_points_changed (); /* EMIT SIGNAL */
+
+       if (_mute_master->muted()) {
+               _mute_master->mute_at (_mute_points);
+               mute_changed (this); /* EMIT SIGNAL */
+       }
 }
 
 void
@@ -592,8 +679,13 @@ 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 */
        }
 }
 
@@ -683,7 +775,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;
                        --ploc;
@@ -706,12 +797,13 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        // 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));
+
+               processor->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;
 }
@@ -748,7 +840,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                        } else if (prop->value() == "meter") {
 
                                if (_meter) {
-                                       if (_meter->set_state (node)) {
+                                       if (_meter->set_state (node, Stateful::loading_state_version)) {
                                                return false;
                                        } else {
                                                return true;
@@ -756,6 +848,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                                }
 
                                _meter.reset (new PeakMeter (_session, node));
+                               _meter->set_display_to_user (_meter_point == MeterCustom);
                                processor = _meter;
 
                        } else if (prop->value() == "amp") {
@@ -763,7 +856,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                                /* amp always exists */
 
                                processor = _amp;
-                               if (processor->set_state (node)) {
+                               if (processor->set_state (node, Stateful::loading_state_version)) {
                                        return false;
                                } else {
                                        /* never any reason to add it */
@@ -777,7 +870,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                        } else if (prop->value() == "intreturn") {
 
                                if (_intreturn) {
-                                       if (_intreturn->set_state (node)) {
+                                       if (_intreturn->set_state (node, Stateful::loading_state_version)) {
                                                return false;
                                        } else {
                                                return true;
@@ -789,7 +882,7 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                        } else if (prop->value() == "main-outs") {
 
                                if (_main_outs) {
-                                       if (_main_outs->set_state (node)) {
+                                       if (_main_outs->set_state (node, Stateful::loading_state_version)) {
                                                return false;
                                        } else {
                                                return true;
@@ -804,12 +897,12 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
                                return false;
                        }
 
-                       if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+                       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)->visible() && p != _processors.begin()) {
+                               while (!(*p)->display_to_user() && p != _processors.begin()) {
                                        --p;
                                }
                                ++p;
@@ -830,6 +923,64 @@ Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter
        }
 }
 
+
+bool
+Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
+{
+       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));
+                               }
+
+                       }
+
+               } 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;
+                       while (!(*p)->display_to_user() && p != _processors.begin()) {
+                               --p;
+                       }
+                       ++p;
+                       iter = p;
+               }
+
+               return (add_processor (processor, iter) == 0);
+       }
+
+       catch (failed_constructor &err) {
+               warning << _("processor could not be created. Ignored.") << endmsg;
+               return false;
+       }
+}
+
 int
 Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
@@ -865,8 +1016,6 @@ 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());
 
@@ -892,22 +1041,25 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                }
                        }
 
-                       _processors.insert (iter, *i);
+                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+                       if ((*i)->active()) {
+                               (*i)->activate ();
+                       }
 
                        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;
 }
@@ -1107,7 +1259,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();
@@ -1198,7 +1350,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
        }
 
        processor->drop_references ();
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1290,7 +1442,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                (*i)->drop_references ();
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1324,8 +1476,19 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        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 {
@@ -1347,11 +1510,14 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                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;
 }
@@ -1440,7 +1606,7 @@ int
 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.
        */
@@ -1479,7 +1645,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                        } else {
 
-                               if (!(*oiter)->visible()) {
+                               if (!(*oiter)->display_to_user()) {
 
                                        as_it_will_be.push_back (*oiter);
 
@@ -1511,7 +1677,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                }
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1571,6 +1737,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));
@@ -1599,14 +1768,17 @@ Route::state(bool full_state)
 }
 
 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;
@@ -1648,9 +1820,9 @@ Route::_set_state (const XMLNode& node, bool /*call_base*/)
                        }
 
                        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);
                        }
                }
 
@@ -1661,9 +1833,13 @@ Route::_set_state (const XMLNode& node, bool /*call_base*/)
 
        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) {
@@ -1684,6 +1860,167 @@ Route::_set_state (const XMLNode& node, bool /*call_base*/)
                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 = string_is_affirmative (prop->value());
 
@@ -1696,14 +2033,9 @@ Route::_set_state (const XMLNode& node, bool /*call_base*/)
                _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) {
 
@@ -1716,11 +2048,11 @@ 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), n);
                                }
@@ -1736,6 +2068,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;
 
@@ -1753,7 +2099,7 @@ Route::_set_state (const XMLNode& node, bool /*call_base*/)
                } 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);
                        }
 
@@ -1764,9 +2110,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;
@@ -1783,6 +2127,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)
 {
@@ -1874,7 +2232,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);
                }
        }
 
@@ -1882,7 +2240,7 @@ Route::set_processor_state (const XMLNode& node)
           the XML state represents a working signal route.
        */
 
-       processors_changed ();
+       processors_changed (RouteProcessorChange ());
 }
 
 void
@@ -2045,33 +2403,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)
 {
@@ -2081,30 +2412,41 @@ 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;
 }
 
@@ -2307,30 +2649,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;
-               }
-               _processors.insert(loc, _meter);
+       if (_meter_point == p) {
+               return;
+       }
+
+       bool meter_was_visible_to_user = _meter->display_to_user ();
 
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
+       {
+               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);
+               }
        }
+
+       _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)
 {
@@ -2338,27 +2723,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 ();
 }
 
@@ -2374,10 +2769,7 @@ 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);
 
@@ -2398,10 +2790,7 @@ Route::update_total_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 ();
 }
@@ -2461,7 +2850,7 @@ Route::SoloControllable::set_value (float val)
 float
 Route::SoloControllable::get_value (void) const
 {
-       return route.soloed() ? 1.0f : 0.0f;
+       return route.self_soloed() ? 1.0f : 0.0f;
 }
 
 void
@@ -2470,7 +2859,8 @@ 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