From c1912b6d516b69db67757687de38a115b3b6ab69 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 13 Jun 2017 18:09:22 +0200 Subject: Write inverse master automation. * 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 | 9 +++++++++ libs/ardour/automation_control.cc | 10 +++++++--- libs/ardour/automation_watch.cc | 7 ++++++- libs/ardour/slavable_automation_control.cc | 20 ++++++++++++++------ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/libs/ardour/ardour/slavable_automation_control.h b/libs/ardour/ardour/slavable_automation_control.h index b24409b0a5..3cfc22405c 100644 --- a/libs/ardour/ardour/slavable_automation_control.h +++ b/libs/ardour/ardour/slavable_automation_control.h @@ -45,11 +45,18 @@ public: void clear_masters (); bool slaved_to (boost::shared_ptr) 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); virtual bool boolean_automation_run_locked (framepos_t start, pframes_t len); bool boolean_automation_run (framepos_t start, pframes_t len); diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index 055c000bc9..e22379f75c 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -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()); diff --git a/libs/ardour/automation_watch.cc b/libs/ardour/automation_watch.cc index 954b65120b..21d1b6a49d 100644 --- a/libs/ardour/automation_watch.cc +++ b/libs/ardour/automation_watch.cc @@ -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 sc = boost::dynamic_pointer_cast (*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) diff --git a/libs/ardour/slavable_automation_control.cc b/libs/ardour/slavable_automation_control.cc index 4b1dc7721e..53808c862a 100644 --- a/libs/ardour/slavable_automation_control.cc +++ b/libs/ardour/slavable_automation_control.cc @@ -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); } -- cgit v1.2.3