, _track_number (0)
, _in_configure_processors (false)
, _initial_io_setup (false)
+ , _strict_io (false)
, _custom_meter_position_noted (false)
{
processor_max_streams.reset();
_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));
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<PortInsert>(*i)) {
break;
}
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<PeakMeter>(*i)) {
(*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
}
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;
}
{
if (_solo_safe != yn) {
_solo_safe = yn;
- solo_safe_changed ();
+ solo_safe_changed (); /* EMIT SIGNAL */
+ _solo_safe_control->Changed(); /* EMIT SIGNAL */
}
}
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;
}
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 */
}
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;
}
/* 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
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;
}
return 1;
}
+ if (_strict_io) {
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+ pi->set_strict_io (true);
+ }
+ }
+
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
- pi->set_count (1);
+ pi->set_count (1); // why? configure_processors_unlocked() will re-do this
+ pi->set_strict_io (_strict_io);
}
_processors.insert (loc, *i);
return 0;
}
+int
+Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Processor> 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<IOProcessor> (old) || boost::dynamic_pointer_cast<IOProcessor> (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<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(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<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*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)
{
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<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
- /* plugins connected via Split Match may have more channels.
- * route/scratch buffers are needed for all of them*/
+ /* 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->natural_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());
}
out = c->second;
return 0;
}
+bool
+Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
+{
+ ChanCount unused;
+ return customize_plugin_insert (proc, 0, unused);
+}
+
+bool
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+{
+ boost::shared_ptr<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
+ return false;
+ }
+
+ {
+ 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) {
+ return false;
+ }
+ }
+
+ {
+ 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 ();
+
+ if (count == 0) {
+ pi->set_custom_cfg (false);
+ } else {
+ pi->set_custom_cfg (true);
+ pi->set_count (count);
+ pi->set_outputs (outs);
+ }
+
+ list<pair<ChanCount, ChanCount> > 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<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+ pi->set_strict_io (_strict_io);
+ }
+ }
+
+ list<pair<ChanCount, ChanCount> > 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<PluginInsert> pi;
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*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()
{
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));
_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);
}
} 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));
+ if (_strict_io) {
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+ pi->set_strict_io (true);
+ }
+
}
} else if (prop->value() == "port") {
if (_phase_invert[c] != yn) {
_phase_invert[c] = yn;
phase_invert_changed (); /* EMIT SIGNAL */
+ _phase_control->Changed(); /* EMIT SIGNAL */
_session.set_dirty ();
}
}
return _trim_control;
}
+boost::shared_ptr<Route::PhaseControllable>
+Route::phase_control() const
+{
+ if (phase_invert().size()) {
+ return _phase_control;
+ } else {
+ return boost::shared_ptr<PhaseControllable>();
+ }
+}
+
boost::shared_ptr<AutomationControl>
Route::get_control (const Evoral::Parameter& param)
{
for (i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<Send> (*i)) {
- if ((*i)->name() == _("Monitor 1")) {
+ if ((*i)->name().find (_("Monitor")) == 0) {
/* send to monitor section is not considered
to be an accessible send.
*/
return _("Compressor");
case 2:
return _("Limiter");
+ case 3:
+ return mixbus() ? _("Sidechain") : _("Limiter");
}
return _("???");
case 1:
return _("Ratio");
case 2:
+ case 3:
return _("Rels");
}
return _("???");