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