no-op: explanatory comments
[ardour.git] / libs / ardour / slavable_automation_control.cc
1 /*
2     Copyright (C) 2016 Paul Davis
3
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)
7     any later version.
8
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
12     for more details.
13
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.
17 */
18
19 #ifndef __libardour_slavable_automation_control_h__
20 #define __libardour_slavable_automation_control_h__
21
22 #include "pbd/enumwriter.h"
23
24 #include "ardour/slavable_automation_control.h"
25 #include "ardour/session.h"
26
27 using namespace std;
28 using namespace ARDOUR;
29 using namespace PBD;
30
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)
37 {
38 }
39
40 double
41 SlavableAutomationControl::get_masters_value_locked () const
42 {
43         double v = _desc.normal;
44
45         if (_desc.toggled) {
46                 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
47                         if (mr->second.master()->get_value()) {
48                                 return _desc.upper;
49                         }
50                 }
51                 return _desc.lower;
52         }
53
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();
57         }
58
59         return min ((double) _desc.upper, v);
60 }
61
62 double
63 SlavableAutomationControl::get_value_locked() const
64 {
65         /* read or write masters lock must be held */
66
67         if (_masters.empty()) {
68                 return Control::get_double (false, _session.transport_frame());
69         }
70
71         if (_desc.toggled) {
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.
75                  */
76                 if (Control::get_double (false, _session.transport_frame())) {
77                         return _desc.upper;
78                 }
79         }
80
81         return get_masters_value_locked ();
82 }
83
84 /** Get the current effective `user' value based on automation state */
85 double
86 SlavableAutomationControl::get_value() const
87 {
88         bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
89
90         if (!from_list) {
91                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
92                 return get_value_locked ();
93         } else {
94                 return Control::get_double (from_list, _session.transport_frame());
95         }
96 }
97
98 void
99 SlavableAutomationControl::actually_set_value (double val, Controllable::GroupControlDisposition group_override)
100 {
101         val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
102
103         {
104                 Glib::Threads::RWLock::WriterLock lm (master_lock);
105
106                 if (!_masters.empty()) {
107                         recompute_masters_ratios (val);
108                 }
109         }
110
111         /* this sets the Evoral::Control::_user_value for us, which will
112            be retrieved by AutomationControl::get_value ()
113         */
114         AutomationControl::actually_set_value (val, group_override);
115
116         _session.set_dirty ();
117 }
118
119 void
120 SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
121 {
122         std::pair<Masters::iterator,bool> res;
123
124         {
125                 Glib::Threads::RWLock::WriterLock lm (master_lock);
126                 const double current_value = get_value_locked ();
127
128                 /* ratio will be recomputed below */
129
130                 pair<PBD::ID,MasterRecord> newpair (m->id(), MasterRecord (m, 1.0));
131                 res = _masters.insert (newpair);
132
133                 if (res.second) {
134
135                         recompute_masters_ratios (current_value);
136
137                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
138                            avoiding holding a reference to the control in the binding
139                            itself.
140                         */
141
142                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, boost::weak_ptr<AutomationControl>(m)));
143
144                         /* Store the connection inside the MasterRecord, so
145                            that when we destroy it, the connection is destroyed
146                            and we no longer hear about changes to the
147                            AutomationControl. 
148
149                            Note that this also makes it safe to store a
150                            boost::shared_ptr<AutomationControl> in the functor,
151                            since we know we will destroy the functor when the 
152                            connection is destroyed, which happens when we
153                            disconnect from the master (for any reason).
154
155                            Note that we fix the "from_self" argument that will
156                            be given to our own Changed signal to "false",
157                            because the change came from the master.
158                         */
159
160                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2, m));
161                         cerr << this << enum_2_string ((AutomationType) _parameter.type()) << " now listening to Changed from " << m << endl;
162                 }
163         }
164
165         if (res.second) {
166                 /* this will notify everyone that we're now slaved to the master */
167                 MasterStatusChange (); /* EMIT SIGNAL */
168         }
169
170         post_add_master (m);
171
172         update_boolean_masters_records (m);
173 }
174
175 int32_t
176 SlavableAutomationControl::get_boolean_masters () const
177 {
178         int32_t n = 0;
179
180         if (_desc.toggled) {
181                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
182                 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
183                         if (mr->second.yn()) {
184                                 ++n;
185                         }
186                 }
187         }
188
189         return n;
190 }
191
192 void
193 SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<AutomationControl> m)
194 {
195         if (_desc.toggled) {
196                 /* We may modify a MasterRecord, but we not modify the master
197                  * map, so we use a ReaderLock
198                  */
199                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
200                 Masters::iterator mi = _masters.find (m->id());
201                 if (mi != _masters.end()) {
202                         /* update MasterRecord to show whether the master is
203                            on/off. We need to store this because the master
204                            may change (in the sense of emitting Changed())
205                            several times without actually changing the result
206                            of ::get_value(). This is a feature of
207                            AutomationControls (or even just Controllables,
208                            really) which have more than a simple scalar
209                            value. For example, the master may be a mute control
210                            which can be muted_by_self() and/or
211                            muted_by_masters(). When either of those two
212                            conditions changes, Changed() will be emitted, even
213                            though ::get_value() will return the same value each
214                            time (1.0 if either are true, 0.0 if neither is).
215
216                            This provides a way for derived types to check
217                            the last known state of a Master when the Master
218                            changes. We update it after calling
219                            ::master_changed() (though derived types must do
220                            this themselves).
221                         */
222                         mi->second.set_yn (m->get_value());
223                 }
224         }
225 }
226
227 void
228 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
229 {
230         update_boolean_masters_records (m);
231         Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
232 }
233
234 void
235 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
236 {
237         boost::shared_ptr<AutomationControl> m = wm.lock();
238         if (m) {
239                 remove_master (m);
240         }
241 }
242
243 void
244 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
245 {
246         double current_value;
247         double new_value;
248         bool masters_left;
249         Masters::size_type erased = 0;
250
251         pre_remove_master (m);
252
253
254         {
255                 Glib::Threads::RWLock::WriterLock lm (master_lock);
256                 current_value = get_value_locked ();
257                 erased = _masters.erase (m->id());
258                 if (erased) {
259                         recompute_masters_ratios (current_value);
260                 }
261                 masters_left = _masters.size ();
262                 new_value = get_value_locked ();
263         }
264
265         if (erased) {
266                 MasterStatusChange (); /* EMIT SIGNAL */
267         }
268
269         if (new_value != current_value) {
270                 if (masters_left == 0) {
271                         /* no masters left, make sure we keep the same value
272                            that we had before.
273                         */
274                         actually_set_value (current_value, Controllable::UseGroup);
275                 }
276         }
277
278         /* no need to update boolean masters records, since the MR will have
279          * been removed already.
280          */
281 }
282
283 void
284 SlavableAutomationControl::clear_masters ()
285 {
286         double current_value;
287         double new_value;
288         bool had_masters = false;
289
290         /* null ptr means "all masters */
291         pre_remove_master (boost::shared_ptr<AutomationControl>());
292
293         {
294                 Glib::Threads::RWLock::WriterLock lm (master_lock);
295                 current_value = get_value_locked ();
296                 if (!_masters.empty()) {
297                         had_masters = true;
298                 }
299                 _masters.clear ();
300                 new_value = get_value_locked ();
301         }
302
303         if (had_masters) {
304                 MasterStatusChange (); /* EMIT SIGNAL */
305         }
306
307         if (new_value != current_value) {
308                 actually_set_value (current_value, Controllable::UseGroup);
309         }
310
311         /* no need to update boolean masters records, since all MRs will have
312          * been removed already.
313          */
314 }
315
316 bool
317 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
318 {
319         Glib::Threads::RWLock::ReaderLock lm (master_lock);
320         return _masters.find (m->id()) != _masters.end();
321 }
322
323 bool
324 SlavableAutomationControl::slaved () const
325 {
326         Glib::Threads::RWLock::ReaderLock lm (master_lock);
327         return !_masters.empty();
328 }
329
330 #endif /* __libardour_slavable_automation_control_h__ */