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"
24 #include "ardour/slavable_automation_control.h"
25 #include "ardour/session.h"
28 using namespace ARDOUR;
31 SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s,
32 const Evoral::Parameter& parameter,
33 const ParameterDescriptor& desc,
34 boost::shared_ptr<ARDOUR::AutomationList> l,
35 const std::string& name)
36 : AutomationControl (s, parameter, desc, l, name)
41 SlavableAutomationControl::get_masters_value_locked () const
43 double v = _desc.normal;
46 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
47 if (mr->second.master()->get_value()) {
54 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
55 /* get current master value, scale by our current ratio with that master */
56 v *= mr->second.master()->get_value () * mr->second.ratio();
59 return min ((double) _desc.upper, v);
63 SlavableAutomationControl::get_value_locked() const
65 /* read or write masters lock must be held */
67 if (_masters.empty()) {
68 return Control::get_double (false, _session.transport_frame());
72 /* for boolean/toggle controls, if this slave OR any master is
73 * enabled, this slave is enabled. So check our own value
74 * first, because if we are enabled, we can return immediately.
76 if (Control::get_double (false, _session.transport_frame())) {
81 return get_masters_value_locked ();
84 /** Get the current effective `user' value based on automation state */
86 SlavableAutomationControl::get_value() const
88 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
91 Glib::Threads::RWLock::ReaderLock lm (master_lock);
92 return get_value_locked ();
94 return Control::get_double (from_list, _session.transport_frame());
99 SlavableAutomationControl::actually_set_value (double val, Controllable::GroupControlDisposition group_override)
101 val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
104 Glib::Threads::RWLock::WriterLock lm (master_lock);
106 if (!_masters.empty()) {
107 recompute_masters_ratios (val);
111 /* this sets the Evoral::Control::_user_value for us, which will
112 be retrieved by AutomationControl::get_value ()
114 AutomationControl::actually_set_value (val, group_override);
116 _session.set_dirty ();
120 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
122 double current_value;
124 std::pair<Masters::iterator,bool> res;
127 Glib::Threads::RWLock::WriterLock lm (master_lock);
128 current_value = get_value_locked ();
130 /* ratio will be recomputed below */
132 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
137 recompute_masters_ratios (current_value);
140 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
141 avoiding holding a reference to the control in the binding
145 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m));
147 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
148 and we no longer hear about changes to the AutomationControl.
150 Note that we fix the "from_self" argument that will
151 be given to our own Changed signal to "false",
152 because the change came from the master.
155 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2));
156 cerr << this << enum_2_string ((AutomationType) _parameter.type()) << " now listening to Changed from " << m << endl;
159 new_value = get_value_locked ();
163 /* this will notify everyone that we're now slaved to the master */
164 MasterStatusChange (); /* EMIT SIGNAL */
167 if (new_value != current_value) {
168 /* need to do this without a writable() check in case
169 * the master is removed while this control is doing
170 * automation playback.
172 actually_set_value (new_value, Controllable::NoGroup);
178 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
180 /* our value has (likely) changed, but not because we were
181 * modified. Just the master.
184 /* propagate master state into our own control so that if we stop
185 * being slaved, our value doesn't change, and propagate to any
186 * group this control is part of.
189 cerr << this << ' ' << enum_2_string ((AutomationType) _parameter.type()) << " pass along " << get_masters_value() << " from master to group\n";
190 actually_set_value (get_masters_value(), Controllable::UseGroup);
194 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
196 boost::shared_ptr<AutomationControl> m = wm.lock();
203 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
205 double current_value;
208 Masters::size_type erased = 0;
211 Glib::Threads::RWLock::WriterLock lm (master_lock);
212 current_value = get_value_locked ();
213 erased = _masters.erase (m->id());
215 recompute_masters_ratios (current_value);
217 masters_left = _masters.size ();
218 new_value = get_value_locked ();
222 MasterStatusChange (); /* EMIT SIGNAL */
225 if (new_value != current_value) {
226 if (masters_left == 0) {
227 /* no masters left, make sure we keep the same value
230 actually_set_value (current_value, Controllable::UseGroup);
236 SlavableAutomationControl::clear_masters ()
238 double current_value;
240 bool had_masters = false;
243 Glib::Threads::RWLock::WriterLock lm (master_lock);
244 current_value = get_value_locked ();
245 if (!_masters.empty()) {
249 new_value = get_value_locked ();
253 MasterStatusChange (); /* EMIT SIGNAL */
256 if (new_value != current_value) {
257 Changed (false, Controllable::NoGroup);
263 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
265 Glib::Threads::RWLock::ReaderLock lm (master_lock);
266 return _masters.find (m->id()) != _masters.end();
270 SlavableAutomationControl::slaved () const
272 Glib::Threads::RWLock::ReaderLock lm (master_lock);
273 return !_masters.empty();
276 #endif /* __libardour_slavable_automation_control_h__ */