fix logic for removing an AutomationControl from a ControlGroup
[ardour.git] / libs / ardour / control_group.cc
1 /*
2     Copyright (C) 2016 Paul Davis
3
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)
7     any later version.
8
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
12     for more details.
13
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.
17 */
18
19 #include <vector>
20
21 #include "pbd/unwind.h"
22
23 #include "ardour/control_group.h"
24 #include "ardour/gain_control.h"
25
26 using namespace ARDOUR;
27 using namespace PBD;
28
29 ControlGroup::ControlGroup (Evoral::Parameter p)
30         : _parameter (p)
31         , _active (true)
32         , _mode (Mode (0))
33         , propagating (false)
34 {
35 }
36
37
38 ControlGroup::~ControlGroup ()
39 {
40         clear ();
41 }
42
43 void
44 ControlGroup::set_active (bool yn)
45 {
46         _active = yn;
47         std::cerr << " CG for " << enum_2_string ((AutomationType) _parameter.type()) << " now active ? " << _active << std::endl;
48 }
49
50 void
51 ControlGroup::clear ()
52 {
53         /* we're giving up on all members, so we don't care about their
54          * DropReferences signals anymore
55          */
56
57         member_connections.drop_connections ();
58
59         /* make a copy so that when the control calls ::remove_control(), we
60          * don't deadlock.
61          */
62
63         std::vector<boost::shared_ptr<AutomationControl> > controls;
64         {
65                 Glib::Threads::RWLock::WriterLock lm (controls_lock);
66                 for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
67                         controls.push_back (i->second);
68                 }
69         }
70
71         _controls.clear ();
72
73         for (std::vector<boost::shared_ptr<AutomationControl> >::iterator c = controls.begin(); c != controls.end(); ++c) {
74                 (*c)->set_group (boost::shared_ptr<ControlGroup>());
75         }
76 }
77
78 ControlList
79 ControlGroup::controls () const
80 {
81         ControlList c;
82
83         if (_active) {
84                 Glib::Threads::RWLock::WriterLock lm (controls_lock);
85                 for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
86                         c.push_back (i->second);
87                 }
88         }
89
90         return c;
91 }
92
93 void
94 ControlGroup::control_going_away (boost::weak_ptr<AutomationControl> wac)
95 {
96         boost::shared_ptr<AutomationControl> ac (wac.lock());
97         if (!ac) {
98                 return;
99         }
100
101         remove_control (ac);
102 }
103
104 int
105 ControlGroup::remove_control (boost::shared_ptr<AutomationControl> ac)
106 {
107         int erased;
108
109         {
110                 Glib::Threads::RWLock::WriterLock lm (controls_lock);
111                 erased = _controls.erase (ac->id());
112         }
113
114         if (erased) {
115                 ac->set_group (boost::shared_ptr<ControlGroup>());
116         }
117
118         /* return zero if erased, non-zero otherwise */
119         return !erased;
120 }
121
122 int
123 ControlGroup::add_control (boost::shared_ptr<AutomationControl> ac)
124 {
125         if (ac->parameter() != _parameter) {
126                 return -1;
127         }
128
129         std::pair<ControlMap::iterator,bool> res;
130
131         {
132                 Glib::Threads::RWLock::WriterLock lm (controls_lock);
133                 res = _controls.insert (std::make_pair (ac->id(), ac));
134         }
135
136         if (!res.second) {
137                 /* already in ControlMap */
138                 return -1;
139         }
140
141         /* Inserted */
142
143         ac->set_group (shared_from_this());
144
145         ac->DropReferences.connect_same_thread (member_connections, boost::bind (&ControlGroup::control_going_away, this, boost::weak_ptr<AutomationControl>(ac)));
146
147         return 0;
148 }
149
150 void
151 ControlGroup::set_group_value (boost::shared_ptr<AutomationControl> control, double val)
152 {
153         double old = control->get_value ();
154
155         /* set the primary control */
156
157         control->set_value (val, Controllable::ForGroup);
158
159         /* now propagate across the group */
160
161         Glib::Threads::RWLock::ReaderLock lm (controls_lock);
162
163         if (_mode & Relative) {
164
165                 const double factor = old / control->get_value ();
166
167                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
168                         if (c->second != control) {
169                                 c->second->set_value (factor * c->second->get_value(), Controllable::ForGroup);
170                         }
171                 }
172
173         } else {
174
175                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
176                         if (c->second != control) {
177                                 c->second->set_value (val, Controllable::ForGroup);
178                         }
179                 }
180         }
181 }
182
183 /*---- GAIN CONTROL GROUP -----------*/
184
185 gain_t
186 GainControlGroup::get_min_factor (gain_t factor)
187 {
188         /* CALLER MUST HOLD READER LOCK */
189
190         for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
191                 gain_t const g = c->second->get_value();
192
193                 if ((g + g * factor) >= 0.0f) {
194                         continue;
195                 }
196
197                 if (g <= 0.0000003f) {
198                         return 0.0f;
199                 }
200
201                 factor = 0.0000003f / g - 1.0f;
202         }
203
204         return factor;
205 }
206
207 gain_t
208 GainControlGroup::get_max_factor (gain_t factor)
209 {
210         /* CALLER MUST HOLD READER LOCK */
211
212         for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
213                 gain_t const g = c->second->get_value();
214
215                 // if the current factor woulnd't raise this route above maximum
216                 if ((g + g * factor) <= 1.99526231f) {
217                         continue;
218                 }
219
220                 // if route gain is already at peak, return 0.0f factor
221                 if (g >= 1.99526231f) {
222                         return 0.0f;
223                 }
224
225                 // factor is calculated so that it would raise current route to max
226                 factor = 1.99526231f / g - 1.0f;
227         }
228
229         return factor;
230 }
231
232 void
233 GainControlGroup::set_group_value (boost::shared_ptr<AutomationControl> control, double val)
234 {
235         /* set the primary control */
236
237         control->set_value (val, Controllable::ForGroup);
238
239         /* now propagate across the group */
240
241         Glib::Threads::RWLock::ReaderLock lm (controls_lock);
242
243         if (_mode & Relative) {
244
245                 gain_t usable_gain = control->get_value();
246
247                 if (usable_gain < 0.000001f) {
248                         usable_gain = 0.000001f;
249                 }
250
251                 gain_t delta = val;
252                 if (delta < 0.000001f) {
253                         delta = 0.000001f;
254                 }
255
256                 delta -= usable_gain;
257
258                 if (delta == 0.0f)
259                         return;
260
261                 gain_t factor = delta / usable_gain;
262
263                 if (factor > 0.0f) {
264                         factor = get_max_factor (factor);
265                         if (factor == 0.0f) {
266                                 control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */
267                                 return;
268                         }
269                 } else {
270                         factor = get_min_factor (factor);
271                         if (factor == 0.0f) {
272                                 control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */
273                                 return;
274                         }
275                 }
276
277                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
278                         if (c->second == control) {
279                                 continue;
280                         }
281
282                         boost::shared_ptr<GainControl> gc = boost::dynamic_pointer_cast<GainControl> (c->second);
283
284                         if (gc) {
285                                 gc->inc_gain (factor);
286                         }
287                 }
288
289         } else {
290
291                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
292                         if (c->second != control) {
293                                 c->second->set_value (val, Controllable::ForGroup);
294                         }
295                 }
296         }
297 }