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