2 Copyright (C) 2001,2007 Paul Davis
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.
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.
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.
23 #include "pbd/gstdio_compat.h"
24 #include <glibmm/miscutils.h>
26 #include "pbd/error.h"
28 #include "ardour/amp.h"
29 #include "ardour/automatable.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/gain_control.h"
32 #include "ardour/monitor_control.h"
33 #include "ardour/midi_track.h"
34 #include "ardour/pan_controllable.h"
35 #include "ardour/pannable.h"
36 #include "ardour/plugin.h"
37 #include "ardour/plugin_insert.h"
38 #include "ardour/record_enable_control.h"
39 #include "ardour/session.h"
41 #include "ardour/uri_map.h"
43 #include "ardour/value_as_string.h"
48 using namespace ARDOUR;
51 /* used for templates (previously: !full_state) */
52 bool Automatable::skip_saving_automation = false;
54 const string Automatable::xml_node_name = X_("Automation");
56 Automatable::Automatable(Session& session)
58 , _automated_controls (new ControlList)
62 Automatable::Automatable (const Automatable& other)
65 , _a_session (other._a_session)
66 , _automated_controls (new ControlList)
68 Glib::Threads::Mutex::Lock lm (other._control_lock);
70 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
71 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
76 Automatable::~Automatable ()
79 RCUWriter<ControlList> writer (_automated_controls);
80 boost::shared_ptr<ControlList> cl = writer.get_copy ();
83 _automated_controls.flush ();
85 Glib::Threads::Mutex::Lock lm (_control_lock);
86 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
87 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
92 Automatable::old_set_automation_state (const XMLNode& node)
94 XMLProperty const * prop;
96 if ((prop = node.property ("path")) != 0) {
97 load_automation (prop->value());
99 warning << _("Automation node has no path property") << endmsg;
106 Automatable::load_automation (const string& path)
110 if (Glib::path_is_absolute (path)) { // legacy
113 fullpath = _a_session.automation_dir();
117 FILE * in = g_fopen (fullpath.c_str (), "rb");
120 warning << string_compose(_("cannot open %2 to load automation data (%3)")
121 , fullpath, strerror (errno)) << endmsg;
125 Glib::Threads::Mutex::Lock lm (control_lock());
126 set<Evoral::Parameter> tosave;
134 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
141 Evoral::Parameter param(PluginAutomation, 0, port);
142 /* FIXME: this is legacy and only used for plugin inserts? I think? */
143 boost::shared_ptr<Evoral::Control> c = control (param, true);
144 c->list()->add (when, value);
145 tosave.insert (param);
152 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
159 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
161 Evoral::Parameter param = ac->parameter();
163 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
165 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
167 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
168 al->automation_state_changed.connect_same_thread (
170 boost::bind (&Automatable::automation_list_automation_state_changed,
171 this, ac->parameter(), _1));
174 ControlSet::add_control (ac);
176 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
177 _can_automate_list.insert (param);
178 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
183 Automatable::describe_parameter (Evoral::Parameter param)
185 /* derived classes like PluginInsert should override this */
187 if (param == Evoral::Parameter(GainAutomation)) {
189 } else if (param.type() == TrimAutomation) {
191 } else if (param.type() == MuteAutomation) {
193 } else if (param.type() == MidiCCAutomation) {
194 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
195 } else if (param.type() == MidiPgmChangeAutomation) {
196 return string_compose("Program [%1]", int(param.channel()) + 1);
197 } else if (param.type() == MidiPitchBenderAutomation) {
198 return string_compose("Bender [%1]", int(param.channel()) + 1);
199 } else if (param.type() == MidiChannelPressureAutomation) {
200 return string_compose("Pressure [%1]", int(param.channel()) + 1);
201 } else if (param.type() == MidiNotePressureAutomation) {
202 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
204 } else if (param.type() == PluginPropertyAutomation) {
205 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
208 return EventTypeMap::instance().to_symbol(param);
213 Automatable::can_automate (Evoral::Parameter what)
215 _can_automate_list.insert (what);
218 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
219 * had a single automation parameter, with it's type implicit. Derived objects should
220 * pass that type and it will be used for the untyped AutomationList found.
223 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
225 Glib::Threads::Mutex::Lock lm (control_lock());
227 /* Don't clear controls, since some may be special derived Controllable classes */
229 XMLNodeList nlist = node.children();
230 XMLNodeIterator niter;
232 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
234 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
235 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
239 if ((*niter)->name() == "AutomationList") {
241 XMLProperty const * id_prop = (*niter)->property("automation-id");
243 Evoral::Parameter param = (id_prop
244 ? EventTypeMap::instance().from_symbol(id_prop->value())
247 if (param.type() == NullAutomation) {
248 warning << "Automation has null type" << endl;
253 warning << "AutomationList node without automation-id property, "
254 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
257 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
258 boost::shared_ptr<AutomationControl> actl = automation_control (param);
259 if (actl && (*niter)->children().size() > 0 && Config->get_limit_n_automatables () > 0) {
260 actl->set_flags (Controllable::Flag ((int)actl->flags() & ~Controllable::NotAutomatable));
261 can_automate (param);
262 info << "Marked parmater as automatable" << endl;
264 warning << "Ignored automation data for non-automatable parameter" << endl;
270 boost::shared_ptr<AutomationControl> existing = automation_control (param);
273 existing->alist()->set_state (**niter, 3000);
275 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
276 add_control (newcontrol);
277 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
278 newcontrol->set_list(al);
282 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
290 Automatable::get_automation_xml_state ()
292 Glib::Threads::Mutex::Lock lm (control_lock());
293 XMLNode* node = new XMLNode (Automatable::xml_node_name);
295 if (controls().empty()) {
299 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
300 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
302 node->add_child_nocopy (l->get_state ());
310 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
312 Glib::Threads::Mutex::Lock lm (control_lock());
314 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
316 if (c && (s != c->automation_state())) {
317 c->set_automation_state (s);
318 _a_session.set_dirty ();
319 AutomationStateChanged(); /* Emit signal */
324 Automatable::get_parameter_automation_state (Evoral::Parameter param)
326 AutoState result = Off;
328 boost::shared_ptr<AutomationControl> c = automation_control(param);
331 result = c->automation_state();
338 Automatable::protect_automation ()
340 typedef set<Evoral::Parameter> ParameterSet;
341 const ParameterSet& automated_params = what_can_be_automated ();
343 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
345 boost::shared_ptr<Evoral::Control> c = control(*i);
346 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
348 switch (l->automation_state()) {
350 l->set_automation_state (Off);
355 l->set_automation_state (Play);
364 Automatable::non_realtime_locate (samplepos_t now)
366 bool rolling = _a_session.transport_rolling ();
368 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
370 boost::shared_ptr<AutomationControl> c
371 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
373 boost::shared_ptr<AutomationList> l
374 = boost::dynamic_pointer_cast<AutomationList>(c->list());
380 bool am_touching = c->touching ();
381 if (rolling && am_touching) {
382 /* when locating while rolling, and writing automation,
383 * start a new write pass.
384 * compare to compare to non_realtime_transport_stop()
386 const bool list_did_write = !l->in_new_write_pass ();
387 c->stop_touch (-1); // time is irrelevant
389 c->commit_transaction (list_did_write);
390 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
392 if (l->automation_state () == Write) {
393 l->set_automation_state (Touch);
395 if (l->automation_playback ()) {
396 c->set_value_unchecked (c->list ()->eval (now));
400 l->start_write_pass (now);
402 if (rolling && am_touching) {
403 c->start_touch (now);
410 Automatable::non_realtime_transport_stop (samplepos_t now, bool /*flush_processors*/)
412 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
413 boost::shared_ptr<AutomationControl> c =
414 boost::dynamic_pointer_cast<AutomationControl>(li->second);
419 boost::shared_ptr<AutomationList> l =
420 boost::dynamic_pointer_cast<AutomationList>(c->list());
425 /* Stop any active touch gesture just before we mark the write pass
426 as finished. If we don't do this, the transport can end up stopped with
427 an AutomationList thinking that a touch is still in progress and,
428 when the transport is re-started, a touch will magically
429 be happening without it ever have being started in the usual way.
431 const bool list_did_write = !l->in_new_write_pass ();
436 c->commit_transaction (list_did_write);
438 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
440 if (l->automation_state () == Write) {
441 l->set_automation_state (Touch);
444 if (l->automation_playback ()) {
445 c->set_value_unchecked (c->list ()->eval (now));
451 Automatable::automation_run (samplepos_t start, pframes_t nframes, bool only_active)
454 boost::shared_ptr<ControlList> cl = _automated_controls.reader ();
455 for (ControlList::const_iterator ci = cl->begin(); ci != cl->end(); ++ci) {
456 (*ci)->automation_run (start, nframes);
461 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
462 boost::shared_ptr<AutomationControl> c =
463 boost::dynamic_pointer_cast<AutomationControl>(li->second);
467 c->automation_run (start, nframes);
472 Automatable::automation_list_automation_state_changed (Evoral::Parameter param, AutoState as)
475 boost::shared_ptr<AutomationControl> c (automation_control(param));
476 assert (c && c->list());
478 RCUWriter<ControlList> writer (_automated_controls);
479 boost::shared_ptr<ControlList> cl = writer.get_copy ();
481 ControlList::const_iterator fi = std::find (cl->begin(), cl->end(), c);
482 if (fi != cl->end()) {
486 /* all potential automation_playback() states */
497 _automated_controls.flush();
500 boost::shared_ptr<Evoral::Control>
501 Automatable::control_factory(const Evoral::Parameter& param)
503 Evoral::Control* control = NULL;
504 bool make_list = true;
505 ParameterDescriptor desc(param);
506 boost::shared_ptr<AutomationList> list;
508 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
509 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
511 control = new MidiTrack::MidiControl(mt, param);
512 make_list = false; // No list, this is region "automation"
514 } else if (param.type() == PluginAutomation) {
515 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
517 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
518 control = new PluginInsert::PluginControl(pi, param, desc);
520 warning << "PluginAutomation for non-Plugin" << endl;
522 } else if (param.type() == PluginPropertyAutomation) {
523 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
525 desc = pi->plugin(0)->get_property_descriptor(param.id());
526 if (desc.datatype != Variant::NOTHING) {
527 if (!Variant::type_is_numeric(desc.datatype)) {
528 make_list = false; // Can't automate non-numeric data yet
530 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
532 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
535 warning << "PluginPropertyAutomation for non-Plugin" << endl;
537 } else if (param.type() == GainAutomation) {
538 control = new GainControl(_a_session, param);
539 } else if (param.type() == TrimAutomation) {
540 control = new GainControl(_a_session, param);
541 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
542 Pannable* pannable = dynamic_cast<Pannable*>(this);
544 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
546 warning << "PanAutomation for non-Pannable" << endl;
548 } else if (param.type() == RecEnableAutomation) {
549 Recordable* re = dynamic_cast<Recordable*> (this);
551 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
553 } else if (param.type() == MonitoringAutomation) {
554 Monitorable* m = dynamic_cast<Monitorable*>(this);
556 control = new MonitorControl (_a_session, X_("monitor"), *m);
558 } else if (param.type() == SoloAutomation) {
559 Soloable* s = dynamic_cast<Soloable*>(this);
560 Muteable* m = dynamic_cast<Muteable*>(this);
562 control = new SoloControl (_a_session, X_("solo"), *s, *m);
564 } else if (param.type() == MuteAutomation) {
565 Muteable* m = dynamic_cast<Muteable*>(this);
567 control = new MuteControl (_a_session, X_("mute"), *m);
571 if (make_list && !list) {
572 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
576 control = new AutomationControl(_a_session, param, desc, list);
579 return boost::shared_ptr<Evoral::Control>(control);
582 boost::shared_ptr<AutomationControl>
583 Automatable::automation_control (PBD::ID const & id) const
585 Controls::const_iterator li;
587 for (li = _controls.begin(); li != _controls.end(); ++li) {
588 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
589 if (ac && (ac->id() == id)) {
594 return boost::shared_ptr<AutomationControl>();
597 boost::shared_ptr<AutomationControl>
598 Automatable::automation_control (const Evoral::Parameter& id, bool create)
600 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
603 boost::shared_ptr<const AutomationControl>
604 Automatable::automation_control (const Evoral::Parameter& id) const
606 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
610 Automatable::clear_controls ()
612 _control_connections.drop_connections ();
613 ControlSet::clear_controls ();
617 Automatable::find_next_event (double start, double end, Evoral::ControlEvent& next_event, bool only_active) const
619 next_event.when = std::numeric_limits<double>::max();
622 boost::shared_ptr<ControlList> cl = _automated_controls.reader ();
623 for (ControlList::const_iterator ci = cl->begin(); ci != cl->end(); ++ci) {
624 if ((*ci)->automation_playback()) {
625 find_next_ac_event (*ci, start, end, next_event);
629 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
630 boost::shared_ptr<AutomationControl> c
631 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
633 find_next_ac_event (c, start, end, next_event);
637 return next_event.when != std::numeric_limits<double>::max();
641 Automatable::find_next_ac_event (boost::shared_ptr<AutomationControl> c, double start, double end, Evoral::ControlEvent& next_event) const
643 boost::shared_ptr<SlavableAutomationControl> sc
644 = boost::dynamic_pointer_cast<SlavableAutomationControl>(c);
647 sc->find_next_event (start, end, next_event);
650 Evoral::ControlList::const_iterator i;
651 boost::shared_ptr<const Evoral::ControlList> alist (c->list());
652 Evoral::ControlEvent cp (start, 0.0f);
657 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator); i != alist->end() && (*i)->when < end; ++i) {
658 if ((*i)->when > start) {
663 if (i != alist->end() && (*i)->when < end) {
664 if ((*i)->when < next_event.when) {
665 next_event.when = (*i)->when;