X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fgain_control.cc;h=d100273640087e372f75dde70bf7b39bfba3c79e;hb=88b3aa926a82ff103d8440e89e28b202a450e18f;hp=5ba7179231bc291fa4a64e2587ca0b63e1e55dd0;hpb=acaaa98bd0a21494ae912dbbc37fbbc33cbbf61d;p=ardour.git diff --git a/libs/ardour/gain_control.cc b/libs/ardour/gain_control.cc index 5ba7179231..d100273640 100644 --- a/libs/ardour/gain_control.cc +++ b/libs/ardour/gain_control.cc @@ -16,48 +16,34 @@ 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + +#include "pbd/convert.h" +#include "pbd/strsplit.h" + #include "ardour/dB.h" #include "ardour/gain_control.h" #include "ardour/session.h" +#include "ardour/vca.h" +#include "ardour/vca_manager.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace ARDOUR; using namespace std; GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boost::shared_ptr al) - : AutomationControl (session, param, ParameterDescriptor(param), - al ? al : boost::shared_ptr (new AutomationList (param)), - param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) { - + : SlavableAutomationControl (session, param, ParameterDescriptor(param), + al ? al : boost::shared_ptr (new AutomationList (param)), + param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol"), + Controllable::GainLike) +{ alist()->reset_default (1.0); lower_db = accurate_coefficient_to_dB (_desc.lower); range_db = accurate_coefficient_to_dB (_desc.upper) - lower_db; } -void -GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -GainControl::set_value_unchecked (double val) -{ - /* used only automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override) -{ - AutomationControl::set_value (std::max (std::min (val, (double)_desc.upper), (double)_desc.lower), group_override); - _session.set_dirty (); -} - double GainControl::internal_to_interface (double v) const { @@ -97,89 +83,102 @@ GainControl::get_user_string () const return std::string(theBuf); } -gain_t -GainControl::get_master_gain () const +void +GainControl::inc_gain (gain_t factor) { - Glib::Threads::Mutex::Lock sm (master_lock, Glib::Threads::TRY_LOCK); + /* To be used ONLY when doing group-relative gain adjustment, from + * ControlGroup::set_group_values(). + */ - if (sm.locked()) { - return get_master_gain_locked (); - } + const float desired_gain = user_double(); - return 1.0; + if (fabsf (desired_gain) < GAIN_COEFF_SMALL) { + // really?! what's the idea here? + actually_set_value (0.000001f + (0.000001f * factor), Controllable::ForGroup); + } else { + actually_set_value (desired_gain + (desired_gain * factor), Controllable::ForGroup); + } } -gain_t -GainControl::get_master_gain_locked () const +void +GainControl::recompute_masters_ratios (double val) { - /* Master lock MUST be held */ + /* Master WRITE lock must be held */ - gain_t g = 1.0; + /* V' is the new gain value for this - for (Masters::const_iterator m = _masters.begin(); m != _masters.end(); ++m) { - g *= (*m)->get_value (); - } + Mv(n) is the return value of ::get_value() for the n-th master + Mr(n) is the return value of ::ratio() for the n-th master record - return g; -} + the slave should return V' on the next call to ::get_value(). -void -GainControl::add_master (boost::shared_ptr m) -{ - gain_t old_master_val; - gain_t new_master_val; + but the value is determined by the masters, so we know: - { - Glib::Threads::Mutex::Lock lm (master_lock); - old_master_val = get_master_gain_locked (); - _masters.push_back (m); - new_master_val = get_master_gain_locked (); - } + V' = (Mv(1) * Mr(1)) * (Mv(2) * Mr(2)) * ... * (Mv(n) * Mr(n)) - if (old_master_val != new_master_val) { - Changed(); /* EMIT SIGNAL */ - } -} + hence: -void -GainControl::remove_master (boost::shared_ptr m) -{ - gain_t old_master_val; - gain_t new_master_val; + Mr(1) * Mr(2) * ... * (Mr(n) = V' / (Mv(1) * Mv(2) * ... * Mv(n)) - { - Glib::Threads::Mutex::Lock lm (master_lock); - old_master_val = get_master_gain_locked (); - _masters.remove (m); - new_master_val = get_master_gain_locked (); + if we make all ratios equal (i.e. each master contributes the same + fraction of its own gain level to make the final slave gain), then we + have: + + pow (Mr(n), n) = V' / (Mv(1) * Mv(2) * ... * Mv(n)) + + which gives + + Mr(n) = pow ((V' / (Mv(1) * Mv(2) * ... * Mv(n))), 1/n) + + Mr(n) is the new ratio number for the slaves + */ + + + const double nmasters = _masters.size(); + double masters_total_gain_coefficient = 1.0; + + for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { + masters_total_gain_coefficient *= mr->second.master()->get_value(); } - if (old_master_val != new_master_val) { - Changed(); /* EMIT SIGNAL */ + const double new_universal_ratio = pow ((val / masters_total_gain_coefficient), (1.0/nmasters)); + + for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { + mr->second.reset_ratio (new_universal_ratio); } } -void -GainControl::clear_masters () +XMLNode& +GainControl::get_state () { - gain_t old_master_val; - gain_t new_master_val; + XMLNode& node (AutomationControl::get_state()); + +#if 0 + /* store VCA master IDs */ + + string str; { - Glib::Threads::Mutex::Lock lm (master_lock); - old_master_val = get_master_gain_locked (); - _masters.clear (); - new_master_val = get_master_gain_locked (); + Glib::Threads::RWLock::ReaderLock lm (master_lock); + for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { + if (!str.empty()) { + str += ','; + } + str += PBD::to_string (mr->first, std::dec); + } } - if (old_master_val != new_master_val) { - Changed(); /* EMIT SIGNAL */ + if (!str.empty()) { + node.add_property (X_("masters"), str); } +#endif + + return node; } -bool -GainControl::slaved_to (boost::shared_ptr gc) const +int +GainControl::set_state (XMLNode const& node, int version) { - Glib::Threads::Mutex::Lock lm (master_lock); - return find (_masters.begin(), _masters.end(), gc) != _masters.end(); + return AutomationControl::set_state (node, version); } +