summaryrefslogtreecommitdiff
path: root/libs/ardour/control_group.cc
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2016-04-08 16:51:34 -0400
committerPaul Davis <paul@linuxaudiosystems.com>2016-05-31 15:30:40 -0400
commit4aa1c242ab1e1bf4fc3575c5d88a22824976a04f (patch)
treef71d4c56255a9c72bfd582668d03caed1d2c731c /libs/ardour/control_group.cc
parent653ae4acd639fef149314fe6f8c7a0d862afae40 (diff)
add new files to source tree
Diffstat (limited to 'libs/ardour/control_group.cc')
-rw-r--r--libs/ardour/control_group.cc287
1 files changed, 287 insertions, 0 deletions
diff --git a/libs/ardour/control_group.cc b/libs/ardour/control_group.cc
new file mode 100644
index 0000000000..6752940d27
--- /dev/null
+++ b/libs/ardour/control_group.cc
@@ -0,0 +1,287 @@
+/*
+ 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 <vector>
+
+#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;
+ std::cerr << " CG for " << enum_2_string ((AutomationType) _parameter.type()) << " now active ? " << _active << std::endl;
+}
+
+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<boost::shared_ptr<AutomationControl> > 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<boost::shared_ptr<AutomationControl> >::iterator c = controls.begin(); c != controls.end(); ++c) {
+ (*c)->set_group (boost::shared_ptr<ControlGroup>());
+ }
+}
+
+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<AutomationControl> wac)
+{
+ boost::shared_ptr<AutomationControl> ac (wac.lock());
+ if (!ac) {
+ return;
+ }
+
+ remove_control (ac);
+}
+
+int
+ControlGroup::remove_control (boost::shared_ptr<AutomationControl> ac)
+{
+ Glib::Threads::RWLock::WriterLock lm (controls_lock);
+ /* return zero if erased, non-zero otherwise */
+ return !(_controls.erase (ac->id()) > 0);
+}
+
+int
+ControlGroup::add_control (boost::shared_ptr<AutomationControl> ac)
+{
+ if (ac->parameter() != _parameter) {
+ return -1;
+ }
+
+ std::pair<ControlMap::iterator,bool> 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<AutomationControl>(ac)));
+
+ return 0;
+}
+
+void
+ControlGroup::set_group_value (boost::shared_ptr<AutomationControl> control, double val)
+{
+ double old = control->get_value ();
+
+ /* set the primary control */
+
+ control->set_value (val, Controllable::ForGroup);
+
+ /* 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 -----------*/
+
+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<AutomationControl> control, double val)
+{
+ /* set the primary control */
+
+ control->set_value (val, Controllable::ForGroup);
+
+ /* now propagate across the group */
+
+ 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;
+ }
+ }
+
+ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
+ if (c->second == control) {
+ continue;
+ }
+
+ boost::shared_ptr<GainControl> gc = boost::dynamic_pointer_cast<GainControl> (c->second);
+
+ if (gc) {
+ gc->inc_gain (factor);
+ }
+ }
+
+ } else {
+
+ for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
+ if (c->second != control) {
+ c->second->set_value (val, Controllable::ForGroup);
+ }
+ }
+ }
+}