124ce97d45a68239e4359c563d23afe7314e00cc
[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->master()->get_value () * mr->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->master()->get_value () * mr->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                 MasterRecord mr (vca->control(), vca->number(), 0.0);
177
178                 res = _masters.insert (mr);
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                 vca->control()->Changed.connect_same_thread (masters_connections, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
188         }
189
190         if (res.second) {
191                 VCAStatusChange (); /* EMIT SIGNAL */
192         }
193 }
194
195 void
196 GainControl::master_going_away (boost::weak_ptr<VCA> wv)
197 {
198         boost::shared_ptr<VCA> v = wv.lock();
199         if (v) {
200                 remove_master (v);
201         }
202 }
203
204 void
205 GainControl::remove_master (boost::shared_ptr<VCA> vca)
206 {
207         gain_t old_master_val;
208         Masters::size_type erased = 0;
209
210         {
211                 Glib::Threads::RWLock::WriterLock lm (master_lock);
212                 old_master_val = get_master_gain_locked ();
213                 MasterRecord mr (vca->control(), vca->number(), 0.0);
214                 erased = _masters.erase (mr);
215                 if (erased) {
216                         recompute_masters_ratios (old_master_val);
217                 }
218         }
219
220         if (erased) {
221                 VCAStatusChange (); /* EMIT SIGNAL */
222         }
223 }
224
225 void
226 GainControl::clear_masters ()
227 {
228         bool had_masters = false;
229
230         {
231                 Glib::Threads::RWLock::WriterLock lm (master_lock);
232                 if (!_masters.empty()) {
233                         had_masters = true;
234                 }
235                 _masters.clear ();
236         }
237
238         if (had_masters) {
239                 VCAStatusChange (); /* EMIT SIGNAL */
240         }
241 }
242
243 void
244 GainControl::recompute_masters_ratios (double val)
245 {
246         /* Master WRITE lock must be held */
247
248         /* V' is the new gain value for this
249
250            Mv(n) is the return value of ::get_value() for the n-th master
251            Mr(n) is the return value of ::ratio() for the n-th master record
252
253            the slave should return V' on the next call to ::get_value().
254
255            but the value is determined by the masters, so we know:
256
257            V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n))
258
259            hence:
260
261            Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
262
263            if we make all ratios equal (i.e. each master contributes the same
264            fraction of its own gain level to make the final slave gain), then we
265            have:
266
267            pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
268
269            which gives
270
271            Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n)
272
273            Mr(n) is the new ratio number for the slaves
274         */
275
276
277         const double nmasters = _masters.size();
278         double masters_total_gain_coefficient = 1.0;
279
280         for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
281                 masters_total_gain_coefficient *= mr->master()->get_value();
282         }
283
284         const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters));
285
286         for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
287                 const_cast<MasterRecord*>(&(*mr))->reset_ratio (new_universal_ratio);
288         }
289 }
290
291 bool
292 GainControl::slaved_to (boost::shared_ptr<VCA> vca) const
293 {
294         Glib::Threads::RWLock::ReaderLock lm (master_lock);
295         return find (_masters.begin(), _masters.end(), MasterRecord (vca->control(), vca->number(), 0.0)) != _masters.end();
296 }
297
298 bool
299 GainControl::slaved () const
300 {
301         Glib::Threads::RWLock::ReaderLock lm (master_lock);
302         return !_masters.empty();
303 }
304
305 XMLNode&
306 GainControl::get_state ()
307 {
308         XMLNode& node (AutomationControl::get_state());
309
310         /* store VCA master IDs */
311
312         string str;
313
314         {
315                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
316                 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
317                         if (!str.empty()) {
318                                 str += ',';
319                         }
320                         str += PBD::to_string (mr->number(), std::dec);
321                 }
322         }
323
324         if (!str.empty()) {
325                 node.add_property (X_("masters"), str);
326         }
327
328         return node;
329 }
330
331 int
332 GainControl::set_state (XMLNode const& node, int version)
333 {
334         AutomationControl::set_state (node, version);
335
336         XMLProperty const* prop = node.property (X_("masters"));
337
338         /* XXX Problem here if we allow VCA's to be slaved to other VCA's .. we
339          * have to load all VCAs first, then call ::set_state() so that
340          * vca_by_number() will succeed.
341          */
342
343         if (prop) {
344                 vector<string> masters;
345                 split (prop->value(), masters, ',');
346
347                 for (vector<string>::const_iterator m = masters.begin(); m != masters.end(); ++m) {
348                         boost::shared_ptr<VCA> vca = _session.vca_manager().vca_by_number (PBD::atoi (*m));
349                         if (vca) {
350                                 add_master (vca);
351                         }
352                 }
353         }
354
355         return 0;
356 }