OMNIBUS COMMIT: prefer const XMLNode::property method (and provide a real one)
[ardour.git] / libs / ardour / route.cc
index c0cbcf21dcf472547530ca10792db86aec4810d3..5466cb5a843a58146bfe926f6b7fc065538d8d7a 100644 (file)
@@ -79,6 +79,7 @@ using namespace PBD;
 PBD::Signal0<void> Route::SyncOrderKeys;
 PBD::Signal0<void> Route::RemoteControlIDChange;
 
+/** Base class for all routable/mixable objects (tracks and busses) */
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , Automatable (sess)
@@ -115,6 +116,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
+       , _in_sidechain_setup (false)
        , _strict_io (false)
        , _custom_meter_position_noted (false)
 {
@@ -1308,9 +1310,9 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
                // configure redirect ports properly, etc.
 
                {
-                       if (configure_processors_unlocked (err)) {
+                       if (configure_processors_unlocked (err, &lm)) {
                                pstate.restore ();
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                                return -1;
                        }
                }
@@ -1337,13 +1339,30 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 
+       boost::shared_ptr<Send> send;
+       if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
+               send->SelfDestruct.connect_same_thread (*this,
+                               boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+       }
+
        return 0;
 }
 
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
+{
+       /* We cannot destruct the processor here (usually RT-thread
+        * with various locks held - in case of sends also io_locks).
+        * Queue for deletion in low-priority thread.
+        */
+       Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+       selfdestruct_sequence.push_back (wp);
+}
+
 bool
 Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        try {
                boost::shared_ptr<Processor> processor;
@@ -1464,7 +1483,6 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
-                               pi->set_count (1); // why? configure_processors_unlocked() will re-do this
                                pi->set_strict_io (_strict_io);
                        }
 
@@ -1477,9 +1495,9 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
 
                        /* Think: does this really need to be called for every processor in the loop? */
                        {
-                               if (configure_processors_unlocked (err)) {
+                               if (configure_processors_unlocked (err, &lm)) {
                                        pstate.restore ();
-                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                                        return -1;
                                }
                        }
@@ -1697,7 +1715,7 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+               configure_processors_unlocked (&err, &lm); // this can't fail
        }
 
        processor_max_streams.reset();
@@ -1769,9 +1787,15 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                                   run.
                                */
 
-                               boost::shared_ptr<IOProcessor> iop;
+                               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
+                               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
 
-                               if ((iop = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+                               if (pi != 0) {
+                                       assert (iop == 0);
+                                       iop = pi->sidechain();
+                               }
+
+                               if (iop != 0) {
                                        iop->disconnect ();
                                }
 
@@ -1791,10 +1815,10 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
 
@@ -1888,9 +1912,9 @@ Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Pr
                        }
                }
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
 
@@ -1963,9 +1987,14 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                           run.
                        */
 
-                       boost::shared_ptr<IOProcessor> iop;
+                       boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(processor);
+                       boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+                       if (pi != 0) {
+                               assert (iop == 0);
+                               iop = pi->sidechain();
+                       }
 
-                       if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+                       if (iop != 0) {
                                iop->disconnect ();
                        }
 
@@ -1980,10 +2009,10 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _output->set_user_latency (0);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
                //lx.unlock();
@@ -2034,7 +2063,7 @@ Route::configure_processors (ProcessorStreams* err)
 
        if (!_in_configure_processors) {
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               return configure_processors_unlocked (err);
+               return configure_processors_unlocked (err, &lm);
        }
 
        return 0;
@@ -2068,6 +2097,32 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
                if ((*p)->can_support_io_configuration(in, out)) {
+
+                       if (boost::dynamic_pointer_cast<Delivery> (*p)
+                                       && boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
+                                       && !(is_monitor() || is_auditioner())
+                                       && ( _strict_io || Profile->get_mixbus ())) {
+                               /* with strict I/O the panner + output are forced to
+                                * follow the last processor's output.
+                                *
+                                * Delivery::can_support_io_configuration() will only add ports,
+                                * but not remove excess ports.
+                                *
+                                * This works because the delivery only requires
+                                * as many outputs as there are inputs.
+                                * Delivery::configure_io() will do the actual removal
+                                * by calling _output->ensure_io()
+                                */
+                               if (!is_master() && _session.master_out ()) {
+                                       /* ..but at least as many as there are master-inputs */
+                                       // XXX this may need special-casing for mixbus (master-outputs)
+                                       // and should maybe be a preference anyway ?!
+                                       out = ChanCount::max (in, _session.master_out ()->n_inputs ());
+                               } else {
+                                       out = in;
+                               }
+                       }
+
                        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));
 
