X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Froute.cc;h=a45e30a91b4bab0ddf8c4d9255047bc5416a055d;hb=e1bcd70712b1aa35fa59d812d679576c88171d0f;hp=8a98149fa05f55f6bdbaddca9647479d6b2f2d80;hpb=7e463bab6eb5a733c507c90cd4cb554b1ca1c34f;p=ardour.git diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 8a98149fa0..a45e30a91b 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -58,6 +58,7 @@ #include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/parameter_descriptor.h" +#include "ardour/phase_control.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" #include "ardour/port_insert.h" @@ -67,8 +68,11 @@ #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" +#include "ardour/solo_control.h" +#include "ardour/solo_isolate_control.h" #include "ardour/unknown_processor.h" #include "ardour/utils.h" +#include "ardour/vca.h" #include "i18n.h" @@ -78,10 +82,12 @@ using namespace PBD; PBD::Signal0 Route::SyncOrderKeys; PBD::Signal0 Route::RemoteControlIDChange; +PBD::Signal3, boost::shared_ptr, Route::PluginSetupOptions > Route::PluginSetup; /** Base class for all routable/mixable objects (tracks and busses) */ Route::Route (Session& sess, string name, Flag flg, DataType default_type) - : SessionObject (sess, name) + : Stripable (sess, name) + , Muteable (sess, name) , Automatable (sess) , GraphNode (sess._process_graph) , _active (true) @@ -97,18 +103,11 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _meter_point (MeterPostFader) , _pending_meter_point (MeterPostFader) , _meter_type (MeterPeak) - , _self_solo (false) - , _soloed_by_others_upstream (0) - , _soloed_by_others_downstream (0) - , _solo_isolated (false) - , _solo_isolated_by_upstream (0) , _denormal_protection (false) , _recordable (true) , _silent (false) , _declickable (false) - , _mute_master (new MuteMaster (sess, name)) , _have_internal_generator (false) - , _solo_safe (false) , _default_type (default_type) , _order_key (0) , _has_order_key (false) @@ -139,21 +138,30 @@ Route::init () /* add standard controls */ - _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 ())); + _gain_control.reset (new GainControl (_session, GainAutomation)); + add_control (_gain_control); - _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ())); - _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ())); + _trim_control.reset (new GainControl (_session, TrimAutomation)); + add_control (_trim_control); + _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *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); + _solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2)); + + _mute_control.reset (new MuteControl (_session, X_("mute"), *this)); + _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); add_control (_mute_control); + + _phase_control.reset (new PhaseControl (_session, X_("phase"))); add_control (_phase_control); + _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this)); + add_control (_solo_isolate_control); + + _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe"))); + add_control (_solo_safe_control); + /* panning */ if (!(_flags & Route::MonitorOut)) { @@ -171,17 +179,9 @@ Route::init () _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2)); _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1)); -#if 0 // not used - just yet - if (!is_master() && !is_monitor() && !is_auditioner()) { - _delayline.reset (new DelayLine (_session, _name)); - add_processor (_delayline, PreFader); - } -#endif - - /* add amp processor */ - - _gain_control = boost::shared_ptr (new GainControllable (_session, GainAutomation, shared_from_this ())); - add_control (_gain_control); + /* add the amp/fader processor. + * it should be the first processor to be added on every route. + */ _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true)); add_processor (_amp, PostFader); @@ -190,10 +190,14 @@ Route::init () _amp->set_display_name (_("Monitor")); } - /* and input trim */ +#if 0 // not used - just yet + if (!is_master() && !is_monitor() && !is_auditioner()) { + _delayline.reset (new DelayLine (_session, _name)); + add_processor (_delayline, PreFader); + } +#endif - _trim_control = boost::shared_ptr (new GainControllable (_session, TrimAutomation, shared_from_this ())); - add_control (_trim_control); + /* and input trim */ _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false)); _trim->set_display_to_user (false); @@ -394,83 +398,11 @@ Route::ensure_track_or_route_name(string name, Session &session) return newname; } -void -Route::inc_gain (gain_t factor) -{ - /* 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, Controllable::GroupControlDisposition group_override) -{ - if (use_group (group_override, &RouteGroup::is_gain)) { - - if (_route_group->is_relative()) { - - gain_t usable_gain = _gain_control->get_value(); - if (usable_gain < 0.000001f) { - usable_gain = 0.000001f; - } - - gain_t delta = val; - if (delta < 0.000001f) { - delta = 0.000001f; - } - - delta -= usable_gain; - - if (delta == 0.0f) - return; - - gain_t factor = delta / usable_gain; - - if (factor > 0.0f) { - factor = _route_group->get_max_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed(); /* EMIT SIGNAL */ - return; - } - } else { - factor = _route_group->get_min_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed(); /* EMIT SIGNAL */ - return; - } - } - - _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor)); - - } else { - - _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup)); - } - - return; - } - - if (val == _gain_control->get_value()) { - return; - } - - _gain_control->route_set_value (val); -} - void Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */) { // TODO route group, see set_gain() - _trim_control->route_set_value (val); + // _trim_control->route_set_value (val); } void @@ -547,7 +479,7 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (_phase_invert.any ()) { + if (!_phase_control->none()) { int chn = 0; @@ -556,7 +488,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; sp[nx] += 1.0e-27f; @@ -573,7 +505,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; } @@ -804,291 +736,48 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t } void -Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override) +Route::set_listen (bool yn) { - if (_solo_safe) { - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup)); - return; - } - if (_monitor_send) { - if (yn != _monitor_send->active()) { - if (yn) { - _monitor_send->activate (); - _mute_master->set_soloed_by_self (true); - } else { - _monitor_send->deactivate (); - _mute_master->set_soloed_by_self (false); - } - _mute_master->set_soloed_by_others (false); - - listen_changed (group_override); /* EMIT SIGNAL */ - } - } -} - -bool -Route::listening_via_monitor () const -{ - if (_monitor_send) { - return _monitor_send->active (); - } else { - return false; - } -} - -void -Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */) -{ - if (_solo_safe != yn) { - _solo_safe = yn; - solo_safe_changed (); /* EMIT SIGNAL */ - _solo_safe_control->Changed(); /* EMIT SIGNAL */ - } -} - -bool -Route::solo_safe() const -{ - return _solo_safe; -} - -void -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) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n", - name(), yn, enum_2_string(group_override), self_soloed())); - - if (_solo_safe) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); - return; - } - - if (is_master() || is_monitor() || is_auditioner()) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name())); - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup)); - return; - } - - if (self_soloed() != yn) { - set_self_solo (yn); - 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, Controllable::UseGroup); - } -} - -void -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) -{ - 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; - - if (delta < 0) { - if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { - _soloed_by_others_upstream += delta; + if (yn) { + _monitor_send->activate (); } else { - _soloed_by_others_upstream = 0; - } - } else { - _soloed_by_others_upstream += delta; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ( - "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", - name(), delta, _soloed_by_others_upstream, old_sbu, - _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); - - /* push the inverse solo change to everything that feeds us. - - This is important for solo-within-group. When we solo 1 track out of N that - feed a bus, that track will cause mod_solo_by_upstream (+1) to be called - on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all - tracks that feed it. This will silence them if they were audible because - of a bus solo, but the newly soloed track will still be audible (because - it is self-soloed). - - but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), - not in reverse. - */ - - if ((_self_solo || _soloed_by_others_downstream) && - ((old_sbu == 0 && _soloed_by_others_upstream > 0) || - (old_sbu > 0 && _soloed_by_others_upstream == 0))) { - - if (delta > 0 || !Config->get_exclusive_solo()) { - 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); - } - } + _monitor_send->deactivate (); } } - - set_mute_master_solo (); - solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ } void -Route::mod_solo_by_others_downstream (int32_t delta) +Route::solo_control_changed (bool, Controllable::GroupControlDisposition) { - 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)); + /* nothing to do if we're not using AFL/PFL. But if we are, we need + to alter the active state of the monitor send. + */ - if (delta < 0) { - if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { - _soloed_by_others_downstream += delta; - } else { - _soloed_by_others_downstream = 0; - } - } else { - _soloed_by_others_downstream += delta; + if (Config->get_solo_control_is_listen_control ()) { + set_listen (_solo_control->self_soloed()); } - - 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, Controllable::UseGroup); /* EMIT SIGNAL */ } -void -Route::set_mute_master_solo () -{ - _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) +bool +Route::listening_via_monitor () const { - 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) { - _solo_isolated_by_upstream--; - } else { - _solo_isolated_by_upstream = 0; - } + if (_monitor_send) { + return _monitor_send->active (); } else { - _solo_isolated_by_upstream++; - } - - if (solo_isolated() != old) { - /* solo isolated status changed */ - _mute_master->set_solo_ignore (solo_isolated()); - solo_isolated_changed (); /* EMIT SIGNAL */ + return false; } } void -Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override) +Route::push_solo_isolate_upstream (int32_t delta) { - if (is_master() || is_monitor() || is_auditioner()) { - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup)); - return; - } - - bool changed = false; - - if (yn) { - if (_solo_isolated == false) { - _mute_master->set_solo_ignore (true); - changed = true; - } - _solo_isolated = true; - } else { - if (_solo_isolated == true) { - _solo_isolated = 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()) { + if ((*i).get() == this || !(*i)->can_solo()) { continue; } @@ -1096,75 +785,26 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o bool does_feed = feeds (*i, &sends_only); if (does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (yn); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta); } } - - /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - - solo_isolated_changed (); /* EMIT SIGNAL */ - _solo_isolate_control->Changed(); /* EMIT SIGNAL */ -} - -bool -Route::solo_isolated () const -{ - return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0); -} - -void -Route::set_mute_points (MuteMaster::MutePoint mp) -{ - _mute_master->set_mute_points (mp); - mute_points_changed (); /* EMIT SIGNAL */ - - if (_mute_master->muted_by_self()) { - mute_changed (); /* EMIT SIGNAL */ - _mute_control->Changed (); /* EMIT SIGNAL */ - } } void -Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override) +Route::push_solo_upstream (int delta) { - if (use_group (group_override, &RouteGroup::is_mute)) { - _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup)); - return; - } - - if (muted() != yn) { - _mute_master->set_muted_by_self (yn); - /* allow any derived classes to respond to the mute change - before anybody else knows about it. - */ - act_on_mute (); - /* tell everyone else */ - mute_changed (); /* EMIT SIGNAL */ - _mute_control->Changed (); /* EMIT SIGNAL */ + 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->solo_control()->mod_solo_by_others_downstream (-delta); + } } } -bool -Route::muted () const -{ - return _mute_master->muted_by_self(); -} - -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) - //see also MuteMaster::mute_gain_at() - return (_session.soloing() && !soloed() && !solo_isolated()); -} - #if 0 static void dump_processors(const string& name, const list >& procs) @@ -1369,6 +1009,15 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) } } + +inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) { + return a = static_cast (static_cast (a) | static_cast (b)); +} + +inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) { + return a = static_cast (static_cast (a) & static_cast (b)); +} + int Route::add_processors (const ProcessorList& others, boost::shared_ptr before, ProcessorStreams* err) { @@ -1392,6 +1041,59 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr return 0; } + ProcessorList to_skip; + + // check if there's an instrument to replace or configure + for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { + boost::shared_ptr pi; + if ((pi = boost::dynamic_pointer_cast(*i)) == 0) { + continue; + } + if (!pi->plugin ()->get_info ()->is_instrument ()) { + continue; + } + boost::shared_ptr instrument = the_instrument (); + ChanCount in (DataType::MIDI, 1); + ChanCount out (DataType::AUDIO, 2); // XXX route's out?! + + PluginSetupOptions flags = None; + if (instrument) { + flags |= CanReplace; + in = instrument->input_streams (); + out = instrument->output_streams (); + } + if (pi->has_output_presets (in, out)) { + flags |= MultiOut; + } + + pi->set_strict_io (_strict_io); + + PluginSetupOptions mask = None; + if (Config->get_ask_replace_instrument ()) { + mask |= CanReplace; + } + if (Config->get_ask_setup_instrument ()) { + mask |= MultiOut; + } + + flags &= mask; + + if (flags != None) { + boost::optional rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */ + switch (rv.get_value_or (0)) { + case 1: + to_skip.push_back (*i); // don't add this one; + break; + case 2: + replace_processor (instrument, *i, err); + to_skip.push_back (*i); + break; + default: + break; + } + } + } + { Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); @@ -1402,6 +1104,10 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr if (*i == _meter) { continue; } + ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i); + if (check != to_skip.end()) { + continue; + } boost::shared_ptr pi; @@ -2669,8 +2375,6 @@ Route::state(bool full_state) node->add_property("active", _active?"yes":"no"); string p; - boost::to_string (_phase_invert, p); - node->add_property("phase-invert", p); node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); @@ -2682,20 +2386,18 @@ Route::state(bool full_state) snprintf (buf, sizeof (buf), "%d", _order_key); node->add_property ("order-key", buf); - node->add_property ("self-solo", (_self_solo ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream); - node->add_property ("soloed-by-upstream", buf); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream); - node->add_property ("soloed-by-downstream", buf); - node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no"); - node->add_property ("solo-safe", _solo_safe ? "yes" : "no"); + + node->add_child_nocopy (_solo_control->get_state ()); + node->add_child_nocopy (_solo_isolate_control->get_state ()); + node->add_child_nocopy (_solo_safe_control->get_state ()); node->add_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); - node->add_child_nocopy (_solo_control->get_state ()); - node->add_child_nocopy (_mute_control->get_state ()); node->add_child_nocopy (_mute_master->get_state ()); + node->add_child_nocopy (_mute_control->get_state ()); + node->add_child_nocopy (_phase_control->get_state ()); + if (full_state) { node->add_child_nocopy (Automatable::get_automation_xml_state ()); } @@ -2714,26 +2416,29 @@ Route::state(bool full_state) node->add_child_nocopy (_pannable->state (full_state)); } - for (i = _processors.begin(); i != _processors.end(); ++i) { - if (!full_state) { - /* 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. - - similarly, do not save listen sends which connect to - the monitor section, because these will always be - added if necessary. - */ - boost::shared_ptr is; + { + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); + for (i = _processors.begin(); i != _processors.end(); ++i) { + if (!full_state) { + /* 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. + + similarly, do not save listen sends which connect to + the monitor section, because these will always be + added if necessary. + */ + boost::shared_ptr is; - if ((is = boost::dynamic_pointer_cast (*i)) != 0) { - if (is->role() == Delivery::Listen) { - continue; + if ((is = boost::dynamic_pointer_cast (*i)) != 0) { + if (is->role() == Delivery::Listen) { + continue; + } } } + node->add_child_nocopy((*i)->state (full_state)); } - node->add_child_nocopy((*i)->state (full_state)); } if (_extra_xml) { @@ -2789,7 +2494,7 @@ Route::set_state (const XMLNode& node, int version) _strict_io = string_is_affirmative (prop->value()); } - if (is_master() || is_monitor() || is_auditioner()) { + if (!can_solo()) { _mute_master->set_solo_ignore (true); } @@ -2835,6 +2540,24 @@ Route::set_state (const XMLNode& node, int version) warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg; } } + + if (child->name() == Controllable::xml_node_name) { + if ((prop = child->property (X_("name"))) == 0) { + continue; + } + + if (prop->value() == _gain_control->name()) { + _gain_control->set_state (*child, version); + } else if (prop->value() == _solo_control->name()) { + _solo_control->set_state (*child, version); + } else if (prop->value() == _solo_safe_control->name()) { + _solo_safe_control->set_state (*child, version); + } else if (prop->value() == _solo_isolate_control->name()) { + _solo_isolate_control->set_state (*child, version); + } else if (prop->value() == _solo_control->name()) { + _mute_control->set_state (*child, version); + } + } } if ((prop = node.property (X_("meter-point"))) != 0) { @@ -2856,32 +2579,6 @@ Route::set_state (const XMLNode& node, int version) // this looks up the internal instrument in processors reset_instrument_info(); - if ((prop = node.property ("self-solo")) != 0) { - set_self_solo (string_is_affirmative (prop->value())); - } - - if ((prop = node.property ("soloed-by-upstream")) != 0) { - _soloed_by_others_upstream = 0; // needed for mod_.... () to work - mod_solo_by_others_upstream (atoi (prop->value())); - } - - if ((prop = node.property ("soloed-by-downstream")) != 0) { - _soloed_by_others_downstream = 0; // needed for mod_.... () to work - mod_solo_by_others_downstream (atoi (prop->value())); - } - - if ((prop = node.property ("solo-isolated")) != 0) { - 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()), Controllable::NoGroup); - } - - if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (boost::dynamic_bitset<> (prop->value ())); - } - if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); } @@ -2969,7 +2666,7 @@ Route::set_state (const XMLNode& node, int version) set_remote_control_id_internal (x); } - } else if (child->name() == X_("MuteMaster")) { + } else if (child->name() == MuteMaster::xml_node_name) { _mute_master->set_state (*child, version); } else if (child->name() == Automatable::xml_node_name) { @@ -3012,26 +2709,10 @@ Route::set_state_2X (const XMLNode& node, int version) _mute_master->set_solo_ignore (true); } - if ((prop = node.property (X_("phase-invert"))) != 0) { - boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); - if (string_is_affirmative (prop->value ())) { - p.set (); - } - set_phase_invert (p); - } - if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); } - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn); - } - if ((prop = node.property (X_("muted"))) != 0) { bool first = true; @@ -3273,7 +2954,7 @@ Route::set_processor_state (const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - XMLProperty const * prop = (*niter)->property ("type"); + XMLProperty* prop = (*niter)->property ("type"); if (prop->value() == "amp") { _amp->set_state (**niter, Stateful::current_state_version); @@ -3793,11 +3474,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) contains ConfigurationChanged */ configure_processors (0); - _phase_invert.resize (_input->n_ports().n_audio ()); + _phase_control->resize (_input->n_ports().n_audio ()); io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_upstream || _solo_isolated_by_upstream) { + if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) { int sbou = 0; int ibou = 0; boost::shared_ptr routes = _session.get_routes (); @@ -3812,38 +3493,36 @@ Route::input_change_handler (IOChange change, void * /*src*/) if ((*i)->soloed()) { ++sbou; } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { ++ibou; } } } } - int delta = sbou - _soloed_by_others_upstream; - int idelta = ibou - _solo_isolated_by_upstream; + int delta = sbou - _solo_control->soloed_by_others_upstream(); + int idelta = ibou - _solo_isolate_control->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) + _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta) << endmsg; } - if (_soloed_by_others_upstream) { + if (_solo_control->soloed_by_others_upstream()) { // ignore new connections (they're not propagated) if (delta <= 0) { - mod_solo_by_others_upstream (delta); + _solo_control->mod_solo_by_others_upstream (delta); } } - if (_solo_isolated_by_upstream) { + if (_solo_isolate_control->solo_isolated_by_upstream()) { // solo-isolate currently only propagates downstream if (idelta < 0) { - mod_solo_isolated_by_upstream (false); + _solo_isolate_control->mod_solo_isolated_by_upstream (1); } - // 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; } @@ -3856,11 +3535,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) bool sends_only; bool does_feed = feeds (*i, &sends_only); if (delta <= 0 && does_feed && !sends_only) { - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } if (idelta < 0 && does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (false); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1); } } } @@ -3886,7 +3565,7 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_downstream) { + if (_solo_control->soloed_by_others_downstream()) { int sbod = 0; /* checking all all downstream routes for * explicit of implict solo is a rather drastic measure, @@ -3909,20 +3588,20 @@ Route::output_change_handler (IOChange change, void * /*src*/) } } } - int delta = sbod - _soloed_by_others_downstream; + int delta = sbod - _solo_control->soloed_by_others_downstream(); if (delta <= 0) { // do not allow new connections to change implicit solo (no propagation) - mod_solo_by_others_downstream (delta); + _solo_control->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()) { + if ((*i).get() == this || !can_solo()) { 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); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } } @@ -4562,42 +4241,6 @@ Route::internal_send_for (boost::shared_ptr target) const return boost::shared_ptr(); } -/** @param c Audio channel index. - * @param yn true to invert phase, otherwise false. - */ -void -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 (); - } -} - -void -Route::set_phase_invert (boost::dynamic_bitset<> p) -{ - if (_phase_invert != p) { - _phase_invert = p; - phase_invert_changed (); /* EMIT SIGNAL */ - _session.set_dirty (); - } -} - -bool -Route::phase_invert (uint32_t c) const -{ - return _phase_invert[c]; -} - -boost::dynamic_bitset<> -Route::phase_invert () const -{ - return _phase_invert; -} - void Route::set_denormal_protection (bool yn) { @@ -4665,14 +4308,10 @@ Route::trim_control() const return _trim_control; } -boost::shared_ptr +boost::shared_ptr Route::phase_control() const { - if (phase_invert().size()) { - return _phase_control; - } else { - return boost::shared_ptr(); - } + return _phase_control; } boost::shared_ptr @@ -4765,12 +4404,6 @@ Route::has_io_processor_named (const string& name) return false; } -MuteMaster::MutePoint -Route::mute_points () const -{ - return _mute_master->mute_points (); -} - void Route::set_processor_positions () { @@ -4981,12 +4614,13 @@ Route::setup_invisible_processors () /* find the amp */ - ProcessorList::iterator amp = new_processors.begin (); - while (amp != new_processors.end() && *amp != _amp) { - ++amp; - } + ProcessorList::iterator amp = find (new_processors.begin(), new_processors.end(), _amp); - assert (amp != new_processors.end ()); + if (amp == new_processors.end ()) { + error << string_compose (_("Amp/Fader on Route '%1' went AWOL. Re-added."), name()) << endmsg; + new_processors.push_front (_amp); + amp = find (new_processors.begin(), new_processors.end(), _amp); + } /* and the processor after the amp */ @@ -5810,3 +5444,74 @@ Route::master_send_enable_controllable () const return boost::shared_ptr(); #endif } + +bool +Route::slaved_to (boost::shared_ptr vca) const +{ + if (!vca || !_gain_control) { + return false; + } + + return _gain_control->slaved_to (vca->gain_control()); +} + +void +Route::vca_assign (boost::shared_ptr vca) +{ + _gain_control->add_master (vca->gain_control()); + _solo_control->add_master (vca->solo_control()); + _mute_control->add_master (vca->mute_control()); +} + +void +Route::vca_unassign (boost::shared_ptr vca) +{ + if (!vca) { + /* unassign from all */ + _gain_control->clear_masters (); + _solo_control->clear_masters (); + _mute_control->clear_masters (); + } else { + _gain_control->remove_master (vca->gain_control()); + _solo_control->remove_master (vca->solo_control()); + _mute_control->remove_master (vca->mute_control()); + } +} + +bool +Route::muted_by_others_soloing () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others ()) { + return false; + } + + return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); +} + +bool +Route::muted_by_others () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others()) { + return false; + } + + return _mute_master->muted_by_others(); +} + +void +Route::clear_all_solo_state () +{ + double v = _solo_safe_control->get_value (); + + _solo_control->clear_all_solo_state (); + + if (v != 0.0) { + _solo_safe_control->set_value (v, Controllable::NoGroup); + } +}