add implicit mute state to MuteMaster and use when a master of a mute control is...
authorPaul Davis <paul@linuxaudiosystems.com>
Sun, 13 Mar 2016 03:58:00 +0000 (22:58 -0500)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 31 May 2016 19:30:40 +0000 (15:30 -0400)
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/mute_master.h
libs/ardour/ardour/route.h
libs/ardour/ardour/session_solo_notifications.h [deleted file]
libs/ardour/ardour/vca.h
libs/ardour/automation_control.cc
libs/ardour/mute_master.cc
libs/ardour/route.cc
libs/ardour/route_controls.cc
libs/ardour/session_rtevents.cc
libs/ardour/vca.cc

index 9462a79748636d12c5388f0a6ea24e90f0855720..455e8891c108fc187c3f7b6ba021de4bc72e5ef7 100644 (file)
@@ -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<AutomationControl>);
        virtual void recompute_masters_ratios (double val) { /* do nothing by default */}
        virtual double get_masters_value_locked () const;
index d88cbdcd39cad8563a82f5287ff07cbe8430cdce..14597cb56b1aa906fd14bc2c74738fdcd5426bb2 100644 (file)
@@ -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<void> 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
index 0afafe82eb22fa0e40cd3c06526e4a6352d9e51e..9abe56afcb13fba5831ba7054933e937bd6b73db 100644 (file)
@@ -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<Route>);
                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<Route::SoloControllable> 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<Route> r(_route.lock()); if (r) r->set_self_solo (yn);
+               }
+               void mod_solo_by_others_upstream (int32_t delta) {
+                       boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta);
+               }
+               void mod_solo_by_others_downstream (int32_t delta) {
+                       boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta);
+               }
+               bool soloed_by_others () const {
+                       boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others(); else return false;
+               }
+               bool soloed_by_others_upstream () const {
+                       boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false;
+               }
+               bool soloed_by_others_downstream () const {
+                       boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false;
+               }
+               bool self_soloed () const {
+                       boost::shared_ptr<Route> 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<Route>);
                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> _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 (file)
index bfb2e7d..0000000
+++ /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 <boost/shared_ptr.hpp>
-#include "pbd/controllable.h"
-
-namespace ARDOUR {
-
-class Route;
-
-template<typename T>
-class SessionSoloNotifications
-{
-    public:
-       void solo_changed (bool self_solo_change, PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr<Route> route) {
-               static_cast<T*>(this)->_route_solo_changed (self_solo_change, gcd, route);
-       }
-
-       void listen_changed (PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr<Route> route) {
-               static_cast<T*>(this)->_route_listen_changed (gcd, route);
-       }
-
-       void mute_changed () {
-               static_cast<T*>(this)->_route_mute_changed ();
-       }
-
-       void solo_isolated_changed (boost::shared_ptr<Route> route) {
-               static_cast<T*>(this)->_route_solo_isolated_changed (route);
-       }
-};
-
-} /* namespace */
-
-#endif /* __libardour_session_solo_notifications_h__ */
index 82e0e7acd244234c9cb6542761715dedc87030fa..dc48ffa4809bb48f733724e0a0caa999f55a7fc3 100644 (file)
@@ -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<Route>);
-       void remove_solo_target (boost::shared_ptr<Route>);
-       void add_mute_target (boost::shared_ptr<Route>);
-       void remove_mute_target (boost::shared_ptr<Route>);
-
        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<GainControl> _gain_control;
        boost::shared_ptr<VCASoloControllable> _solo_control;
        boost::shared_ptr<VCAMuteControllable> _mute_control;
index 2ac6574528b1cbdb34a803aafdc6db539e23d43d..b00c615625982e0c5fc7a80a670a4fed5d9f6862 100644 (file)
@@ -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<AutomationControl> m)
                        */
 
 
-                       m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal2<void,bool,Controllable::GroupControlDisposition>::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<AutomationControl> wm)
 {
index 4b83d57a9b6f126a8f4089193504fd9a45891a4a..dc40d1fd6b596b1dd3e3d9d371dec4196d5207d0 100644 (file)
@@ -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);
+}
index c9b9aff3ac1a16875291462bed8b1e424a9d8a96..8207176729c43223043ec8e240a11e573022bda5 100644 (file)
@@ -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> vca)
                _gain_control->remove_master (vca->gain_control());
                _solo_control->remove_master (vca->solo_control());
                _mute_control->remove_master (vca->mute_control());
-
        }
 }
index 59920145634c1359324246b5a0dec5c568335fa3..ad5408d06d416765628c4b0fe47e1097b36019a0 100644 (file)
@@ -129,6 +129,31 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<R
        set_list (gl);
 }
 
+void
+Route::SoloControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
+{
+       boost::shared_ptr<Route> 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<Route> 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<Route> 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<Route> r)
index 68ba15550e7d49ed04aa56292c52ceda89face2a..6b807fbf52555bd6b6757fd5407cddc90bacf4dd 100644 (file)
@@ -110,8 +110,10 @@ Session::rt_set_implicit_solo (boost::shared_ptr<RouteList> 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);
                        }
                }
index 3fec2d93057553b9a4e1e0d77d179bae70a44730..d92fa67c0356d111b85e2d3855165053ae8fa434 100644 (file)
@@ -136,95 +136,15 @@ VCA::set_state (XMLNode const& node, int version)
        return 0;
 }
 
-void
-VCA::add_solo_target (boost::shared_ptr<Route> 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<Route> (r)));
-}
-
-void
-VCA::remove_solo_target (boost::shared_ptr<Route> r)
-{
-       Glib::Threads::RWLock::WriterLock lm (solo_lock);
-       solo_targets.remove (r);
-}
-
-void
-VCA::solo_target_going_away (boost::weak_ptr<Route> wr)
-{
-       boost::shared_ptr<Route> 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<RouteList> 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<Route> 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<Route> (r)));
-}
-
-void
-VCA::remove_mute_target (boost::shared_ptr<Route> r)
-{
-       Glib::Threads::RWLock::WriterLock lm (mute_lock);
-       mute_targets.remove (r);
-}
-
-void
-VCA::mute_target_going_away (boost::weak_ptr<Route> wr)
-{
-       boost::shared_ptr<Route> 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<RouteList> rl (new RouteList (mute_targets));
-               _session.set_mute (rl, yn, Session::rt_cleanup, Controllable::NoGroup);
-       }
-
        _mute_requested = yn;
 }