Remove Cruft -- AutomationStyle never did anything.
[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::transport_located (framepos_t now)
342 {
343         for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
344
345                 boost::shared_ptr<AutomationControl> c
346                                 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
347                 if (c) {
348                         boost::shared_ptr<AutomationList> l
349                                 = boost::dynamic_pointer_cast<AutomationList>(c->list());
350
351                         if (l) {
352                                 l->start_write_pass (now);
353                         }
354                 }
355         }
356 }
357
358 void
359 Automatable::transport_stopped (framepos_t now)
360 {
361         for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
362                 boost::shared_ptr<AutomationControl> c =
363                         boost::dynamic_pointer_cast<AutomationControl>(li->second);
364                 if (!c) {
365                         continue;
366                 }
367
368                 boost::shared_ptr<AutomationList> l =
369                         boost::dynamic_pointer_cast<AutomationList>(c->list());
370                 if (!l) {
371                         continue;
372                 }
373
374                 /* Stop any active touch gesture just before we mark the write pass
375                    as finished.  If we don't do this, the transport can end up stopped with
376                    an AutomationList thinking that a touch is still in progress and,
377                    when the transport is re-started, a touch will magically
378                    be happening without it ever have being started in the usual way.
379                 */
380                 const bool list_did_write = !l->in_new_write_pass ();
381
382                 l->stop_touch (true, now);
383
384                 c->commit_transaction (list_did_write);
385
386                 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
387
388                 if (l->automation_state () == Write) {
389                         l->set_automation_state (Touch);
390                 }
391
392                 if (l->automation_playback ()) {
393                         c->set_value_unchecked (c->list ()->eval (now));
394                 }
395         }
396 }
397
398 boost::shared_ptr<Evoral::Control>
399 Automatable::control_factory(const Evoral::Parameter& param)
400 {
401         Evoral::Control*                  control   = NULL;
402         bool                              make_list = true;
403         ParameterDescriptor               desc(param);
404         boost::shared_ptr<AutomationList> list;
405
406         if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
407                 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
408                 if (mt) {
409                         control = new MidiTrack::MidiControl(mt, param);
410                         make_list = false;  // No list, this is region "automation"
411                 }
412         } else if (param.type() == PluginAutomation) {
413                 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
414                 if (pi) {
415                         pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
416                         control = new PluginInsert::PluginControl(pi, param, desc);
417                 } else {
418                         warning << "PluginAutomation for non-Plugin" << endl;
419                 }
420         } else if (param.type() == PluginPropertyAutomation) {
421                 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
422                 if (pi) {
423                         desc = pi->plugin(0)->get_property_descriptor(param.id());
424                         if (desc.datatype != Variant::NOTHING) {
425                                 if (!Variant::type_is_numeric(desc.datatype)) {
426                                         make_list = false;  // Can't automate non-numeric data yet
427                                 } else {
428                                         list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
429                                 }
430                                 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
431                         }
432                 } else {
433                         warning << "PluginPropertyAutomation for non-Plugin" << endl;
434                 }
435         } else if (param.type() == GainAutomation) {
436                 control = new GainControl(_a_session, param);
437         } else if (param.type() == TrimAutomation) {
438                 control = new GainControl(_a_session, param);
439         } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
440                 Pannable* pannable = dynamic_cast<Pannable*>(this);
441                 if (pannable) {
442                         control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
443                 } else {
444                         warning << "PanAutomation for non-Pannable" << endl;
445                 }
446         } else if (param.type() == RecEnableAutomation) {
447                 Recordable* re = dynamic_cast<Recordable*> (this);
448                 if (re) {
449                         control = new RecordEnableControl (_a_session, X_("recenable"), *re);
450                 }
451         } else if (param.type() == MonitoringAutomation) {
452                 Monitorable* m = dynamic_cast<Monitorable*>(this);
453                 if (m) {
454                         control = new MonitorControl (_a_session, X_("monitor"), *m);
455                 }
456         } else if (param.type() == SoloAutomation) {
457                 Soloable* s = dynamic_cast<Soloable*>(this);
458                 Muteable* m = dynamic_cast<Muteable*>(this);
459                 if (s && m) {
460                         control = new SoloControl (_a_session, X_("solo"), *s, *m);
461                 }
462         } else if (param.type() == MuteAutomation) {
463                 Muteable* m = dynamic_cast<Muteable*>(this);
464                 if (m) {
465                         control = new MuteControl (_a_session, X_("mute"), *m);
466                 }
467         }
468
469         if (make_list && !list) {
470                 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
471         }
472
473         if (!control) {
474                 control = new AutomationControl(_a_session, param, desc, list);
475         }
476
477         return boost::shared_ptr<Evoral::Control>(control);
478 }
479
480 boost::shared_ptr<AutomationControl>
481 Automatable::automation_control (PBD::ID const & id) const
482 {
483         Controls::const_iterator li;
484
485         for (li = _controls.begin(); li != _controls.end(); ++li) {
486                 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
487                 if (ac && (ac->id() == id)) {
488                         return ac;
489                 }
490         }
491
492         return boost::shared_ptr<AutomationControl>();
493 }
494
495 boost::shared_ptr<AutomationControl>
496 Automatable::automation_control (const Evoral::Parameter& id, bool create)
497 {
498         return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
499 }
500
501 boost::shared_ptr<const AutomationControl>
502 Automatable::automation_control (const Evoral::Parameter& id) const
503 {
504         return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
505 }
506
507 void
508 Automatable::clear_controls ()
509 {
510         _control_connections.drop_connections ();
511         ControlSet::clear_controls ();
512 }
513
514 bool
515 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
516 {
517         Controls::const_iterator li;
518
519         next_event.when = std::numeric_limits<double>::max();
520
521         for (li = _controls.begin(); li != _controls.end(); ++li) {
522                 boost::shared_ptr<AutomationControl> c
523                         = boost::dynamic_pointer_cast<AutomationControl>(li->second);
524
525                 if (only_active && (!c || !c->automation_playback())) {
526                         continue;
527                 }
528
529                 boost::shared_ptr<SlavableAutomationControl> sc
530                         = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
531
532                 if (sc) {
533                         sc->find_next_event (now, end, next_event);
534                 }
535
536                 Evoral::ControlList::const_iterator i;
537                 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
538                 Evoral::ControlEvent cp (now, 0.0f);
539                 if (!alist) {
540                         continue;
541                 }
542
543                 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
544                      i != alist->end() && (*i)->when < end; ++i) {
545                         if ((*i)->when > now) {
546                                 break;
547                         }
548                 }
549
550                 if (i != alist->end() && (*i)->when < end) {
551                         if ((*i)->when < next_event.when) {
552                                 next_event.when = (*i)->when;
553                         }
554                 }
555         }
556
557         return next_event.when != std::numeric_limits<double>::max();
558 }