X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fslavable_automation_control.cc;h=0bfa2b72496d2b6278e8fd889a124d19803d8641;hb=425c54883ab280e62e575bef98e60bfa0eeac803;hp=9c2ce14ae7593b4d2b9756eb4acd3c2ad75b155d;hpb=79ccabdc93aacb7d9299963118f305f9ea26110d;p=ardour.git diff --git a/libs/ardour/slavable_automation_control.cc b/libs/ardour/slavable_automation_control.cc index 9c2ce14ae7..0bfa2b7249 100644 --- a/libs/ardour/slavable_automation_control.cc +++ b/libs/ardour/slavable_automation_control.cc @@ -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(_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(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 m, bool loading) +SlavableAutomationControl::add_master (boost::shared_ptr m) { std::pair res; @@ -280,11 +291,7 @@ SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDispo { boost::shared_ptr 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 } } +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 m) { @@ -312,17 +335,25 @@ SlavableAutomationControl::remove_master (boost::shared_ptr 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 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 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()); @@ -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 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_ptrrt_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); } }