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