add implicit mute state to MuteMaster and use when a master of a mute control is...
[ardour.git] / libs / ardour / automation_control.cc
1 /*
2     Copyright (C) 2007 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <math.h>
22 #include <iostream>
23 #include "ardour/automation_control.h"
24 #include "ardour/automation_watch.h"
25 #include "ardour/event_type_map.h"
26 #include "ardour/session.h"
27
28 #include "pbd/memento_command.h"
29 #include "pbd/stacktrace.h"
30
31 #include "i18n.h"
32
33 #ifdef COMPILER_MSVC
34 #include <float.h>
35 // C99 'isfinite()' is not available in MSVC.
36 #define isfinite_local(val) (bool)_finite((double)val)
37 #else
38 #define isfinite_local isfinite
39 #endif
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44
45 AutomationControl::AutomationControl(ARDOUR::Session&                          session,
46                                      const Evoral::Parameter&                  parameter,
47                                      const ParameterDescriptor&                desc,
48                                      boost::shared_ptr<ARDOUR::AutomationList> list,
49                                      const string&                             name)
50         : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name)
51         , Evoral::Control(parameter, desc, list)
52         , _session(session)
53         , _desc(desc)
54 {
55 }
56
57 AutomationControl::~AutomationControl ()
58 {
59         DropReferences (); /* EMIT SIGNAL */
60 }
61
62 bool
63 AutomationControl::writable() const
64 {
65         boost::shared_ptr<AutomationList> al = alist();
66         if (al) {
67                 return al->automation_state() != Play;
68         }
69         return true;
70 }
71
72 double
73 AutomationControl::get_masters_value_locked () const
74 {
75         gain_t v = 1.0;
76
77         for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
78                 /* get current master value, scale by our current ratio with that master */
79                 v *= mr->second.master()->get_value () * mr->second.ratio();
80         }
81
82         return min (_desc.upper, v);
83 }
84
85 double
86 AutomationControl::get_value_locked() const
87 {
88         /* read or write masters lock must be held */
89
90         if (_masters.empty()) {
91                 return Control::get_double (false, _session.transport_frame());
92         }
93
94         return get_masters_value_locked ();
95 }
96
97 /** Get the current effective `user' value based on automation state */
98 double
99 AutomationControl::get_value() const
100 {
101         bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
102
103         if (!from_list) {
104                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
105                 return get_value_locked ();
106         } else {
107                 return Control::get_double (from_list, _session.transport_frame());
108         }
109 }
110
111 /** Set the value and do the right thing based on automation state
112  *  (e.g. record if necessary, etc.)
113  *  @param value `user' value
114  */
115 void
116 AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
117 {
118         bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
119
120         Control::set_double (value, _session.transport_frame(), to_list);
121
122         Changed (true, gcd);
123 }
124
125 void
126 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
127 {
128         Control::set_list (list);
129         Changed (true, Controllable::NoGroup);
130 }
131
132 void
133 AutomationControl::set_automation_state (AutoState as)
134 {
135         if (_list && as != alist()->automation_state()) {
136
137                 alist()->set_automation_state (as);
138                 if (_desc.toggled) {
139                         return;  // No watch for boolean automation
140                 }
141
142                 if (as == Write) {
143                         AutomationWatch::instance().add_automation_watch (shared_from_this());
144                 } else if (as == Touch) {
145                         if (!touching()) {
146                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
147                         } else {
148                                 /* this seems unlikely, but the combination of
149                                  * a control surface and the mouse could make
150                                  * it possible to put the control into Touch
151                                  * mode *while* touching it.
152                                  */
153                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
154                         }
155                 } else {
156                         AutomationWatch::instance().remove_automation_watch (shared_from_this());
157                 }
158         }
159 }
160
161 void
162 AutomationControl::set_automation_style (AutoStyle as)
163 {
164         if (!_list) return;
165         alist()->set_automation_style (as);
166 }
167
168 void
169 AutomationControl::start_touch(double when)
170 {
171         if (!_list) {
172                 return;
173         }
174
175         if (!touching()) {
176
177                 if (alist()->automation_state() == Touch) {
178                         /* subtle. aligns the user value with the playback */
179                         set_value (get_value (), Controllable::NoGroup);
180                         alist()->start_touch (when);
181                         if (!_desc.toggled) {
182                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
183                         }
184                 }
185                 set_touching (true);
186         }
187 }
188
189 void
190 AutomationControl::stop_touch(bool mark, double when)
191 {
192         if (!_list) return;
193         if (touching()) {
194                 set_touching (false);
195
196                 if (alist()->automation_state() == Touch) {
197                         alist()->stop_touch (mark, when);
198                         if (!_desc.toggled) {
199                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
200
201                         }
202                 }
203         }
204 }
205
206 void
207 AutomationControl::commit_transaction (bool did_write)
208 {
209         if (did_write) {
210                 if (alist ()->before ()) {
211                         _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
212                         _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
213                 }
214         } else {
215                 alist ()->clear_history ();
216         }
217 }
218
219 double
220 AutomationControl::internal_to_interface (double val) const
221 {
222         if (_desc.integer_step) {
223                 // both upper and lower are inclusive.
224                 val =  (val - lower()) / (1 + upper() - lower());
225         } else {
226                 val =  (val - lower()) / (upper() - lower());
227         }
228
229         if (_desc.logarithmic) {
230                 if (val > 0) {
231                         val = pow (val, 1./2.0);
232                 } else {
233                         val = 0;
234                 }
235         }
236
237         return val;
238 }
239
240 double
241 AutomationControl::interface_to_internal (double val) const
242 {
243         if (!isfinite_local (val)) {
244                 val = 0;
245         }
246         if (_desc.logarithmic) {
247                 if (val <= 0) {
248                         val = 0;
249                 } else {
250                         val = pow (val, 2.0);
251                 }
252         }
253
254         if (_desc.integer_step) {
255                 val =  lower() + val * (1 + upper() - lower());
256         } else {
257                 val =  lower() + val * (upper() - lower());
258         }
259
260         if (val < lower()) val = lower();
261         if (val > upper()) val = upper();
262
263         return val;
264 }
265
266
267 void
268 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
269 {
270         double current_value;
271         double new_value;
272         std::pair<Masters::iterator,bool> res;
273
274         {
275                 Glib::Threads::RWLock::WriterLock lm (master_lock);
276                 current_value = get_value_locked ();
277
278                 /* ratio will be recomputed below */
279
280                 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
281
282                 if (res.second) {
283
284                         recompute_masters_ratios (current_value);
285
286                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
287                            avoiding holding a reference to the control in the binding
288                            itself.
289                         */
290
291                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
292
293                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
294                            and we no longer hear about changes to the AutomationControl.
295
296                            Note that we fix the "from_self" argument that will
297                            be given to our own Changed signal to "false",
298                            because the change came from the master.
299                         */
300
301
302                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2));
303                 }
304
305                 new_value = get_value_locked ();
306         }
307
308         if (res.second) {
309                 /* this will notify everyone that we're now slaved to the master */
310                 MasterStatusChange (); /* EMIT SIGNAL */
311         }
312
313         if (new_value != current_value) {
314                 /* force a call to to ::master_changed() to carry the
315                  * consequences that would occur if the master assumed
316                  * its current value WHILE we were slaved.
317                  */
318                 master_changed (false, Controllable::NoGroup);
319                 /* effective value changed by master */
320                 Changed (false, Controllable::NoGroup);
321         }
322
323 }
324
325 void
326 AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
327 {
328         /* our value has (likely) changed, but not because we were
329          * modified. Just the master.
330          */
331
332         Changed (false, gcd); /* EMIT SIGNAL */
333 }
334
335 void
336 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
337 {
338         boost::shared_ptr<AutomationControl> m = wm.lock();
339         if (m) {
340                 remove_master (m);
341         }
342 }
343
344 void
345 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
346 {
347         double current_value;
348         double new_value;
349         Masters::size_type erased = 0;
350
351         {
352                 Glib::Threads::RWLock::WriterLock lm (master_lock);
353                 current_value = get_value_locked ();
354                 erased = _masters.erase (m->id());
355                 if (erased) {
356                         recompute_masters_ratios (current_value);
357                 }
358                 new_value = get_value_locked ();
359         }
360
361         if (erased) {
362                 MasterStatusChange (); /* EMIT SIGNAL */
363         }
364
365         if (new_value != current_value) {
366                 Changed (false, Controllable::NoGroup);
367         }
368 }
369
370 void
371 AutomationControl::clear_masters ()
372 {
373         double current_value;
374         double new_value;
375         bool had_masters = false;
376
377         {
378                 Glib::Threads::RWLock::WriterLock lm (master_lock);
379                 current_value = get_value_locked ();
380                 if (!_masters.empty()) {
381                         had_masters = true;
382                 }
383                 _masters.clear ();
384                 new_value = get_value_locked ();
385         }
386
387         if (had_masters) {
388                 MasterStatusChange (); /* EMIT SIGNAL */
389         }
390
391         if (new_value != current_value) {
392                 Changed (false, Controllable::NoGroup);
393         }
394
395 }
396
397 bool
398 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
399 {
400         Glib::Threads::RWLock::ReaderLock lm (master_lock);
401         return _masters.find (m->id()) != _masters.end();
402 }
403
404 bool
405 AutomationControl::slaved () const
406 {
407         Glib::Threads::RWLock::ReaderLock lm (master_lock);
408         return !_masters.empty();
409 }