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::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
50 _set_value (val, group_override);
55 GainControl::set_value_unchecked (double val)
57 /* used only automation playback */
58 _set_value (val, Controllable::NoGroup);
62 GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override)
64 val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower);
67 Glib::Threads::RWLock::WriterLock lm (master_lock);
69 if (!_masters.empty()) {
70 recompute_masters_ratios (val);
74 /* this sets the Evoral::Control::_user_value for us, which will
75 be retrieved by AutomationControl::get_value ()
78 AutomationControl::set_value (val, group_override);
80 _session.set_dirty ();
84 GainControl::internal_to_interface (double v) const
86 if (_desc.type == GainAutomation) {
87 return gain_to_slider_position (v);
89 return (accurate_coefficient_to_dB (v) - lower_db) / range_db;
94 GainControl::interface_to_internal (double v) const
96 if (_desc.type == GainAutomation) {
97 return slider_position_to_gain (v);
99 return dB_to_coefficient (lower_db + v * range_db);
104 GainControl::internal_to_user (double v) const
106 return accurate_coefficient_to_dB (v);
110 GainControl::user_to_internal (double u) const
112 return dB_to_coefficient (u);
116 GainControl::get_user_string () const
118 char theBuf[32]; sprintf( theBuf, _("%3.1f dB"), accurate_coefficient_to_dB (get_value()));
119 return std::string(theBuf);
123 GainControl::recompute_masters_ratios (double val)
125 /* Master WRITE lock must be held */
127 /* V' is the new gain value for this
129 Mv(n) is the return value of ::get_value() for the n-th master
130 Mr(n) is the return value of ::ratio() for the n-th master record
132 the slave should return V' on the next call to ::get_value().
134 but the value is determined by the masters, so we know:
136 V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n))
140 Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
142 if we make all ratios equal (i.e. each master contributes the same
143 fraction of its own gain level to make the final slave gain), then we
146 pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n))
150 Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n)
152 Mr(n) is the new ratio number for the slaves
156 const double nmasters = _masters.size();
157 double masters_total_gain_coefficient = 1.0;
159 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
160 masters_total_gain_coefficient *= mr->second.master()->get_value();
163 const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters));
165 for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
166 mr->second.reset_ratio (new_universal_ratio);
171 GainControl::get_state ()
173 XMLNode& node (AutomationControl::get_state());
176 /* store VCA master IDs */
181 Glib::Threads::RWLock::ReaderLock lm (master_lock);
182 for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
186 str += PBD::to_string (mr->first, std::dec);
191 node.add_property (X_("masters"), str);
199 GainControl::set_state (XMLNode const& node, int version)
201 AutomationControl::set_state (node, version);
204 XMLProperty const* prop = node.property (X_("masters"));
206 /* Problem here if we allow VCA's to be slaved to other VCA's .. we
207 * have to load all VCAs first, then set up slave/master relationships
208 * once we have them all.
212 masters_string = prop->value ();
214 if (_session.vca_manager().vcas_loaded()) {
217 _session.vca_manager().VCAsLoaded.connect_same_thread (vca_loaded_connection, boost::bind (&GainControl::vcas_loaded, this));
226 GainControl::vcas_loaded ()
228 if (masters_string.empty()) {
232 vector<string> masters;
233 split (masters_string, masters, ',');
235 for (vector<string>::const_iterator m = masters.begin(); m != masters.end(); ++m) {
236 boost::shared_ptr<VCA> vca = _session.vca_manager().vca_by_number (PBD::atoi (*m));
238 add_master (vca->gain_control());
242 vca_loaded_connection.disconnect ();
243 masters_string.clear ();