Write inverse master automation.
authorRobin Gareus <robin@gareus.org>
Tue, 13 Jun 2017 16:09:22 +0000 (18:09 +0200)
committerRobin Gareus <robin@gareus.org>
Tue, 13 Jun 2017 16:09:59 +0000 (18:09 +0200)
 * The UI and ctrl-surface controls use and display the combined value,
including control-masters.

 * The Automation lane of a control is the raw value of the control
without masters.

When touching (or writing) automation, the control-master needs to be
factored out (or subtracted). e.g press+hold a control -> write inverse
master automation.

libs/ardour/ardour/slavable_automation_control.h
libs/ardour/automation_control.cc
libs/ardour/automation_watch.cc
libs/ardour/slavable_automation_control.cc

index b24409b0a52edae2d1f3a8ac28063ccc3d95cc39..3cfc22405c88c4e1311784b1cef7173e9e18b18b 100644 (file)
@@ -45,11 +45,18 @@ public:
        void clear_masters ();
        bool slaved_to (boost::shared_ptr<AutomationControl>) const;
        bool slaved () const;
+
        double get_masters_value () const {
                Glib::Threads::RWLock::ReaderLock lm (master_lock);
                return get_masters_value_locked ();
        }
 
+       /* factor out get_masters_value() */
+       double reduce_by_masters (double val, bool ignore_automation_state = false) const {
+               Glib::Threads::RWLock::ReaderLock lm (master_lock);
+               return reduce_by_masters_locked (val, ignore_automation_state);
+       }
+
        bool get_masters_curve (framepos_t s, framepos_t e, float* v, framecnt_t l) const {
                Glib::Threads::RWLock::ReaderLock lm (master_lock);
                return get_masters_curve_locked (s, e, v, l);
@@ -130,6 +137,8 @@ protected:
        virtual bool get_masters_curve_locked (framepos_t, framepos_t, float*, framecnt_t) const;
        bool masters_curve_multiply (framepos_t, framepos_t, float*, framecnt_t) const;
 
+       virtual double reduce_by_masters_locked (double val, bool) const;
+
        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);
index 055c000bc9b9fec97a0a74389fa48cd19c206bfd..e22379f75c2986d51b0446674b98557dec6f73cf 100644 (file)
@@ -257,10 +257,14 @@ AutomationControl::start_touch(double when)
        }
 
        if (!touching()) {
-
                if (alist()->automation_state() == Touch) {
-                       /* subtle. aligns the user value with the playback */
-                       set_value (get_value (), Controllable::NoGroup);
+                       /* subtle. aligns the user value with the playback and
+                        * use take actual value (incl masters).
+                        *
+                        * Touch + hold writes inverse curve of master-automation
+                        * using AutomationWatch::timer ()
+                        */
+                       AutomationControl::actually_set_value (get_value (), Controllable::NoGroup);
                        alist()->start_touch (when);
                        if (!_desc.toggled) {
                                AutomationWatch::instance().add_automation_watch (shared_from_this());
index 954b65120b10da81eeef474183d4e88dfc172de4..21d1b6a49d18b92dff060cefd01957b5517d684b 100644 (file)
@@ -149,7 +149,12 @@ AutomationWatch::timer ()
                if (time > _last_time) {  //we only write automation in the forward direction; this fixes automation-recording in a loop
                        for (AutomationWatches::iterator aw = automation_watches.begin(); aw != automation_watches.end(); ++aw) {
                                if ((*aw)->alist()->automation_write()) {
-                                       (*aw)->list()->add (time, (*aw)->user_double(), true);
+                                       double val = (*aw)->user_double();
+                                       boost::shared_ptr<SlavableAutomationControl> sc = boost::dynamic_pointer_cast<SlavableAutomationControl> (*aw);
+                                       if (sc) {
+                                               val = sc->reduce_by_masters (val, true);
+                                       }
+                                       (*aw)->list()->add (time, val, true);
                                }
                        }
                } else if (time != _last_time) {  //transport stopped or reversed.  stop the automation pass and start a new one (for bonus points, someday store the previous pass in an undo record)
index 4b1dc7721e6b8c95ddbd54ae546fdf8f7da78a90..53808c862a13ca56da92f936ed43a18bb6637b5b 100644 (file)
@@ -106,6 +106,10 @@ 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();
@@ -153,14 +157,12 @@ SlavableAutomationControl::masters_curve_multiply (framepos_t start, framepos_t
        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) {
@@ -171,7 +173,13 @@ 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);
 }