Implement slaved boolean automation and update mute special-case
authorRobin Gareus <robin@gareus.org>
Sat, 10 Jun 2017 12:36:03 +0000 (14:36 +0200)
committerRobin Gareus <robin@gareus.org>
Sat, 10 Jun 2017 12:38:21 +0000 (14:38 +0200)
libs/ardour/ardour/mute_control.h
libs/ardour/ardour/slavable_automation_control.h
libs/ardour/automation_control.cc
libs/ardour/mute_control.cc
libs/ardour/slavable_automation_control.cc
libs/evoral/evoral/ControlList.hpp

index aeeccceab6896b21c6b5fdbcf529db34427c89d2..d54ff2dc34a43bc269845a4fe5a1f4740b973dad 100644 (file)
@@ -71,7 +71,7 @@ public:
        void automation_run (framepos_t start, pframes_t nframes);
 
 protected:
-       void master_changed (bool, PBD::Controllable::GroupControlDisposition, boost::shared_ptr<AutomationControl>);
+       bool handle_master_change (boost::shared_ptr<AutomationControl>);
        void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override);
 
        void pre_remove_master (boost::shared_ptr<AutomationControl>);
index acb5ad747185fcbd90d89ea2034aebb8c8e89724..ceafc79193fb950ff4287aac75dbeeb8be31a4c5 100644 (file)
@@ -27,7 +27,7 @@ namespace ARDOUR {
 
 class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
 {
-    public:
+public:
        SlavableAutomationControl(ARDOUR::Session&,
                                  const Evoral::Parameter&                  parameter,
                                  const ParameterDescriptor&                desc,
@@ -80,10 +80,10 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
 
        bool find_next_event_locked (double now, double end, Evoral::ControlEvent& next_event) const;
 
-    protected:
+protected:
 
        class MasterRecord {
-          public:
+       public:
                MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
                        : _master (gc)
                        , _yn (false)
@@ -101,7 +101,7 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
 
                PBD::ScopedConnection connection;
 
-         private:
+  private:
                boost::shared_ptr<AutomationControl> _master;
                /* holds most recently seen master value for boolean/toggle controls */
                bool   _yn;
@@ -117,6 +117,10 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
        void   actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
        void   update_boolean_masters_records (boost::shared_ptr<AutomationControl>);
 
+       virtual bool handle_master_change (boost::shared_ptr<AutomationControl>);
+       virtual bool boolean_automation_run_locked (framepos_t start, pframes_t len);
+       bool boolean_automation_run (framepos_t start, pframes_t len);
+
        virtual void   master_changed (bool from_self, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl>);
        virtual double get_masters_value_locked () const;
        virtual void   pre_remove_master (boost::shared_ptr<AutomationControl>) {}
index 7e74ce7cf75fb9c7567b1bcc190a7f07038ff16c..4e81de8aaffb067d917d07fb2b434aeb6d9c4f1b 100644 (file)
@@ -207,6 +207,7 @@ AutomationControl::set_automation_state (AutoState as)
 
                alist()->set_automation_state (as);
                if (_desc.toggled) {
+                       Changed (false, Controllable::NoGroup); // notify slaves, update boolean masters
                        return;  // No watch for boolean automation
                }
 
index 6d5b1e42f00df1dbdf5e484f23ec12ef47fdc905..71bcf3517c9d7b1d33ae14fd51842d007d25d74d 100644 (file)
@@ -52,7 +52,7 @@ MuteControl::post_add_master (boost::shared_ptr<AutomationControl> m)
 
                if (!muted_by_self() && !get_boolean_masters()) {
                        _muteable.mute_master()->set_muted_by_masters (true);
-                       Changed (false, Controllable::NoGroup);
+                       Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
                }
        }
 }
@@ -67,9 +67,10 @@ MuteControl::pre_remove_master (boost::shared_ptr<AutomationControl> m)
                return;
        }
 
-       if (m->get_value()) {
-               if (!muted_by_self() && (get_boolean_masters() == 1)) {
-                       Changed (false, Controllable::NoGroup);
+       if (m->get_value() && get_boolean_masters() == 1) {
+               _muteable.mute_master()->set_muted_by_masters (false);
+               if (!muted_by_self()) {
+                       Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
                }
        }
 }
@@ -89,31 +90,33 @@ MuteControl::actually_set_value (double val, Controllable::GroupControlDispositi
        SlavableAutomationControl::actually_set_value (val, gcd);
 }
 
-void
-MuteControl::master_changed (bool self_change, Controllable::GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
+bool
+MuteControl::handle_master_change (boost::shared_ptr<AutomationControl> m)
 {
        bool send_signal = false;
        boost::shared_ptr<MuteControl> mc = boost::dynamic_pointer_cast<MuteControl> (m);
+       if (!mc) {
+               return false;
+       }
 
        if (m->get_value()) {
                /* this master is now enabled */
-               if (!muted_by_self() && get_boolean_masters() == 0) {
+               if (get_boolean_masters() == 0) {
                        _muteable.mute_master()->set_muted_by_masters (true);
-                       send_signal = true;
+                       if (!muted_by_self()) {
+                               send_signal = true;
+                       }
                }
        } else {
                /* this master is disabled and there was only 1 enabled before */
-               if (!muted_by_self() && get_boolean_masters() == 1) {
+               if (get_boolean_masters() == 1) {
                        _muteable.mute_master()->set_muted_by_masters (false);
-                       send_signal = true;
+                       if (!muted_by_self()) {
+                               send_signal = true;
+                       }
                }
        }
-
-       update_boolean_masters_records (m);
-
-       if (send_signal) {
-               Changed (false, Controllable::NoGroup);
-       }
+       return send_signal;
 }
 
 double
@@ -177,19 +180,30 @@ MuteControl::muted_by_others_soloing () const
 }
 
 void
-MuteControl::automation_run (framepos_t start, pframes_t)
+MuteControl::automation_run (framepos_t start, pframes_t len)
 {
-       if (!list() || !automation_playback()) {
+       boolean_automation_run (start, len);
+
+       if (muted_by_masters ()) {
+               // already muted, no need to check further
                return;
        }
 
-       bool        valid = false;
-       const float mute  = list()->rt_safe_eval (start, valid);
+       bool valid = false;
+       bool mute  = false;
+
+       if (list() && automation_playback()) {
+               mute = list()->rt_safe_eval (start, valid) >= 0.5;
+       }
+
+       if (!valid) {
+               return;
+       }
 
-       if (mute >= 0.5 && !muted()) {
+       if (mute && !muted()) {
                set_value_unchecked (1.0);  // mute
                Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
-       } else if (mute < 0.5 && muted ()) {
+       } else if (!mute && muted()) {
                set_value_unchecked (0.0);  // unmute
                Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
        }
index c912d163707acb0a9f67e6bb73f86b3e01cdf20d..8a7a8ba00f79b9d28fe2c1fd8d9322ea817a1597 100644 (file)
@@ -312,8 +312,16 @@ SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<Aut
 void
 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
 {
+       Glib::Threads::RWLock::ReaderLock lm (master_lock, Glib::Threads::TRY_LOCK);
+       if (!lm.locked ()) {
+               /* boolean_automation_run_locked () special case */
+               return;
+       }
+       bool send_signal = handle_master_change (m);
        update_boolean_masters_records (m);
-       Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
+       if (send_signal) {
+               Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
+       }
 }
 
 void
@@ -450,6 +458,66 @@ SlavableAutomationControl::find_next_event_locked (double now, double end, Evora
        return rv;
 }
 
+bool
+SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationControl>)
+{
+       return true; // emit Changed
+}
+
+bool
+SlavableAutomationControl::boolean_automation_run_locked (framepos_t start, pframes_t len)
+{
+       bool rv = false;
+       if (!_desc.toggled) {
+               return false;
+       }
+       for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
+               boost::shared_ptr<AutomationControl> ac (mr->second.master());
+               if (!ac->automation_playback ()) {
+                       continue;
+               }
+               if (!ac->toggled ()) {
+                       continue;
+               }
+               boost::shared_ptr<SlavableAutomationControl> sc = boost::dynamic_pointer_cast<MuteControl>(ac);
+               if (sc) {
+                       rv |= sc->boolean_automation_run (start, len);
+               }
+               boost::shared_ptr<const Evoral::ControlList> alist (ac->list());
+               bool valid = false;
+               const bool yn = alist->rt_safe_eval (start, valid) >= 0.5;
+               if (!valid) {
+                       continue;
+               }
+               /* ideally we'd call just master_changed() which calls update_boolean_masters_records()
+                * but that takes the master_lock, which is already locked */
+               if (mr->second.yn() != yn) {
+                       rv |= handle_master_change (ac);
+                       mr->second.set_yn (yn);
+                       /* notify the GUI, without recursion:
+                        * master_changed() above will ignore the change if the lock is held.
+                        */
+                       ac->set_value_unchecked (yn ? 1. : 0.);
+                       ac->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
+               }
+       }
+       return rv;
+}
+
+bool
+SlavableAutomationControl::boolean_automation_run (framepos_t start, pframes_t len)
+{
+       bool change = false;
+       {
+                Glib::Threads::RWLock::ReaderLock lm (master_lock);
+                change = boolean_automation_run_locked (start, len);
+       }
+       if (change) {
+               Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
+       }
+       return change;
+}
+
 bool
 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
 {
index b58c186c216c5fa1be2c293e6c46a7e4f229431e..2a80ad5bd4e16daff158e88e822dc0f4cb412434 100644 (file)
@@ -225,7 +225,7 @@ public:
         * @param ok boolean reference if returned value is valid
         * @returns parameter value
         */
-       double rt_safe_eval (double where, bool& ok) {
+       double rt_safe_eval (double where, bool& ok) const {
 
                Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);