redesign control slave/master system, move code from GainControl to AutomationControl
[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 /** Get the current effective `user' value based on automation state */
73 double
74 AutomationControl::get_value() const
75 {
76         bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
77
78         if (!from_list) {
79                 Glib::Threads::RWLock::ReaderLock lm (master_lock);
80                 return get_value_locked ();
81         } else {
82                 return Control::get_double (from_list, _session.transport_frame());
83         }
84 }
85
86 double
87 AutomationControl::get_value_locked() const
88 {
89         /* read or write masters lock must be held */
90
91         if (_masters.empty()) {
92                 return Control::get_double (false, _session.transport_frame());
93         }
94
95         gain_t v = 1.0;
96
97         for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
98                 /* get current master value, scale by our current ratio with that master */
99                 v *= mr->second.master()->get_value () * mr->second.ratio();
100         }
101
102         return min (_desc.upper, v);
103 }
104
105
106
107 /** Set the value and do the right thing based on automation state
108  *  (e.g. record if necessary, etc.)
109  *  @param value `user' value
110  */
111 void
112 AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition /* group_override */)
113 {
114         bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
115
116         Control::set_double (value, _session.transport_frame(), to_list);
117
118         Changed(); /* EMIT SIGNAL */
119 }
120
121 void
122 AutomationControl::set_list (boost::shared_ptr<Evoral::ControlList> list)
123 {
124         Control::set_list (list);
125         Changed();  /* EMIT SIGNAL */
126 }
127
128 void
129 AutomationControl::set_automation_state (AutoState as)
130 {
131         if (_list && as != alist()->automation_state()) {
132
133                 alist()->set_automation_state (as);
134                 if (_desc.toggled) {
135                         return;  // No watch for boolean automation
136                 }
137
138                 if (as == Write) {
139                         AutomationWatch::instance().add_automation_watch (shared_from_this());
140                 } else if (as == Touch) {
141                         if (!touching()) {
142                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
143                         } else {
144                                 /* this seems unlikely, but the combination of
145                                  * a control surface and the mouse could make
146                                  * it possible to put the control into Touch
147                                  * mode *while* touching it.
148                                  */
149                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
150                         }
151                 } else {
152                         AutomationWatch::instance().remove_automation_watch (shared_from_this());
153                 }
154         }
155 }
156
157 void
158 AutomationControl::set_automation_style (AutoStyle as)
159 {
160         if (!_list) return;
161         alist()->set_automation_style (as);
162 }
163
164 void
165 AutomationControl::start_touch(double when)
166 {
167         if (!_list) {
168                 return;
169         }
170
171         if (!touching()) {
172
173                 if (alist()->automation_state() == Touch) {
174                         /* subtle. aligns the user value with the playback */
175                         set_value (get_value (), Controllable::NoGroup);
176                         alist()->start_touch (when);
177                         if (!_desc.toggled) {
178                                 AutomationWatch::instance().add_automation_watch (shared_from_this());
179                         }
180                 }
181                 set_touching (true);
182         }
183 }
184
185 void
186 AutomationControl::stop_touch(bool mark, double when)
187 {
188         if (!_list) return;
189         if (touching()) {
190                 set_touching (false);
191
192                 if (alist()->automation_state() == Touch) {
193                         alist()->stop_touch (mark, when);
194                         if (!_desc.toggled) {
195                                 AutomationWatch::instance().remove_automation_watch (shared_from_this());
196
197                         }
198                 }
199         }
200 }
201
202 void
203 AutomationControl::commit_transaction (bool did_write)
204 {
205         if (did_write) {
206                 if (alist ()->before ()) {
207                         _session.begin_reversible_command (string_compose (_("record %1 automation"), name ()));
208                         _session.commit_reversible_command (new MementoCommand<AutomationList> (*alist ().get (), alist ()->before (), &alist ()->get_state ()));
209                 }
210         } else {
211                 alist ()->clear_history ();
212         }
213 }
214
215 double
216 AutomationControl::internal_to_interface (double val) const
217 {
218         if (_desc.integer_step) {
219                 // both upper and lower are inclusive.
220                 val =  (val - lower()) / (1 + upper() - lower());
221         } else {
222                 val =  (val - lower()) / (upper() - lower());
223         }
224
225         if (_desc.logarithmic) {
226                 if (val > 0) {
227                         val = pow (val, 1./2.0);
228                 } else {
229                         val = 0;
230                 }
231         }
232
233         return val;
234 }
235
236 double
237 AutomationControl::interface_to_internal (double val) const
238 {
239         if (!isfinite_local (val)) {
240                 val = 0;
241         }
242         if (_desc.logarithmic) {
243                 if (val <= 0) {
244                         val = 0;
245                 } else {
246                         val = pow (val, 2.0);
247                 }
248         }
249
250         if (_desc.integer_step) {
251                 val =  lower() + val * (1 + upper() - lower());
252         } else {
253                 val =  lower() + val * (upper() - lower());
254         }
255
256         if (val < lower()) val = lower();
257         if (val > upper()) val = upper();
258
259         return val;
260 }
261
262
263 void
264 AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
265 {
266         double current_value;
267         std::pair<Masters::iterator,bool> res;
268
269         {
270                 Glib::Threads::RWLock::WriterLock lm (master_lock);
271                 current_value = get_value_locked ();
272
273                 /* ratio will be recomputed below */
274
275                 res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0)));
276
277                 if (res.second) {
278
279                         recompute_masters_ratios (current_value);
280
281                         /* note that we bind @param m as a weak_ptr<AutomationControl>, thus
282                            avoiding holding a reference to the control in the binding
283                            itself.
284                         */
285
286                         m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m));
287
288                         /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed
289                            and we no longer hear about changes to the AutomationControl.
290                         */
291
292                         m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
293                 }
294         }
295
296         if (res.second) {
297                 MasterStatusChange (); /* EMIT SIGNAL */
298         }
299 }
300
301 void
302 AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
303 {
304         boost::shared_ptr<AutomationControl> m = wm.lock();
305         if (m) {
306                 remove_master (m);
307         }
308 }
309
310 void
311 AutomationControl::remove_master (boost::shared_ptr<AutomationControl> m)
312 {
313         double current_value;
314         Masters::size_type erased = 0;
315
316         {
317                 Glib::Threads::RWLock::WriterLock lm (master_lock);
318                 current_value = get_value_locked ();
319                 erased = _masters.erase (m->id());
320                 if (erased) {
321                         recompute_masters_ratios (current_value);
322                 }
323         }
324
325         if (erased) {
326                 MasterStatusChange (); /* EMIT SIGNAL */
327         }
328 }
329
330 void
331 AutomationControl::clear_masters ()
332 {
333         bool had_masters = false;
334
335         {
336                 Glib::Threads::RWLock::WriterLock lm (master_lock);
337                 if (!_masters.empty()) {
338                         had_masters = true;
339                 }
340                 _masters.clear ();
341         }
342
343         if (had_masters) {
344                 MasterStatusChange (); /* EMIT SIGNAL */
345         }
346 }
347
348 bool
349 AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
350 {
351         Glib::Threads::RWLock::ReaderLock lm (master_lock);
352         return _masters.find (m->id()) != _masters.end();
353 }
354
355 bool
356 AutomationControl::slaved () const
357 {
358         Glib::Threads::RWLock::ReaderLock lm (master_lock);
359         return !_masters.empty();
360 }
361