2 Copyright (C) 2016 Paul Davis
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2 of the License, or (at your option)
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 675 Mass Ave, Cambridge, MA 02139, USA.
19 #ifndef __libardour_slavable_automation_control_h__
20 #define __libardour_slavable_automation_control_h__
22 #include "pbd/enumwriter.h"
23 #include "pbd/error.h"
24 #include "pbd/memento_command.h"
25 #include "pbd/types_convert.h"
27 #include "evoral/Curve.hpp"
29 #include "ardour/audioengine.h"
30 #include "ardour/runtime_functions.h"
31 #include "ardour/slavable_automation_control.h"
32 #include "ardour/session.h"
37 using namespace ARDOUR;
40 SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s,
41 const Evoral::Parameter& parameter,
42 const ParameterDescriptor& desc,
43 boost::shared_ptr<ARDOUR::AutomationList> l,
44 const std::string& name,
45 Controllable::Flag flags)
46 : AutomationControl (s, parameter, desc, l, name, flags)
51 SlavableAutomationControl::~SlavableAutomationControl ()
60 SlavableAutomationControl::get_masters_value_locked () const
63 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
64 if (mr->second.master()->get_value()) {
71 double v = 1.0; /* the masters function as a scaling factor */
73 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
74 v *= mr->second.master_ratio ();
82 SlavableAutomationControl::get_value_locked() const
84 /* read or write masters lock must be held */
86 if (_masters.empty()) {
87 return Control::get_double (false, _session.transport_sample());
91 /* for boolean/toggle controls, if this slave OR any master is
92 * enabled, this slave is enabled. So check our own value
93 * first, because if we are enabled, we can return immediately.
95 if (Control::get_double (false, _session.transport_sample())) {
100 return Control::get_double() * get_masters_value_locked ();
103 /** Get the current effective `user' value based on automation state */
105 SlavableAutomationControl::get_value() const
107 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
109 Glib::Threads::RWLock::ReaderLock lm (master_lock);
111 if (!_masters.empty() && automation_write ()) {
112 /* writing automation takes the fader value as-is, factor out the master */
113 return Control::user_double ();
115 return get_value_locked ();
117 return Control::get_double (true, _session.transport_sample()) * get_masters_value_locked();
122 SlavableAutomationControl::get_masters_curve_locked (samplepos_t, samplepos_t, float*, samplecnt_t) const
124 /* Every AutomationControl needs to implement this as-needed.
126 * This class also provides some convenient methods which
127 * could be used as defaults here (depending on AutomationType)
128 * e.g. masters_curve_multiply()
134 SlavableAutomationControl::masters_curve_multiply (samplepos_t start, samplepos_t end, float* vec, samplecnt_t veclen) const
136 gain_t* scratch = _session.scratch_automation_buffer ();
137 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
138 bool rv = from_list && list()->curve().rt_safe_get_vector (start, end, scratch, veclen);
140 for (samplecnt_t i = 0; i < veclen; ++i) {
141 vec[i] *= scratch[i];
144 apply_gain_to_buffer (vec, veclen, Control::get_double ());
146 if (_masters.empty()) {
150 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
151 boost::shared_ptr<SlavableAutomationControl> sc
152 = boost::dynamic_pointer_cast<SlavableAutomationControl>(mr->second.master());
154 rv |= sc->masters_curve_multiply (start, end, vec, veclen);
155 apply_gain_to_buffer (vec, veclen, mr->second.val_master_inv ());
161 SlavableAutomationControl::reduce_by_masters_locked (double value, bool ignore_automation_state) const
163 if (!_desc.toggled) {
164 Glib::Threads::RWLock::ReaderLock lm (master_lock);
165 if (!_masters.empty() && (ignore_automation_state || !automation_write ())) {
166 /* need to scale given value by current master's scaling */
167 const double masters_value = get_masters_value_locked();
168 if (masters_value == 0.0) {
171 value /= masters_value;
172 value = std::max (lower(), std::min(upper(), value));
180 SlavableAutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
182 value = reduce_by_masters (value);
183 /* this will call Control::set_double() and emit Changed signals as appropriate */
184 AutomationControl::actually_set_value (value, gcd);
188 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
190 std::pair<Masters::iterator,bool> res;
193 const double master_value = m->get_value();
194 Glib::Threads::RWLock::WriterLock lm (master_lock);
196 pair<PBD::ID,MasterRecord> newpair (m->id(), MasterRecord (boost::weak_ptr<AutomationControl> (m), get_value_locked(), master_value));
197 res = _masters.insert (newpair);
201 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
202 avoiding holding a reference to the control in the binding
205 m->DropReferences.connect_same_thread (res.first->second.dropped_connection, boost::bind (&SlavableAutomationControl::master_going_away, this, boost::weak_ptr<AutomationControl>(m)));
207 /* Store the connection inside the MasterRecord, so
208 that when we destroy it, the connection is destroyed
209 and we no longer hear about changes to the
212 Note that this also makes it safe to store a
213 boost::shared_ptr<AutomationControl> in the functor,
214 since we know we will destroy the functor when the
215 connection is destroyed, which happens when we
216 disconnect from the master (for any reason).
218 Note that we fix the "from_self" argument that will
219 be given to our own Changed signal to "false",
220 because the change came from the master.
223 m->Changed.connect_same_thread (res.first->second.changed_connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2, boost::weak_ptr<AutomationControl>(m)));
228 /* this will notify everyone that we're now slaved to the master */
229 MasterStatusChange (); /* EMIT SIGNAL */
234 update_boolean_masters_records (m);
238 SlavableAutomationControl::get_boolean_masters () const
243 Glib::Threads::RWLock::ReaderLock lm (master_lock);
244 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
245 if (mr->second.yn()) {
255 SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<AutomationControl> m)
258 /* We may modify a MasterRecord, but we not modify the master
259 * map, so we use a ReaderLock
261 Glib::Threads::RWLock::ReaderLock lm (master_lock);
262 Masters::iterator mi = _masters.find (m->id());
263 if (mi != _masters.end()) {
264 /* update MasterRecord to show whether the master is
265 on/off. We need to store this because the master
266 may change (in the sense of emitting Changed())
267 several times without actually changing the result
268 of ::get_value(). This is a feature of
269 AutomationControls (or even just Controllables,
270 really) which have more than a simple scalar
271 value. For example, the master may be a mute control
272 which can be muted_by_self() and/or
273 muted_by_masters(). When either of those two
274 conditions changes, Changed() will be emitted, even
275 though ::get_value() will return the same value each
276 time (1.0 if either are true, 0.0 if neither is).
278 This provides a way for derived types to check
279 the last known state of a Master when the Master
280 changes. We update it after calling
281 ::master_changed() (though derived types must do
284 mi->second.set_yn (m->get_value());
290 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::weak_ptr<AutomationControl> wm)
292 boost::shared_ptr<AutomationControl> m = wm.lock ();
294 Glib::Threads::RWLock::ReaderLock lm (master_lock);
295 bool send_signal = handle_master_change (m);
296 lm.release (); // update_boolean_masters_records() takes lock
298 update_boolean_masters_records (m);
300 Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
305 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
307 boost::shared_ptr<AutomationControl> m = wm.lock();
314 SlavableAutomationControl::scale_automation_callback (double value, double ratio) const
316 /* derived classes can override this and e.g. add/subtract. */
318 // XXX we should use the master's upper/lower as threshold
319 if (ratio >= 0.5 * (upper () - lower ())) {
325 value = std::max (lower(), std::min(upper(), value));
330 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
332 if (_session.deletion_in_progress()) {
333 /* no reason to care about new values or sending signals */
337 pre_remove_master (m);
339 const double old_val = AutomationControl::get_double();
341 bool update_value = false;
342 double master_ratio = 0;
343 double list_ratio = toggled () ? 0 : 1;
345 boost::shared_ptr<AutomationControl> master;
348 Glib::Threads::RWLock::WriterLock lm (master_lock);
350 Masters::const_iterator mi = _masters.find (m->id ());
352 if (mi != _masters.end()) {
353 master_ratio = mi->second.master_ratio ();
355 master = mi->second.master();
356 list_ratio *= mi->second.val_master_inv ();
359 if (!_masters.erase (m->id())) {
365 /* when un-assigning we apply the master-value permanently */
366 double new_val = old_val * master_ratio;
368 if (old_val != new_val) {
369 AutomationControl::set_double (new_val, Controllable::NoGroup);
372 /* ..and update automation */
374 XMLNode* before = &alist ()->get_state ();
375 if (master->automation_playback () && master->list()) {
376 _list->list_merge (*master->list().get(), boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, _2));
377 printf ("y-t %s %f\n", name().c_str(), list_ratio);
378 _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, list_ratio));
380 // do we need to freeze/thaw the list? probably no: iterators & positions don't change
381 _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, master_ratio));
383 XMLNode* after = &alist ()->get_state ();
384 if (*before != *after) {
385 _session.begin_reversible_command (string_compose (_("Merge VCA automation into %1"), name ()));
386 _session.commit_reversible_command (alist()->memento_command (before, after));
391 MasterStatusChange (); /* EMIT SIGNAL */
393 /* no need to update boolean masters records, since the MR will have
394 * been removed already.
399 SlavableAutomationControl::clear_masters ()
401 if (_session.deletion_in_progress()) {
402 /* no reason to care about new values or sending signals */
406 const double old_val = AutomationControl::get_double();
409 bool update_value = false;
410 double master_ratio = 0;
411 double list_ratio = toggled () ? 0 : 1;
413 /* null ptr means "all masters */
414 pre_remove_master (boost::shared_ptr<AutomationControl>());
417 Glib::Threads::RWLock::WriterLock lm (master_lock);
418 if (_masters.empty()) {
422 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
423 boost::shared_ptr<AutomationControl> master = mr->second.master();
424 if (master->automation_playback () && master->list()) {
425 masters.push_back (mr->second.master());
426 list_ratio *= mr->second.val_master_inv ();
428 list_ratio *= mr->second.master_ratio ();
432 master_ratio = get_masters_value_locked ();
438 /* permanently apply masters value */
439 double new_val = old_val * master_ratio;
441 if (old_val != new_val) {
442 AutomationControl::set_double (new_val, Controllable::NoGroup);
445 /* ..and update automation */
447 XMLNode* before = &alist ()->get_state ();
448 if (!masters.empty()) {
449 for (ControlList::const_iterator m = masters.begin(); m != masters.end(); ++m) {
450 _list->list_merge (*(*m)->list().get(), boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, _2));
452 _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, list_ratio));
454 _list->y_transform (boost::bind (&SlavableAutomationControl::scale_automation_callback, this, _1, master_ratio));
456 XMLNode* after = &alist ()->get_state ();
457 if (*before != *after) {
458 _session.begin_reversible_command (string_compose (_("Merge VCA automation into %1"), name ()));
459 _session.commit_reversible_command (alist()->memento_command (before, after));
464 MasterStatusChange (); /* EMIT SIGNAL */
466 /* no need to update boolean masters records, since all MRs will have
467 * been removed already.
472 SlavableAutomationControl::find_next_event_locked (double now, double end, Evoral::ControlEvent& next_event) const
474 if (_masters.empty()) {
478 /* iterate over all masters check their automation lists
479 * for any event between "now" and "end" which is earlier than
480 * next_event.when. If found, set next_event.when and return true.
481 * (see also Automatable::find_next_event)
483 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
484 boost::shared_ptr<AutomationControl> ac (mr->second.master());
486 boost::shared_ptr<SlavableAutomationControl> sc
487 = boost::dynamic_pointer_cast<SlavableAutomationControl>(ac);
489 if (sc && sc->find_next_event_locked (now, end, next_event)) {
493 Evoral::ControlList::const_iterator i;
494 boost::shared_ptr<const Evoral::ControlList> alist (ac->list());
495 Evoral::ControlEvent cp (now, 0.0f);
500 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
501 i != alist->end() && (*i)->when < end; ++i) {
502 if ((*i)->when > now) {
507 if (i != alist->end() && (*i)->when < end) {
508 if ((*i)->when < next_event.when) {
509 next_event.when = (*i)->when;
519 SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationControl>)
521 /* Derived classes can implement this for special cases (e.g. mute).
522 * This method is called with a ReaderLock (master_lock) held.
524 * return true if the changed master value resulted
525 * in a change of the control itself. */
526 return true; // emit Changed
530 SlavableAutomationControl::automation_run (samplepos_t start, pframes_t nframes)
532 if (!automation_playback ()) {
538 double val = _list->rt_safe_eval (start, valid);
543 const double thresh = .5 * (_desc.upper - _desc.lower);
544 bool on = (val >= thresh) || (get_masters_value () >= thresh);
545 set_value_unchecked (on ? _desc.upper : _desc.lower);
547 set_value_unchecked (val * get_masters_value ());
552 SlavableAutomationControl::boolean_automation_run_locked (samplepos_t start, pframes_t len)
555 if (!_desc.toggled) {
558 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
559 boost::shared_ptr<AutomationControl> ac (mr->second.master());
560 if (!ac->automation_playback ()) {
563 if (!ac->toggled ()) {
566 boost::shared_ptr<SlavableAutomationControl> sc = boost::dynamic_pointer_cast<MuteControl>(ac);
568 rv |= sc->boolean_automation_run (start, len);
570 boost::shared_ptr<const Evoral::ControlList> alist (ac->list());
572 const bool yn = alist->rt_safe_eval (start, valid) >= 0.5;
576 /* ideally we'd call just master_changed() which calls update_boolean_masters_records()
577 * but that takes the master_lock, which is already locked */
578 if (mr->second.yn() != yn) {
579 rv |= handle_master_change (ac);
580 mr->second.set_yn (yn);
587 SlavableAutomationControl::boolean_automation_run (samplepos_t start, pframes_t len)
591 Glib::Threads::RWLock::ReaderLock lm (master_lock);
592 change = boolean_automation_run_locked (start, len);
595 Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
601 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
603 Glib::Threads::RWLock::ReaderLock lm (master_lock);
604 return _masters.find (m->id()) != _masters.end();
608 SlavableAutomationControl::slaved () const
610 Glib::Threads::RWLock::ReaderLock lm (master_lock);
611 return !_masters.empty();
615 SlavableAutomationControl::MasterRecord::set_state (XMLNode const& n, int)
617 n.get_property (X_("yn"), _yn);
618 n.get_property (X_("val-ctrl"), _val_ctrl);
619 n.get_property (X_("val-master"), _val_master);
624 SlavableAutomationControl::use_saved_master_ratios ()
626 if (!_masters_node) {
630 Glib::Threads::RWLock::ReaderLock lm (master_lock);
632 XMLNodeList nlist = _masters_node->children();
633 XMLNodeIterator niter;
635 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
637 if (!(*niter)->get_property (X_("id"), id_val)) {
640 Masters::iterator mi = _masters.find (id_val);
641 if (mi == _masters.end()) {
644 mi->second.set_state (**niter, Stateful::loading_state_version);
647 delete _masters_node;
655 SlavableAutomationControl::get_state ()
657 XMLNode& node (AutomationControl::get_state());
659 /* store VCA master ratios */
662 Glib::Threads::RWLock::ReaderLock lm (master_lock);
663 if (!_masters.empty()) {
664 XMLNode* masters_node = new XMLNode (X_("masters"));
665 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
666 XMLNode* mnode = new XMLNode (X_("master"));
667 mnode->set_property (X_("id"), mr->second.master()->id());
670 mnode->set_property (X_("yn"), mr->second.yn());
672 mnode->set_property (X_("val-ctrl"), mr->second.val_ctrl());
673 mnode->set_property (X_("val-master"), mr->second.val_master());
675 masters_node->add_child_nocopy (*mnode);
677 node.add_child_nocopy (*masters_node);
685 SlavableAutomationControl::set_state (XMLNode const& node, int version)
687 XMLNodeList nlist = node.children();
688 XMLNodeIterator niter;
690 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
691 if ((*niter)->name() == X_("masters")) {
692 _masters_node = new XMLNode (**niter);
696 return AutomationControl::set_state (node, version);
700 #endif /* __libardour_slavable_automation_control_h__ */