X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=3442d0ec8aca145c56c0bc774ec8f06bd1e4c3b5;hb=1d48648a734423d2a1b1689efaa32e7f6452c5ea;hp=2b9301c715a5f94794670de181df808d91823c2c;hpb=66b14b7bbb1231d87742b83c0699ac748fb498a9;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 2b9301c715..3442d0ec8a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -33,7 +33,6 @@ #include "pbd/memento_command.h" #include "pbd/stacktrace.h" #include "pbd/convert.h" -#include "pbd/boost_debug.h" #include "pbd/unwind.h" #include "ardour/amp.h" @@ -41,6 +40,7 @@ #include "ardour/audio_track.h" #include "ardour/audio_port.h" #include "ardour/audioengine.h" +#include "ardour/boost_debug.h" #include "ardour/buffer.h" #include "ardour/buffer_set.h" #include "ardour/capturing_processor.h" @@ -78,6 +78,7 @@ using namespace PBD; PBD::Signal0 Route::SyncOrderKeys; PBD::Signal0 Route::RemoteControlIDChange; +PBD::Signal3, boost::shared_ptr, Route::PluginSetupOptions > Route::PluginSetup; /** Base class for all routable/mixable objects (tracks and busses) */ Route::Route (Session& sess, string name, Flag flg, DataType default_type) @@ -119,6 +120,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _in_sidechain_setup (false) , _strict_io (false) , _custom_meter_position_noted (false) + , _pinmgr_proxy (0) { processor_max_streams.reset(); } @@ -1256,96 +1258,37 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< DEBUG_TRACE (DEBUG::Processors, string_compose ( "%1 adding processor %2\n", name(), processor->name())); - if (!AudioEngine::instance()->connected() || !processor) { - return 1; - } - - if (_strict_io) { - boost::shared_ptr pi; - if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { - pi->set_strict_io (true); - } - } - - { - Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); - Glib::Threads::RWLock::WriterLock lm (_processor_lock); - ProcessorState pstate (this); + ProcessorList pl; - boost::shared_ptr pi; - boost::shared_ptr porti; - - if (processor == _amp) { - /* Ensure that only one amp is in the list at any time */ - ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor); - if (check != _processors.end()) { - if (before == _amp) { - /* Already in position; all is well */ - return 0; - } else { - _processors.erase (check); - } - } - } - - assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ()); - - ProcessorList::iterator loc; - if (before) { - /* inserting before a processor; find it */ - loc = find (_processors.begin(), _processors.end(), before); - if (loc == _processors.end ()) { - /* Not found */ - return 1; - } - } else { - /* inserting at end */ - loc = _processors.end (); - } + pl.push_back (processor); + int rv = add_processors (pl, before, err); - _processors.insert (loc, processor); - processor->set_owner (this); - - // Set up processor list channels. This will set processor->[input|output]_streams(), - // configure redirect ports properly, etc. - - { - if (configure_processors_unlocked (err)) { - pstate.restore (); - configure_processors_unlocked (0); // it worked before we tried to add it ... - return -1; - } - } - - if ((pi = boost::dynamic_pointer_cast(processor)) != 0) { - - if (pi->has_no_inputs ()) { - /* generator plugin */ - _have_internal_generator = true; - } - - } - - if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) { - processor->activate (); - } - - processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); - - _output->set_user_latency (0); + if (rv) { + return rv; } - reset_instrument_info (); - processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ - set_processor_positions (); + if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) { + processor->activate (); + } return 0; } +void +Route::processor_selfdestruct (boost::weak_ptr 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; @@ -1427,24 +1370,31 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) } } + +inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) { + return a = static_cast (static_cast (a) | static_cast (b)); +} + +inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) { + return a = static_cast (static_cast (a) & static_cast (b)); +} + int Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) { - /* NOTE: this is intended to be used ONLY when copying - processors from another Route. Hence the subtle - differences between this and ::add_processor() - */ - ProcessorList::iterator loc; if (before) { loc = find(_processors.begin(), _processors.end(), before); + if (loc == _processors.end ()) { + return 1; + } } else { /* nothing specified - at end */ loc = _processors.end (); } - if (!_session.engine().connected()) { + if (!AudioEngine::instance()->connected()) { return 1; } @@ -1452,6 +1402,59 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr return 0; } + ProcessorList to_skip; + + // check if there's an instrument to replace or configure + for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*i)) == 0) { + continue; + } + if (!pi->plugin ()->get_info ()->is_instrument ()) { + continue; + } + boost::shared_ptr instrument = the_instrument (); + ChanCount in (DataType::MIDI, 1); + ChanCount out (DataType::AUDIO, 2); // XXX route's out?! + + PluginSetupOptions flags = None; + if (instrument) { + flags |= CanReplace; + in = instrument->input_streams (); + out = instrument->output_streams (); + } + if (pi->has_output_presets (in, out)) { + flags |= MultiOut; + } + + pi->set_strict_io (_strict_io); + + PluginSetupOptions mask = None; + if (Config->get_ask_replace_instrument ()) { + mask |= CanReplace; + } + if (Config->get_ask_setup_instrument ()) { + mask |= MultiOut; + } + + flags &= mask; + + if (flags != None) { + boost::optional rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */ + switch (rv.get_value_or (0)) { + case 1: + to_skip.push_back (*i); // don't add this one; + break; + case 2: + replace_processor (instrument, *i, err); + to_skip.push_back (*i); + break; + default: + break; + } + } + } + { Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); @@ -1462,31 +1465,54 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr if (*i == _meter) { continue; } + ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i); + if (check != to_skip.end()) { + continue; + } boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - pi->set_count (1); // why? configure_processors_unlocked() will re-do this pi->set_strict_io (_strict_io); } + if (*i == _amp) { + /* Ensure that only one amp is in the list at any time */ + ProcessorList::iterator check = find (_processors.begin(), _processors.end(), *i); + if (check != _processors.end()) { + if (before == _amp) { + /* Already in position; all is well */ + continue; + } else { + _processors.erase (check); + } + } + } + + assert (find (_processors.begin(), _processors.end(), *i) == _processors.end ()); + _processors.insert (loc, *i); (*i)->set_owner (this); - if ((*i)->active()) { - (*i)->activate (); - } - - /* 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; } } + if ((*i)->active()) { + (*i)->activate (); + } + (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); + + boost::shared_ptr send; + if ((send = boost::dynamic_pointer_cast (*i))) { + send->SelfDestruct.connect_same_thread (*this, + boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr (*i))); + } } for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -1699,7 +1725,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(); @@ -1799,10 +1825,10 @@ Route::remove_processor (boost::shared_ptr 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; } @@ -1896,9 +1922,9 @@ Route::replace_processor (boost::shared_ptr old, boost::shared_ptrset_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(); @@ -2047,7 +2073,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; @@ -2084,7 +2110,8 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if (boost::dynamic_pointer_cast (*p) && boost::dynamic_pointer_cast (*p)->role() == Delivery::Main - && _strict_io) { + && !(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. * @@ -2162,7 +2189,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()); @@ -2189,28 +2216,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 >::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 iop; boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*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->internal_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(*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; @@ -2227,6 +2280,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err) } } + lr.release (); + lm->acquire (); + if (_meter) { _meter->set_max_channels (processor_max_streams); @@ -2420,7 +2476,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; } @@ -2493,7 +2549,7 @@ Route::add_remove_sidechain (boost::shared_ptr proc, bool add) return false; } lx.acquire (); - configure_processors_unlocked (0); + configure_processors_unlocked (0, &lm); } if (pi->has_sidechain ()) { @@ -2505,15 +2561,54 @@ Route::add_remove_sidechain (boost::shared_ptr proc, bool add) return true; } +bool +Route::plugin_preset_output (boost::shared_ptr proc, ChanCount outs) +{ + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(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 > 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 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 proc, uint32_t count, ChanCount outs) +Route::customize_plugin_insert (boost::shared_ptr proc, uint32_t count, ChanCount outs, ChanCount sinks) { boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(proc)) == 0) { @@ -2532,9 +2627,10 @@ Route::customize_plugin_insert (boost::shared_ptr proc, uint32_t coun Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); - 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); @@ -2542,6 +2638,7 @@ Route::customize_plugin_insert (boost::shared_ptr proc, uint32_t coun pi->set_custom_cfg (true); pi->set_count (count); pi->set_outputs (outs); + pi->set_sinks (sinks); } list > c = try_configure_processors_unlocked (n_inputs (), 0); @@ -2549,12 +2646,13 @@ Route::customize_plugin_insert (boost::shared_ptr 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 */ @@ -2565,6 +2663,8 @@ Route::customize_plugin_insert (boost::shared_ptr 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); @@ -2590,10 +2690,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 (); } @@ -2615,6 +2714,7 @@ Route::get_template() XMLNode& Route::state(bool full_state) { + LocaleGuard lg; if (!_session._template_state_dir.empty()) { assert (!full_state); // only for templates foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir)); @@ -2732,7 +2832,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; @@ -2855,7 +2955,6 @@ Route::set_state (const XMLNode& node, int version) if ((prop = node.property (X_("active"))) != 0) { bool yn = string_is_affirmative (prop->value()); - _active = !yn; // force switch set_active (yn, this); } @@ -2951,11 +3050,11 @@ Route::set_state (const XMLNode& node, int version) int Route::set_state_2X (const XMLNode& node, int version) { - LocaleGuard lg (X_("C")); + LocaleGuard lg; XMLNodeList nlist; XMLNodeConstIterator niter; XMLNode *child; - const XMLProperty *prop; + XMLProperty const * prop; /* 2X things which still remain to be handled: * default-type @@ -3241,7 +3340,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); @@ -3278,7 +3377,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); @@ -3321,6 +3420,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 = boost::dynamic_pointer_cast (processor); + send->SelfDestruct.connect_same_thread (*this, + boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr (processor))); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; @@ -3362,7 +3464,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) { @@ -3682,8 +3784,8 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* return true; } + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // XXX for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { boost::shared_ptr iop = boost::dynamic_pointer_cast(*r); @@ -3896,18 +3998,13 @@ Route::output_change_handler (IOChange change, void * /*src*/) } void -Route::sidechain_change_handler (IOChange change, void * /*src*/) +Route::sidechain_change_handler (IOChange change, void* src) { if (_initial_io_setup || _in_sidechain_setup) { return; } - if ((change.type & IOChange::ConfigurationChanged)) { - /* This is called with the process lock held if change - contains ConfigurationChanged - */ - configure_processors (0); - } + input_change_handler (change, src); } uint32_t @@ -4067,13 +4164,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(); @@ -4087,6 +4183,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 proc = selfdestruct_sequence.back ().lock (); + selfdestruct_sequence.pop_back (); + lx.release (); + if (proc) { + remove_processor (proc); + } + } } void @@ -4195,10 +4309,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; } } @@ -4219,7 +4333,7 @@ Route::add_export_point() _capturing_processor.reset (new CapturingProcessor (_session)); _capturing_processor->activate (); - configure_processors_unlocked (0); + configure_processors_unlocked (0, &lw); } @@ -4481,7 +4595,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); } @@ -4758,6 +4872,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; @@ -5190,11 +5307,9 @@ boost::shared_ptr Route::the_instrument_unlocked () const { for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (boost::dynamic_pointer_cast(*i)) { - if ((*i)->input_streams().n_midi() > 0 && - (*i)->output_streams().n_audio() > 0) { - return (*i); - } + boost::shared_ptr pi = boost::dynamic_pointer_cast(*i); + if (pi && pi->plugin ()->get_info ()->is_instrument ()) { + return (*i); } } return boost::shared_ptr();