From 194b213456df43cb522651db6f27286a48eadec6 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sat, 12 Mar 2016 22:58:00 -0500 Subject: add implicit mute state to MuteMaster and use when a master of a mute control is enabled/disabled. Add AutomationControl::master_changed() as a virtual method to handle ... master value changes --- libs/ardour/ardour/automation_control.h | 1 + libs/ardour/ardour/mute_master.h | 14 +++-- libs/ardour/ardour/route.h | 54 +++++++++++++++-- libs/ardour/ardour/session_solo_notifications.h | 53 ---------------- libs/ardour/ardour/vca.h | 13 ---- libs/ardour/automation_control.cc | 20 ++++++- libs/ardour/mute_master.cc | 8 ++- libs/ardour/route.cc | 29 +++++---- libs/ardour/route_controls.cc | 54 ++++++++++++++--- libs/ardour/session_rtevents.cc | 2 + libs/ardour/vca.cc | 80 ------------------------- 11 files changed, 150 insertions(+), 178 deletions(-) delete mode 100644 libs/ardour/ardour/session_solo_notifications.h (limited to 'libs') diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 9462a79748..455e8891c1 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -156,6 +156,7 @@ public: Masters _masters; PBD::ScopedConnectionList masters_connections; + virtual void master_changed (bool from_self, GroupControlDisposition gcd); void master_going_away (boost::weak_ptr); virtual void recompute_masters_ratios (double val) { /* do nothing by default */} virtual double get_masters_value_locked () const; diff --git a/libs/ardour/ardour/mute_master.h b/libs/ardour/ardour/mute_master.h index d88cbdcd39..14597cb56b 100644 --- a/libs/ardour/ardour/mute_master.h +++ b/libs/ardour/ardour/mute_master.h @@ -66,17 +66,21 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful void set_soloed_by_others (bool yn) { _soloed_by_others = yn; } void set_solo_ignore (bool yn) { _solo_ignore = yn; } + void mod_muted_by_others (int32_t delta); + bool muted_by_others () const { return _muted_by_others; } + PBD::Signal0 MutePointChanged; XMLNode& get_state(); int set_state(const XMLNode&, int version); private: - volatile MutePoint _mute_point; - volatile bool _muted_by_self; - volatile bool _soloed_by_self; - volatile bool _soloed_by_others; - volatile bool _solo_ignore; + MutePoint _mute_point; + bool _muted_by_self; + bool _soloed_by_self; + bool _soloed_by_others; + bool _solo_ignore; + int32_t _muted_by_others; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 0afafe82eb..9abe56afcb 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -160,7 +160,8 @@ public: bool muted () const; void set_mute (bool yn, PBD::Controllable::GroupControlDisposition); - bool muted_by_others() const; + bool muted_by_others_soloing () const; + bool muted_by_others () const; /* controls use set_solo() to modify this route's solo state */ @@ -508,17 +509,57 @@ public: }; class SoloControllable : public BooleanRouteAutomationControl { - public: + public: SoloControllable (std::string name, boost::shared_ptr); void set_value (double, PBD::Controllable::GroupControlDisposition group_override); void set_value_unchecked (double); double get_value () const; - private: + + /* Export additional API so that objects that only get access + * to a Controllable/AutomationControl can do more fine-grained + * operations with respect to solo. Obviously, they would need + * to dynamic_cast first. + * + * Solo state is not representable by a single scalar value, + * so this AutomationControl maps set_value() and get_value() + * to r->set_self_solo() and r->soloed() respectively. This + * means that the Controllable is technically asymmetric. It is + * possible to call ::set_value (0.0) to disable (self)solo, + * and then call ::get_value() and get a return of 1.0 because + * the Route owner is soloed by upstream/downstream. + */ + + void set_self_solo (bool yn) { + boost::shared_ptr r(_route.lock()); if (r) r->set_self_solo (yn); + } + void mod_solo_by_others_upstream (int32_t delta) { + boost::shared_ptr r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta); + } + void mod_solo_by_others_downstream (int32_t delta) { + boost::shared_ptr r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta); + } + bool soloed_by_others () const { + boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others(); else return false; + } + bool soloed_by_others_upstream () const { + boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false; + } + bool soloed_by_others_downstream () const { + boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false; + } + bool self_soloed () const { + boost::shared_ptr r(_route.lock()); if (r) return r->self_soloed(); else return false; + } + + protected: + void master_changed (bool, PBD::Controllable::GroupControlDisposition); + + private: void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); }; struct MuteControllable : public BooleanRouteAutomationControl { - public: + public: MuteControllable (std::string name, boost::shared_ptr); void set_value (double, PBD::Controllable::GroupControlDisposition group_override); void set_value_unchecked (double); @@ -526,7 +567,10 @@ public: /* Pretend to change value, but do not affect actual route mute. */ void set_superficial_value(bool muted); - private: + protected: + void master_changed (bool, PBD::Controllable::GroupControlDisposition); + + private: boost::weak_ptr _route; void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); }; diff --git a/libs/ardour/ardour/session_solo_notifications.h b/libs/ardour/ardour/session_solo_notifications.h deleted file mode 100644 index bfb2e7d333..0000000000 --- a/libs/ardour/ardour/session_solo_notifications.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright (C) 2016 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __libardour_session_solo_notifications_h__ -#define __libardour_session_solo_notifications_h__ - -#include -#include "pbd/controllable.h" - -namespace ARDOUR { - -class Route; - -template -class SessionSoloNotifications -{ - public: - void solo_changed (bool self_solo_change, PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr route) { - static_cast(this)->_route_solo_changed (self_solo_change, gcd, route); - } - - void listen_changed (PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr route) { - static_cast(this)->_route_listen_changed (gcd, route); - } - - void mute_changed () { - static_cast(this)->_route_mute_changed (); - } - - void solo_isolated_changed (boost::shared_ptr route) { - static_cast(this)->_route_solo_isolated_changed (route); - } -}; - -} /* namespace */ - -#endif /* __libardour_session_solo_notifications_h__ */ diff --git a/libs/ardour/ardour/vca.h b/libs/ardour/ardour/vca.h index 82e0e7acd2..dc48ffa480 100644 --- a/libs/ardour/ardour/vca.h +++ b/libs/ardour/ardour/vca.h @@ -46,11 +46,6 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en XMLNode& get_state(); int set_state (XMLNode const&, int version); - void add_solo_target (boost::shared_ptr); - void remove_solo_target (boost::shared_ptr); - void add_mute_target (boost::shared_ptr); - void remove_mute_target (boost::shared_ptr); - bool soloed () const; bool muted () const; @@ -128,14 +123,6 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en uint32_t _number; - RouteList solo_targets; - PBD::ScopedConnectionList solo_connections; - mutable Glib::Threads::RWLock solo_lock; - - RouteList mute_targets; - PBD::ScopedConnectionList mute_connections; - mutable Glib::Threads::RWLock mute_lock; - boost::shared_ptr _gain_control; boost::shared_ptr _solo_control; boost::shared_ptr _mute_control; diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index 2ac6574528..b00c615625 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -119,8 +119,6 @@ AutomationControl::set_value (double value, PBD::Controllable::GroupControlDispo Control::set_double (value, _session.transport_frame(), to_list); - cerr << "AC was set to " << value << endl; - Changed (true, gcd); } @@ -301,23 +299,39 @@ AutomationControl::add_master (boost::shared_ptr m) */ - m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal2::operator(), &Changed, false, _2)); + m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2)); } new_value = get_value_locked (); } if (res.second) { + /* this will notify everyone that we're now slaved to the master */ MasterStatusChange (); /* EMIT SIGNAL */ } if (new_value != current_value) { + /* force a call to to ::master_changed() to carry the + * consequences that would occur if the master assumed + * its current value WHILE we were slaved. + */ + master_changed (false, Controllable::NoGroup); /* effective value changed by master */ Changed (false, Controllable::NoGroup); } } +void +AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd) +{ + /* our value has (likely) changed, but not because we were + * modified. Just the master. + */ + + Changed (false, gcd); /* EMIT SIGNAL */ +} + void AutomationControl::master_going_away (boost::weak_ptr wm) { diff --git a/libs/ardour/mute_master.cc b/libs/ardour/mute_master.cc index 4b83d57a9b..dc40d1fd6b 100644 --- a/libs/ardour/mute_master.cc +++ b/libs/ardour/mute_master.cc @@ -41,6 +41,7 @@ MuteMaster::MuteMaster (Session& s, const std::string&) , _soloed_by_self (false) , _soloed_by_others (false) , _solo_ignore (false) + , _muted_by_others (0) { if (Config->get_mute_affects_pre_fader ()) { @@ -163,6 +164,11 @@ MuteMaster::get_state() bool MuteMaster::muted_by_others_at (MutePoint mp) const { - return (!_solo_ignore && _session.soloing() && (_mute_point & mp)); + return (!_solo_ignore && (_muted_by_others || _session.soloing()) && (_mute_point & mp)); } +void +MuteMaster::mod_muted_by_others (int32_t delta) +{ + _muted_by_others = max (0, _muted_by_others + delta); +} diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index c9b9aff3ac..8207176729 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -924,14 +924,6 @@ Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) } 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 @@ -997,7 +989,8 @@ Route::mod_solo_by_others_upstream (int32_t delta) } set_mute_master_solo (); - _solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ + cerr << name() << " SC->Changed (false, UseGroup)\n"; + _solo_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ } void @@ -1150,6 +1143,21 @@ Route::muted () const return _mute_master->muted_by_self(); } +bool +Route::muted_by_others_soloing () 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(); +} + bool Route::muted_by_others () const { @@ -1162,7 +1170,7 @@ Route::muted_by_others () const //now check to see if something is soloed (and I am not) //see also MuteMaster::mute_gain_at() - return (_session.soloing() && !soloed() && !solo_isolated()); + return _mute_master->muted_by_others() || (_session.soloing() && !soloed() && !solo_isolated()); } #if 0 @@ -5910,6 +5918,5 @@ Route::vca_unassign (boost::shared_ptr vca) _gain_control->remove_master (vca->gain_control()); _solo_control->remove_master (vca->solo_control()); _mute_control->remove_master (vca->mute_control()); - } } diff --git a/libs/ardour/route_controls.cc b/libs/ardour/route_controls.cc index 5992014563..ad5408d06d 100644 --- a/libs/ardour/route_controls.cc +++ b/libs/ardour/route_controls.cc @@ -129,6 +129,31 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r = _route.lock (); + + if (!r) { + return; + } + + bool master_soloed; + + { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + master_soloed = (bool) get_masters_value_locked (); + } + + /* Master is considered equivalent to an upstream solo control, not + * direct control over self-soloed. + */ + + r->mod_solo_by_others_upstream (master_soloed ? 1 : -1); + + AutomationControl::master_changed (false, gcd); +} + void Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { @@ -158,13 +183,9 @@ Route::SoloControllable::set_value_unchecked (double val) double Route::SoloControllable::get_value () const { - std::cerr << "RSC get value\n"; - if (slaved()) { - std::cerr << "slaved solo control, get master value ... "; Glib::Threads::RWLock::ReaderLock lm (master_lock); - double v = get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - std::cerr << v << std::endl; + return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; } if (_list && ((AutomationList*)_list.get())->automation_playback()) { @@ -202,6 +223,7 @@ Route::MuteControllable::set_superficial_value(bool muted) 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 ()) { @@ -218,6 +240,24 @@ Route::MuteControllable::set_superficial_value(bool muted) Control::set_double (muted, where, to_list); } +void +Route::MuteControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) +{ + bool master_muted; + + { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + master_muted = (bool) get_masters_value_locked (); + } + + boost::shared_ptr r (_route.lock()); + if (r) { + r->mute_master()->mod_muted_by_others (master_muted ? 1 : -1); + } + + AutomationControl::master_changed (false, gcd); +} + void Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { @@ -258,7 +298,7 @@ Route::MuteControllable::get_value () const { if (slaved()) { Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; + return get_masters_value_locked () ? 1.0 : 0.0; } if (_list && ((AutomationList*)_list.get())->automation_playback()) { @@ -268,7 +308,7 @@ Route::MuteControllable::get_value () const // 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; + return (r && r->muted()) ? 1.0 : 0.0; } Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr r) diff --git a/libs/ardour/session_rtevents.cc b/libs/ardour/session_rtevents.cc index 68ba15550e..6b807fbf52 100644 --- a/libs/ardour/session_rtevents.cc +++ b/libs/ardour/session_rtevents.cc @@ -110,8 +110,10 @@ Session::rt_set_implicit_solo (boost::shared_ptr rl, int delta, bool for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { if (!(*i)->is_auditioner()) { if (upstream) { + std::cerr << "Changing " << (*i)->name() << " upstream by " << delta << std::endl; (*i)->mod_solo_by_others_upstream (delta); } else { + std::cerr << "Changing " << (*i)->name() << " downstream by " << delta << std::endl; (*i)->mod_solo_by_others_downstream (delta); } } diff --git a/libs/ardour/vca.cc b/libs/ardour/vca.cc index 3fec2d9305..d92fa67c03 100644 --- a/libs/ardour/vca.cc +++ b/libs/ardour/vca.cc @@ -136,95 +136,15 @@ VCA::set_state (XMLNode const& node, int version) return 0; } -void -VCA::add_solo_target (boost::shared_ptr r) -{ - Glib::Threads::RWLock::WriterLock lm (solo_lock); - solo_targets.push_back (r); - r->DropReferences.connect_same_thread (solo_connections, boost::bind (&VCA::solo_target_going_away, this, boost::weak_ptr (r))); -} - -void -VCA::remove_solo_target (boost::shared_ptr r) -{ - Glib::Threads::RWLock::WriterLock lm (solo_lock); - solo_targets.remove (r); -} - -void -VCA::solo_target_going_away (boost::weak_ptr wr) -{ - boost::shared_ptr r (wr.lock()); - if (!r) { - return; - } - remove_solo_target (r); -} - void VCA::set_solo (bool yn) { - { - Glib::Threads::RWLock::ReaderLock lm (solo_lock); - - if (yn == _solo_requested) { - return; - } - - if (solo_targets.empty()) { - return; - } - - boost::shared_ptr rl (new RouteList (solo_targets)); - - if (Config->get_solo_control_is_listen_control()) { - _session.set_listen (rl, yn, Session::rt_cleanup, Controllable::NoGroup); - } else { - _session.set_implicit_solo (rl, (yn ? 1 : -1), true, Session::rt_cleanup, Controllable::NoGroup); - } - } - _solo_requested = yn; } -void -VCA::add_mute_target (boost::shared_ptr r) -{ - Glib::Threads::RWLock::WriterLock lm (mute_lock); - mute_targets.push_back (r); - r->DropReferences.connect_same_thread (mute_connections, boost::bind (&VCA::mute_target_going_away, this, boost::weak_ptr (r))); -} - -void -VCA::remove_mute_target (boost::shared_ptr r) -{ - Glib::Threads::RWLock::WriterLock lm (mute_lock); - mute_targets.remove (r); -} - -void -VCA::mute_target_going_away (boost::weak_ptr wr) -{ - boost::shared_ptr r (wr.lock()); - if (!r) { - return; - } - remove_mute_target (r); -} - void VCA::set_mute (bool yn) { - { - Glib::Threads::RWLock::ReaderLock lm (mute_lock); - if (yn == _mute_requested) { - return; - } - - boost::shared_ptr rl (new RouteList (mute_targets)); - _session.set_mute (rl, yn, Session::rt_cleanup, Controllable::NoGroup); - } - _mute_requested = yn; } -- cgit v1.2.3