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