X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=75f58b528a7cd50f378d8df966c7ecf36a46686c;hb=22a20c7333a14ac0c4af20287d8643e07ff92903;hp=ca01b597c0feed4669993bc3ca1363efba865d52;hpb=44f1f0caf49ff8c4fa7e656606b141bdab0276da;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index ca01b597c0..75f58b528a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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" @@ -135,12 +136,15 @@ 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_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 */ @@ -168,11 +172,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)) { @@ -372,19 +387,30 @@ Route::ensure_track_or_route_name(string name, Session &session) } 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; } @@ -415,34 +441,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); -} - -void -Route::inc_trim (gain_t fraction, void *src) -{ - _trim->inc_gain (fraction, src); + _gain_control->route_set_value (val); } 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 @@ -764,19 +784,14 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t } 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::NoGroup)); return; } @@ -791,7 +806,7 @@ Route::set_listen (bool yn, void* src, bool group_override) } _mute_master->set_soloed_by_others (false); - listen_changed (src, group_override); /* EMIT SIGNAL */ + listen_changed (group_override); /* EMIT SIGNAL */ } } } @@ -807,11 +822,11 @@ 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 (); } } @@ -848,17 +863,17 @@ Route::clear_all_solo_state () { PBD::Unwinder 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) { if (_solo_safe) { DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); @@ -870,22 +885,17 @@ Route::set_solo (bool yn, void *src, bool group_override) 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::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, group_override); /* EMIT SIGNAL */ + solo_changed (true, group_override); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } @@ -896,7 +906,7 @@ Route::set_solo (bool yn, void *src, bool group_override) */ if (yn && Profile->get_trx()) { - set_mute (false, src); + set_mute (false, Controllable::UseGroup); } } @@ -905,6 +915,7 @@ 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 @@ -962,7 +973,7 @@ Route::mod_solo_by_others_upstream (int32_t delta) } set_mute_master_solo (); - solo_changed (false, this, false); /* EMIT SIGNAL */ + solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ } void @@ -984,7 +995,7 @@ 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, false); /* EMIT SIGNAL */ + solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ } void @@ -995,7 +1006,7 @@ Route::set_mute_master_solo () } 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", @@ -1014,19 +1025,19 @@ Route::mod_solo_isolated_by_upstream (bool yn, void* src) 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::NoGroup)); return; } @@ -1064,13 +1075,13 @@ Route::set_solo_isolated (bool yn, void *src) 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 */ } bool @@ -1086,16 +1097,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; } @@ -1106,7 +1117,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 */ } } @@ -1933,6 +1944,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) { @@ -2245,6 +2287,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]; @@ -2339,6 +2386,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; } @@ -2454,11 +2505,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) { @@ -2613,7 +2664,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) { @@ -2752,7 +2803,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); } } @@ -3180,7 +3231,7 @@ void Route::set_comment (string cmt, void *src) { _comment = cmt; - comment_changed (src); + comment_changed (); _session.set_dirty (); } @@ -3354,7 +3405,7 @@ Route::input_change_handler (IOChange change, void * /*src*/) 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? @@ -3374,7 +3425,7 @@ Route::input_change_handler (IOChange change, void * /*src*/) } if (idelta < 0 && does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (false, this); + (*i)->mod_solo_isolated_by_upstream (false); } } } @@ -3833,12 +3884,82 @@ 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) +void +Route::set_control (AutomationType type, double val, PBD::Controllable::GroupControlDisposition group_override) +{ + boost::shared_ptr rl; + + switch (type) { + case GainAutomation: + /* route must mediate group control */ + set_gain (val, group_override); + return; + break; + + case TrimAutomation: + /* route must mediate group control */ + set_trim (val, group_override); + 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, Session::rt_cleanup, group_override); + 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, Session::rt_cleanup, group_override); + } 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(), Session::rt_cleanup, group_override); + return; + break; + + default: + /* Not a route automation control */ + fatal << string_compose (_("programming error: %1%2\n"), X_("illegal type of route automation control passed to Route::set_control(): "), enum_2_string(type)) << endmsg; + /*NOTREACHED*/ + return; + } +} + + +Route::RouteAutomationControl::RouteAutomationControl (const std::string& name, + AutomationType atype, + boost::shared_ptr alist, + boost::shared_ptr r) + : AutomationControl (r->session(), Evoral::Parameter (atype), + ParameterDescriptor (Evoral::Parameter (atype)), + alist, name) , _route (r) +{ +} + +Route::GainControllable::GainControllable (Session& s, AutomationType atype, boost::shared_ptr r) + : GainControl (s, Evoral::Parameter(atype)) + , _route (r) +{ + +} + +Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r) + : RouteAutomationControl (name, SoloAutomation, boost::shared_ptr(), r) { boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloAutomation))); gl->set_interpolation(Evoral::ControlList::Discrete); @@ -3846,7 +3967,15 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr= 0.5) ? true : false); @@ -3860,12 +3989,20 @@ Route::SoloControllable::set_value (double val) rl->push_back (r); if (Config->get_solo_control_is_listen_control()) { - _session.set_listen (rl, bval); + _session.set_listen (rl, bval, Session::rt_cleanup, group_override); } else { - _session.set_solo (rl, bval); + _session.set_solo (rl, bval, Session::rt_cleanup, group_override); } } +void +Route::SoloControllable::set_value_unchecked (double val) +{ + /* Used only by automation playback */ + + _set_value (val, Controllable::NoGroup); +} + double Route::SoloControllable::get_value () const { @@ -3882,11 +4019,7 @@ Route::SoloControllable::get_value () const } Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr r) - : AutomationControl (r->session(), - Evoral::Parameter (MuteAutomation), - ParameterDescriptor (Evoral::Parameter (MuteAutomation)), - boost::shared_ptr(), - name) + : RouteAutomationControl (name, MuteAutomation, boost::shared_ptr(), r) , _route (r) { boost::shared_ptr gl(new AutomationList(Evoral::Parameter(MuteAutomation))); @@ -3900,13 +4033,41 @@ 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(); + 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 (val, group_override); + } +} - Control::set_double (muted, _session.transport_frame(), to_list); +void +Route::MuteControllable::set_value_unchecked (double val) +{ + /* used only automation playback */ + _set_value (val, Controllable::NoGroup); } void -Route::MuteControllable::set_value (double val) +Route::MuteControllable::_set_value (double val, Controllable::GroupControlDisposition group_override) { const bool bval = ((val >= 0.5) ? true : false); @@ -3916,17 +4077,16 @@ Route::MuteControllable::set_value (double val) } 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); + r->set_mute (bval, Controllable::NoGroup); } 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); + _session.set_mute (rl, bval, Session::rt_cleanup, group_override); } - - // Set superficial/automation value to drive controller (and possibly record) - set_superficial_value(bval); } double @@ -3942,6 +4102,46 @@ Route::MuteControllable::get_value () const return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; } +Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr r) + : RouteAutomationControl (name, PhaseAutomation, boost::shared_ptr(), r) +{ + boost::shared_ptr 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 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 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) { @@ -4044,17 +4244,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()); } @@ -4095,7 +4311,7 @@ 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); @@ -4115,7 +4331,9 @@ Route::set_name_in_state (XMLNode& node, string const & 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); } @@ -4230,10 +4448,16 @@ 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 @@ -4633,7 +4857,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 */ @@ -4913,3 +5137,362 @@ 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(); + assert (plug); + 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"); + } + + 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: + return _("Rels"); + } + return _("???"); +#else + return _("???"); +#endif +}