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