correctly restore VCA-VCA slave/master relationships at session load time
[ardour.git] / libs / ardour / gain_control.cc
1 /*
2     Copyright (C) 2006-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 #include <cmath>
20
21 #include "pbd/convert.h"
22 #include "pbd/strsplit.h"
23
24 #include "ardour/dB.h"
25 #include "ardour/gain_control.h"
26 #include "ardour/session.h"
27 #include "ardour/vca.h"
28 #include "ardour/vca_manager.h"
29
30 #include "i18n.h"
31
32 using namespace ARDOUR;
33 using namespace std;
34
35 GainControl::GainControl (Session& session, const Evoral::Parameter &param, boost::shared_ptr<AutomationList> al)
36         : AutomationControl (session, param, ParameterDescriptor(param),
37                              al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)),
38                              param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) {
39
40         alist()->reset_default (1.0);
41
42         lower_db = accurate_coefficient_to_dB (_desc.lower);
43         range_db = accurate_coefficient_to_dB (_desc.upper) - lower_db;
44 }
45
46 gain_t
47 GainControl::get_value_locked () const {
48
49         /* read or write masters lock must be held */
50
51         if (_masters.empty()) {
52                 return AutomationControl::get_value();
53         }
54
55         gain_t g = 1.0;
56
57         for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
58                 /* get current master value, scale by our current ratio with that master */
59                 g *= mr->second.master()->get_value () * mr->second.ratio();
60         }
61
62         return min (Config->get_max_gain(), g);
63 }
64
65 double
66 GainControl::get_value () const
67 {
68         Glib::Threads::RWLock::ReaderLock lm (master_lock);
69         return get_value_locked ();
70 }
71
72 void
73 GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
74 {
75         if (writable()) {
76                 _set_value (val, group_override);
77         }
78 }
79
80 void
81 GainControl::set_value_unchecked (double val)
82 {
83         /* used only automation playback */
84         _set_value (val, Controllable::NoGroup);
85 }
86
87 void
88 GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
89 {
90         val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
91
92         {
93                 Glib::Threads::RWLock::WriterLock lm (master_lock);
94
95                 if (!_masters.empty()) {
96                         recompute_masters_ratios (val);
97                 }
98         }
99
100         AutomationControl::set_value (val, group_override);
101
102         _session.set_dirty ();
103 }
104
105 double
106 GainControl::internal_to_interface (double v) const
107 {
108         if (_desc.type == GainAutomation) {
109                 return gain_to_slider_position (v);
110         } else {
111                 return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
112         }
113 }
114
115 double
116 GainControl::interface_to_internal (double v) const
117 {
118         if (_desc.type == GainAutomation) {
119                 return slider_position_to_gain (v);
120         } else {
121                 return dB_to_coefficient (lower_db + v * range_db);
122         }
123 }
124
125 double
126 GainControl::internal_to_user (double v) const
127 {
128         return accurate_coefficient_to_dB (v);
129 }
130
131 double
132 GainControl::user_to_internal (double u) const
133 {
134         return dB_to_coefficient (u);
135 }
136
137 std::string
138 GainControl::get_user_string () const
139 {
140         char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
141         return std::string(theBuf);
142 }
143
144 gain_t
145 GainControl::get_master_gain () const
146 {
147         Glib::Threads::RWLock::ReaderLock sm (master_lock, Glib::Threads::TRY_LOCK);
148
149         if (sm.locked()) {
150                 return get_master_gain_locked ();
151         }
152
153         return 1.0;
154 }
155
156 gain_t
157 GainControl::get_master_gain_locked () const
158 {
159         /* Master lock MUST be held (read or write lock is acceptable) */
160
161         gain_t g = 1.0;
162
163         for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
164                 /* get current master value, scale by our current ratio with that master */
165                 g *= mr->second.master()->get_value () * mr->second.ratio();
166         }
167
168         return g;
169 }
170
171 void
172 GainControl::add_master (boost::shared_ptr<VCA> vca)
173 {
174         gain_t current_value;
175         std::pair<Masters::iterator,bool> res;
176
177         {
178                 Glib::Threads::RWLock::WriterLock lm (master_lock);
179                 current_value = get_value_locked ();
180
181                 /* ratio will be recomputed below */
182
183                 res = _masters.insert (make_pair<uint32_t,MasterRecord> (vca->number(), MasterRecord (vca->control(), 0.0)));
184
185                 if (res.second) {
186
187                         recompute_masters_ratios (current_value);
188
189                         /* note that we bind @param m as a weak_ptr<GainControl>, thus
190                            avoiding holding a reference to the control in the binding
191                            itself.
192                         */
193
194                         vca->DropReferences.connect_same_thread (masters_connections, boost::bind (&GainControl::master_going_away, this, vca));
195
196                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
197                            and we no longer hear about changes to the VCA.
198                         */
199
200                         vca->control()->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
201                 }
202         }
203
204         if (res.second) {
205                 VCAStatusChange (); /* EMIT SIGNAL */
206         }
207 }
208
209 void
210 GainControl::master_going_away (boost::weak_ptr<VCA> wv)
211 {
212         boost::shared_ptr<VCA> v = wv.lock();
213         if (v) {
214                 remove_master (v);
215         }
216 }
217
218 void
219 GainControl::remove_master (boost::shared_ptr<VCA> vca)
220 {
221         gain_t current_value;
222         Masters::size_type erased = 0;
223
224         {
225                 Glib::Threads::RWLock::WriterLock lm (master_lock);
226                 current_value = get_value_locked ();
227                 erased = _masters.erase (vca->number());
228                 if (erased) {
229                         recompute_masters_ratios (current_value);
230                 }
231         }
232
233         if (erased) {
234                 VCAStatusChange (); /* EMIT SIGNAL */
235         }
236 }
237
238 void
239 GainControl::clear_masters ()
240 {
241         bool had_masters = false;
242
243         {
244                 Glib::Threads::RWLock::WriterLock lm (master_lock);
245                 if (!_masters.empty()) {
246                         had_masters = true;
247                 }
248                 _masters.clear ();
249         }
250
251         if (had_masters) {
252                 VCAStatusChange (); /* EMIT SIGNAL */
253         }
254 }
255
256 void
257 GainControl::recompute_masters_ratios (double val)
258 {
259         /* Master WRITE lock must be held */
260
261         /* V' is the new gain value for this
262
263            Mv(n) is the return value of ::get_value() for the n-th master
264            Mr(n) is the return value of ::ratio() for the n-th master record
265
266            the slave should return V' on the next call to ::get_value().
267
268            but the value is determined by the masters, so we know:
269
270            V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n))
271
272            hence:
273
274            Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
275
276            if we make all ratios equal (i.e. each master contributes the same
277            fraction of its own gain level to make the final slave gain), then we
278            have:
279
280            pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
281
282            which gives
283
284            Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n)
285
286            Mr(n) is the new ratio number for the slaves
287         */
288
289
290         const double nmasters = _masters.size();
291         double masters_total_gain_coefficient = 1.0;
292
293         for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
294                 masters_total_gain_coefficient *= mr->second.master()->get_value();
295         }
296
297         const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters));
298
299         for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
300                 mr->second.reset_ratio (new_universal_ratio);
301         }
302 }
303
304 bool
305 GainControl::slaved_to (boost::shared_ptr<VCA> vca) const
306 {
307         Glib::Threads::RWLock::ReaderLock lm (master_lock);
308         return _masters.find (vca->number()) != _masters.end();
309 }
310
311 bool
312 GainControl::slaved () const
313 {
314         Glib::Threads::RWLock::ReaderLock lm (master_lock);
315         return !_masters.empty();
316 }
317
318 XMLNode&
319 GainControl::get_state ()
320 {
321         XMLNode& node (AutomationControl::get_state());
322
323         /* store VCA master IDs */
324
325         string str;
326
327         {
328                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
329                 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
330                         if (!str.empty()) {
331                                 str += ',';
332                         }
333                         str += PBD::to_string (mr->first, std::dec);
334                 }
335         }
336
337         if (!str.empty()) {
338                 node.add_property (X_("masters"), str);
339         }
340
341         return node;
342 }
343
344 int
345 GainControl::set_state (XMLNode const& node, int version)
346 {
347         AutomationControl::set_state (node, version);
348
349         XMLProperty const* prop = node.property (X_("masters"));
350
351         /* Problem here if we allow VCA's to be slaved to other VCA's .. we
352          * have to load all VCAs first, then set up slave/master relationships
353          * once we have them all.
354          */
355
356         if (prop) {
357                 masters_string = prop->value ();
358
359                 if (_session.vca_manager().vcas_loaded()) {
360                         vcas_loaded ();
361                 } else {
362                         _session.vca_manager().VCAsLoaded.connect_same_thread (vca_loaded_connection, boost::bind (&GainControl::vcas_loaded, this));
363                 }
364         }
365
366         return 0;
367 }
368
369 void
370 GainControl::vcas_loaded ()
371 {
372         if (masters_string.empty()) {
373                 return;
374         }
375
376         vector<string> masters;
377         split (masters_string, masters, ',');
378
379         for (vector<string>::const_iterator m = masters.begin(); m != masters.end(); ++m) {
380                 boost::shared_ptr<VCA> vca = _session.vca_manager().vca_by_number (PBD::atoi (*m));
381                 if (vca) {
382                         add_master (vca);
383                 }
384         }
385
386         vca_loaded_connection.disconnect ();
387         masters_string.clear ();
388 }