2 Copyright (C) 2006-2016 Paul Davis
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)
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
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.
21 #include "pbd/convert.h"
22 #include "pbd/strsplit.h"
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"
32 using namespace ARDOUR;
35 GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, 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")) {
40 alist()->reset_default (1.0);
42 lower_db = accurate_coefficient_to_dB (_desc.lower);
43 range_db = accurate_coefficient_to_dB (_desc.upper) - lower_db;
47 GainControl::get_value () const
49 Glib::Threads::RWLock::ReaderLock lm (master_lock);
51 if (_masters.empty()) {
52 return AutomationControl::get_value();
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();
66 GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
69 _set_value (val, group_override);
74 GainControl::set_value_unchecked (double val)
76 /* used only automation playback */
77 _set_value (val, Controllable::NoGroup);
81 GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
83 val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
86 Glib::Threads::RWLock::WriterLock lm (master_lock);
88 if (!_masters.empty()) {
89 recompute_masters_ratios (val);
93 AutomationControl::set_value (val, group_override);
95 _session.set_dirty ();
99 GainControl::internal_to_interface (double v) const
101 if (_desc.type == GainAutomation) {
102 return gain_to_slider_position (v);
104 return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
109 GainControl::interface_to_internal (double v) const
111 if (_desc.type == GainAutomation) {
112 return slider_position_to_gain (v);
114 return dB_to_coefficient (lower_db + v * range_db);
119 GainControl::internal_to_user (double v) const
121 return accurate_coefficient_to_dB (v);
125 GainControl::user_to_internal (double u) const
127 return dB_to_coefficient (u);
131 GainControl::get_user_string () const
133 char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
134 return std::string(theBuf);
138 GainControl::get_master_gain () const
140 Glib::Threads::RWLock::ReaderLock sm (master_lock, Glib::Threads::TRY_LOCK);
143 return get_master_gain_locked ();
150 GainControl::get_master_gain_locked () const
152 /* Master lock MUST be held (read or write lock is acceptable) */
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();
165 GainControl::add_master (boost::shared_ptr<VCA> vca)
167 gain_t old_master_val;
168 std::pair<Masters::iterator,bool> res;
171 Glib::Threads::RWLock::WriterLock lm (master_lock);
172 old_master_val = get_master_gain_locked ();
174 /* ratio will be recomputed below */
176 MasterRecord mr (vca->control(), vca->number(), 0.0);
178 res = _masters.insert (mr);
179 recompute_masters_ratios (old_master_val);
181 /* note that we bind @param m as a weak_ptr<GainControl>, thus
182 avoiding holding a reference to the control in the binding
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));
191 VCAStatusChange (); /* EMIT SIGNAL */
196 GainControl::master_going_away (boost::weak_ptr<VCA> wv)
198 boost::shared_ptr<VCA> v = wv.lock();
205 GainControl::remove_master (boost::shared_ptr<VCA> vca)
207 gain_t old_master_val;
208 Masters::size_type erased = 0;
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);
216 recompute_masters_ratios (old_master_val);
221 VCAStatusChange (); /* EMIT SIGNAL */
226 GainControl::clear_masters ()
228 bool had_masters = false;
231 Glib::Threads::RWLock::WriterLock lm (master_lock);
232 if (!_masters.empty()) {
239 VCAStatusChange (); /* EMIT SIGNAL */
244 GainControl::recompute_masters_ratios (double val)
246 /* Master WRITE lock must be held */
248 /* V' is the new gain value for this
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
253 the slave should return V' on the next call to ::get_value().
255 but the value is determined by the masters, so we know:
257 V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n))
261 Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
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
267 pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
271 Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n)
273 Mr(n) is the new ratio number for the slaves
277 const double nmasters = _masters.size();
278 double masters_total_gain_coefficient = 1.0;
280 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
281 masters_total_gain_coefficient *= mr->master()->get_value();
284 const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters));
286 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
287 const_cast<MasterRecord*>(&(*mr))->reset_ratio (new_universal_ratio);
292 GainControl::slaved_to (boost::shared_ptr<VCA> vca) const
294 Glib::Threads::RWLock::ReaderLock lm (master_lock);
295 return find (_masters.begin(), _masters.end(), MasterRecord (vca->control(), vca->number(), 0.0)) != _masters.end();
299 GainControl::slaved () const
301 Glib::Threads::RWLock::ReaderLock lm (master_lock);
302 return !_masters.empty();
306 GainControl::get_state ()
308 XMLNode& node (AutomationControl::get_state());
310 /* store VCA master IDs */
315 Glib::Threads::RWLock::ReaderLock lm (master_lock);
316 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
320 str += PBD::to_string (mr->number(), std::dec);
325 node.add_property (X_("masters"), str);
332 GainControl::set_state (XMLNode const& node, int version)
334 AutomationControl::set_state (node, version);
336 XMLProperty const* prop = node.property (X_("masters"));
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.
344 vector<string> masters;
345 split (prop->value(), masters, ',');
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));