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);
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);
}
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());
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)
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 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) {
}
}
}
+ 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);
}