don't attempt MIDI playback if there are no MIDI buffers provided for processing
[ardour.git] / libs / ardour / slavable_automation_control.cc
index 655863738396d504111ab2f605e7b13a8049c67c..491914747c9281006771f879733ac2901bcbe055 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "pbd/enumwriter.h"
 #include "pbd/error.h"
+#include "pbd/memento_command.h"
 #include "pbd/types_convert.h"
 #include "pbd/i18n.h"
 
@@ -289,11 +290,7 @@ SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDispo
 {
        boost::shared_ptr<AutomationControl> m = wm.lock ();
        assert (m);
-       Glib::Threads::RWLock::ReaderLock lm (master_lock, Glib::Threads::TRY_LOCK);
-       if (!lm.locked ()) {
-               /* boolean_automation_run_locked () special case */
-               return;
-       }
+       Glib::Threads::RWLock::ReaderLock lm (master_lock);
        bool send_signal = handle_master_change (m);
        lm.release (); // update_boolean_masters_records() takes lock
 
@@ -316,7 +313,14 @@ double
 SlavableAutomationControl::scale_automation_callback (double value, double ratio) const
 {
        /* derived classes can override this and e.g. add/subtract. */
-       value *= ratio;
+       if (toggled ()) {
+               // XXX we should use the master's upper/lower as threshold
+               if (ratio >= 0.5 * (upper () - lower ())) {
+                       value = upper ();
+               }
+       } else {
+               value *= ratio;
+       }
        value = std::max (lower(), std::min(upper(), value));
        return value;
 }
@@ -335,7 +339,7 @@ SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m
 
        bool update_value = false;
        double master_ratio = 0;
-       double list_ratio = 1;
+       double list_ratio = toggled () ? 0 : 1;
 
        boost::shared_ptr<AutomationControl> master;
 
@@ -366,13 +370,20 @@ SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m
 
                /* ..and update automation */
                if (_list) {
+                       XMLNode* before = &alist ()->get_state ();
                        if (master->automation_playback () && master->list()) {
                                _list->list_merge (*master->list().get(), boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, _2));
+                               printf ("y-t %s  %f\n", name().c_str(), list_ratio);
                                _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, list_ratio));
                        } else {
                                // do we need to freeze/thaw the list? probably no: iterators & positions don't change
                                _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, master_ratio));
                        }
+                       XMLNode* after = &alist ()->get_state ();
+                       if (*before != *after) {
+                               _session.begin_reversible_command (string_compose (_("Merge VCA automation into %1"), name ()));
+                               _session.commit_reversible_command (alist()->memento_command (before, after));
+                       }
                }
        }
 
@@ -396,7 +407,7 @@ SlavableAutomationControl::clear_masters ()
        ControlList masters;
        bool update_value = false;
        double master_ratio = 0;
-       double list_ratio = 1;
+       double list_ratio = toggled () ? 0 : 1;
 
        /* null ptr means "all masters */
        pre_remove_master (boost::shared_ptr<AutomationControl>());
@@ -432,6 +443,7 @@ SlavableAutomationControl::clear_masters ()
 
                        /* ..and update automation */
                        if (_list) {
+                               XMLNode* before = &alist ()->get_state ();
                                if (!masters.empty()) {
                                        for (ControlList::const_iterator m = masters.begin(); m != masters.end(); ++m) {
                                                _list->list_merge (*(*m)->list().get(), boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, _2));
@@ -440,6 +452,11 @@ SlavableAutomationControl::clear_masters ()
                                } else {
                                        _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, master_ratio));
                                }
+                               XMLNode* after = &alist ()->get_state ();
+                               if (*before != *after) {
+                                       _session.begin_reversible_command (string_compose (_("Merge VCA automation into %1"), name ()));
+                                       _session.commit_reversible_command (alist()->memento_command (before, after));
+                               }
                        }
        }
 
@@ -508,6 +525,28 @@ SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationCon
        return true; // emit Changed
 }
 
+void
+SlavableAutomationControl::automation_run (framepos_t start, pframes_t nframes)
+{
+       if (!automation_playback ()) {
+               return;
+       }
+
+       assert (_list);
+       bool valid = false;
+       double val = _list->rt_safe_eval (start, valid);
+       if (!valid) {
+               return;
+       }
+       if (toggled ()) {
+               const double thresh = .5 * (_desc.upper - _desc.lower);
+               bool on = (val >= thresh) || (get_masters_value () >= thresh);
+               set_value_unchecked (on ? _desc.upper : _desc.lower);
+       } else {
+               set_value_unchecked (val * get_masters_value ());
+       }
+}
+
 bool
 SlavableAutomationControl::boolean_automation_run_locked (framepos_t start, pframes_t len)
 {
@@ -538,11 +577,6 @@ SlavableAutomationControl::boolean_automation_run_locked (framepos_t start, pfra
                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;