fix chaining of masters for boolean controls
[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                 PBD::ID id (m->id());
131
132                 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (id, MasterRecord (m, 1.0)));
133
134                 if (res.second) {
135
136                         recompute_masters_ratios (current_value);
137
138                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
139                            avoiding holding a reference to the control in the binding
140                            itself.
141                         */
142
143                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m));
144
145                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
146                            and we no longer hear about changes to the AutomationControl.
147
148                            Note that we fix the "from_self" argument that will
149                            be given to our own Changed signal to "false",
150                            because the change came from the master.
151                         */
152
153                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2, m));
154                         cerr << this << enum_2_string ((AutomationType) _parameter.type()) << " now listening to Changed from " << m << endl;
155                 }
156         }
157
158         if (res.second) {
159                 /* this will notify everyone that we're now slaved to the master */
160                 MasterStatusChange (); /* EMIT SIGNAL */
161         }
162
163         post_add_master (m);
164
165         update_boolean_masters_records (m);
166 }
167
168 int32_t
169 SlavableAutomationControl::get_boolean_masters () const
170 {
171         int32_t n = 0;
172
173         if (_desc.toggled) {
174                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
175                 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
176                         if (mr->second.yn()) {
177                                 ++n;
178                         }
179                 }
180         }
181
182         return n;
183 }
184
185 void
186 SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<AutomationControl> m)
187 {
188         if (_desc.toggled) {
189                 /* We may modify a MasterRecord, but we not modify the master
190                  * map, so we use a ReaderLock
191                  */
192                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
193                 Masters::iterator mi = _masters.find (m->id());
194                 if (mi != _masters.end()) {
195                         /* update MasterRecord to show whether the master is
196                            on/off. We need to store this because the master
197                            may change (in the sense of emitting Changed())
198                            several times without actually changing the result
199                            of ::get_value(). This is a feature of
200                            AutomationControls (or even just Controllables,
201                            really) which have more than a simple scalar
202                            value. For example, the master may be a mute control
203                            which can be muted_by_self() and/or
204                            muted_by_masters(). When either of those two
205                            conditions changes, Changed() will be emitted, even
206                            though ::get_value() will return the same value each
207                            time (1.0 if either are true, 0.0 if neither is).
208
209                            This provides a way for derived types to check
210                            the last known state of a Master when the Master
211                            changes. We update it after calling
212                            ::master_changed() (though derived types must do
213                            this themselves).
214                         */
215                         mi->second.set_yn (m->get_value());
216                 }
217         }
218 }
219
220 void
221 SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
222 {
223         update_boolean_masters_records (m);
224         Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
225 }
226
227 void
228 SlavableAutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
229 {
230         boost::shared_ptr<AutomationControl> m = wm.lock();
231         if (m) {
232                 remove_master (m);
233         }
234 }
235
236 void
237 SlavableAutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
238 {
239         double current_value;
240         double new_value;
241         bool masters_left;
242         Masters::size_type erased = 0;
243
244         pre_remove_master (m);
245
246         {
247                 Glib::Threads::RWLock::WriterLock lm (master_lock);
248                 current_value = get_value_locked ();
249                 erased = _masters.erase (m->id());
250                 if (erased) {
251                         recompute_masters_ratios (current_value);
252                 }
253                 masters_left = _masters.size ();
254                 new_value = get_value_locked ();
255         }
256
257         if (erased) {
258                 MasterStatusChange (); /* EMIT SIGNAL */
259         }
260
261         if (new_value != current_value) {
262                 if (masters_left == 0) {
263                         /* no masters left, make sure we keep the same value
264                            that we had before.
265                         */
266                         actually_set_value (current_value, Controllable::UseGroup);
267                 }
268         }
269
270         /* no need to update boolean masters records, since the MR will have
271          * been removed already.
272          */
273 }
274
275 void
276 SlavableAutomationControl::clear_masters ()
277 {
278         double current_value;
279         double new_value;
280         bool had_masters = false;
281
282         /* null ptr means "all masters */
283         pre_remove_master (boost::shared_ptr<AutomationControl>());
284
285         {
286                 Glib::Threads::RWLock::WriterLock lm (master_lock);
287                 current_value = get_value_locked ();
288                 if (!_masters.empty()) {
289                         had_masters = true;
290                 }
291                 _masters.clear ();
292                 new_value = get_value_locked ();
293         }
294
295         if (had_masters) {
296                 MasterStatusChange (); /* EMIT SIGNAL */
297         }
298
299         if (new_value != current_value) {
300                 actually_set_value (current_value, Controllable::UseGroup);
301         }
302
303         /* no need to update boolean masters records, since all MRs will have
304          * been removed already.
305          */
306 }
307
308 bool
309 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
310 {
311         Glib::Threads::RWLock::ReaderLock lm (master_lock);
312         return _masters.find (m->id()) != _masters.end();
313 }
314
315 bool
316 SlavableAutomationControl::slaved () const
317 {
318         Glib::Threads::RWLock::ReaderLock lm (master_lock);
319         return !_masters.empty();
320 }
321
322 #endif /* __libardour_slavable_automation_control_h__ */