@@ -2124,7 +2179,7 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
  *  Return 0 on success, otherwise configuration is impossible.
  */
 int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLock::WriterLock* lm)
 {
 #ifndef PLATFORM_WINDOWS
        assert (!AudioEngine::instance()->process_lock().trylock());
@@ -2151,27 +2206,54 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        processor_out_streams = _input->n_ports();
        processor_max_streams.reset();
 
+       /* processor configure_io() may result in adding ports
+        * e.g. Delivery::configure_io -> ARDOUR::IO::ensure_io ()
+        *
+        * with jack2 adding ports results in a graph-order callback,
+        * which calls Session::resort_routes() and eventually
+        * Route::direct_feeds_according_to_reality()
+        * which takes a ReaderLock (_processor_lock).
+        *
+        * so we can't hold a WriterLock here until jack2 threading
+        * is fixed.
+        *
+        * NB. we still hold the process lock
+        *
+        * (ardour's own engines do call graph-order from the
+        * process-thread and hence do not have this issue; besides
+        * merely adding ports won't trigger a graph-order, only
+        * making connections does)
+        */
+       lm->release ();
+
+       // TODO check for a potential ReaderLock after ReaderLock ??
+       Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
                if (!(*p)->configure_io(c->first, c->second)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
                        _in_configure_processors = false;
+                       lr.release ();
+                       lm->acquire ();
                        return -1;
                }
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
 
+               boost::shared_ptr<IOProcessor> iop;
                boost::shared_ptr<PluginInsert> pi;
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
                        /* plugins connected via Split or Hide Match may have more channels.
                         * route/scratch buffers are needed for all of them
                         * The configuration may only be a subset (both input and output)
                         */
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->output_streams());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams() * pi->get_count());
-                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_output_streams() * pi->get_count());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->required_buffers());
+               }
+               else if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*p)) != 0) {
+                       processor_max_streams = ChanCount::max(processor_max_streams, iop->natural_input_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, iop->natural_output_streams());
                }
                out = c->second;
 
@@ -2188,6 +2270,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
+       lr.release ();
+       lm->acquire ();
+
 
        if (_meter) {
                _meter->set_max_channels (processor_max_streams);
@@ -2381,7 +2466,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                apply_processor_order (new_order);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        return -1;
                }
@@ -2404,15 +2489,116 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
        return 0;
 }
 
+bool
+Route::add_remove_sidechain (boost::shared_ptr<Processor> proc, bool add)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       if (pi->has_sidechain () == add) {
+               return true; // ?? call failed, but result is as expected.
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); // take before Writerlock to avoid deadlock
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               PBD::Unwinder<bool> uw (_in_sidechain_setup, true);
+
+               lx.release (); // IO::add_port() and ~IO takes process lock  - XXX check if this is safe
+               if (add) {
+                       if (!pi->add_sidechain ()) {
+                               return false;
+                       }
+               } else {
+                       if (!pi->del_sidechain ()) {
+                               return false;
+                       }
+               }
+
+               lx.acquire ();
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               lx.release ();
+
+               if (c.empty()) {
+                       if (add) {
+                               pi->del_sidechain ();
+                       } else {
+                               pi->add_sidechain ();
+                               // TODO restore side-chain's state.
+                       }
+                       return false;
+               }
+               lx.acquire ();
+               configure_processors_unlocked (0, &lm);
+       }
+
+       if (pi->has_sidechain ()) {
+               pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
+bool
+Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
+{
+       boost::shared_ptr<PluginInsert> pi;
+       if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+               return false;
+       }
+
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
+                       return false;
+               }
+       }
+
+       {
+               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+
+               const ChanCount& old (pi->preset_out ());
+               if (!pi->set_preset_out (outs)) {
+                       return true; // no change, OK
+               }
+
+               list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
+               if (c.empty()) {
+                       /* not possible */
+                       pi->set_preset_out (old);
+                       return false;
+               }
+               configure_processors_unlocked (0, &lm);
+       }
+
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       _session.set_dirty ();
+       return true;
+}
+
 bool
 Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
 {
        ChanCount unused;
-       return customize_plugin_insert (proc, 0, unused);
+       return customize_plugin_insert (proc, 0, unused, unused);
 }
 
 bool
-Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
 {
        boost::shared_ptr<PluginInsert> pi;
        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
@@ -2420,15 +2606,9 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
        }
 
        {
-               bool found = false;
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-               for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
-                       if (*p == proc) {
-                               found = true;
-                               break;
-                       }
-               }
-               if (!found) {
+               ProcessorList::iterator i = find (_processors.begin(), _processors.end(), proc);
+               if (i == _processors.end ()) {
                        return false;
                }
        }
