#include "ardour/capturing_processor.h"
#include "ardour/debug.h"
#include "ardour/delivery.h"
+#include "ardour/gain_control.h"
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/meter.h"
#include "ardour/pannable.h"
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
+#include "ardour/parameter_descriptor.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
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)
, _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();
_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));
/* add amp processor */
- boost::shared_ptr<AutomationList> gl (new AutomationList (Evoral::Parameter (GainAutomation)));
- _gain_control = boost::shared_ptr<Amp::GainControl> (new Amp::GainControl (_session, Evoral::Parameter(GainAutomation), gl));
+ _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
add_control (_gain_control);
_amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
/* and input trim */
- boost::shared_ptr<AutomationList> tl (new AutomationList (Evoral::Parameter (TrimAutomation)));
- _trim_control = boost::shared_ptr<Amp::GainControl> (new Amp::GainControl (_session, Evoral::Parameter(TrimAutomation), tl));
+ _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
add_control (_trim_control);
_trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
}
void
-Route::inc_gain (gain_t fraction, void *src)
+Route::inc_gain (gain_t factor)
{
- _amp->inc_gain (fraction, src);
+ /* To be used ONLY when doing group-relative gain adjustment, from
+ * ::set_gain()
+ */
+
+ float desired_gain = _gain_control->user_double();
+
+ if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
+ // really?! what's the idea here?
+ _gain_control->route_set_value (0.000001f + (0.000001f * factor));
+ } else {
+ _gain_control->route_set_value (desired_gain + (desired_gain * factor));
+ }
}
void
-Route::set_gain (gain_t val, void *src)
+Route::set_gain (gain_t val, Controllable::GroupControlDisposition group_override)
{
- if (src != 0 && _route_group && src != _route_group && _route_group->is_active() && _route_group->is_gain()) {
+ if (use_group (group_override, &RouteGroup::is_gain)) {
if (_route_group->is_relative()) {
- gain_t usable_gain = _amp->gain();
+ gain_t usable_gain = _gain_control->get_value();
if (usable_gain < 0.000001f) {
usable_gain = 0.000001f;
}
}
}
- _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
+ _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
} else {
- _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
+ _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
}
return;
}
- if (val == _amp->gain()) {
+ if (val == _gain_control->get_value()) {
return;
}
- _amp->set_gain (val, src);
+ _gain_control->route_set_value (val);
}
void
-Route::inc_trim (gain_t fraction, void *src)
-{
- _trim->inc_gain (fraction, src);
-}
-
-void
-Route::set_trim (gain_t val, void * /* src */)
+Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
{
// TODO route group, see set_gain()
- _trim->set_gain (val, 0);
+ _trim_control->route_set_value (val);
}
void
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);
}
void
-Route::set_listen (bool yn, void* src, bool group_override)
+Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
{
if (_solo_safe) {
return;
}
- bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
- if (group_override && _route_group) {
- group_active = !group_active;
- }
-
- if (_route_group && src != _route_group && group_active) {
- _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group, group_override));
+ if (use_group (group_override, &RouteGroup::is_solo)) {
+ _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
return;
}
}
_mute_master->set_soloed_by_others (false);
- listen_changed (src, group_override); /* EMIT SIGNAL */
+ listen_changed (group_override); /* EMIT SIGNAL */
}
}
}
}
void
-Route::set_solo_safe (bool yn, void *src)
+Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */)
{
if (_solo_safe != yn) {
_solo_safe = yn;
- solo_safe_changed (src);
+ solo_safe_changed (); /* EMIT SIGNAL */
+ _solo_safe_control->Changed(); /* EMIT SIGNAL */
}
}
{
PBD::Unwinder<bool> uw (_solo_safe, false);
- set_solo (false, this);
+ set_solo (false, Controllable::NoGroup);
}
if (emit_changed) {
set_mute_master_solo ();
- solo_changed (false, this, false); /* EMIT SIGNAL */
+ solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
}
}
void
-Route::set_solo (bool yn, void *src, bool group_override)
+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;
return;
}
- bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
- if (group_override && _route_group) {
- group_active = !group_active;
- }
- if (_route_group && src != _route_group && group_active) {
- _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group, group_override));
+ if (use_group (group_override, &RouteGroup::is_solo)) {
+ _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
return;
}
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
- name(), yn, src, (src == _route_group), self_soloed()));
-
if (self_soloed() != yn) {
set_self_solo (yn);
- solo_changed (true, src, group_override); /* EMIT SIGNAL */
+ solo_changed (true, group_override); /* EMIT SIGNAL */
_solo_control->Changed (); /* EMIT SIGNAL */
}
*/
if (yn && Profile->get_trx()) {
- set_mute (false, src);
+ set_mute (false, Controllable::UseGroup);
}
}
}
set_mute_master_solo ();
- solo_changed (false, this, false); /* EMIT SIGNAL */
+ solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
}
void
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
set_mute_master_solo ();
- solo_changed (false, this, false); /* EMIT SIGNAL */
+ solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
}
void
}
void
-Route::mod_solo_isolated_by_upstream (bool yn, void* src)
+Route::mod_solo_isolated_by_upstream (bool yn)
{
bool old = solo_isolated ();
DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
if (solo_isolated() != old) {
/* solo isolated status changed */
_mute_master->set_solo_ignore (solo_isolated());
- solo_isolated_changed (src); /* EMIT SIGNAL */
+ solo_isolated_changed (); /* EMIT SIGNAL */
}
}
void
-Route::set_solo_isolated (bool yn, void *src)
+Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
{
if (is_master() || is_monitor() || is_auditioner()) {
return;
}
- if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
- _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
+ if (use_group (group_override, &RouteGroup::is_solo)) {
+ _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
return;
}
bool does_feed = feeds (*i, &sends_only);
if (does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (yn, src);
+ (*i)->mod_solo_isolated_by_upstream (yn);
}
}
/* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
- solo_isolated_changed (src); /* EMIT SIGNAL */
+ solo_isolated_changed (); /* EMIT SIGNAL */
+ _solo_isolate_control->Changed(); /* EMIT SIGNAL */
}
bool
mute_points_changed (); /* EMIT SIGNAL */
if (_mute_master->muted_by_self()) {
- mute_changed (this); /* EMIT SIGNAL */
+ mute_changed (); /* EMIT SIGNAL */
_mute_control->Changed (); /* EMIT SIGNAL */
}
}
void
-Route::set_mute (bool yn, void *src)
+Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
{
- if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
- _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
+ if (use_group (group_override, &RouteGroup::is_mute)) {
+ _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
return;
}
*/
act_on_mute ();
/* tell everyone else */
- mute_changed (src); /* EMIT SIGNAL */
+ mute_changed (); /* EMIT SIGNAL */
_mute_control->Changed (); /* EMIT SIGNAL */
}
}
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);
// 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;
}
}
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;
processor.reset (new UnknownProcessor (_session, node));
} else {
processor.reset (new PluginInsert (_session));
+ processor->set_owner (this);
}
} else {
boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
- pi->set_count (1);
+ pi->set_strict_io (_strict_io);
}
_processors.insert (loc, *i);
/* 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;
}
}
}
_processors = new_list;
- configure_processors_unlocked (&err); // this can't fail
+ configure_processors_unlocked (&err, &lm); // this can't fail
}
processor_max_streams.reset();
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 ();
}
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;
}
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, &lm)) {
+ pstate.restore ();
+ configure_processors_unlocked (0, &lm);
+ 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)
{
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 ();
}
_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();
if (!_in_configure_processors) {
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
- return configure_processors_unlocked (err);
+ return configure_processors_unlocked (err, &lm);
}
return 0;
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));
* 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());
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 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<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;
}
}
+ lr.release ();
+ lm->acquire ();
+
if (_meter) {
_meter->set_max_channels (processor_max_streams);
apply_processor_order (new_order);
- if (configure_processors_unlocked (err)) {
+ if (configure_processors_unlocked (err, &lm)) {
pstate.restore ();
return -1;
}
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, unused);
+}
+
+bool
+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) {
+ 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 ();
+ ChanCount old_sinks = pi->natural_input_streams ();
+
+ if (count == 0) {
+ pi->set_custom_cfg (false);
+ } else {
+ 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);
+ if (c.empty()) {
+ /* 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, &lm);
+ }
+
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return true;
+}
+
+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);
+ 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 ();
+
+ configure_processors (0);
+ lx.release ();
+
+ 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));
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;
_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);
}
}
if ((prop = node.property ("solo-isolated")) != 0) {
- set_solo_isolated (string_is_affirmative (prop->value()), this);
+ set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
}
if ((prop = node.property ("solo-safe")) != 0) {
- set_solo_safe (string_is_affirmative (prop->value()), this);
+ set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
}
if ((prop = node.property (X_("phase-invert"))) != 0) {
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- const XMLProperty *prop;
+ XMLProperty const * prop;
/* 2X things which still remain to be handled:
* default-type
/* XXX force reset of solo status */
- set_solo (yn, this);
+ set_solo (yn);
}
if ((prop = node.property (X_("muted"))) != 0) {
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);
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);
} 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<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(processor);
+ pi->set_strict_io (true);
+ }
+
}
} else if (prop->value() == "port") {
} 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;
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.
*/
_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) {
Route::set_comment (string cmt, void *src)
{
_comment = cmt;
- comment_changed (src);
+ comment_changed ();
_session.set_dirty ();
}
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;
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;
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)
if (_solo_isolated_by_upstream) {
// solo-isolate currently only propagates downstream
if (idelta < 0) {
- mod_solo_isolated_by_upstream (false, this);
+ mod_solo_isolated_by_upstream (false);
}
// TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
// but idelta can't be smaller than -1, can it?
}
if (idelta < 0 && does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (false, this);
+ (*i)->mod_solo_isolated_by_upstream (false);
}
}
}
}
}
+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
{
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();
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
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;
}
}
_capturing_processor.reset (new CapturingProcessor (_session));
_capturing_processor->activate ();
- configure_processors_unlocked (0);
+ configure_processors_unlocked (0, &lw);
}
}
}
-void
-Route::set_control (RouteAutomationControl& control, double val, PBD::Controllable::GroupControlDisposition /*group_override*/)
-{
- boost::shared_ptr<RouteList> rl;
-
- switch (control.parameter().type()) {
- case GainAutomation:
- /* route must mediate group control */
- set_gain (val, this); /* any "src" argument will do other than our route group */
- return;
- break;
-
- case RecEnableAutomation:
- /* session must mediate group control */
- rl.reset (new RouteList);
- rl->push_back (shared_from_this());
- _session.set_record_enabled (rl, val >= 0.5 ? true : false);
- return;
- break;
-
- case SoloAutomation:
- /* session must mediate group control */
- rl.reset (new RouteList);
- rl->push_back (shared_from_this());
- if (Config->get_solo_control_is_listen_control()) {
- _session.set_listen (rl, val >= 0.5 ? true : false);
- } else {
- _session.set_solo (rl, val >= 0.5 ? true : false);
- }
-
- return;
- break;
-
- case MuteAutomation:
- /* session must mediate group control */
- rl.reset (new RouteList);
- rl->push_back (shared_from_this());
- _session.set_mute (rl, !muted());
- return;
- break;
-
- case PanAzimuthAutomation:
- case PanElevationAutomation:
- case PanWidthAutomation:
- case PanFrontBackAutomation:
- case PanLFEAutomation:
- break;
-
- default:
- /* Not a route automation control */
- return;
- }
-
- control.route_set_value (val);
-}
-
-
-Route::RouteAutomationControl::RouteAutomationControl (const std::string& name,
- AutomationType atype,
- boost::shared_ptr<AutomationList> alist,
- boost::shared_ptr<Route> r)
- : AutomationControl (r->session(), Evoral::Parameter (atype),
- ParameterDescriptor (Evoral::Parameter (atype)),
- alist, name)
- , _route (r)
-{
-}
-
-Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
- : RouteAutomationControl (name, SoloAutomation, boost::shared_ptr<AutomationList>(), r)
-{
- boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
- gl->set_interpolation(Evoral::ControlList::Discrete);
- set_list (gl);
-}
-
-void
-Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /* group_override */)
-{
- if (writable()) {
- set_value_unchecked (val);
- }
-}
-
-void
-Route::SoloControllable::set_value_unchecked (double val)
-{
- const bool bval = ((val >= 0.5) ? true : false);
-
- boost::shared_ptr<RouteList> rl (new RouteList);
-
- boost::shared_ptr<Route> r = _route.lock ();
- if (!r) {
- return;
- }
-
- rl->push_back (r);
-
- if (Config->get_solo_control_is_listen_control()) {
- _session.set_listen (rl, bval);
- } else {
- _session.set_solo (rl, bval);
- }
-}
-
-double
-Route::SoloControllable::get_value () const
-{
- boost::shared_ptr<Route> r = _route.lock ();
- if (!r) {
- return 0;
- }
-
- if (Config->get_solo_control_is_listen_control()) {
- return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
- } else {
- return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
- }
-}
-
-Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
- : RouteAutomationControl (name, MuteAutomation, boost::shared_ptr<AutomationList>(), r)
- , _route (r)
-{
- boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
- gl->set_interpolation(Evoral::ControlList::Discrete);
- set_list (gl);
-}
-
-void
-Route::MuteControllable::set_superficial_value(bool muted)
-{
- /* Note we can not use AutomationControl::set_value here since it will emit
- Changed(), but the value will not be correct to the observer. */
-
- const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
- const double where = _session.audible_frame ();
- if (to_list) {
- /* Note that we really need this:
- * if (as == Touch && _list->in_new_write_pass ()) {
- * alist->start_write_pass (_session.audible_frame ());
- * }
- * here in the case of the user calling from a GUI or whatever.
- * Without the ability to distinguish between user and
- * automation-initiated changes, we lose the "touch mute"
- * behaviour we have in AutomationController::toggled ().
- */
- _list->set_in_write_pass (true, false, where);
- }
-
- Control::set_double (muted, where, to_list);
-}
-
-void
-Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition /* group_override */)
-{
- if (writable()) {
- set_value_unchecked (val);
- }
-}
-
-void
-Route::MuteControllable::set_value_unchecked (double val)
-{
- const bool bval = ((val >= 0.5) ? true : false);
-
- boost::shared_ptr<Route> r = _route.lock ();
- if (!r) {
- return;
- }
-
- if (_list && ((AutomationList*)_list.get())->automation_playback()) {
- // Set superficial/automation value to drive controller (and possibly record)
- set_superficial_value (bval);
- // Playing back automation, set route mute directly
- r->set_mute (bval, this);
- } else {
- // Set from user, queue mute event
- boost::shared_ptr<RouteList> rl (new RouteList);
- rl->push_back (r);
- _session.set_mute (rl, bval, Session::rt_cleanup);
- }
-}
-
-double
-Route::MuteControllable::get_value () const
-{
- if (_list && ((AutomationList*)_list.get())->automation_playback()) {
- // Playing back automation, get the value from the list
- return AutomationControl::get_value();
- }
-
- // Not playing back automation, get the actual route mute value
- boost::shared_ptr<Route> r = _route.lock ();
- return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
-}
-
-Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)
- : RouteAutomationControl (name, PhaseAutomation, boost::shared_ptr<AutomationList>(), r)
-{
- boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation)));
- gl->set_interpolation(Evoral::ControlList::Discrete);
- set_list (gl);
-}
-
-void
-Route::PhaseControllable::set_value (double v, PBD::Controllable::GroupControlDisposition /* group_override */)
-{
- boost::shared_ptr<Route> r = _route.lock ();
- if (r->phase_invert().size()) {
- if (v == 0 || (v < 1 && v > 0.9) ) {
- r->set_phase_invert (_current_phase, false);
- } else {
- r->set_phase_invert (_current_phase, true);
- }
- }
-}
-
-double
-Route::PhaseControllable::get_value () const
-{
- boost::shared_ptr<Route> r = _route.lock ();
- return (double) r->phase_invert (_current_phase);
-}
-
-void
-Route::PhaseControllable::set_channel (uint32_t c)
-{
- _current_phase = c;
-}
-
-uint32_t
-Route::PhaseControllable::channel () const
-{
- return _current_phase;
-}
-
void
Route::set_block_size (pframes_t nframes)
{
} 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);
}
if (_phase_invert[c] != yn) {
_phase_invert[c] = yn;
phase_invert_changed (); /* EMIT SIGNAL */
+ _phase_control->Changed(); /* EMIT SIGNAL */
_session.set_dirty ();
}
}
return _main_outs->panner_shell();
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
Route::gain_control() const
{
- return _amp->gain_control();
+ return _gain_control;
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
Route::trim_control() const
{
- return _trim->gain_control();
+ 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>
}
boost::shared_ptr<Processor>
-Route::nth_plugin (uint32_t n)
+Route::nth_plugin (uint32_t n) const
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- ProcessorList::iterator i;
+ ProcessorList::const_iterator i;
for (i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
}
boost::shared_ptr<Processor>
-Route::nth_send (uint32_t n)
+Route::nth_send (uint32_t n) const
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
- ProcessorList::iterator i;
+ ProcessorList::const_iterator i;
for (i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<Send> (*i)) {
+
+ if ((*i)->name().find (_("Monitor")) == 0) {
+ /* send to monitor section is not considered
+ to be an accessible send.
+ */
+ continue;
+ }
+
if (n-- == 0) {
return *i;
}
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;
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>();
{
#ifdef MIXBUS
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
- assert (plug);
+ if (!plug) {
+ return boost::shared_ptr<AutomationControl>();
+ }
const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan)));
#else
return _("Compressor");
case 2:
return _("Limiter");
+ case 3:
+ return mixbus() ? _("Sidechain") : _("Limiter");
}
return _("???");
case 1:
return _("Ratio");
case 2:
+ case 3:
return _("Rels");
}
return _("???");
return _("???");
#endif
}
+
+boost::shared_ptr<AutomationControl>
+Route::send_level_controllable (uint32_t n) const
+{
+#ifdef MIXBUS
+ boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+ if (!plug) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+
+ if (n >= 8) {
+ /* no such bus */
+ return boost::shared_ptr<AutomationControl>();
+ }
+
+ const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+ boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(nth_send (n));
+ if (!s) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ return s->gain_control ();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::send_enable_controllable (uint32_t n) const
+{
+#ifdef MIXBUS
+ boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+ if (!plug) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+
+ if (n >= 8) {
+ /* no such bus */
+ return boost::shared_ptr<AutomationControl>();
+ }
+
+ const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+#else
+ /* although Ardour sends have enable/disable as part of the Processor
+ API, it is not exposed as a controllable.
+
+ XXX: we should fix this.
+ */
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+string
+Route::send_name (uint32_t n) const
+{
+#ifdef MIXBUS
+ if (n >= 8) {
+ return string();
+ }
+ boost::shared_ptr<Route> r = _session.get_mixbus (n);
+ assert (r);
+ return r->name();
+#else
+ boost::shared_ptr<Processor> p = nth_send (n);
+ if (p) {
+ return p->name();
+ } else {
+ return string();
+ }
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::master_send_enable_controllable () const
+{
+#ifdef MIXBUS
+ boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
+ if (!plug) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign)));
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}