X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=5eb962da5aa0ff081eff697aa6087e334d224fc6;hb=8f7ae26a962fb100c4190b04aff3fad337e78c04;hp=f461fe58d19473afc29215a99d688187b242caa9;hpb=e7bbed3150ea1c1994ac97c99b8058fc9401ca6f;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index f461fe58d1..5eb962da5a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -22,7 +22,6 @@ #endif #include -#include #include #include @@ -35,6 +34,7 @@ #include "pbd/stacktrace.h" #include "pbd/convert.h" #include "pbd/boost_debug.h" +#include "pbd/unwind.h" #include "ardour/amp.h" #include "ardour/audio_buffer.h" @@ -46,6 +46,7 @@ #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" @@ -56,6 +57,7 @@ #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" @@ -88,6 +90,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _initial_delay (0) , _roll_delay (0) , _pending_process_reorder (0) + , _pending_signals (0) , _flags (flg) , _pending_declick (true) , _meter_point (MeterPostFader) @@ -134,12 +137,18 @@ Route::init () _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ())); _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_control (_solo_control); add_control (_mute_control); + add_control (_phase_control); /* panning */ @@ -167,11 +176,22 @@ Route::init () /* add amp processor */ - _amp.reset (new Amp (_session)); + _gain_control = boost::shared_ptr (new GainControllable (_session, GainAutomation, shared_from_this ())); + add_control (_gain_control); + + _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true)); add_processor (_amp, PostFader); + if (is_monitor ()) { + _amp->set_display_name (_("Monitor")); + } + /* and input trim */ - _trim.reset (new Amp (_session, "trim")); + + _trim_control = boost::shared_ptr (new GainControllable (_session, TrimAutomation, shared_from_this ())); + add_control (_trim_control); + + _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false)); _trim->set_display_to_user (false); if (dynamic_cast(this)) { @@ -257,7 +277,7 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners) void Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners) { - /* force IDs for master/monitor busses and prevent + /* force IDs for master/monitor busses and prevent any other route from accidentally getting these IDs (i.e. legacy sessions) */ @@ -276,7 +296,7 @@ Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners) /* don't allow it to collide */ - if (!is_master () && !is_monitor() && + if (!is_master () && !is_monitor() && (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) { id += MonitorBusRemoteControlID; } @@ -296,7 +316,7 @@ Route::remote_control_id() const { if (is_master()) { return MasterBusRemoteControlID; - } + } if (is_monitor()) { return MonitorBusRemoteControlID; @@ -364,26 +384,37 @@ Route::ensure_track_or_route_name(string name, Session &session) string newname = name; while (!session.io_name_is_legal (newname)) { - newname = bump_name_once (newname, '.'); + newname = bump_name_once (newname, ' '); } return newname; } 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; } @@ -414,34 +445,28 @@ Route::set_gain (gain_t val, void *src) } } - _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 @@ -498,13 +523,12 @@ Route::process_output_buffers (BufferSet& bufs, /* Tell main outs what to do about monitoring. We do this so that on a transition between monitoring states we get a de-clicking gain - change in the _main_outs delivery. - */ - bool silence = monitoring_state () == MonitoringSilence; + change in the _main_outs delivery, if config.get_use_monitor_fades() + is true. - //but we override this in the case where we have an internal generator - if ( _have_internal_generator ) - silence = false; + We override this in the case where we have an internal generator. + */ + bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence); _main_outs->no_outs_cuz_we_no_monitor (silence); @@ -764,14 +788,14 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t } void -Route::set_listen (bool yn, void* src) +Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override) { if (_solo_safe) { return; } - if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { - _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group)); + if (use_group (group_override, &RouteGroup::is_solo)) { + _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::NoGroup)); return; } @@ -779,13 +803,14 @@ Route::set_listen (bool yn, void* src) if (yn != _monitor_send->active()) { if (yn) { _monitor_send->activate (); - _mute_master->set_soloed (true); + _mute_master->set_soloed_by_self (true); } else { _monitor_send->deactivate (); - _mute_master->set_soloed (false); + _mute_master->set_soloed_by_self (false); } + _mute_master->set_soloed_by_others (false); - listen_changed (src); /* EMIT SIGNAL */ + listen_changed (group_override); /* EMIT SIGNAL */ } } } @@ -801,11 +826,12 @@ Route::listening_via_monitor () const } 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 */ } } @@ -816,7 +842,43 @@ Route::solo_safe() const } void -Route::set_solo (bool yn, void *src) +Route::clear_all_solo_state () +{ + // ideally this function will never do anything, it only exists to forestall Murphy + bool emit_changed = false; + +#ifndef NDEBUG + // these are really debug messages, but of possible interest. + if (_self_solo) { + PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name()); + } + if (_soloed_by_others_upstream || _soloed_by_others_downstream) { + PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"), + name(), _soloed_by_others_upstream, _soloed_by_others_downstream); + } +#endif + + if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) { + // if self-soled, set_solo() will do signal emission + emit_changed = true; + } + + _soloed_by_others_upstream = 0; + _soloed_by_others_downstream = 0; + + { + PBD::Unwinder uw (_solo_safe, false); + set_solo (false, Controllable::NoGroup); + } + + if (emit_changed) { + set_mute_master_solo (); + solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ + } +} + +void +Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) { if (_solo_safe) { DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); @@ -828,27 +890,28 @@ Route::set_solo (bool yn, void *src) return; } - if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) { - _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group)); + if (use_group (group_override, &RouteGroup::is_solo)) { + _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::NoGroup)); 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())); + 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); - set_mute_master_solo (); - solo_changed (true, src); /* EMIT SIGNAL */ + solo_changed (true, group_override); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } + assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active()); + /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF Config->get_solo_mute_overrride(). */ if (yn && Profile->get_trx()) { - set_mute (false, src); + set_mute (false, Controllable::UseGroup); } } @@ -857,17 +920,13 @@ Route::set_self_solo (bool yn) { DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn)); _self_solo = yn; + set_mute_master_solo (); } void Route::mod_solo_by_others_upstream (int32_t delta) { - if (_solo_safe) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name())); - return; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); uint32_t old_sbu = _soloed_by_others_upstream; @@ -905,8 +964,11 @@ Route::mod_solo_by_others_upstream (int32_t delta) (old_sbu > 0 && _soloed_by_others_upstream == 0))) { if (delta > 0 || !Config->get_exclusive_solo()) { - DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n"); + DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name)); for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { + if (i->sends_only) { + continue; + } boost::shared_ptr sr = i->r.lock(); if (sr) { sr->mod_solo_by_others_downstream (-delta); @@ -916,18 +978,13 @@ Route::mod_solo_by_others_upstream (int32_t delta) } set_mute_master_solo (); - solo_changed (false, this); + solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ } void Route::mod_solo_by_others_downstream (int32_t delta) { - if (_solo_safe) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name())); - return; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); if (delta < 0) { @@ -943,19 +1000,22 @@ Route::mod_solo_by_others_downstream (int32_t delta) 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); + solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ } void Route::set_mute_master_solo () { - _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream()); + _mute_master->set_soloed_by_self (self_soloed()); + _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream()); } 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", + name(), _solo_isolated_by_upstream, yn ? "+1" : "-1")); if (!yn) { if (_solo_isolated_by_upstream >= 1) { @@ -969,20 +1029,20 @@ Route::mod_solo_isolated_by_upstream (bool yn, void* src) if (solo_isolated() != old) { /* solo isolated status changed */ - _mute_master->set_solo_ignore (yn); - solo_isolated_changed (src); + _mute_master->set_solo_ignore (solo_isolated()); + 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::NoGroup)); return; } @@ -997,36 +1057,37 @@ Route::set_solo_isolated (bool yn, void *src) } else { if (_solo_isolated == true) { _solo_isolated = false; - _mute_master->set_solo_ignore (false); + _mute_master->set_solo_ignore (false); changed = true; } } - + if (!changed) { return; } - + /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */ boost::shared_ptr routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { continue; } - + bool sends_only; 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); + solo_isolated_changed (); /* EMIT SIGNAL */ + _solo_isolate_control->Changed(); /* EMIT SIGNAL */ } bool @@ -1042,16 +1103,16 @@ Route::set_mute_points (MuteMaster::MutePoint mp) 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::NoGroup)); return; } @@ -1062,7 +1123,7 @@ Route::set_mute (bool yn, void *src) */ act_on_mute (); /* tell everyone else */ - mute_changed (src); /* EMIT SIGNAL */ + mute_changed (); /* EMIT SIGNAL */ _mute_control->Changed (); /* EMIT SIGNAL */ } } @@ -1076,12 +1137,16 @@ Route::muted () const bool Route::muted_by_others () const { + // This method is only used by route_ui for display state. + // The real thing is MuteMaster::muted_by_others_at() + //master is never muted by others if (is_master()) return false; - + //now check to see if something is soloed (and I am not) - return (_session.soloing() && !self_soloed() && !solo_isolated()); + //see also MuteMaster::mute_gain_at() + return (_session.soloing() && !soloed() && !solo_isolated()); } #if 0 @@ -1106,7 +1171,7 @@ Route::before_processor_for_placement (Placement p) Glib::Threads::RWLock::ReaderLock lm (_processor_lock); ProcessorList::iterator loc; - + if (p == PreFader) { /* generic pre-fader: insert immediately before the amp */ loc = find (_processors.begin(), _processors.end(), _amp); @@ -1129,14 +1194,14 @@ Route::before_processor_for_index (int index) } Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - + ProcessorList::iterator i = _processors.begin (); int j = 0; while (i != _processors.end() && j < index) { if ((*i)->display_to_user()) { ++j; } - + ++i; } @@ -1239,7 +1304,7 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< } - if (activation_allowed && (!_session.get_disable_all_loaded_plugins () || !processor->display_to_user ())) { + if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) { processor->activate (); } @@ -1292,7 +1357,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) prop->value() == "lxvst" || prop->value() == "audiounit") { - processor.reset (new PluginInsert (_session)); + if (_session.get_disable_all_loaded_plugins ()) { + processor.reset (new UnknownProcessor (_session, node)); + } else { + processor.reset (new PluginInsert (_session)); + } } else { @@ -1315,11 +1384,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if (processor->set_state (node, version)) { return false; } - + //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO if (i != children.end()) { if ((prop = (*i)->property (X_("active"))) != 0) { - if ( string_is_affirmative (prop->value()) && (!_session.get_disable_all_loaded_plugins () || !processor->display_to_user () ) ) + if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) ) processor->activate(); else processor->deactivate(); @@ -1698,7 +1767,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream if (!removed) { /* what? */ return 1; - } + } if (configure_processors_unlocked (err)) { pstate.restore (); @@ -1881,6 +1950,37 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err) if ((*p)->can_support_io_configuration(in, out)) { 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)); + + if (is_monitor()) { + // restriction for Monitor Section Processors + if (in.n_audio() != out.n_audio() || out.n_midi() > 0) { + /* do not allow to add/remove channels (for now) + * The Monitor follows the master-bus and has no panner (unpan) + * but do allow processors with midi-in to be added (e.g VSTs with control that + * will remain unconnected) + */ + DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n"); + return list > (); + } + if (boost::dynamic_pointer_cast (*p)) { + // internal sends make no sense, only feedback + DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n"); + return list > (); + } + if (boost::dynamic_pointer_cast (*p)) { + /* External Sends can be problematic. one can add/remove ports + * there signal leaves the DAW to external monitors anyway, so there's + * no real use for allowing them here anyway. + */ + DEBUG_TRACE (DEBUG::Processors, "Monitor: No External Sends allowed.\n"); + return list > (); + } + if (boost::dynamic_pointer_cast (*p)) { + // ditto + DEBUG_TRACE (DEBUG::Processors, "Monitor: No Sends allowed.\n"); + return list > (); + } + } in = out; } else { if (err) { @@ -1934,7 +2034,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err) list< pair >::iterator c = configuration.begin(); for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) { - (*p)->configure_io(c->first, c->second); + if (!(*p)->configure_io(c->first, c->second)) { + DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name)); + } processor_max_streams = ChanCount::max(processor_max_streams, c->first); processor_max_streams = ChanCount::max(processor_max_streams, c->second); @@ -1966,7 +2068,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err) } /* make sure we have sufficient scratch buffers to cope with the new processor - configuration + configuration */ _session.ensure_buffers (n_process_buffers ()); @@ -1987,12 +2089,12 @@ Route::all_visible_processors_active (bool state) if (_processors.empty()) { return; } - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { if (!(*i)->display_to_user() || boost::dynamic_pointer_cast (*i)) { continue; } - + if (state) { (*i)->activate (); } else { @@ -2191,6 +2293,11 @@ Route::get_template() XMLNode& Route::state(bool full_state) { + 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)); + } + XMLNode *node = new XMLNode("Route"); ProcessorList::iterator i; char buf[32]; @@ -2253,7 +2360,7 @@ Route::state(bool full_state) for (i = _processors.begin(); i != _processors.end(); ++i) { if (!full_state) { - /* template save: do not include internal sends functioning as + /* template save: do not include internal sends functioning as aux sends because the chance of the target ID in the session where this template is used is not very likely. @@ -2285,6 +2392,10 @@ Route::state(bool full_state) } } + if (!_session._template_state_dir.empty()) { + foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), "")); + } + return *node; } @@ -2400,11 +2511,11 @@ Route::set_state (const XMLNode& node, int version) } 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) { @@ -2559,7 +2670,7 @@ Route::set_state_2X (const XMLNode& node, int version) /* XXX force reset of solo status */ - set_solo (yn, this); + set_solo (yn); } if ((prop = node.property (X_("muted"))) != 0) { @@ -2698,7 +2809,7 @@ Route::set_state_2X (const XMLNode& node, int version) gain_t val; if (sscanf (prop->value().c_str(), "%f", &val) == 1) { - _amp->gain_control()->set_value (val); + _amp->gain_control()->set_value (val, Controllable::NoGroup); } } @@ -2864,8 +2975,11 @@ Route::set_processor_state (const XMLNode& node) prop->value() == "lxvst" || prop->value() == "audiounit") { - processor.reset (new PluginInsert(_session)); - + if (_session.get_disable_all_loaded_plugins ()) { + processor.reset (new UnknownProcessor (_session, **niter)); + } else { + processor.reset (new PluginInsert (_session)); + } } else if (prop->value() == "port") { processor.reset (new PortInsert (_session, _pannable, _mute_master)); @@ -3094,19 +3208,26 @@ Route::remove_aux_or_listen (boost::shared_ptr route) again: for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) { - + boost::shared_ptr d = boost::dynamic_pointer_cast(*x); - + if (d && d->target_route() == route) { rl.release (); - remove_processor (*x, &err, false); + if (remove_processor (*x, &err, false) > 0) { + rl.acquire (); + continue; + } rl.acquire (); /* list could have been demolished while we dropped the lock so start over. */ - - goto again; + if (_session.engine().connected()) { + /* i/o processors cannot be removed if the engine is not running + * so don't live-loop in case the engine is N/A or dies + */ + goto again; + } } } } @@ -3116,7 +3237,7 @@ void Route::set_comment (string cmt, void *src) { _comment = cmt; - comment_changed (src); + comment_changed (); _session.set_dirty (); } @@ -3238,23 +3359,80 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_ void Route::input_change_handler (IOChange change, void * /*src*/) { - bool need_to_queue_solo_change = true; - if ((change.type & IOChange::ConfigurationChanged)) { - /* This is called with the process lock held if change - contains ConfigurationChanged + /* This is called with the process lock held if change + contains ConfigurationChanged */ - need_to_queue_solo_change = false; configure_processors (0); _phase_invert.resize (_input->n_ports().n_audio ()); io_changed (); /* EMIT SIGNAL */ } - if (!_input->connected() && _soloed_by_others_upstream) { - if (need_to_queue_solo_change) { - _session.cancel_solo_after_disconnect (shared_from_this(), true); - } else { - cancel_solo_after_disconnect (true); + if (_soloed_by_others_upstream || _solo_isolated_by_upstream) { + int sbou = 0; + int ibou = 0; + boost::shared_ptr routes = _session.get_routes (); + if (_input->connected()) { + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only); + if (does_feed && !sends_only) { + if ((*i)->soloed()) { + ++sbou; + } + if ((*i)->solo_isolated()) { + ++ibou; + } + } + } + } + + int delta = sbou - _soloed_by_others_upstream; + int idelta = ibou - _solo_isolated_by_upstream; + + if (idelta < -1) { + PBD::warning << string_compose ( + _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"), + _name, ibou, _solo_isolated_by_upstream, idelta) + << endmsg; + + } + + if (_soloed_by_others_upstream) { + // ignore new connections (they're not propagated) + if (delta <= 0) { + mod_solo_by_others_upstream (delta); + } + } + + if (_solo_isolated_by_upstream) { + // solo-isolate currently only propagates downstream + if (idelta < 0) { + 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? + //_solo_isolated_by_upstream = ibou; + } + + // Session::route_solo_changed does not propagate indirect solo-changes + // propagate downstream to tracks + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = feeds (*i, &sends_only); + if (delta <= 0 && does_feed && !sends_only) { + (*i)->mod_solo_by_others_upstream (delta); + } + + if (idelta < 0 && does_feed && !sends_only) { + (*i)->mod_solo_isolated_by_upstream (false); + } } } } @@ -3262,16 +3440,14 @@ Route::input_change_handler (IOChange change, void * /*src*/) void Route::output_change_handler (IOChange change, void * /*src*/) { - bool need_to_queue_solo_change = true; if (_initial_io_setup) { return; } if ((change.type & IOChange::ConfigurationChanged)) { - /* This is called with the process lock held if change - contains ConfigurationChanged + /* This is called with the process lock held if change + contains ConfigurationChanged */ - need_to_queue_solo_change = false; configure_processors (0); if (is_master()) { @@ -3281,25 +3457,48 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (!_output->connected() && _soloed_by_others_downstream) { - if (need_to_queue_solo_change) { - _session.cancel_solo_after_disconnect (shared_from_this(), false); - } else { - cancel_solo_after_disconnect (false); + if (_soloed_by_others_downstream) { + int sbod = 0; + /* checking all all downstream routes for + * explicit of implict solo is a rather drastic measure, + * ideally the input_change_handler() of the other route + * would propagate the change to us. + */ + boost::shared_ptr routes = _session.get_routes (); + if (_output->connected()) { + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = direct_feeds_according_to_reality (*i, &sends_only); + if (does_feed && !sends_only) { + if ((*i)->soloed()) { + ++sbod; + break; + } + } + } } - } -} + int delta = sbod - _soloed_by_others_downstream; + if (delta <= 0) { + // do not allow new connections to change implicit solo (no propagation) + mod_solo_by_others_downstream (delta); + // Session::route_solo_changed() does not propagate indirect solo-changes + // propagate upstream to tracks + for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { + if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + continue; + } + bool sends_only; + bool does_feed = (*i)->feeds (shared_from_this(), &sends_only); + if (delta != 0 && does_feed && !sends_only) { + (*i)->mod_solo_by_others_downstream (delta); + } + } -void -Route::cancel_solo_after_disconnect (bool upstream) -{ - if (upstream) { - _soloed_by_others_upstream = 0; - } else { - _soloed_by_others_downstream = 0; + } } - set_mute_master_solo (); - solo_changed (false, this); } uint32_t @@ -3553,9 +3752,9 @@ Route::set_meter_point_unlocked () /* Set up the meter for its new position */ ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter); - + ChanCount m_in; - + if (loc == _processors.begin()) { m_in = _input->n_ports(); } else { @@ -3588,6 +3787,7 @@ Route::listen_position_changed () ProcessorState pstate (this); if (configure_processors_unlocked (0)) { + DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n"); pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... return; @@ -3690,115 +3890,6 @@ Route::set_latency_compensation (framecnt_t longest_session_latency) } } -Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r) - : AutomationControl (r->session(), - Evoral::Parameter (SoloAutomation), - ParameterDescriptor(Evoral::Parameter (SoloAutomation)), - boost::shared_ptr(), name) - , _route (r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::SoloControllable::set_value (double val) -{ - const bool bval = ((val >= 0.5) ? true : false); - - boost::shared_ptr rl (new RouteList); - - boost::shared_ptr 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 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 r) - : AutomationControl (r->session(), - Evoral::Parameter (MuteAutomation), - ParameterDescriptor (Evoral::Parameter (MuteAutomation)), - boost::shared_ptr(), - name) - , _route (r) -{ - boost::shared_ptr 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. */ - - bool to_list = _list && ((AutomationList*)_list.get())->automation_write(); - - Control::set_double (muted, _session.transport_frame(), to_list); -} - -void -Route::MuteControllable::set_value (double val) -{ - const bool bval = ((val >= 0.5) ? true : false); - - boost::shared_ptr r = _route.lock (); - if (!r) { - return; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Playing back automation, set route mute directly - r->set_mute (bval, this); - } else { - // Set from user, queue mute event - boost::shared_ptr rl (new RouteList); - rl->push_back (r); - _session.set_mute (rl, bval, Session::rt_cleanup); - } - - // Set superficial/automation value to drive controller (and possibly record) - set_superficial_value(bval); -} - -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 r = _route.lock (); - return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; -} - void Route::set_block_size (pframes_t nframes) { @@ -3901,17 +3992,33 @@ Route::shift (framepos_t pos, framecnt_t frames) } } +void +Route::set_plugin_state_dir (boost::weak_ptr p, const std::string& d) +{ + boost::shared_ptr processor (p.lock ()); + boost::shared_ptr pi = boost::dynamic_pointer_cast (processor); + if (!pi) { + return; + } + pi->set_state_dir (d); +} int Route::save_as_template (const string& path, const string& name) { + std::string state_dir = path.substr (0, path.find_last_of ('.')); // strip template_suffix + PBD::Unwinder uw (_session._template_state_dir, state_dir); + XMLNode& node (state (false)); + XMLTree tree; IO::set_name_in_state (*node.children().front(), name); tree.set_root (&node); - return tree.write (path.c_str()); + + /* return zero on success, non-zero otherwise */ + return !tree.write (path.c_str()); } @@ -3952,13 +4059,13 @@ Route::set_name (const string& str) * @param name New name. */ void -Route::set_name_in_state (XMLNode& node, string const & name) +Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist) { node.add_property (X_("name"), name); XMLNodeList children = node.children(); for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) { - + if ((*i)->name() == X_("IO")) { IO::set_name_in_state (**i, name); @@ -3969,12 +4076,14 @@ Route::set_name_in_state (XMLNode& node, string const & name) if (role && role->value() == X_("Main")) { (*i)->add_property (X_("name"), name); } - + } else if ((*i)->name() == X_("Diskstream")) { - (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str()); + if (rename_playlist) { + (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str()); + } (*i)->add_property (X_("name"), name); - + } } } @@ -4006,6 +4115,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 (); } } @@ -4087,10 +4197,26 @@ Route::panner_shell() const return _main_outs->panner_shell(); } -boost::shared_ptr +boost::shared_ptr Route::gain_control() const { - return _amp->gain_control(); + return _gain_control; +} + +boost::shared_ptr +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 @@ -4124,10 +4250,10 @@ Route::get_control (const Evoral::Parameter& param) } boost::shared_ptr -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 (*i)) { @@ -4141,13 +4267,21 @@ Route::nth_plugin (uint32_t n) } boost::shared_ptr -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 (*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; } @@ -4229,6 +4363,11 @@ Route::unknown_processors () const { list p; + if (_session.get_disable_all_loaded_plugins ()) { + // Do not list "missing plugins" if they are explicitly disabled + return p; + } + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { if (boost::dynamic_pointer_cast (*i)) { @@ -4255,17 +4394,17 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn } else { all_connections.min = ~((pframes_t) 0); all_connections.max = 0; - + /* iterate over all "from" ports and determine the latency range for all of their connections to the "outside" (outside of this Route). */ - + for (PortSet::iterator p = from.begin(); p != from.end(); ++p) { - + LatencyRange range; - + p->get_connected_latency_range (range, playback); - + all_connections.min = min (all_connections.min, range.min); all_connections.max = max (all_connections.max, range.max); } @@ -4368,7 +4507,7 @@ Route::setup_invisible_processors () /* we'll build this new list here and then use it * - * TODO put the ProcessorList is on the stack for RT-safety. + * TODO put the ProcessorList is on the stack for RT-safety. */ ProcessorList new_processors; @@ -4449,34 +4588,29 @@ Route::setup_invisible_processors () if (_monitor_send && !is_monitor ()) { assert (!_monitor_send->display_to_user ()); - if (Config->get_solo_control_is_listen_control()) { - switch (Config->get_listen_position ()) { - case PreFaderListen: - switch (Config->get_pfl_position ()) { - case PFLFromBeforeProcessors: - new_processors.push_front (_monitor_send); - break; - case PFLFromAfterProcessors: - new_processors.insert (amp, _monitor_send); - break; - } - _monitor_send->set_can_pan (false); + switch (Config->get_listen_position ()) { + case PreFaderListen: + switch (Config->get_pfl_position ()) { + case PFLFromBeforeProcessors: + new_processors.push_front (_monitor_send); break; - case AfterFaderListen: - switch (Config->get_afl_position ()) { - case AFLFromBeforeProcessors: - new_processors.insert (after_amp, _monitor_send); - break; - case AFLFromAfterProcessors: - new_processors.insert (new_processors.end(), _monitor_send); - break; - } - _monitor_send->set_can_pan (true); + case PFLFromAfterProcessors: + new_processors.insert (amp, _monitor_send); break; } - } else { - new_processors.insert (new_processors.end(), _monitor_send); _monitor_send->set_can_pan (false); + break; + case AfterFaderListen: + switch (Config->get_afl_position ()) { + case AFLFromBeforeProcessors: + new_processors.insert (after_amp, _monitor_send); + break; + case AFLFromAfterProcessors: + new_processors.insert (new_processors.end(), _monitor_send); + break; + } + _monitor_send->set_can_pan (true); + break; } } @@ -4490,7 +4624,7 @@ Route::setup_invisible_processors () if (_monitor_control && is_monitor ()) { assert (!_monitor_control->display_to_user ()); - new_processors.push_front (_monitor_control); + new_processors.insert (amp, _monitor_control); } /* INTERNAL RETURN */ @@ -4518,7 +4652,7 @@ Route::setup_invisible_processors () _processors = new_processors; for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if (!(*i)->display_to_user () && !(*i)->active ()) { + if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) { (*i)->activate (); } } @@ -4558,7 +4692,7 @@ Route::maybe_note_meter_position () if (_meter_point != MeterCustom) { return; } - + _custom_meter_position_noted = true; /* custom meter points range from after trim to before panner/main_outs * this is a limitation by the current processor UI @@ -4627,7 +4761,7 @@ Route::has_external_redirects () const /* ignore inactive processors and obviously ignore the main * outs since everything has them and we don't care. */ - + if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) { return true;; } @@ -4673,11 +4807,12 @@ Route::non_realtime_locate (framepos_t pos) { //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::ReaderLock lm (_processor_lock); - + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { (*i)->transport_located (pos); } } + _roll_delay = _initial_delay; } void @@ -4685,9 +4820,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pfram { size_t n_buffers; size_t i; - - /* MIDI - * + + /* MIDI + * * We don't currently mix MIDI input together, so we don't need the * complex logic of the audio case. */ @@ -4698,7 +4833,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pfram boost::shared_ptr source_port = io->midi (i); MidiBuffer& buf (bufs.get_midi (i)); - + if (source_port) { buf.copy (source_port->get_midi_buffer(nframes)); } else { @@ -4716,19 +4851,19 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pfram if (n_ports > n_buffers) { scaling = ((float) n_buffers) / n_ports; } - + for (i = 0; i < n_ports; ++i) { - + /* if there are more ports than buffers, map them onto buffers * in a round-robin fashion */ boost::shared_ptr source_port = io->audio (i); AudioBuffer& buf (bufs.get_audio (i%n_buffers)); - + if (i < n_buffers) { - + /* first time through just copy a channel into the output buffer. */ @@ -4738,11 +4873,11 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pfram if (scaling != 1.0f) { buf.apply_gain (scaling, nframes); } - + } else { - + /* on subsequent times around, merge data from - * the port with what is already there + * the port with what is already there */ if (scaling != 1.0f) { @@ -4769,3 +4904,452 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr io, pfram bufs.set_count (io->n_ports()); } } + +boost::shared_ptr +Route::pan_azimuth_control() const +{ +#ifdef MIXBUS + boost::shared_ptr plug = ch_post(); + if (!plug) { + return boost::shared_ptr(); + } + const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan))); +#else + if (!_pannable || !panner()) { + return boost::shared_ptr(); + } + return _pannable->pan_azimuth_control; +#endif +} + +boost::shared_ptr +Route::pan_elevation_control() const +{ + if (Profile->get_mixbus() || !_pannable || !panner()) { + return boost::shared_ptr(); + } + + set c = panner()->what_can_be_automated (); + + if (c.find (PanElevationAutomation) != c.end()) { + return _pannable->pan_elevation_control; + } else { + return boost::shared_ptr(); + } +} +boost::shared_ptr +Route::pan_width_control() const +{ + if (Profile->get_mixbus() || !_pannable || !panner()) { + return boost::shared_ptr(); + } + + set c = panner()->what_can_be_automated (); + + if (c.find (PanWidthAutomation) != c.end()) { + return _pannable->pan_width_control; + } else { + return boost::shared_ptr(); + } +} +boost::shared_ptr +Route::pan_frontback_control() const +{ + if (Profile->get_mixbus() || !_pannable || !panner()) { + return boost::shared_ptr(); + } + + set c = panner()->what_can_be_automated (); + + if (c.find (PanFrontBackAutomation) != c.end()) { + return _pannable->pan_frontback_control; + } else { + return boost::shared_ptr(); + } +} +boost::shared_ptr +Route::pan_lfe_control() const +{ + if (Profile->get_mixbus() || !_pannable || !panner()) { + return boost::shared_ptr(); + } + + set c = panner()->what_can_be_automated (); + + if (c.find (PanLFEAutomation) != c.end()) { + return _pannable->pan_lfe_control; + } else { + return boost::shared_ptr(); + } +} + +uint32_t +Route::eq_band_cnt () const +{ + if (Profile->get_mixbus()) { + return 3; + } else { + /* Ardour has no well-known EQ object */ + return 0; + } +} + +boost::shared_ptr +Route::eq_gain_controllable (uint32_t band) const +{ +#ifdef MIXBUS + boost::shared_ptr eq = ch_eq(); + + if (!eq) { + return boost::shared_ptr(); + } + + uint32_t port_number; + switch (band) { + case 0: + if (is_master() || mixbus()) { + port_number = 4; + } else { + port_number = 8; + } + break; + case 1: + if (is_master() || mixbus()) { + port_number = 3; + } else { + port_number = 6; + } + break; + case 2: + if (is_master() || mixbus()) { + port_number = 2; + } else { + port_number = 4; + } + break; + default: + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::eq_freq_controllable (uint32_t band) const +{ +#ifdef MIXBUS + + if (mixbus() || is_master()) { + /* no frequency controls for mixbusses or master */ + return boost::shared_ptr(); + } + + boost::shared_ptr eq = ch_eq(); + + if (!eq) { + return boost::shared_ptr(); + } + + uint32_t port_number; + switch (band) { + case 0: + port_number = 7; + break; + case 1: + port_number = 5; + break; + case 2: + port_number = 3; + break; + default: + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number))); +#else + return boost::shared_ptr(); +#endif +} + +boost::shared_ptr +Route::eq_q_controllable (uint32_t band) const +{ + return boost::shared_ptr(); +} + +boost::shared_ptr +Route::eq_shape_controllable (uint32_t band) const +{ + return boost::shared_ptr(); +} + +boost::shared_ptr +Route::eq_enable_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr eq = ch_eq(); + + if (!eq) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1))); +#else + return boost::shared_ptr(); +#endif +} + +boost::shared_ptr +Route::eq_hpf_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr eq = ch_eq(); + + if (!eq) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2))); +#else + return boost::shared_ptr(); +#endif +} + +string +Route::eq_band_name (uint32_t band) const +{ + if (Profile->get_mixbus()) { + switch (band) { + case 0: + return _("lo"); + case 1: + return _("mid"); + case 2: + return _("hi"); + default: + return string(); + } + } else { + return string (); + } +} + +boost::shared_ptr +Route::comp_enable_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 1))); +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::comp_threshold_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2))); + +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::comp_speed_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3))); +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::comp_mode_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4))); +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::comp_makeup_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5))); +#else + return boost::shared_ptr(); +#endif +} +boost::shared_ptr +Route::comp_redux_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr comp = ch_comp(); + + if (!comp) { + return boost::shared_ptr(); + } + + return boost::dynamic_pointer_cast (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); +#else + return boost::shared_ptr(); +#endif +} + +string +Route::comp_mode_name (uint32_t mode) const +{ +#ifdef MIXBUS + switch (mode) { + case 0: + return _("Leveler"); + case 1: + return _("Compressor"); + case 2: + return _("Limiter"); + case 3: + return mixbus() ? _("Sidechain") : _("Limiter"); + } + + return _("???"); +#else + return _("???"); +#endif +} + +string +Route::comp_speed_name (uint32_t mode) const +{ +#ifdef MIXBUS + switch (mode) { + case 0: + return _("Attk"); + case 1: + return _("Ratio"); + case 2: + case 3: + return _("Rels"); + } + return _("???"); +#else + return _("???"); +#endif +} + +boost::shared_ptr +Route::send_level_controllable (uint32_t n) const +{ +#ifdef MIXBUS + boost::shared_ptr plug = ch_post(); + if (!plug) { + return boost::shared_ptr(); + } + + if (n >= 8) { + /* no such bus */ + return boost::shared_ptr(); + } + + const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id))); +#else + boost::shared_ptr s = boost::dynamic_pointer_cast(nth_send (n)); + if (!s) { + return boost::shared_ptr(); + } + return s->gain_control (); +#endif +} + +boost::shared_ptr +Route::send_enable_controllable (uint32_t n) const +{ +#ifdef MIXBUS + boost::shared_ptr plug = ch_post(); + if (!plug) { + return boost::shared_ptr(); + } + + if (n >= 8) { + /* no such bus */ + return boost::shared_ptr(); + } + + const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h + return boost::dynamic_pointer_cast (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(); +#endif +} + +string +Route::send_name (uint32_t n) const +{ +#ifdef MIXBUS + if (n >= 8) { + return string(); + } + boost::shared_ptr r = _session.get_mixbus (n); + assert (r); + return r->name(); +#else + boost::shared_ptr p = nth_send (n); + if (p) { + return p->name(); + } else { + return string(); + } +#endif +} + +boost::shared_ptr +Route::master_send_enable_controllable () const +{ +#ifdef MIXBUS + boost::shared_ptr plug = ch_post(); + if (!plug) { + return boost::shared_ptr(); + } + return boost::dynamic_pointer_cast (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign))); +#else + return boost::shared_ptr(); +#endif +}