Lua bindings to access MIDI region/source note-events
[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
24 #include "pbd/memento_command.h"
25 #include "pbd/stacktrace.h"
26
27 #include "ardour/audioengine.h"
28 #include "ardour/automation_control.h"
29 #include "ardour/automation_watch.h"
30 #include "ardour/control_group.h"
31 #include "ardour/event_type_map.h"
32 #include "ardour/session.h"
33
34 #include "pbd/i18n.h"
35
36 #ifdef COMPILER_MSVC
37 #include <float.h>
38 // C99 'isfinite()' is not available in MSVC.
39 #define isfinite_local(val) (bool)_finite((double)val)
40 #else
41 #define isfinite_local isfinite
42 #endif
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47
48 AutomationControl::AutomationControl(ARDOUR::Session&                          session,
49                                      const Evoral::Parameter&                  parameter,
50                                      const ParameterDescriptor&                desc,
51                                      boost::shared_ptr<ARDOUR::AutomationList> list,
52                                      const string&                             name,
53                                      Controllable::Flag                        flags)
54
55         : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name, flags)
56         , Evoral::Control(parameter, desc, list)
57         , _session(session)
58         , _desc(desc)
59 {
60         if (_desc.toggled) {
61                 set_flags (Controllable::Toggle);
62         }
63         boost::shared_ptr<AutomationList> al = alist();
64         if (al) {
65                 al->StateChanged.connect_same_thread (_state_changed_connection, boost::bind (&Session::set_dirty, &_session));
66         }
67 }
68
69 AutomationControl::~AutomationControl ()
70 {
71         DropReferences (); /* EMIT SIGNAL */
72 }
73
74 bool
75 AutomationControl::writable() const
76 {
77         boost::shared_ptr<AutomationList> al = alist();
78         if (al) {
79                 return al->automation_state() != Play;
80         }
81         return true;
82 }
83
84 /** Get the current effective `user' value based on automation state */
85 double
86 AutomationControl::get_value() const
87 {
88         bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback();
89         return Control::get_double (from_list, _session.transport_frame());
90 }
91
92 void
93 AutomationControl::pre_realtime_queue_stuff (double val, PBD::Controllable::GroupControlDisposition gcd)
94 {
95         if (_group && _group->use_me (gcd)) {
96                 _group->pre_realtime_queue_stuff (val);
97         } else {
98                 do_pre_realtime_queue_stuff (val);
99         }
100 }
101
102 void
103 AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd)
104 {
105         if (!writable()) {
106                 return;
107         }
108
109         /* enforce strict double/boolean value mapping */
110
111         if (_desc.toggled) {
112                 if (val != 0.0) {
113                         val = 1.0;
114                 }
115         }
116
117         if (check_rt (val, gcd)) {
118                 /* change has been queued to take place in an RT context */
119                 return;
120         }
121
122         if (_group && _group->use_me (gcd)) {
123                 _group->set_group_value (shared_from_this(), val);
124         } else {
125                 actually_set_value (val, gcd);
126         }
127 }
128
129 /** Set the value and do the right thing based on automation state
130  *  (e.g. record if necessary, etc.)
131  *  @param value `user' value
132  */
133 void
134 AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd)
135 {
136         boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (_list);
137         const framepos_t pos = _session.transport_frame();
138         bool to_list;
139         double old_value;
140
141         /* We cannot use ::get_value() here since that is virtual, and intended
142            to return a scalar value that in some way reflects the state of the
143            control (with semantics defined by the control itself, since it's
144            internal state may be more complex than can be fully represented by
145            a single scalar).
146
147            This method's only job is to set the "user_double()" value of the
148            underlying Evoral::Control object, and so we should compare the new
149            value we're being given to the current user_double().
150
151            Unless ... we're doing automation playback, in which case the
152            current effective value of the control (used to determine if
153            anything has changed) is the one derived from the automation event
154            list.
155         */
156
157         if (!al) {
158                 to_list = false;
159                 old_value = Control::user_double();
160         } else {
161                 if (al->automation_write ()) {
162                         to_list = true;
163                         old_value = Control::user_double ();
164                 } else if (al->automation_playback()) {
165                         to_list = false;
166                         old_value = al->eval (pos);
167                 } else {
168                         to_list = false;
169                         old_value = Control::user_double ();
170                 }
171         }
172
173         Control::set_double (value, pos, to_list);
174
175         if (old_value != value) {
176                 //AutomationType at = (AutomationType) _parameter.type();
177                 //std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value
178                 //<< " (was " << old_value << ") @ " << this << std::endl;
179
180                 Changed (true, gcd);
181                 if (!al || !al->automation_playback ()) {
182                         _session.set_dirty ();
183                 }
184         }
185
186 }
187
188 void
189 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
190 {
191         Control::set_list (list);
192         Changed (true, Controllable::NoGroup);
193 }
194
195 void
196 AutomationControl::set_automation_state (AutoState as)
197 {
198         if (flags() & NotAutomatable) {
199                 return;
200         }
201         if (_list && as != alist()->automation_state()) {
202
203                 const double val = get_value ();
204
205                 alist()->set_automation_state (as);
206                 if (_desc.toggled) {
207                         return;  // No watch for boolean automation
208                 }
209
210                 if (as == Write) {
211                         AutomationWatch::instance().add_automation_watch (shared_from_this());
212                 } else if (as == Touch) {
213                         if (alist()->empty()) {
214                                 Control::set_double (val, _session.current_start_frame (), true);
215                                 Control::set_double (val, _session.current_end_frame (), true);
216                                 Changed (true, Controllable::NoGroup);
217                         }
218                         if (!touching()) {
219                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
220                         } else {
221                                 /* this seems unlikely, but the combination of
222                                  * a control surface and the mouse could make
223                                  * it possible to put the control into Touch
224                                  * mode *while* touching it.
225                                  */
226                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
227                         }
228                 } else {
229                         AutomationWatch::instance().remove_automation_watch (shared_from_this());
230                 }
231         }
232 }
233
234 void
235 AutomationControl::set_automation_style (AutoStyle as)
236 {
237         if (!_list) return;
238         alist()->set_automation_style (as);
239 }
240
241 void
242 AutomationControl::start_touch(double when)
243 {
244         if (!_list) {
245                 return;
246         }
247
248         if (!touching()) {
249
250                 if (alist()->automation_state() == Touch) {
251                         /* subtle. aligns the user value with the playback */
252                         set_value (get_value (), Controllable::NoGroup);
253                         alist()->start_touch (when);
254                         if (!_desc.toggled) {
255                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
256                         }
257                 }
258                 set_touching (true);
259         }
260 }
261
262 void
263 AutomationControl::stop_touch(bool mark, double when)
264 {
265         if (!_list) return;
266         if (touching()) {
267                 set_touching (false);
268
269                 if (alist()->automation_state() == Touch) {
270                         alist()->stop_touch (mark, when);
271                         if (!_desc.toggled) {
272                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
273
274                         }
275                 }
276         }
277 }
278
279 void
280 AutomationControl::commit_transaction (bool did_write)
281 {
282         if (did_write) {
283                 if (alist ()->before ()) {
284                         _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
285                         _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
286                 }
287         } else {
288                 alist ()->clear_history ();
289         }
290 }
291
292 double
293 AutomationControl::internal_to_interface (double val) const
294 {
295         if (_desc.integer_step) {
296                 // both upper and lower are inclusive.
297                 val =  (val - lower()) / (1 + upper() - lower());
298         } else {
299                 val =  (val - lower()) / (upper() - lower());
300         }
301
302         if (_desc.logarithmic) {
303                 if (val > 0) {
304                         val = pow (val, 1./2.0);
305                 } else {
306                         val = 0;
307                 }
308         }
309
310         return val;
311 }
312
313 double
314 AutomationControl::interface_to_internal (double val) const
315 {
316         if (!isfinite_local (val)) {
317                 val = 0;
318         }
319         if (_desc.logarithmic) {
320                 if (val <= 0) {
321                         val = 0;
322                 } else {
323                         val = pow (val, 2.0);
324                 }
325         }
326
327         if (_desc.integer_step) {
328                 val =  lower() + val * (1 + upper() - lower());
329         } else {
330                 val =  lower() + val * (upper() - lower());
331         }
332
333         if (val < lower()) val = lower();
334         if (val > upper()) val = upper();
335
336         return val;
337 }
338
339 void
340 AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg)
341 {
342         /* this method can only be called by a ControlGroup. We do not need
343            to ensure consistency by calling ControlGroup::remove_control(),
344            since we are guaranteed that the ControlGroup will take care of that
345            for us.
346         */
347
348         _group = cg;
349 }
350
351 bool
352 AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd)
353 {
354         if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
355                 /* queue change in RT context */
356                 _session.set_control (shared_from_this(), val, gcd);
357                 return true;
358         }
359
360         return false;
361 }