restore excess calls to sync-order stuff (for now); allow MIDI controllers to use...
[ardour.git] / libs / ardour / route.cc
index 009adce9c8311df5bd73f1f4999847a461960e8b..c5d26f7b7c8e537aac31cedcd7c2c22769542b7d 100644 (file)
 #include <cassert>
 #include <algorithm>
 
-#include <sigc++/bind.h>
 #include "pbd/xml++.h"
 #include "pbd/enumwriter.h"
 #include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
 
 #include "evoral/Curve.hpp"
 
@@ -36,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"
@@ -63,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)
@@ -79,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 */
@@ -92,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)
@@ -108,17 +111,18 @@ Route::Route (Session& sess, const XMLNode& node, 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)));
 }
 
 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;
@@ -130,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;
@@ -146,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  */
 
@@ -157,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 */
 
-       clear_processors (PreFader);
-       clear_processors (PostFader);
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->drop_references ();
+       }
+
+       _processors.clear ();
 }
 
 void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
+               if (notify_class_listeners) {
+                       RemoteControlIDChange ();
+               }
        }
 }
 
@@ -419,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()));
                }
 
@@ -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 */
        }
 }
 
@@ -705,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;
 }
@@ -755,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") {
@@ -803,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;
@@ -866,12 +960,12 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorLis
                        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;
@@ -922,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());
 
@@ -949,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;
 }
@@ -1164,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();
@@ -1255,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;
 }
@@ -1347,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;
 }
@@ -1381,9 +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 {
@@ -1396,15 +1501,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
-       /* Take the process lock so that if we add a processor which increases the required
-          number of scratch buffers, we create those scratch buffers before the process
-          thread has a chance to ask for them.
-          XXX: in an ideal world we'd perhaps use some RCU magic to avoid having to take
-          the lock here.
-       */
-       
-       Glib::Mutex::Lock pl (_session.engine().process_lock ());
-       
        // We can, so configure everything
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
@@ -1414,6 +1510,10 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                out = c->second;
        }
 
+       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 ());
@@ -1506,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.
        */
@@ -1545,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);
 
@@ -1577,7 +1677,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                }
        }
 
-       processors_changed (); /* EMIT SIGNAL */
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
 
        return 0;
 }
@@ -1637,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));
@@ -1730,9 +1833,13 @@ Route::_set_state (const XMLNode& node, int version, 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) {
@@ -1753,24 +1860,10 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                set_active (yn);
        }
 
-       if ((prop = node.property (X_("soloed"))) != 0) {
-               bool yn = string_is_affirmative (prop->value());
-
-               /* XXX force reset of solo status */
-
-               set_solo (yn, this);
-       }
-
        if ((prop = node.property (X_("meter-point"))) != 0) {
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
-       }
-
-       if ((prop = node.property (X_("route-group"))) != 0) {
-               RouteGroup* route_group = _session.route_group_by_name(prop->value());
-               if (route_group == 0) {
-                       error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
-               } else {
-                       set_route_group (route_group, this);
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
                }
        }
 
@@ -1940,26 +2033,9 @@ Route::_set_state_2X (const XMLNode& node, int version)
                _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
        }
 
-       /* XXX: if the route was in both a mix group and an edit group, it'll end up
-          just in the edit group. */
-
-       if ((prop = node.property (X_("mix-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);
-               }
-       }
-
-       if ((prop = node.property (X_("edit-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) {
 
@@ -2164,7 +2240,7 @@ Route::set_processor_state (const XMLNode& node)
           the XML state represents a working signal route.
        */
 
-       processors_changed ();
+       processors_changed (RouteProcessorChange ());
 }
 
 void
@@ -2327,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)
 {
@@ -2365,11 +2414,10 @@ Route::set_comment (string cmt, void *src)
 bool
 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;
                }
@@ -2384,18 +2432,21 @@ Route::feeds (boost::shared_ptr<Route> other, bool* only_send)
 
                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;
 }
 
@@ -2598,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;
+       }
 
-                meter_change (src); /* EMIT SIGNAL */
-               processors_changed (); /* EMIT SIGNAL */
-               _session.set_dirty ();
+       bool meter_was_visible_to_user = _meter->display_to_user ();
+
+       {
+               Glib::RWLock::WriterLock lm (_processor_lock);
+       
+               if (p != MeterCustom) {
+                       // Move meter in the processors list to reflect the new position
+                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+                       _processors.erase(loc);
+                       switch (p) {
+                       case MeterInput:
+                               loc = _processors.begin();
+                               break;
+                       case MeterPreFader:
+                               loc = find (_processors.begin(), _processors.end(), _amp);
+                               break;
+                       case MeterPostFader:
+                               loc = _processors.end();
+                               break;
+                       default:
+                               break;
+                       }
+                       
+                       ChanCount m_in;
+                       
+                       if (loc == _processors.begin()) {
+                               m_in = _input->n_ports();
+                       } else {
+                               ProcessorList::iterator before = loc;
+                               --before;
+                               m_in = (*before)->output_streams ();
+                       }
+                       
+                       _meter->reflect_inputs (m_in);
+                       
+                       _processors.insert (loc, _meter);
+                       
+                       /* we do not need to reconfigure the processors, because the meter
+                          (a) is always ready to handle processor_max_streams
+                          (b) is always an N-in/N-out processor, and thus moving
+                          it doesn't require any changes to the other processors.
+                       */
+                       
+                       _meter->set_display_to_user (false);
+                       
+               } else {
+                       
+                       // just make it visible and let the user move it
+                       
+                       _meter->set_display_to_user (true);
+               }
        }
+
+       _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)
 {
@@ -2629,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 ();
 }
 
@@ -2665,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);
 
@@ -2689,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 ();
 }
@@ -2752,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