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