X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=1db571385fbd98de2427b63fba3dbb148f013394;hb=a3eaa4d3f0606349b91c7660da19eb3fee91bce3;hp=da0183304ce4681a89b3fe275ac61f4afb9756b8;hpb=9128fbd68f7bc03ea0b3238fc9dd24ff326f1ad8;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index da0183304c..1db571385f 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -79,6 +79,7 @@ using namespace PBD; PBD::Signal0 Route::SyncOrderKeys; PBD::Signal0 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,8 @@ 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) { processor_max_streams.reset(); @@ -139,6 +142,9 @@ Route::init () _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ())); _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ())); + _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ())); + _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ())); + _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle)); @@ -660,7 +666,7 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, break; } - /* if we're not exporting, stop processing if we come across a routing processor. */ + /* if we're *not* exporting, stop processing if we come across a routing processor. */ if (!for_export && boost::dynamic_pointer_cast(*i)) { break; } @@ -668,8 +674,20 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes, break; } - /* don't run any processors that does routing. - * oh, and don't bother with the peak meter either. + /* special case the panner (export outputs) + * Ideally we'd only run the panner, not the delivery itself... + * but panners need separate input/output buffers and some context + * (panshell, panner type, etc). AFAICT there is no ill side effect + * of re-using the main delivery when freewheeling/exporting a region. + */ + if ((*i) == _main_outs) { + assert ((*i)->does_routing()); + (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); + buffers.set_count ((*i)->output_streams()); + } + + /* don't run any processors that do routing. + * Also don't bother with metering. */ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast(*i)) { (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true); @@ -792,7 +810,7 @@ Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override } if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::NoGroup)); + _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup)); return; } @@ -827,7 +845,8 @@ Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_ov { if (_solo_safe != yn) { _solo_safe = yn; - solo_safe_changed (); + solo_safe_changed (); /* EMIT SIGNAL */ + _solo_safe_control->Changed(); /* EMIT SIGNAL */ } } @@ -876,6 +895,9 @@ Route::clear_all_solo_state () void Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) { + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n", + name(), yn, enum_2_string(group_override), self_soloed())); + if (_solo_safe) { DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); return; @@ -887,13 +909,10 @@ Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) } if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::NoGroup)); + _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup)); return; } - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n", - name(), yn, enum_2_string(group_override), self_soloed())); - if (self_soloed() != yn) { set_self_solo (yn); solo_changed (true, group_override); /* EMIT SIGNAL */ @@ -1038,7 +1057,7 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o } if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::NoGroup)); + _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup)); return; } @@ -1083,6 +1102,7 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ solo_isolated_changed (); /* EMIT SIGNAL */ + _solo_isolate_control->Changed(); /* EMIT SIGNAL */ } bool @@ -1107,7 +1127,7 @@ void Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override) { if (use_group (group_override, &RouteGroup::is_mute)) { - _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::NoGroup)); + _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup)); return; } @@ -1240,6 +1260,13 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< 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); @@ -1356,6 +1383,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) processor.reset (new UnknownProcessor (_session, node)); } else { processor.reset (new PluginInsert (_session)); + processor->set_owner (this); } } else { @@ -1438,7 +1466,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr boost::shared_ptr pi; if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { - pi->set_count (1); + pi->set_strict_io (_strict_io); } _processors.insert (loc, *i); @@ -1742,9 +1770,15 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream run. */ - boost::shared_ptr iop; + boost::shared_ptr iop = boost::dynamic_pointer_cast (*i); + boost::shared_ptr pi = boost::dynamic_pointer_cast(*i); + + if (pi != 0) { + assert (iop == 0); + iop = pi->sidechain(); + } - if ((iop = boost::dynamic_pointer_cast (*i)) != 0) { + if (iop != 0) { iop->disconnect (); } @@ -1796,6 +1830,104 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream return 0; } +int +Route::replace_processor (boost::shared_ptr old, boost::shared_ptr sub, ProcessorStreams* err) +{ + /* these can never be removed */ + if (old == _amp || old == _meter || old == _main_outs || old == _delayline || old == _trim) { + return 1; + } + /* and can't be used as substitute, either */ + if (sub == _amp || sub == _meter || sub == _main_outs || sub == _delayline || sub == _trim) { + return 1; + } + + /* I/Os are out, too */ + if (boost::dynamic_pointer_cast (old) || boost::dynamic_pointer_cast (sub)) { + return 1; + } + + /* this function cannot be used to swap/reorder processors */ + if (find (_processors.begin(), _processors.end(), sub) != _processors.end ()) { + return 1; + } + + if (!AudioEngine::instance()->connected() || !old || !sub) { + return 1; + } + + /* ensure that sub is not owned by another route */ + if (sub->owner ()) { + return 1; + } + + { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); + ProcessorState pstate (this); + + assert (find (_processors.begin(), _processors.end(), sub) == _processors.end ()); + + ProcessorList::iterator i; + bool replaced = false; + bool enable = old->active (); + + for (i = _processors.begin(); i != _processors.end(); ) { + if (*i == old) { + i = _processors.erase (i); + _processors.insert (i, sub); + sub->set_owner (this); + replaced = true; + break; + } else { + ++i; + } + } + + if (!replaced) { + return 1; + } + + if (_strict_io) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(sub)) != 0) { + pi->set_strict_io (true); + } + } + + if (configure_processors_unlocked (err)) { + pstate.restore (); + configure_processors_unlocked (0); + return -1; + } + + _have_internal_generator = false; + + for (i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*i)) != 0) { + if (pi->has_no_inputs ()) { + _have_internal_generator = true; + break; + } + } + } + + if (enable) { + sub->activate (); + } + + sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false)); + _output->set_user_latency (0); + } + + reset_instrument_info (); + old->drop_references (); + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + set_processor_positions (); + return 0; +} + int Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err) { @@ -1838,9 +1970,14 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* run. */ - boost::shared_ptr iop; + boost::shared_ptr iop = boost::dynamic_pointer_cast(processor); + boost::shared_ptr pi = boost::dynamic_pointer_cast(processor); + if (pi != 0) { + assert (iop == 0); + iop = pi->sidechain(); + } - if ((iop = boost::dynamic_pointer_cast (processor)) != 0) { + if (iop != 0) { iop->disconnect (); } @@ -1943,6 +2080,34 @@ 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 (*p) + && boost::dynamic_pointer_cast (*p)->role() == Delivery::Main +#ifndef MIXBUS + && _strict_io +#endif + ) { + /* 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)); @@ -2031,16 +2196,24 @@ Route::configure_processors_unlocked (ProcessorStreams* err) if (!(*p)->configure_io(c->first, c->second)) { DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name)); + _in_configure_processors = false; + 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 Match may have more channels. - * route/scratch buffers are needed for all of them*/ - processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams()); - processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams()); + /* 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->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; @@ -2273,6 +2446,163 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err return 0; } +bool +Route::add_remove_sidechain (boost::shared_ptr proc, bool add) +{ + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(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 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 > 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); + } + + 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::reset_plugin_insert (boost::shared_ptr proc) +{ + ChanCount unused; + return customize_plugin_insert (proc, 0, unused); +} + +bool +Route::customize_plugin_insert (boost::shared_ptr proc, uint32_t count, 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); + + bool old_cust = pi->custom_cfg (); + uint32_t old_cnt = pi->get_count (); + ChanCount old_chan = pi->output_streams (); + + if (count == 0) { + pi->set_custom_cfg (false); + } else { + pi->set_custom_cfg (true); + pi->set_count (count); + pi->set_outputs (outs); + } + + list > c = try_configure_processors_unlocked (n_inputs (), 0); + if (c.empty()) { + /* not possible */ + + pi->set_count (old_cnt); + pi->set_outputs (old_chan); + pi->set_custom_cfg (old_cust); + + return false; + } + configure_processors_unlocked (0); + } + + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + _session.set_dirty (); + return true; +} + +bool +Route::set_strict_io (const bool enable) +{ + if (_strict_io != enable) { + _strict_io = enable; + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*p)) != 0) { + pi->set_strict_io (_strict_io); + } + } + + list > c = try_configure_processors_unlocked (n_inputs (), 0); + + if (c.empty()) { + // not possible + _strict_io = !enable; // restore old value + for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*p)) != 0) { + pi->set_strict_io (_strict_io); + } + } + return false; + } + lm.release (); + + { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + configure_processors (0); + } + processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ + _session.set_dirty (); + } + return true; +} + XMLNode& Route::get_state() { @@ -2301,6 +2631,7 @@ Route::state(bool full_state) node->add_property("id", buf); node->add_property ("name", _name); node->add_property("default-type", _default_type.to_string()); + node->add_property ("strict-io", _strict_io); if (_flags) { node->add_property("flags", enum_2_string (_flags)); @@ -2424,6 +2755,10 @@ Route::set_state (const XMLNode& node, int version) _flags = Flag (0); } + if ((prop = node.property (X_("strict-io"))) != 0) { + _strict_io = string_is_affirmative (prop->value()); + } + if (is_master() || is_monitor() || is_auditioner()) { _mute_master->set_solo_ignore (true); } @@ -2967,13 +3302,20 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "windows-vst" || - prop->value() == "lxvst" || + prop->value() == "lxvst" || + prop->value() == "luaproc" || prop->value() == "audiounit") { if (_session.get_disable_all_loaded_plugins ()) { processor.reset (new UnknownProcessor (_session, **niter)); } else { processor.reset (new PluginInsert (_session)); + processor->set_owner (this); + if (_strict_io) { + boost::shared_ptr pi = boost::dynamic_pointer_cast(processor); + pi->set_strict_io (true); + } + } } else if (prop->value() == "port") { @@ -2993,6 +3335,12 @@ Route::set_processor_state (const XMLNode& node) processor.reset (new UnknownProcessor (_session, **niter)); } + /* subscribe to Sidechain IO changes */ + boost::shared_ptr pi = boost::dynamic_pointer_cast (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. */ @@ -3282,12 +3630,53 @@ Route::feeds (boost::shared_ptr 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 iop = boost::dynamic_pointer_cast(*r); + boost::shared_ptr pi = boost::dynamic_pointer_cast(*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 iop = boost::dynamic_pointer_cast(*r); + if (iop != 0 && iop->output()) { + ios.push_back (iop->output()); + } + } + return ios; +} + bool Route::direct_feeds_according_to_reality (boost::shared_ptr 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; @@ -3297,12 +3686,19 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr other, bool* } + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // XXX for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) { - boost::shared_ptr iop; + boost::shared_ptr iop = boost::dynamic_pointer_cast(*r); + boost::shared_ptr pi = boost::dynamic_pointer_cast(*r); + if (pi != 0) { + assert (iop == 0); + iop = pi->sidechain(); + } - if ((iop = boost::dynamic_pointer_cast(*r)) != 0) { - if (iop->feeds (other)) { + if (iop != 0) { + boost::shared_ptr 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; @@ -3327,6 +3723,12 @@ Route::direct_feeds_according_to_graph (boost::shared_ptr 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 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) @@ -3496,6 +3898,21 @@ 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; + } + + if ((change.type & IOChange::ConfigurationChanged)) { + /* This is called with the process lock held if change + contains ConfigurationChanged + */ + configure_processors (0); + } +} + uint32_t Route::pans_required () const { @@ -4110,6 +4527,7 @@ Route::set_phase_invert (uint32_t c, bool yn) if (_phase_invert[c] != yn) { _phase_invert[c] = yn; phase_invert_changed (); /* EMIT SIGNAL */ + _phase_control->Changed(); /* EMIT SIGNAL */ _session.set_dirty (); } } @@ -4203,6 +4621,16 @@ Route::trim_control() const return _trim_control; } +boost::shared_ptr +Route::phase_control() const +{ + if (phase_invert().size()) { + return _phase_control; + } else { + return boost::shared_ptr(); + } +} + boost::shared_ptr Route::get_control (const Evoral::Parameter& param) { @@ -4259,7 +4687,7 @@ Route::nth_send (uint32_t n) const for (i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { - if ((*i)->name() == _("Monitor 1")) { + if ((*i)->name().find (_("Monitor")) == 0) { /* send to monitor section is not considered to be an accessible send. */ @@ -4333,6 +4761,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; @@ -5224,6 +5655,8 @@ Route::comp_mode_name (uint32_t mode) const return _("Compressor"); case 2: return _("Limiter"); + case 3: + return mixbus() ? _("Sidechain") : _("Limiter"); } return _("???"); @@ -5242,6 +5675,7 @@ Route::comp_speed_name (uint32_t mode) const case 1: return _("Ratio"); case 2: + case 3: return _("Rels"); } return _("???");