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
46 gain_t v = _desc.normal;
48 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
50 /* if any master is enabled, the slaves are too */
51 if (mr->second.master()->get_value()) {
55 /* get current master value, scale by our current ratio with that master */
56 v *= mr->second.master()->get_value () * mr->second.ratio();
60 return min (_desc.upper, v);
64 SlavableAutomationControl::get_value_locked() const
66 /* read or write masters lock must be held */
68 if (_masters.empty()) {
69 return Control::get_double (false, _session.transport_frame());
72 return get_masters_value_locked ();
75 /** Get the current effective `user' value based on automation state */
77 SlavableAutomationControl::get_value() const
79 bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
82 Glib::Threads::RWLock::ReaderLock lm (master_lock);
83 return get_value_locked ();
85 return Control::get_double (from_list, _session.transport_frame());
90 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
94 std::pair<Masters::iterator,bool> res;
97 Glib::Threads::RWLock::WriterLock lm (master_lock);
98 current_value = get_value_locked ();
100 /* ratio will be recomputed below */
102 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
106 recompute_masters_ratios (current_value);
108 /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
109 avoiding holding a reference to the control in the binding
113 m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m));
115 /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
116 and we no longer hear about changes to the AutomationControl.
118 Note that we fix the "from_self" argument that will
119 be given to our own Changed signal to "false",
120 because the change came from the master.
123 m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2));
124 cerr << this << enum_2_string ((AutomationType) _parameter.type()) << " now listening to Changed from " << m << endl;
127 new_value = get_value_locked ();
131 /* this will notify everyone that we're now slaved to the master */
132 MasterStatusChange (); /* EMIT SIGNAL */
135 if (new_value != current_value) {
136 /* need to do this without a writable() check in case
137 * the master is removed while this control is doing
138 * automation playback.
140 actually_set_value (new_value, Controllable::NoGroup);
146 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
148 cerr << this << enum_2_string ((AutomationType)_parameter.type()) << " master changed, relay changed along\n";
150 /* our value has (likely) changed, but not because we were
151 * modified. Just the master.
154 Changed (false, gcd); /* EMIT SIGNAL */
158 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
160 boost::shared_ptr<AutomationControl> m = wm.lock();
167 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
169 double current_value;
171 Masters::size_type erased = 0;
174 Glib::Threads::RWLock::WriterLock lm (master_lock);
175 current_value = get_value_locked ();
176 erased = _masters.erase (m->id());
178 recompute_masters_ratios (current_value);
180 new_value = get_value_locked ();
184 MasterStatusChange (); /* EMIT SIGNAL */
187 if (new_value != current_value) {
188 Changed (false, Controllable::NoGroup);
193 SlavableAutomationControl::clear_masters ()
195 double current_value;
197 bool had_masters = false;
200 Glib::Threads::RWLock::WriterLock lm (master_lock);
201 current_value = get_value_locked ();
202 if (!_masters.empty()) {
206 new_value = get_value_locked ();
210 MasterStatusChange (); /* EMIT SIGNAL */
213 if (new_value != current_value) {
214 Changed (false, Controllable::NoGroup);
220 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
222 Glib::Threads::RWLock::ReaderLock lm (master_lock);
223 return _masters.find (m->id()) != _masters.end();
227 SlavableAutomationControl::slaved () const
229 Glib::Threads::RWLock::ReaderLock lm (master_lock);
230 return !_masters.empty();
233 #endif /* __libardour_slavable_automation_control_h__ */