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