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