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