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_locked () const {
49 /* read or write masters lock must be held */
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->second.master()->get_value () * mr->second.ratio();
62 return min (Config->get_max_gain(), g);
66 GainControl::get_value () const
68 Glib::Threads::RWLock::ReaderLock lm (master_lock);
69 return get_value_locked ();
73 GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
76 _set_value (val, group_override);
81 GainControl::set_value_unchecked (double val)
83 /* used only automation playback */
84 _set_value (val, Controllable::NoGroup);
88 GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
90 val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
93 Glib::Threads::RWLock::WriterLock lm (master_lock);
95 if (!_masters.empty()) {
96 recompute_masters_ratios (val);
100 AutomationControl::set_value (val, group_override);
102 _session.set_dirty ();
106 GainControl::internal_to_interface (double v) const
108 if (_desc.type == GainAutomation) {
109 return gain_to_slider_position (v);
111 return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
116 GainControl::interface_to_internal (double v) const
118 if (_desc.type == GainAutomation) {
119 return slider_position_to_gain (v);
121 return dB_to_coefficient (lower_db + v * range_db);
126 GainControl::internal_to_user (double v) const
128 return accurate_coefficient_to_dB (v);
132 GainControl::user_to_internal (double u) const
134 return dB_to_coefficient (u);
138 GainControl::get_user_string () const
140 char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
141 return std::string(theBuf);
145 GainControl::get_master_gain () const
147 Glib::Threads::RWLock::ReaderLock sm (master_lock, Glib::Threads::TRY_LOCK);
150 return get_master_gain_locked ();
157 GainControl::get_master_gain_locked () const
159 /* Master lock MUST be held (read or write lock is acceptable) */
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();
172 GainControl::add_master (boost::shared_ptr<VCA> vca)
174 gain_t current_value;
175 std::pair<Masters::iterator,bool> res;
178 Glib::Threads::RWLock::WriterLock lm (master_lock);
179 current_value = get_value_locked ();
181 /* ratio will be recomputed below */
183 res = _masters.insert (make_pair<uint32_t,MasterRecord> (vca->number(), MasterRecord (vca->control(), 0.0)));
187 recompute_masters_ratios (current_value);
189 /* note that we bind @param m as a weak_ptr<GainControl>, thus
190 avoiding holding a reference to the control in the binding
194 vca->DropReferences.connect_same_thread (masters_connections, boost::bind (&GainControl::master_going_away, this, vca));
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.
200 vca->control()->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
205 VCAStatusChange (); /* EMIT SIGNAL */
210 GainControl::master_going_away (boost::weak_ptr<VCA> wv)
212 boost::shared_ptr<VCA> v = wv.lock();
219 GainControl::remove_master (boost::shared_ptr<VCA> vca)
221 gain_t current_value;
222 Masters::size_type erased = 0;
225 Glib::Threads::RWLock::WriterLock lm (master_lock);
226 current_value = get_value_locked ();
227 erased = _masters.erase (vca->number());
229 recompute_masters_ratios (current_value);
234 VCAStatusChange (); /* EMIT SIGNAL */
239 GainControl::clear_masters ()
241 bool had_masters = false;
244 Glib::Threads::RWLock::WriterLock lm (master_lock);
245 if (!_masters.empty()) {
252 VCAStatusChange (); /* EMIT SIGNAL */
257 GainControl::recompute_masters_ratios (double val)
259 /* Master WRITE lock must be held */
261 /* V' is the new gain value for this
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
266 the slave should return V' on the next call to ::get_value().
268 but the value is determined by the masters, so we know:
270 V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n))
274 Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
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
280 pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
284 Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n)
286 Mr(n) is the new ratio number for the slaves
290 const double nmasters = _masters.size();
291 double masters_total_gain_coefficient = 1.0;
293 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
294 masters_total_gain_coefficient *= mr->second.master()->get_value();
297 const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters));
299 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
300 mr->second.reset_ratio (new_universal_ratio);
305 GainControl::slaved_to (boost::shared_ptr<VCA> vca) const
307 Glib::Threads::RWLock::ReaderLock lm (master_lock);
308 return _masters.find (vca->number()) != _masters.end();
312 GainControl::slaved () const
314 Glib::Threads::RWLock::ReaderLock lm (master_lock);
315 return !_masters.empty();
319 GainControl::get_state ()
321 XMLNode& node (AutomationControl::get_state());
323 /* store VCA master IDs */
328 Glib::Threads::RWLock::ReaderLock lm (master_lock);
329 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
333 str += PBD::to_string (mr->first, std::dec);
338 node.add_property (X_("masters"), str);
345 GainControl::set_state (XMLNode const& node, int version)
347 AutomationControl::set_state (node, version);
349 XMLProperty const* prop = node.property (X_("masters"));
351 /* XXX Problem here if we allow VCA's to be slaved to other VCA's .. we
352 * have to load all VCAs first, then call ::set_state() so that
353 * vca_by_number() will succeed.
357 vector<string> masters;
358 split (prop->value(), masters, ',');
360 for (vector<string>::const_iterator m = masters.begin(); m != masters.end(); ++m) {
361 boost::shared_ptr<VCA> vca = _session.vca_manager().vca_by_number (PBD::atoi (*m));