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