fix crash when copy'ing latent plugins
[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                 Glib::Threads::RWLock::WriterLock lm (master_lock);
255                 current_value = get_value_locked ();
256                 erased = _masters.erase (m->id());
257                 if (erased && !_session.deletion_in_progress()) {
258                         recompute_masters_ratios (current_value);
259                 }
260                 masters_left = _masters.size ();
261                 new_value = get_value_locked ();
262         }
263
264         if (_session.deletion_in_progress()) {
265                 /* no reason to care about new values or sending signals */
266                 return;
267         }
268
269         if (erased) {
270                 MasterStatusChange (); /* EMIT SIGNAL */
271         }
272
273         if (new_value != current_value) {
274                 if (masters_left == 0) {
275                         /* no masters left, make sure we keep the same value
276                            that we had before.
277                         */
278                         actually_set_value (current_value, Controllable::UseGroup);
279                 }
280         }
281
282         /* no need to update boolean masters records, since the MR will have
283          * been removed already.
284          */
285 }
286
287 void
288 SlavableAutomationControl::clear_masters ()
289 {
290         double current_value;
291         double new_value;
292         bool had_masters = false;
293
294         /* null ptr means "all masters */
295         pre_remove_master (boost::shared_ptr<AutomationControl>());
296
297         {
298                 Glib::Threads::RWLock::WriterLock lm (master_lock);
299                 current_value = get_value_locked ();
300                 if (!_masters.empty()) {
301                         had_masters = true;
302                 }
303                 _masters.clear ();
304                 new_value = get_value_locked ();
305         }
306
307         if (had_masters) {
308                 MasterStatusChange (); /* EMIT SIGNAL */
309         }
310
311         if (new_value != current_value) {
312                 actually_set_value (current_value, Controllable::UseGroup);
313         }
314
315         /* no need to update boolean masters records, since all MRs will have
316          * been removed already.
317          */
318 }
319
320 bool
321 SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
322 {
323         Glib::Threads::RWLock::ReaderLock lm (master_lock);
324         return _masters.find (m->id()) != _masters.end();
325 }
326
327 bool
328 SlavableAutomationControl::slaved () const
329 {
330         Glib::Threads::RWLock::ReaderLock lm (master_lock);
331         return !_masters.empty();
332 }
333
334 #endif /* __libardour_slavable_automation_control_h__ */