2 * Copyright (C) 2001-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
4 * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
5 * Copyright (C) 2010-2011 Carl Hetherington <carl@carlh.net>
6 * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
7 * Copyright (C) 2015 Nick Mainsbridge <mainsbridge@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "pbd/gstdio_compat.h"
28 #include <glibmm/miscutils.h>
30 #include "pbd/error.h"
32 #include "ardour/amp.h"
33 #include "ardour/automatable.h"
34 #include "ardour/event_type_map.h"
35 #include "ardour/gain_control.h"
36 #include "ardour/monitor_control.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/pan_controllable.h"
39 #include "ardour/pannable.h"
40 #include "ardour/plugin.h"
41 #include "ardour/plugin_insert.h"
42 #include "ardour/record_enable_control.h"
43 #include "ardour/session.h"
45 #include "ardour/uri_map.h"
47 #include "ardour/value_as_string.h"
52 using namespace ARDOUR;
55 /* used for templates (previously: !full_state) */
56 bool Automatable::skip_saving_automation = false;
58 const string Automatable::xml_node_name = X_("Automation");
60 Automatable::Automatable(Session& session)
62 , _automated_controls (new ControlList)
66 Automatable::Automatable (const Automatable& other)
69 , _a_session (other._a_session)
70 , _automated_controls (new ControlList)
72 Glib::Threads::Mutex::Lock lm (other._control_lock);
74 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
75 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
80 Automatable::~Automatable ()
83 RCUWriter<ControlList> writer (_automated_controls);
84 boost::shared_ptr<ControlList> cl = writer.get_copy ();
87 _automated_controls.flush ();
89 Glib::Threads::Mutex::Lock lm (_control_lock);
90 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
91 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
96 Automatable::old_set_automation_state (const XMLNode& node)
98 XMLProperty const * prop;
100 if ((prop = node.property ("path")) != 0) {
101 load_automation (prop->value());
103 warning << _("Automation node has no path property") << endmsg;
110 Automatable::load_automation (const string& path)
114 if (Glib::path_is_absolute (path)) { // legacy
117 fullpath = _a_session.automation_dir();
121 FILE * in = g_fopen (fullpath.c_str (), "rb");
124 warning << string_compose(_("cannot open %2 to load automation data (%3)")
125 , fullpath, strerror (errno)) << endmsg;
129 Glib::Threads::Mutex::Lock lm (control_lock());
130 set<Evoral::Parameter> tosave;
138 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
145 Evoral::Parameter param(PluginAutomation, 0, port);
146 /* FIXME: this is legacy and only used for plugin inserts? I think? */
147 boost::shared_ptr<Evoral::Control> c = control (param, true);
148 c->list()->add (when, value);
149 tosave.insert (param);
156 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
163 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
165 Evoral::Parameter param = ac->parameter();
167 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
169 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
171 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
172 al->automation_state_changed.connect_same_thread (
174 boost::bind (&Automatable::automation_list_automation_state_changed,
175 this, ac->parameter(), _1));
178 ControlSet::add_control (ac);
180 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
181 _can_automate_list.insert (param);
182 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
187 Automatable::describe_parameter (Evoral::Parameter param)
189 /* derived classes like PluginInsert should override this */
191 if (param == Evoral::Parameter(GainAutomation)) {
193 } else if (param.type() == TrimAutomation) {
195 } else if (param.type() == MuteAutomation) {
197 } else if (param.type() == MidiCCAutomation) {
198 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
199 } else if (param.type() == MidiPgmChangeAutomation) {
200 return string_compose("Program [%1]", int(param.channel()) + 1);
201 } else if (param.type() == MidiPitchBenderAutomation) {
202 return string_compose("Bender [%1]", int(param.channel()) + 1);
203 } else if (param.type() == MidiChannelPressureAutomation) {
204 return string_compose("Pressure [%1]", int(param.channel()) + 1);
205 } else if (param.type() == MidiNotePressureAutomation) {
206 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
208 } else if (param.type() == PluginPropertyAutomation) {
209 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
212 return EventTypeMap::instance().to_symbol(param);
217 Automatable::can_automate (Evoral::Parameter what)
219 _can_automate_list.insert (what);
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.
227 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
229 Glib::Threads::Mutex::Lock lm (control_lock());
231 /* Don't clear controls, since some may be special derived Controllable classes */
233 XMLNodeList nlist = node.children();
234 XMLNodeIterator niter;
236 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
238 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
239 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
243 if ((*niter)->name() == "AutomationList") {
245 XMLProperty const * id_prop = (*niter)->property("automation-id");
247 Evoral::Parameter param = (id_prop
248 ? EventTypeMap::instance().from_symbol(id_prop->value())
251 if (param.type() == NullAutomation) {
252 warning << "Automation has null type" << endl;
257 warning << "AutomationList node without automation-id property, "
258 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
261 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
262 boost::shared_ptr<AutomationControl> actl = automation_control (param);
263 if (actl && (*niter)->children().size() > 0 && Config->get_limit_n_automatables () > 0) {
264 actl->set_flags (Controllable::Flag ((int)actl->flags() & ~Controllable::NotAutomatable));
265 can_automate (param);
266 info << "Marked parmater as automatable" << endl;
268 warning << "Ignored automation data for non-automatable parameter" << endl;
274 boost::shared_ptr<AutomationControl> existing = automation_control (param);
277 existing->alist()->set_state (**niter, 3000);
279 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
280 add_control (newcontrol);
281 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
282 newcontrol->set_list(al);
286 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
294 Automatable::get_automation_xml_state ()
296 Glib::Threads::Mutex::Lock lm (control_lock());
297 XMLNode* node = new XMLNode (Automatable::xml_node_name);
299 if (controls().empty()) {
303 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
304 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
306 node->add_child_nocopy (l->get_state ());
314 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
316 Glib::Threads::Mutex::Lock lm (control_lock());
318 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
320 if (c && (s != c->automation_state())) {
321 c->set_automation_state (s);
322 _a_session.set_dirty ();
323 AutomationStateChanged(); /* Emit signal */
328 Automatable::get_parameter_automation_state (Evoral::Parameter param)
330 AutoState result = Off;
332 boost::shared_ptr<AutomationControl> c = automation_control(param);
335 result = c->automation_state();
342 Automatable::protect_automation ()
344 typedef set<Evoral::Parameter> ParameterSet;
345 const ParameterSet& automated_params = what_can_be_automated ();
347 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
349 boost::shared_ptr<Evoral::Control> c = control(*i);
350 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
352 switch (l->automation_state()) {
354 l->set_automation_state (Off);
359 l->set_automation_state (Play);
368 Automatable::non_realtime_locate (samplepos_t now)
370 bool rolling = _a_session.transport_rolling ();
372 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
374 boost::shared_ptr<AutomationControl> c
375 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
377 boost::shared_ptr<AutomationList> l
378 = boost::dynamic_pointer_cast<AutomationList>(c->list());
384 bool am_touching = c->touching ();
385 if (rolling && am_touching) {
386 /* when locating while rolling, and writing automation,
387 * start a new write pass.
388 * compare to compare to non_realtime_transport_stop()
390 const bool list_did_write = !l->in_new_write_pass ();
391 c->stop_touch (-1); // time is irrelevant
393 c->commit_transaction (list_did_write);
394 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
396 if (l->automation_state () == Write) {
397 l->set_automation_state (Touch);
399 if (l->automation_playback ()) {
400 c->set_value_unchecked (c->list ()->eval (now));
404 l->start_write_pass (now);
406 if (rolling && am_touching) {
407 c->start_touch (now);
414 Automatable::non_realtime_transport_stop (samplepos_t now, bool /*flush_processors*/)
416 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
417 boost::shared_ptr<AutomationControl> c =
418 boost::dynamic_pointer_cast<AutomationControl>(li->second);
423 boost::shared_ptr<AutomationList> l =
424 boost::dynamic_pointer_cast<AutomationList>(c->list());
429 /* Stop any active touch gesture just before we mark the write pass
430 as finished. If we don't do this, the transport can end up stopped with
431 an AutomationList thinking that a touch is still in progress and,
432 when the transport is re-started, a touch will magically
433 be happening without it ever have being started in the usual way.
435 const bool list_did_write = !l->in_new_write_pass ();
440 c->commit_transaction (list_did_write);
442 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
444 if (l->automation_state () == Write) {
445 l->set_automation_state (Touch);
448 if (l->automation_playback ()) {
449 c->set_value_unchecked (c->list ()->eval (now));
455 Automatable::automation_run (samplepos_t start, pframes_t nframes, bool only_active)
458 boost::shared_ptr<ControlList> cl = _automated_controls.reader ();
459 for (ControlList::const_iterator ci = cl->begin(); ci != cl->end(); ++ci) {
460 (*ci)->automation_run (start, nframes);
465 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
466 boost::shared_ptr<AutomationControl> c =
467 boost::dynamic_pointer_cast<AutomationControl>(li->second);
471 c->automation_run (start, nframes);
476 Automatable::automation_list_automation_state_changed (Evoral::Parameter param, AutoState as)
479 boost::shared_ptr<AutomationControl> c (automation_control(param));
480 assert (c && c->list());
482 RCUWriter<ControlList> writer (_automated_controls);
483 boost::shared_ptr<ControlList> cl = writer.get_copy ();
485 ControlList::iterator fi = std::find (cl->begin(), cl->end(), c);
486 if (fi != cl->end()) {
490 /* all potential automation_playback() states */
501 _automated_controls.flush();
504 boost::shared_ptr<Evoral::Control>
505 Automatable::control_factory(const Evoral::Parameter& param)
507 Evoral::Control* control = NULL;
508 bool make_list = true;
509 ParameterDescriptor desc(param);
510 boost::shared_ptr<AutomationList> list;
512 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
513 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
515 control = new MidiTrack::MidiControl(mt, param);
516 make_list = false; // No list, this is region "automation"
518 } else if (param.type() == PluginAutomation) {
519 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
521 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
522 control = new PluginInsert::PluginControl(pi, param, desc);
524 warning << "PluginAutomation for non-Plugin" << endl;
526 } else if (param.type() == PluginPropertyAutomation) {
527 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
529 desc = pi->plugin(0)->get_property_descriptor(param.id());
530 if (desc.datatype != Variant::NOTHING) {
531 if (!Variant::type_is_numeric(desc.datatype)) {
532 make_list = false; // Can't automate non-numeric data yet
534 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
536 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
539 warning << "PluginPropertyAutomation for non-Plugin" << endl;
541 } else if (param.type() == GainAutomation) {
542 control = new GainControl(_a_session, param);
543 } else if (param.type() == TrimAutomation) {
544 control = new GainControl(_a_session, param);
545 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
546 Pannable* pannable = dynamic_cast<Pannable*>(this);
548 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
550 warning << "PanAutomation for non-Pannable" << endl;
552 } else if (param.type() == RecEnableAutomation) {
553 Recordable* re = dynamic_cast<Recordable*> (this);
555 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
557 } else if (param.type() == MonitoringAutomation) {
558 Monitorable* m = dynamic_cast<Monitorable*>(this);
560 control = new MonitorControl (_a_session, X_("monitor"), *m);
562 } else if (param.type() == SoloAutomation) {
563 Soloable* s = dynamic_cast<Soloable*>(this);
564 Muteable* m = dynamic_cast<Muteable*>(this);
566 control = new SoloControl (_a_session, X_("solo"), *s, *m);
568 } else if (param.type() == MuteAutomation) {
569 Muteable* m = dynamic_cast<Muteable*>(this);
571 control = new MuteControl (_a_session, X_("mute"), *m);
575 if (make_list && !list) {
576 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
580 control = new AutomationControl(_a_session, param, desc, list);
583 return boost::shared_ptr<Evoral::Control>(control);
586 boost::shared_ptr<AutomationControl>
587 Automatable::automation_control (PBD::ID const & id) const
589 Controls::const_iterator li;
591 for (li = _controls.begin(); li != _controls.end(); ++li) {
592 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
593 if (ac && (ac->id() == id)) {
598 return boost::shared_ptr<AutomationControl>();
601 boost::shared_ptr<AutomationControl>
602 Automatable::automation_control (const Evoral::Parameter& id, bool create)
604 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
607 boost::shared_ptr<const AutomationControl>
608 Automatable::automation_control (const Evoral::Parameter& id) const
610 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
614 Automatable::clear_controls ()
616 _control_connections.drop_connections ();
617 ControlSet::clear_controls ();
621 Automatable::find_next_event (double start, double end, Evoral::ControlEvent& next_event, bool only_active) const
623 next_event.when = std::numeric_limits<double>::max();
626 boost::shared_ptr<ControlList> cl = _automated_controls.reader ();
627 for (ControlList::const_iterator ci = cl->begin(); ci != cl->end(); ++ci) {
628 if ((*ci)->automation_playback()) {
629 find_next_ac_event (*ci, start, end, next_event);
633 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
634 boost::shared_ptr<AutomationControl> c
635 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
637 find_next_ac_event (c, start, end, next_event);
641 return next_event.when != std::numeric_limits<double>::max();
645 Automatable::find_next_ac_event (boost::shared_ptr<AutomationControl> c, double start, double end, Evoral::ControlEvent& next_event) const
647 boost::shared_ptr<SlavableAutomationControl> sc
648 = boost::dynamic_pointer_cast<SlavableAutomationControl>(c);
651 sc->find_next_event (start, end, next_event);
654 Evoral::ControlList::const_iterator i;
655 boost::shared_ptr<const Evoral::ControlList> alist (c->list());
656 Evoral::ControlEvent cp (start, 0.0f);
661 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator); i != alist->end() && (*i)->when < end; ++i) {
662 if ((*i)->when > start) {
667 if (i != alist->end() && (*i)->when < end) {
668 if ((*i)->when < next_event.when) {
669 next_event.when = (*i)->when;