#include "pbd/enumwriter.h"
#include "pbd/error.h"
+#include "pbd/memento_command.h"
#include "pbd/types_convert.h"
#include "pbd/i18n.h"
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();
SlavableAutomationControl::masters_curve_multiply (framepos_t start, framepos_t end, float* vec, framecnt_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) {
vec[i] *= scratch[i];
= 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) {
}
}
}
+ 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;
{
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
}
}
+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)
{
}
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())) {
}
}
- 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 */
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>());
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
return true; // emit Changed
}
+void
+SlavableAutomationControl::automation_run (framepos_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)
{
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;