/* Copyright (C) 2016 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "pbd/unwind.h" #include "ardour/control_group.h" #include "ardour/gain_control.h" using namespace ARDOUR; using namespace PBD; ControlGroup::ControlGroup (Evoral::Parameter p) : _parameter (p) , _active (true) , _mode (Mode (0)) , propagating (false) { } ControlGroup::~ControlGroup () { clear (); } void ControlGroup::set_active (bool yn) { _active = yn; } void ControlGroup::set_mode (Mode m) { _mode = m; } void ControlGroup::clear () { /* we're giving up on all members, so we don't care about their * DropReferences signals anymore */ member_connections.drop_connections (); /* make a copy so that when the control calls ::remove_control(), we * don't deadlock. */ std::vector > controls; { Glib::Threads::RWLock::WriterLock lm (controls_lock); for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) { controls.push_back (i->second); } } _controls.clear (); for (std::vector >::iterator c = controls.begin(); c != controls.end(); ++c) { (*c)->set_group (boost::shared_ptr()); } } ControlList ControlGroup::controls () const { ControlList c; if (_active) { Glib::Threads::RWLock::WriterLock lm (controls_lock); for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) { c.push_back (i->second); } } return c; } void ControlGroup::control_going_away (boost::weak_ptr wac) { boost::shared_ptr ac (wac.lock()); if (!ac) { return; } remove_control (ac); } int ControlGroup::remove_control (boost::shared_ptr ac) { int erased; { Glib::Threads::RWLock::WriterLock lm (controls_lock); erased = _controls.erase (ac->id()); } if (erased) { ac->set_group (boost::shared_ptr()); } /* return zero if erased, non-zero otherwise */ return !erased; } int ControlGroup::add_control (boost::shared_ptr ac) { if (ac->parameter() != _parameter) { return -1; } std::pair res; { Glib::Threads::RWLock::WriterLock lm (controls_lock); res = _controls.insert (std::make_pair (ac->id(), ac)); } if (!res.second) { /* already in ControlMap */ return -1; } /* Inserted */ ac->set_group (shared_from_this()); ac->DropReferences.connect_same_thread (member_connections, boost::bind (&ControlGroup::control_going_away, this, boost::weak_ptr(ac))); return 0; } void ControlGroup::set_group_value (boost::shared_ptr control, double val) { double old = control->get_value (); /* set the primary control */ control->set_value (val, Controllable::ForGroup); if (!_active) { return; } /* now propagate across the group */ Glib::Threads::RWLock::ReaderLock lm (controls_lock); if (_mode & Relative) { const double factor = old / control->get_value (); for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { if (c->second != control) { c->second->set_value (factor * c->second->get_value(), Controllable::ForGroup); } } } else { for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { if (c->second != control) { c->second->set_value (val, Controllable::ForGroup); } } } } /*---- GAIN CONTROL GROUP -----------*/ GainControlGroup::GainControlGroup () : ControlGroup (GainAutomation) { } gain_t GainControlGroup::get_min_factor (gain_t factor) { /* CALLER MUST HOLD READER LOCK */ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { gain_t const g = c->second->get_value(); if ((g + g * factor) >= 0.0f) { continue; } if (g <= 0.0000003f) { return 0.0f; } factor = 0.0000003f / g - 1.0f; } return factor; } gain_t GainControlGroup::get_max_factor (gain_t factor) { /* CALLER MUST HOLD READER LOCK */ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { gain_t const g = c->second->get_value(); // if the current factor woulnd't raise this route above maximum if ((g + g * factor) <= 1.99526231f) { continue; } // if route gain is already at peak, return 0.0f factor if (g >= 1.99526231f) { return 0.0f; } // factor is calculated so that it would raise current route to max factor = 1.99526231f / g - 1.0f; } return factor; } void GainControlGroup::set_group_value (boost::shared_ptr control, double val) { if (!_active) { /* set the primary control */ control->set_value (val, Controllable::ForGroup); return; } Glib::Threads::RWLock::ReaderLock lm (controls_lock); if (_mode & Relative) { gain_t usable_gain = control->get_value(); if (usable_gain < 0.000001f) { usable_gain = 0.000001f; } gain_t delta = val; if (delta < 0.000001f) { delta = 0.000001f; } delta -= usable_gain; if (delta == 0.0f) { return; } gain_t factor = delta / usable_gain; if (factor > 0.0f) { factor = get_max_factor (factor); if (factor == 0.0f) { control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */ return; } } else { factor = get_min_factor (factor); if (factor == 0.0f) { control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */ return; } } /* set the primary control */ control->set_value (val, Controllable::ForGroup); /* now propagate across the group */ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { if (c->second == control) { continue; } boost::shared_ptr gc = boost::dynamic_pointer_cast (c->second); if (gc) { gc->inc_gain (factor); } } } else { /* just set entire group */ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { c->second->set_value (val, Controllable::ForGroup); } } }