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