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 "ardour/automation_control.h"
23 #include "ardour/session.h"
26 using namespace ARDOUR;
29 SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s,
30 const Evoral::Parameter& parameter,
31 const ParameterDescriptor& desc,
32 boost::shared_ptr<ARDOUR::AutomationList> l,
33 const std::string& name)
34 : AutomationControl (s, parameter, desc, l, name)
38 SlavableAutomationControl::~SlavableAutomationControl ()
44 SlavableAutomationControl::get_masters_value_locked () const
48 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
49 /* get current master value, scale by our current ratio with that master */
50 v *= mr->second.master()->get_value () * mr->second.ratio();
53 return min (_desc.upper, v);
57 SlavableAutomationControl::get_value_locked() const
59 /* read or write masters lock must be held */
61 if (_masters.empty()) {
62 return Control::get_double (false, _session.transport_frame());
65 return get_masters_value_locked ();
68 /** Get the current effective `user' value based on automation state */
70 SlavableAutomationControl::get_value() const
72 bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
75 Glib::Threads::RWLock::ReaderLock lm (master_lock);
76 return get_value_locked ();
78 return Control::get_double (from_list, _session.transport_frame());
83 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
87 std::pair<Masters::iterator,bool> res;
90 Glib::Threads::RWLock::WriterLock lm (master_lock);
91 current_value = get_value_locked ();
93 /* ratio will be recomputed below */
95 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
99 recompute_masters_ratios (current_value);
101 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
102 avoiding holding a reference to the control in the binding
106 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m));
108 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
109 and we no longer hear about changes to the AutomationControl.
111 Note that we fix the "from_self" argument that will
112 be given to our own Changed signal to "false",
113 because the change came from the master.
117 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2));
120 new_value = get_value_locked ();
124 /* this will notify everyone that we're now slaved to the master */
125 MasterStatusChange (); /* EMIT SIGNAL */
128 if (new_value != current_value) {
129 /* force a call to to ::master_changed() to carry the
130 * consequences that would occur if the master assumed
131 * its current value WHILE we were slaved.
133 master_changed (false, Controllable::NoGroup);
134 /* effective value changed by master */
135 Changed (false, Controllable::NoGroup);
141 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
143 /* our value has (likely) changed, but not because we were
144 * modified. Just the master.
147 Changed (false, gcd); /* EMIT SIGNAL */
151 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
153 boost::shared_ptr<AutomationControl> m = wm.lock();
160 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
162 double current_value;
164 Masters::size_type erased = 0;
167 Glib::Threads::RWLock::WriterLock lm (master_lock);
168 current_value = get_value_locked ();
169 erased = _masters.erase (m->id());
171 recompute_masters_ratios (current_value);
173 new_value = get_value_locked ();
177 MasterStatusChange (); /* EMIT SIGNAL */
180 if (new_value != current_value) {
181 Changed (false, Controllable::NoGroup);
186 SlavableAutomationControl::clear_masters ()
188 double current_value;
190 bool had_masters = false;
193 Glib::Threads::RWLock::WriterLock lm (master_lock);
194 current_value = get_value_locked ();
195 if (!_masters.empty()) {
199 new_value = get_value_locked ();
203 MasterStatusChange (); /* EMIT SIGNAL */
206 if (new_value != current_value) {
207 Changed (false, Controllable::NoGroup);
213 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
215 Glib::Threads::RWLock::ReaderLock lm (master_lock);
216 return _masters.find (m->id()) != _masters.end();
220 SlavableAutomationControl::slaved () const
222 Glib::Threads::RWLock::ReaderLock lm (master_lock);
223 return !_masters.empty();
226 #endif /* __libardour_slavable_automation_control_h__ */