@@ -2436,11 +2616,11 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               ProcessorState pstate (this);
 
-               bool      old_cust = pi->custom_cfg ();
-               uint32_t  old_cnt  = pi->get_count ();
-               ChanCount old_chan = pi->output_streams ();
+               bool      old_cust  = pi->custom_cfg ();
+               uint32_t  old_cnt   = pi->get_count ();
+               ChanCount old_chan  = pi->output_streams ();
+               ChanCount old_sinks = pi->natural_input_streams ();
 
                if (count == 0) {
                        pi->set_custom_cfg (false);
@@ -2448,6 +2628,7 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        pi->set_custom_cfg (true);
                        pi->set_count (count);
                        pi->set_outputs (outs);
+                       pi->set_sinks (sinks);
                }
 
                list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
@@ -2455,12 +2636,13 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        /* not possible */
 
                        pi->set_count (old_cnt);
+                       pi->set_sinks (old_sinks);
                        pi->set_outputs (old_chan);
                        pi->set_custom_cfg (old_cust);
 
                        return false;
                }
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lm);
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
@@ -2471,6 +2653,8 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
 bool
 Route::set_strict_io (const bool enable)
 {
+       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+
        if (_strict_io != enable) {
                _strict_io = enable;
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
@@ -2496,10 +2680,9 @@ Route::set_strict_io (const bool enable)
                }
                lm.release ();
 
-               {
-                       Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-                       configure_processors (0);
-               }
+               configure_processors (0);
+               lx.release ();
+
                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
@@ -2638,7 +2821,7 @@ Route::set_state (const XMLNode& node, int version)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        if (node.name() != "Route"){
                error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
@@ -2861,7 +3044,7 @@ Route::set_state_2X (const XMLNode& node, int version)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        /* 2X things which still remain to be handled:
         * default-type
@@ -3147,7 +3330,7 @@ Route::set_processor_state (const XMLNode& node)
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-               XMLProperty* prop = (*niter)->property ("type");
+               XMLProperty const * prop = (*niter)->property ("type");
 
                if (prop->value() == "amp") {
                        _amp->set_state (**niter, Stateful::current_state_version);
@@ -3184,7 +3367,7 @@ Route::set_processor_state (const XMLNode& node)
                        ProcessorList::iterator o;
 
                        for (o = _processors.begin(); o != _processors.end(); ++o) {
-                               XMLProperty* id_prop = (*niter)->property(X_("id"));
+                               XMLProperty const * id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
                                        (*o)->set_state (**niter, Stateful::current_state_version);
                                        new_order.push_back (*o);
@@ -3227,6 +3410,9 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "send") {
 
                                        processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
+                                       boost::shared_ptr<Send> send = boost::dynamic_pointer_cast<Send> (processor);
+                                       send->SelfDestruct.connect_same_thread (*this,
+                                                       boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
 
                                } else {
                                        error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -3238,6 +3424,12 @@ Route::set_processor_state (const XMLNode& node)
                                        processor.reset (new UnknownProcessor (_session, **niter));
                                }
 
+                               /* subscribe to Sidechain IO changes */
+                               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
+                               if (pi && pi->has_sidechain ()) {
+                                       pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+                               }
+
                                /* we have to note the monitor send here, otherwise a new one will be created
                                   and the state of this one will be lost.
                                */
@@ -3262,7 +3454,7 @@ Route::set_processor_state (const XMLNode& node)
                _processors = new_order;
 
                if (must_configure) {
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -3527,12 +3719,53 @@ Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
        return false;
 }
 
+IOVector
+Route::all_inputs () const
+{
+       /* TODO, if this works as expected,
+        * cache the IOVector and maintain it via
+        * input_change_handler(), sidechain_change_handler() etc
+        */
+       IOVector ios;
+       ios.push_back (_input);
+
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator r = _processors.begin(); r != _processors.end(); ++r) {
+
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*r);
+               if (pi != 0) {
+                       assert (iop == 0);
+                       iop = pi->sidechain();
+               }
+
+               if (iop != 0 && iop->input()) {
+                       ios.push_back (iop->input());
+               }
+       }
+       return ios;
+}
+
+IOVector
+Route::all_outputs () const
+{
+       IOVector ios;
+       // _output is included via Delivery
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator r = _processors.begin(); r != _processors.end(); ++r) {
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               if (iop != 0 && iop->output()) {
+                       ios.push_back (iop->output());
+               }
+       }
+       return ios;
+}
+
 bool
 Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool* via_send_only)
 {
        DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
-
-       if (_output->connected_to (other->input())) {
+       if (other->all_inputs().fed_by (_output)) {
                DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
                if (via_send_only) {
                        *via_send_only = false;
@@ -3541,13 +3774,20 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool*
                return true;
        }
 
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
-               boost::shared_ptr<IOProcessor> iop;
+               boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*r);
+               if (pi != 0) {
+                       assert (iop == 0);
+                       iop = pi->sidechain();
+               }
 
-               if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
-                       if (iop->feeds (other)) {
+               if (iop != 0) {
+                       boost::shared_ptr<const IO> iop_out = iop->output();
+                       if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) {
                                DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
                                if (via_send_only) {
                                        *via_send_only = true;
@@ -3572,6 +3812,12 @@ Route::direct_feeds_according_to_graph (boost::shared_ptr<Route> other, bool* vi
        return _session._current_route_graph.has (shared_from_this (), other, via_send_only);
 }
 
+bool
+Route::feeds_according_to_graph (boost::shared_ptr<Route> other)
+{
+       return _session._current_route_graph.feeds (shared_from_this (), other);
+}
+
 /** Called from the (non-realtime) butler thread when the transport is stopped */
 void
 Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_locate*/, bool can_flush_processors)
@@ -3741,6 +3987,16 @@ Route::output_change_handler (IOChange change, void * /*src*/)
        }
 }
 
+void
+Route::sidechain_change_handler (IOChange change, void* src)
+{
+       if (_initial_io_setup || _in_sidechain_setup) {
+               return;
+       }
+
+       input_change_handler (change, src);
+}
+
 uint32_t
 Route::pans_required () const
 {
@@ -3898,13 +4154,12 @@ Route::apply_processor_changes_rt ()
                g_atomic_int_set (&_pending_signals, emissions);
                return true;
        }
-       return false;
+       return (!selfdestruct_sequence.empty ());
 }
 
 void
 Route::emit_pending_signals ()
 {
-
        int sig = g_atomic_int_and (&_pending_signals, 0);
        if (sig & EmitMeterChanged) {
                _meter->emit_configuration_changed();
@@ -3918,6 +4173,24 @@ Route::emit_pending_signals ()
        if (sig & EmitRtProcessorChange) {
                processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
        }
+
+       /* this would be a job for the butler.
+        * Conceptually we should not take processe/processor locks here.
+        * OTOH its more efficient (less overhead for summoning the butler and
+        * telling her what do do) and signal emission is called
+        * directly after the process callback, which decreases the chance
+        * of x-runs when taking the locks.
+        */
+       while (!selfdestruct_sequence.empty ()) {
+               Glib::Threads::Mutex::Lock lx (selfdestruct_lock);
+               if (selfdestruct_sequence.empty ()) { break; } // re-check with lock
+               boost::shared_ptr<Processor> proc = selfdestruct_sequence.back ().lock ();
+               selfdestruct_sequence.pop_back ();
+               lx.release ();
+               if (proc) {
+                       remove_processor (proc);
+               }
+       }
 }
 
 void
@@ -4026,10 +4299,10 @@ Route::listen_position_changed ()
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
                ProcessorState pstate (this);
 
-               if (configure_processors_unlocked (0)) {
+               if (configure_processors_unlocked (0, &lm)) {
                        DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
                        pstate.restore ();
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                        return;
                }
        }
@@ -4050,7 +4323,7 @@ Route::add_export_point()
                _capturing_processor.reset (new CapturingProcessor (_session));
                _capturing_processor->activate ();
 
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lw);
 
        }
 
@@ -4312,7 +4585,7 @@ Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playli
 
                } else if ((*i)->name() == X_("Processor")) {
 
-                       XMLProperty* role = (*i)->property (X_("role"));
+                       XMLProperty const * role = (*i)->property (X_("role"));
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
@@ -4589,6 +4862,9 @@ Route::input_port_count_changing (ChanCount to)
 bool
 Route::output_port_count_changing (ChanCount to)
 {
+       if (_strict_io && !_in_configure_processors) {
+               return true;
+       }
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                if (processor_out_streams.get(*t) > to.get(*t)) {
                        return true;
@@ -5021,11 +5297,9 @@ boost::shared_ptr<Processor>
 Route::the_instrument_unlocked () const
 {
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
-                       if ((*i)->input_streams().n_midi() > 0 &&
-                           (*i)->output_streams().n_audio() > 0) {
-                               return (*i);
-                       }
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+               if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+                       return (*i);
                }
        }
        return boost::shared_ptr<Processor>();