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