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