change API of Controllable::Changed signal to include (from_self, GroupControlDisposi...
[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         cerr << "AC was set to " << value << endl;
123
124         Changed (true, gcd);
125 }
126
127 void
128 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
129 {
130         Control::set_list (list);
131         Changed (true, Controllable::NoGroup);
132 }
133
134 void
135 AutomationControl::set_automation_state (AutoState as)
136 {
137         if (_list && as != alist()->automation_state()) {
138
139                 alist()->set_automation_state (as);
140                 if (_desc.toggled) {
141                         return;  // No watch for boolean automation
142                 }
143
144                 if (as == Write) {
145                         AutomationWatch::instance().add_automation_watch (shared_from_this());
146                 } else if (as == Touch) {
147                         if (!touching()) {
148                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
149                         } else {
150                                 /* this seems unlikely, but the combination of
151                                  * a control surface and the mouse could make
152                                  * it possible to put the control into Touch
153                                  * mode *while* touching it.
154                                  */
155                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
156                         }
157                 } else {
158                         AutomationWatch::instance().remove_automation_watch (shared_from_this());
159                 }
160         }
161 }
162
163 void
164 AutomationControl::set_automation_style (AutoStyle as)
165 {
166         if (!_list) return;
167         alist()->set_automation_style (as);
168 }
169
170 void
171 AutomationControl::start_touch(double when)
172 {
173         if (!_list) {
174                 return;
175         }
176
177         if (!touching()) {
178
179                 if (alist()->automation_state() == Touch) {
180                         /* subtle. aligns the user value with the playback */
181                         set_value (get_value (), Controllable::NoGroup);
182                         alist()->start_touch (when);
183                         if (!_desc.toggled) {
184                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
185                         }
186                 }
187                 set_touching (true);
188         }
189 }
190
191 void
192 AutomationControl::stop_touch(bool mark, double when)
193 {
194         if (!_list) return;
195         if (touching()) {
196                 set_touching (false);
197
198                 if (alist()->automation_state() == Touch) {
199                         alist()->stop_touch (mark, when);
200                         if (!_desc.toggled) {
201                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
202
203                         }
204                 }
205         }
206 }
207
208 void
209 AutomationControl::commit_transaction (bool did_write)
210 {
211         if (did_write) {
212                 if (alist ()->before ()) {
213                         _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
214                         _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
215                 }
216         } else {
217                 alist ()->clear_history ();
218         }
219 }
220
221 double
222 AutomationControl::internal_to_interface (double val) const
223 {
224         if (_desc.integer_step) {
225                 // both upper and lower are inclusive.
226                 val =  (val - lower()) / (1 + upper() - lower());
227         } else {
228                 val =  (val - lower()) / (upper() - lower());
229         }
230
231         if (_desc.logarithmic) {
232                 if (val > 0) {
233                         val = pow (val, 1./2.0);
234                 } else {
235                         val = 0;
236                 }
237         }
238
239         return val;
240 }
241
242 double
243 AutomationControl::interface_to_internal (double val) const
244 {
245         if (!isfinite_local (val)) {
246                 val = 0;
247         }
248         if (_desc.logarithmic) {
249                 if (val <= 0) {
250                         val = 0;
251                 } else {
252                         val = pow (val, 2.0);
253                 }
254         }
255
256         if (_desc.integer_step) {
257                 val =  lower() + val * (1 + upper() - lower());
258         } else {
259                 val =  lower() + val * (upper() - lower());
260         }
261
262         if (val < lower()) val = lower();
263         if (val > upper()) val = upper();
264
265         return val;
266 }
267
268
269 void
270 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
271 {
272         double current_value;
273         double new_value;
274         std::pair<Masters::iterator,bool> res;
275
276         {
277                 Glib::Threads::RWLock::WriterLock lm (master_lock);
278                 current_value = get_value_locked ();
279
280                 /* ratio will be recomputed below */
281
282                 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
283
284                 if (res.second) {
285
286                         recompute_masters_ratios (current_value);
287
288                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
289                            avoiding holding a reference to the control in the binding
290                            itself.
291                         */
292
293                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
294
295                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
296                            and we no longer hear about changes to the AutomationControl.
297
298                            Note that we fix the "from_self" argument that will
299                            be given to our own Changed signal to "false",
300                            because the change came from the master.
301                         */
302
303
304                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal2<void,bool,Controllable::GroupControlDisposition>::operator(), &Changed, false, _2));
305                 }
306
307                 new_value = get_value_locked ();
308         }
309
310         if (res.second) {
311                 MasterStatusChange (); /* EMIT SIGNAL */
312         }
313
314         if (new_value != current_value) {
315                 /* effective value changed by master */
316                 Changed (false, Controllable::NoGroup);
317         }
318
319 }
320
321 void
322 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
323 {
324         boost::shared_ptr<AutomationControl> m = wm.lock();
325         if (m) {
326                 remove_master (m);
327         }
328 }
329
330 void
331 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
332 {
333         double current_value;
334         double new_value;
335         Masters::size_type erased = 0;
336
337         {
338                 Glib::Threads::RWLock::WriterLock lm (master_lock);
339                 current_value = get_value_locked ();
340                 erased = _masters.erase (m->id());
341                 if (erased) {
342                         recompute_masters_ratios (current_value);
343                 }
344                 new_value = get_value_locked ();
345         }
346
347         if (erased) {
348                 MasterStatusChange (); /* EMIT SIGNAL */
349         }
350
351         if (new_value != current_value) {
352                 Changed (false, Controllable::NoGroup);
353         }
354 }
355
356 void
357 AutomationControl::clear_masters ()
358 {
359         double current_value;
360         double new_value;
361         bool had_masters = false;
362
363         {
364                 Glib::Threads::RWLock::WriterLock lm (master_lock);
365                 current_value = get_value_locked ();
366                 if (!_masters.empty()) {
367                         had_masters = true;
368                 }
369                 _masters.clear ();
370                 new_value = get_value_locked ();
371         }
372
373         if (had_masters) {
374                 MasterStatusChange (); /* EMIT SIGNAL */
375         }
376
377         if (new_value != current_value) {
378                 Changed (false, Controllable::NoGroup);
379         }
380
381 }
382
383 bool
384 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
385 {
386         Glib::Threads::RWLock::ReaderLock lm (master_lock);
387         return _masters.find (m->id()) != _masters.end();
388 }
389
390 bool
391 AutomationControl::slaved () const
392 {
393         Glib::Threads::RWLock::ReaderLock lm (master_lock);
394         return !_masters.empty();
395 }