Remove unused variable
[ardour.git] / libs / ardour / slavable_automation_control.cc
index 9c2ce14ae7593b4d2b9756eb4acd3c2ad75b155d..0bfa2b72496d2b6278e8fd889a124d19803d8641 100644 (file)
@@ -21,8 +21,8 @@
 
 #include "pbd/enumwriter.h"
 #include "pbd/error.h"
+#include "pbd/memento_command.h"
 #include "pbd/types_convert.h"
-#include "pbd/i18n.h"
 
 #include "evoral/Curve.hpp"
 
@@ -31,6 +31,8 @@
 #include "ardour/slavable_automation_control.h"
 #include "ardour/session.h"
 
+#include "pbd/i18n.h"
+
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
@@ -82,7 +84,7 @@ SlavableAutomationControl::get_value_locked() const
        /* read or write masters lock must be held */
 
        if (_masters.empty()) {
-               return Control::get_double (false, _session.transport_frame());
+               return Control::get_double (false, _session.transport_sample());
        }
 
        if (_desc.toggled) {
@@ -90,7 +92,7 @@ SlavableAutomationControl::get_value_locked() const
                 * enabled, this slave is enabled. So check our own value
                 * first, because if we are enabled, we can return immediately.
                 */
-               if (Control::get_double (false, _session.transport_frame())) {
+               if (Control::get_double (false, _session.transport_sample())) {
                        return _desc.upper;
                }
        }
@@ -106,14 +108,18 @@ SlavableAutomationControl::get_value() const
 
        Glib::Threads::RWLock::ReaderLock lm (master_lock);
        if (!from_list) {
+               if (!_masters.empty() && automation_write ()) {
+                       /* writing automation takes the fader value as-is, factor out the master */
+                       return Control::user_double ();
+               }
                return get_value_locked ();
        } else {
-               return Control::get_double (true, _session.transport_frame()) * get_masters_value_locked();
+               return Control::get_double (true, _session.transport_sample()) * get_masters_value_locked();
        }
 }
 
 bool
-SlavableAutomationControl::get_masters_curve_locked (framepos_t, framepos_t, float*, framecnt_t) const
+SlavableAutomationControl::get_masters_curve_locked (samplepos_t, samplepos_t, float*, samplecnt_t) const
 {
        /* Every AutomationControl needs to implement this as-needed.
         *
@@ -125,12 +131,13 @@ SlavableAutomationControl::get_masters_curve_locked (framepos_t, framepos_t, flo
 }
 
 bool
-SlavableAutomationControl::masters_curve_multiply (framepos_t start, framepos_t end, float* vec, framecnt_t veclen) const
+SlavableAutomationControl::masters_curve_multiply (samplepos_t start, samplepos_t end, float* vec, samplecnt_t veclen) const
 {
        gain_t* scratch = _session.scratch_automation_buffer ();
-       bool rv = list()->curve().rt_safe_get_vector (start, end, scratch, veclen);
+       bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
+       bool rv = from_list && list()->curve().rt_safe_get_vector (start, end, scratch, veclen);
        if (rv) {
-               for (framecnt_t i = 0; i < veclen; ++i) {
+               for (samplecnt_t i = 0; i < veclen; ++i) {
                        vec[i] *= scratch[i];
                }
        } else {
@@ -145,19 +152,17 @@ SlavableAutomationControl::masters_curve_multiply (framepos_t start, framepos_t
                        = boost::dynamic_pointer_cast<SlavableAutomationControl>(mr->second.master());
                assert (sc);
                rv |= sc->masters_curve_multiply (start, end, vec, veclen);
-               apply_gain_to_buffer (vec, veclen, mr->second.master_ratio ());
+               apply_gain_to_buffer (vec, veclen, mr->second.val_master_inv ());
        }
        return rv;
 }
 
-void
-SlavableAutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
+double
+SlavableAutomationControl::reduce_by_masters_locked (double value, bool ignore_automation_state) const
 {
        if (!_desc.toggled) {
-
-               Glib::Threads::RWLock::WriterLock lm (master_lock);
-
-               if (!_masters.empty()) {
+               Glib::Threads::RWLock::ReaderLock lm (master_lock);
+               if (!_masters.empty() && (ignore_automation_state || !automation_write ())) {
                        /* need to scale given value by current master's scaling */
                        const double masters_value = get_masters_value_locked();
                        if (masters_value == 0.0) {
@@ -168,13 +173,19 @@ SlavableAutomationControl::actually_set_value (double value, PBD::Controllable::
                        }
                }
        }
+       return value;
+}
 
+void
+SlavableAutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
+{
+       value = reduce_by_masters (value);
        /* this will call Control::set_double() and emit Changed signals as appropriate */
        AutomationControl::actually_set_value (value, gcd);
 }
 
 void
-SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m, bool loading)
+SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
 {
        std::pair<Masters::iterator,bool> res;
 
@@ -280,11 +291,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
 
@@ -303,6 +310,22 @@ SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl>
        }
 }
 
+double
+SlavableAutomationControl::scale_automation_callback (double value, double ratio) const
+{
+       /* derived classes can override this and e.g. add/subtract. */
+       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;
+}
+
 void
 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
 {
@@ -312,17 +335,25 @@ SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m
        }
 
        pre_remove_master (m);
-       double new_val = AutomationControl::get_double();
-       const double old_val = new_val;
+
+       const double old_val = AutomationControl::get_double();
+
+       bool update_value = false;
+       double master_ratio = 0;
+       double list_ratio = toggled () ? 0 : 1;
+
+       boost::shared_ptr<AutomationControl> master;
 
        {
                Glib::Threads::RWLock::WriterLock lm (master_lock);
 
                Masters::const_iterator mi = _masters.find (m->id ());
 
-               /* when un-assigning we apply the master-value permanently */
                if (mi != _masters.end()) {
-                       new_val *= mi->second.master_ratio ();
+                       master_ratio = mi->second.master_ratio ();
+                       update_value = true;
+                       master = mi->second.master();
+                       list_ratio *= mi->second.val_master_inv ();
                }
 
                if (!_masters.erase (m->id())) {
@@ -330,8 +361,31 @@ SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m
                }
        }
 
-       if (old_val != new_val) {
-               AutomationControl::set_double (new_val, Controllable::NoGroup);
+       if (update_value) {
+               /* when un-assigning we apply the master-value permanently */
+               double new_val = old_val * master_ratio;
+
+               if (old_val != new_val) {
+                       AutomationControl::set_double (new_val, Controllable::NoGroup);
+               }
+
+               /* ..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));
+                       }
+               }
        }
 
        MasterStatusChange (); /* EMIT SIGNAL */
@@ -349,8 +403,12 @@ SlavableAutomationControl::clear_masters ()
                return;
        }
 
-       double new_val = AutomationControl::get_double();
-       const double old_val = new_val;
+       const double old_val = AutomationControl::get_double();
+
+       ControlList masters;
+       bool update_value = false;
+       double master_ratio = 0;
+       double list_ratio = toggled () ? 0 : 1;
 
        /* null ptr means "all masters */
        pre_remove_master (boost::shared_ptr<AutomationControl>());
@@ -360,15 +418,49 @@ SlavableAutomationControl::clear_masters ()
                if (_masters.empty()) {
                        return;
                }
-               /* permanently apply masters value */
-               new_val *= get_masters_value_locked ();
 
+               for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
+                       boost::shared_ptr<AutomationControl> master = mr->second.master();
+                       if (master->automation_playback () && master->list()) {
+                               masters.push_back (mr->second.master());
+                               list_ratio *= mr->second.val_master_inv ();
+                       } else {
+                               list_ratio *= mr->second.master_ratio ();
+                       }
+               }
+
+               master_ratio = get_masters_value_locked ();
+               update_value = true;
                _masters.clear ();
        }
 
-       if (old_val != new_val) {
-               AutomationControl::set_double (new_val, Controllable::NoGroup);
+       if (update_value) {
+               /* permanently apply masters value */
+                       double new_val = old_val * master_ratio;
+
+                       if (old_val != new_val) {
+                               AutomationControl::set_double (new_val, Controllable::NoGroup);
+                       }
+
+                       /* ..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));
+                                       }
+                                       _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, list_ratio));
+                               } 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));
+                               }
+                       }
        }
+
        MasterStatusChange (); /* EMIT SIGNAL */
 
        /* no need to update boolean masters records, since all MRs will have
@@ -434,8 +526,30 @@ SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationCon
        return true; // emit Changed
 }
 
+void
+SlavableAutomationControl::automation_run (samplepos_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)
+SlavableAutomationControl::boolean_automation_run_locked (samplepos_t start, pframes_t len)
 {
        bool rv = false;
        if (!_desc.toggled) {
@@ -464,18 +578,13 @@ 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;
 }
 
 bool
-SlavableAutomationControl::boolean_automation_run (framepos_t start, pframes_t len)
+SlavableAutomationControl::boolean_automation_run (samplepos_t start, pframes_t len)
 {
        bool change = false;
        {
@@ -564,8 +673,8 @@ SlavableAutomationControl::get_state ()
                                        mnode->set_property (X_("val-master"), mr->second.val_master());
                                }
                                masters_node->add_child_nocopy (*mnode);
-                               node.add_child_nocopy (*masters_node);
                        }
+                       node.add_child_nocopy (*masters_node);
                }
        }