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