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