Make exact beat calculation of tempi a bit less cumbersome. Move tempi on an audio...
[ardour.git] / libs / ardour / automatable.cc
1 /*
2     Copyright (C) 2001,2007 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdio>
21 #include <errno.h>
22
23 #include "pbd/gstdio_compat.h"
24 #include <glibmm/miscutils.h>
25
26 #include "pbd/error.h"
27
28 #include "ardour/amp.h"
29 #include "ardour/automatable.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/gain_control.h"
32 #include "ardour/monitor_control.h"
33 #include "ardour/midi_track.h"
34 #include "ardour/pan_controllable.h"
35 #include "ardour/pannable.h"
36 #include "ardour/plugin.h"
37 #include "ardour/plugin_insert.h"
38 #include "ardour/record_enable_control.h"
39 #include "ardour/session.h"
40 #include "ardour/uri_map.h"
41 #include "ardour/value_as_string.h"
42
43 #include "i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 const string Automatable::xml_node_name = X_("Automation");
50
51 Automatable::Automatable(Session& session)
52         : _a_session(session)
53 {
54 }
55
56 Automatable::Automatable (const Automatable& other)
57         : ControlSet (other)
58         , _a_session (other._a_session)
59 {
60         Glib::Threads::Mutex::Lock lm (other._control_lock);
61
62         for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
63                 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
64                 add_control (ac);
65         }
66 }
67
68 Automatable::~Automatable ()
69 {
70         {
71                 Glib::Threads::Mutex::Lock lm (_control_lock);
72
73                 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
74                         boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
75                 }
76         }
77 }
78
79 int
80 Automatable::old_set_automation_state (const XMLNode& node)
81 {
82         XMLProperty const * prop;
83
84         if ((prop = node.property ("path")) != 0) {
85                 load_automation (prop->value());
86         } else {
87                 warning << _("Automation node has no path property") << endmsg;
88         }
89
90         return 0;
91 }
92
93 int
94 Automatable::load_automation (const string& path)
95 {
96         string fullpath;
97
98         if (Glib::path_is_absolute (path)) { // legacy
99                 fullpath = path;
100         } else {
101                 fullpath = _a_session.automation_dir();
102                 fullpath += path;
103         }
104
105         FILE * in = g_fopen (fullpath.c_str (), "rb");
106
107         if (!in) {
108                 warning << string_compose(_("cannot open %2 to load automation data (%3)")
109                                 , fullpath, strerror (errno)) << endmsg;
110                 return 1;
111         }
112
113         Glib::Threads::Mutex::Lock lm (control_lock());
114         set<Evoral::Parameter> tosave;
115         controls().clear ();
116
117         while (!feof(in)) {
118                 double when;
119                 double value;
120                 uint32_t port;
121
122                 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
123                         if (feof(in)) {
124                                 break;
125                         }
126                         goto bad;
127                 }
128
129                 Evoral::Parameter param(PluginAutomation, 0, port);
130                 /* FIXME: this is legacy and only used for plugin inserts?  I think? */
131                 boost::shared_ptr<Evoral::Control> c = control (param, true);
132                 c->list()->add (when, value);
133                 tosave.insert (param);
134         }
135         ::fclose (in);
136
137         return 0;
138
139   bad:
140         error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
141         controls().clear ();
142         ::fclose (in);
143         return -1;
144 }
145
146 void
147 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
148 {
149         Evoral::Parameter param = ac->parameter();
150
151         boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
152
153         boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
154
155         if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
156                 al->automation_state_changed.connect_same_thread (
157                         _list_connections,
158                         boost::bind (&Automatable::automation_list_automation_state_changed,
159                                      this, ac->parameter(), _1));
160         }
161
162         ControlSet::add_control (ac);
163
164         if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
165                 _can_automate_list.insert (param);
166                 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
167         }
168 }
169
170 string
171 Automatable::describe_parameter (Evoral::Parameter param)
172 {
173         /* derived classes like PluginInsert should override this */
174
175         if (param == Evoral::Parameter(GainAutomation)) {
176                 return _("Fader");
177         } else if (param.type() == TrimAutomation) {
178                 return _("Trim");
179         } else if (param.type() == MuteAutomation) {
180                 return _("Mute");
181         } else if (param.type() == MidiCCAutomation) {
182                 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
183         } else if (param.type() == MidiPgmChangeAutomation) {
184                 return string_compose("Program [%1]", int(param.channel()) + 1);
185         } else if (param.type() == MidiPitchBenderAutomation) {
186                 return string_compose("Bender [%1]", int(param.channel()) + 1);
187         } else if (param.type() == MidiChannelPressureAutomation) {
188                 return string_compose("Pressure [%1]", int(param.channel()) + 1);
189 #ifdef LV2_SUPPORT
190         } else if (param.type() == PluginPropertyAutomation) {
191                 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
192 #endif
193         } else {
194                 return EventTypeMap::instance().to_symbol(param);
195         }
196 }
197
198 void
199 Automatable::can_automate (Evoral::Parameter what)
200 {
201         _can_automate_list.insert (what);
202 }
203
204 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
205  * had a single automation parameter, with it's type implicit.  Derived objects should
206  * pass that type and it will be used for the untyped AutomationList found.
207  */
208 int
209 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
210 {
211         Glib::Threads::Mutex::Lock lm (control_lock());
212
213         /* Don't clear controls, since some may be special derived Controllable classes */
214
215         XMLNodeList nlist = node.children();
216         XMLNodeIterator niter;
217
218         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
219
220                 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
221                   error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
222                   continue;
223                   }*/
224
225                 if ((*niter)->name() == "AutomationList") {
226
227                         XMLProperty const * id_prop = (*niter)->property("automation-id");
228
229                         Evoral::Parameter param = (id_prop
230                                         ? EventTypeMap::instance().from_symbol(id_prop->value())
231                                         : legacy_param);
232
233                         if (param.type() == NullAutomation) {
234                                 warning << "Automation has null type" << endl;
235                                 continue;
236                         }
237
238                         if (!id_prop) {
239                                 warning << "AutomationList node without automation-id property, "
240                                         << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
241                         }
242
243                         boost::shared_ptr<AutomationControl> existing = automation_control (param);
244
245                         if (existing) {
246                                 existing->alist()->set_state (**niter, 3000);
247                         } else {
248                                 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
249                                 add_control (newcontrol);
250                                 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
251                                 newcontrol->set_list(al);
252                         }
253
254                 } else {
255                         error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
256                 }
257         }
258
259         return 0;
260 }
261
262 XMLNode&
263 Automatable::get_automation_xml_state ()
264 {
265         Glib::Threads::Mutex::Lock lm (control_lock());
266         XMLNode* node = new XMLNode (Automatable::xml_node_name);
267
268         if (controls().empty()) {
269                 return *node;
270         }
271
272         for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
273                 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
274                 if (l && !l->empty()) {
275                         node->add_child_nocopy (l->get_state ());
276                 }
277         }
278
279         return *node;
280 }
281
282 void
283 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
284 {
285         Glib::Threads::Mutex::Lock lm (control_lock());
286
287         boost::shared_ptr<AutomationControl> c = automation_control (param, true);
288
289         if (c && (s != c->automation_state())) {
290                 c->set_automation_state (s);
291                 _a_session.set_dirty ();
292                 AutomationStateChanged(); /* Emit signal */
293         }
294 }
295
296 AutoState
297 Automatable::get_parameter_automation_state (Evoral::Parameter param)
298 {
299         AutoState result = Off;
300
301         boost::shared_ptr<AutomationControl> c = automation_control(param);
302
303         if (c) {
304                 result = c->automation_state();
305         }
306
307         return result;
308 }
309
310 void
311 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
312 {
313         Glib::Threads::Mutex::Lock lm (control_lock());
314
315         boost::shared_ptr<AutomationControl> c = automation_control(param, true);
316
317         if (c && (s != c->automation_style())) {
318                 c->set_automation_style (s);
319                 _a_session.set_dirty ();
320         }
321 }
322
323 AutoStyle
324 Automatable::get_parameter_automation_style (Evoral::Parameter param)
325 {
326         Glib::Threads::Mutex::Lock lm (control_lock());
327
328         boost::shared_ptr<Evoral::Control> c = control(param);
329         boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
330
331         if (c) {
332                 return l->automation_style();
333         } else {
334                 return Absolute; // whatever
335         }
336 }
337
338 void
339 Automatable::protect_automation ()
340 {
341         typedef set<Evoral::Parameter> ParameterSet;
342         const ParameterSet& automated_params = what_can_be_automated ();
343
344         for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
345
346                 boost::shared_ptr<Evoral::Control> c = control(*i);
347                 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
348
349                 switch (l->automation_state()) {
350                 case Write:
351                         l->set_automation_state (Off);
352                         break;
353                 case Touch:
354                         l->set_automation_state (Play);
355                         break;
356                 default:
357                         break;
358                 }
359         }
360 }
361
362 void
363 Automatable::transport_located (framepos_t now)
364 {
365         for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
366
367                 boost::shared_ptr<AutomationControl> c
368                                 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
369                 if (c) {
370                         boost::shared_ptr<AutomationList> l
371                                 = boost::dynamic_pointer_cast<AutomationList>(c->list());
372
373                         if (l) {
374                                 l->start_write_pass (now);
375                         }
376                 }
377         }
378 }
379
380 void
381 Automatable::transport_stopped (framepos_t now)
382 {
383         for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
384                 boost::shared_ptr<AutomationControl> c =
385                         boost::dynamic_pointer_cast<AutomationControl>(li->second);
386                 if (!c) {
387                         continue;
388                 }
389
390                 boost::shared_ptr<AutomationList> l =
391                         boost::dynamic_pointer_cast<AutomationList>(c->list());
392                 if (!l) {
393                         continue;
394                 }
395
396                 /* Stop any active touch gesture just before we mark the write pass
397                    as finished.  If we don't do this, the transport can end up stopped with
398                    an AutomationList thinking that a touch is still in progress and,
399                    when the transport is re-started, a touch will magically
400                    be happening without it ever have being started in the usual way.
401                 */
402                 const bool list_did_write = !l->in_new_write_pass ();
403
404                 l->stop_touch (true, now);
405
406                 c->commit_transaction (list_did_write);
407
408                 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
409
410                 if (l->automation_state () == Write) {
411                         l->set_automation_state (Touch);
412                 }
413
414                 if (l->automation_playback ()) {
415                         c->set_value_unchecked (c->list ()->eval (now));
416                 }
417         }
418 }
419
420 boost::shared_ptr<Evoral::Control>
421 Automatable::control_factory(const Evoral::Parameter& param)
422 {
423         Evoral::Control*                  control   = NULL;
424         bool                              make_list = true;
425         ParameterDescriptor               desc(param);
426         boost::shared_ptr<AutomationList> list;
427
428         if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
429                 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
430                 if (mt) {
431                         control = new MidiTrack::MidiControl(mt, param);
432                         make_list = false;  // No list, this is region "automation"
433                 }
434         } else if (param.type() == PluginAutomation) {
435                 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
436                 if (pi) {
437                         pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
438                         control = new PluginInsert::PluginControl(pi, param, desc);
439                 } else {
440                         warning << "PluginAutomation for non-Plugin" << endl;
441                 }
442         } else if (param.type() == PluginPropertyAutomation) {
443                 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
444                 if (pi) {
445                         desc = pi->plugin(0)->get_property_descriptor(param.id());
446                         if (desc.datatype != Variant::NOTHING) {
447                                 if (!Variant::type_is_numeric(desc.datatype)) {
448                                         make_list = false;  // Can't automate non-numeric data yet
449                                 } else {
450                                         list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
451                                 }
452                                 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
453                         }
454                 } else {
455                         warning << "PluginPropertyAutomation for non-Plugin" << endl;
456                 }
457         } else if (param.type() == GainAutomation) {
458                 control = new GainControl(_a_session, param);
459         } else if (param.type() == TrimAutomation) {
460                 control = new GainControl(_a_session, param);
461         } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
462                 Pannable* pannable = dynamic_cast<Pannable*>(this);
463                 if (pannable) {
464                         control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
465                 } else {
466                         warning << "PanAutomation for non-Pannable" << endl;
467                 }
468         } else if (param.type() == RecEnableAutomation) {
469                 Recordable* re = dynamic_cast<Recordable*> (this);
470                 if (re) {
471                         control = new RecordEnableControl (_a_session, X_("recenable"), *re);
472                 }
473         } else if (param.type() == MonitoringAutomation) {
474                 Monitorable* m = dynamic_cast<Monitorable*>(this);
475                 if (m) {
476                         control = new MonitorControl (_a_session, X_("monitor"), *m);
477                 }
478         } else if (param.type() == SoloAutomation) {
479                 Soloable* s = dynamic_cast<Soloable*>(this);
480                 Muteable* m = dynamic_cast<Muteable*>(this);
481                 if (s && m) {
482                         control = new SoloControl (_a_session, X_("solo"), *s, *m);
483                 }
484         } else if (param.type() == MuteAutomation) {
485                 Muteable* m = dynamic_cast<Muteable*>(this);
486                 if (m) {
487                         control = new MuteControl (_a_session, X_("mute"), *m);
488                 }
489         }
490
491         if (make_list && !list) {
492                 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
493         }
494
495         if (!control) {
496                 control = new AutomationControl(_a_session, param, desc, list);
497         }
498
499         return boost::shared_ptr<Evoral::Control>(control);
500 }
501
502 boost::shared_ptr<AutomationControl>
503 Automatable::automation_control (const Evoral::Parameter& id, bool create)
504 {
505         return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
506 }
507
508 boost::shared_ptr<const AutomationControl>
509 Automatable::automation_control (const Evoral::Parameter& id) const
510 {
511         return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
512 }
513
514 void
515 Automatable::clear_controls ()
516 {
517         _control_connections.drop_connections ();
518         ControlSet::clear_controls ();
519 }
520
521 string
522 Automatable::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
523 {
524         return ARDOUR::value_as_string(ac->desc(), ac->get_value());
525 }
526
527 bool
528 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
529 {
530         Controls::const_iterator li;
531
532         next_event.when = std::numeric_limits<double>::max();
533
534         for (li = _controls.begin(); li != _controls.end(); ++li) {
535                 boost::shared_ptr<AutomationControl> c
536                         = boost::dynamic_pointer_cast<AutomationControl>(li->second);
537
538                 if (only_active && (!c || !c->automation_playback())) {
539                         continue;
540                 }
541
542                 Evoral::ControlList::const_iterator i;
543                 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
544                 Evoral::ControlEvent cp (now, 0.0f);
545                 if (!alist) {
546                         continue;
547                 }
548
549                 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
550                      i != alist->end() && (*i)->when < end; ++i) {
551                         if ((*i)->when > now) {
552                                 break;
553                         }
554                 }
555
556                 if (i != alist->end() && (*i)->when < end) {
557                         if ((*i)->when < next_event.when) {
558                                 next_event.when = (*i)->when;
559                         }
560                 }
561         }
562
563         return next_event.when != std::numeric_limits<double>::max();
564 }