36ea1efc63141db29d0faa1878cd32b79919a8ca
[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         /* now propagate across the group */
159
160         Glib::Threads::RWLock::ReaderLock lm (controls_lock);
161
162         if (_mode & Relative) {
163
164                 const double factor = old / control->get_value ();
165
166                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
167                         if (c->second != control) {
168                                 c->second->set_value (factor * c->second->get_value(), Controllable::ForGroup);
169                         }
170                 }
171
172         } else {
173
174                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
175                         if (c->second != control) {
176                                 c->second->set_value (val, Controllable::ForGroup);
177                         }
178                 }
179         }
180 }
181
182 /*---- GAIN CONTROL GROUP -----------*/
183
184 gain_t
185 GainControlGroup::get_min_factor (gain_t factor)
186 {
187         /* CALLER MUST HOLD READER LOCK */
188
189         for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
190                 gain_t const g = c->second->get_value();
191
192                 if ((g + g * factor) >= 0.0f) {
193                         continue;
194                 }
195
196                 if (g <= 0.0000003f) {
197                         return 0.0f;
198                 }
199
200                 factor = 0.0000003f / g - 1.0f;
201         }
202
203         return factor;
204 }
205
206 gain_t
207 GainControlGroup::get_max_factor (gain_t factor)
208 {
209         /* CALLER MUST HOLD READER LOCK */
210
211         for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
212                 gain_t const g = c->second->get_value();
213
214                 // if the current factor woulnd't raise this route above maximum
215                 if ((g + g * factor) <= 1.99526231f) {
216                         continue;
217                 }
218
219                 // if route gain is already at peak, return 0.0f factor
220                 if (g >= 1.99526231f) {
221                         return 0.0f;
222                 }
223
224                 // factor is calculated so that it would raise current route to max
225                 factor = 1.99526231f / g - 1.0f;
226         }
227
228         return factor;
229 }
230
231 void
232 GainControlGroup::set_group_value (boost::shared_ptr<AutomationControl> control, double val)
233 {
234         /* set the primary control */
235
236         control->set_value (val, Controllable::ForGroup);
237
238         /* now propagate across the group */
239
240         Glib::Threads::RWLock::ReaderLock lm (controls_lock);
241
242         if (_mode & Relative) {
243
244                 gain_t usable_gain = control->get_value();
245
246                 if (usable_gain < 0.000001f) {
247                         usable_gain = 0.000001f;
248                 }
249
250                 gain_t delta = val;
251                 if (delta < 0.000001f) {
252                         delta = 0.000001f;
253                 }
254
255                 delta -= usable_gain;
256
257                 if (delta == 0.0f)
258                         return;
259
260                 gain_t factor = delta / usable_gain;
261
262                 if (factor > 0.0f) {
263                         factor = get_max_factor (factor);
264                         if (factor == 0.0f) {
265                                 control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */
266                                 return;
267                         }
268                 } else {
269                         factor = get_min_factor (factor);
270                         if (factor == 0.0f) {
271                                 control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */
272                                 return;
273                         }
274                 }
275
276                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
277                         if (c->second == control) {
278                                 continue;
279                         }
280
281                         boost::shared_ptr<GainControl> gc = boost::dynamic_pointer_cast<GainControl> (c->second);
282
283                         if (gc) {
284                                 gc->inc_gain (factor);
285                         }
286                 }
287
288         } else {
289
290                 for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) {
291                         if (c->second != control) {
292                                 c->second->set_value (val, Controllable::ForGroup);
293                         }
294                 }
295         }
296 }