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