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)
61 Automatable::Automatable (const Automatable& other)
64 , _a_session (other._a_session)
66 Glib::Threads::Mutex::Lock lm (other._control_lock);
68 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
69 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
74 Automatable::~Automatable ()
76 Glib::Threads::Mutex::Lock lm (_control_lock);
77 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
78 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
83 Automatable::old_set_automation_state (const XMLNode& node)
85 XMLProperty const * prop;
87 if ((prop = node.property ("path")) != 0) {
88 load_automation (prop->value());
90 warning << _("Automation node has no path property") << endmsg;
97 Automatable::load_automation (const string& path)
101 if (Glib::path_is_absolute (path)) { // legacy
104 fullpath = _a_session.automation_dir();
108 FILE * in = g_fopen (fullpath.c_str (), "rb");
111 warning << string_compose(_("cannot open %2 to load automation data (%3)")
112 , fullpath, strerror (errno)) << endmsg;
116 Glib::Threads::Mutex::Lock lm (control_lock());
117 set<Evoral::Parameter> tosave;
125 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
132 Evoral::Parameter param(PluginAutomation, 0, port);
133 /* FIXME: this is legacy and only used for plugin inserts? I think? */
134 boost::shared_ptr<Evoral::Control> c = control (param, true);
135 c->list()->add (when, value);
136 tosave.insert (param);
143 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
150 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
152 Evoral::Parameter param = ac->parameter();
154 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
156 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
158 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
159 al->automation_state_changed.connect_same_thread (
161 boost::bind (&Automatable::automation_list_automation_state_changed,
162 this, ac->parameter(), _1));
165 ControlSet::add_control (ac);
167 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
168 _can_automate_list.insert (param);
169 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
174 Automatable::describe_parameter (Evoral::Parameter param)
176 /* derived classes like PluginInsert should override this */
178 if (param == Evoral::Parameter(GainAutomation)) {
180 } else if (param.type() == TrimAutomation) {
182 } else if (param.type() == MuteAutomation) {
184 } else if (param.type() == MidiCCAutomation) {
185 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
186 } else if (param.type() == MidiPgmChangeAutomation) {
187 return string_compose("Program [%1]", int(param.channel()) + 1);
188 } else if (param.type() == MidiPitchBenderAutomation) {
189 return string_compose("Bender [%1]", int(param.channel()) + 1);
190 } else if (param.type() == MidiChannelPressureAutomation) {
191 return string_compose("Pressure [%1]", int(param.channel()) + 1);
192 } else if (param.type() == MidiNotePressureAutomation) {
193 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
195 } else if (param.type() == PluginPropertyAutomation) {
196 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
199 return EventTypeMap::instance().to_symbol(param);
204 Automatable::can_automate (Evoral::Parameter what)
206 _can_automate_list.insert (what);
209 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
210 * had a single automation parameter, with it's type implicit. Derived objects should
211 * pass that type and it will be used for the untyped AutomationList found.
214 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
216 Glib::Threads::Mutex::Lock lm (control_lock());
218 /* Don't clear controls, since some may be special derived Controllable classes */
220 XMLNodeList nlist = node.children();
221 XMLNodeIterator niter;
223 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
225 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
226 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
230 if ((*niter)->name() == "AutomationList") {
232 XMLProperty const * id_prop = (*niter)->property("automation-id");
234 Evoral::Parameter param = (id_prop
235 ? EventTypeMap::instance().from_symbol(id_prop->value())
238 if (param.type() == NullAutomation) {
239 warning << "Automation has null type" << endl;
244 warning << "AutomationList node without automation-id property, "
245 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
248 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
249 boost::shared_ptr<AutomationControl> actl = automation_control (param);
250 if (actl && (*niter)->children().size() > 0 && Config->get_limit_n_automatables () > 0) {
251 actl->set_flags (Controllable::Flag ((int)actl->flags() & ~Controllable::NotAutomatable));
252 can_automate (param);
253 info << "Marked parmater as automatable" << endl;
255 warning << "Ignored automation data for non-automatable parameter" << endl;
261 boost::shared_ptr<AutomationControl> existing = automation_control (param);
264 existing->alist()->set_state (**niter, 3000);
266 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
267 add_control (newcontrol);
268 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
269 newcontrol->set_list(al);
273 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
281 Automatable::get_automation_xml_state ()
283 Glib::Threads::Mutex::Lock lm (control_lock());
284 XMLNode* node = new XMLNode (Automatable::xml_node_name);
286 if (controls().empty()) {
290 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
291 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
293 node->add_child_nocopy (l->get_state ());
301 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
303 Glib::Threads::Mutex::Lock lm (control_lock());
305 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
307 if (c && (s != c->automation_state())) {
308 c->set_automation_state (s);
309 _a_session.set_dirty ();
310 AutomationStateChanged(); /* Emit signal */
315 Automatable::get_parameter_automation_state (Evoral::Parameter param)
317 AutoState result = Off;
319 boost::shared_ptr<AutomationControl> c = automation_control(param);
322 result = c->automation_state();
329 Automatable::protect_automation ()
331 typedef set<Evoral::Parameter> ParameterSet;
332 const ParameterSet& automated_params = what_can_be_automated ();
334 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
336 boost::shared_ptr<Evoral::Control> c = control(*i);
337 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
339 switch (l->automation_state()) {
341 l->set_automation_state (Off);
346 l->set_automation_state (Play);
355 Automatable::non_realtime_locate (samplepos_t now)
357 bool rolling = _a_session.transport_rolling ();
359 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
361 boost::shared_ptr<AutomationControl> c
362 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
364 boost::shared_ptr<AutomationList> l
365 = boost::dynamic_pointer_cast<AutomationList>(c->list());
371 bool am_touching = c->touching ();
372 if (rolling && am_touching) {
373 /* when locating while rolling, and writing automation,
374 * start a new write pass.
375 * compare to compare to non_realtime_transport_stop()
377 const bool list_did_write = !l->in_new_write_pass ();
378 c->stop_touch (-1); // time is irrelevant
380 c->commit_transaction (list_did_write);
381 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
383 if (l->automation_state () == Write) {
384 l->set_automation_state (Touch);
386 if (l->automation_playback ()) {
387 c->set_value_unchecked (c->list ()->eval (now));
391 l->start_write_pass (now);
393 if (rolling && am_touching) {
394 c->start_touch (now);
401 Automatable::non_realtime_transport_stop (samplepos_t now, bool /*flush_processors*/)
403 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
404 boost::shared_ptr<AutomationControl> c =
405 boost::dynamic_pointer_cast<AutomationControl>(li->second);
410 boost::shared_ptr<AutomationList> l =
411 boost::dynamic_pointer_cast<AutomationList>(c->list());
416 /* Stop any active touch gesture just before we mark the write pass
417 as finished. If we don't do this, the transport can end up stopped with
418 an AutomationList thinking that a touch is still in progress and,
419 when the transport is re-started, a touch will magically
420 be happening without it ever have being started in the usual way.
422 const bool list_did_write = !l->in_new_write_pass ();
427 c->commit_transaction (list_did_write);
429 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
431 if (l->automation_state () == Write) {
432 l->set_automation_state (Touch);
435 if (l->automation_playback ()) {
436 c->set_value_unchecked (c->list ()->eval (now));
442 Automatable::automation_run (samplepos_t start, pframes_t nframes)
444 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
445 boost::shared_ptr<AutomationControl> c =
446 boost::dynamic_pointer_cast<AutomationControl>(li->second);
450 c->automation_run (start, nframes);
454 boost::shared_ptr<Evoral::Control>
455 Automatable::control_factory(const Evoral::Parameter& param)
457 Evoral::Control* control = NULL;
458 bool make_list = true;
459 ParameterDescriptor desc(param);
460 boost::shared_ptr<AutomationList> list;
462 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
463 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
465 control = new MidiTrack::MidiControl(mt, param);
466 make_list = false; // No list, this is region "automation"
468 } else if (param.type() == PluginAutomation) {
469 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
471 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
472 control = new PluginInsert::PluginControl(pi, param, desc);
474 warning << "PluginAutomation for non-Plugin" << endl;
476 } else if (param.type() == PluginPropertyAutomation) {
477 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
479 desc = pi->plugin(0)->get_property_descriptor(param.id());
480 if (desc.datatype != Variant::NOTHING) {
481 if (!Variant::type_is_numeric(desc.datatype)) {
482 make_list = false; // Can't automate non-numeric data yet
484 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
486 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
489 warning << "PluginPropertyAutomation for non-Plugin" << endl;
491 } else if (param.type() == GainAutomation) {
492 control = new GainControl(_a_session, param);
493 } else if (param.type() == TrimAutomation) {
494 control = new GainControl(_a_session, param);
495 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
496 Pannable* pannable = dynamic_cast<Pannable*>(this);
498 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
500 warning << "PanAutomation for non-Pannable" << endl;
502 } else if (param.type() == RecEnableAutomation) {
503 Recordable* re = dynamic_cast<Recordable*> (this);
505 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
507 } else if (param.type() == MonitoringAutomation) {
508 Monitorable* m = dynamic_cast<Monitorable*>(this);
510 control = new MonitorControl (_a_session, X_("monitor"), *m);
512 } else if (param.type() == SoloAutomation) {
513 Soloable* s = dynamic_cast<Soloable*>(this);
514 Muteable* m = dynamic_cast<Muteable*>(this);
516 control = new SoloControl (_a_session, X_("solo"), *s, *m);
518 } else if (param.type() == MuteAutomation) {
519 Muteable* m = dynamic_cast<Muteable*>(this);
521 control = new MuteControl (_a_session, X_("mute"), *m);
525 if (make_list && !list) {
526 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
530 control = new AutomationControl(_a_session, param, desc, list);
533 return boost::shared_ptr<Evoral::Control>(control);
536 boost::shared_ptr<AutomationControl>
537 Automatable::automation_control (PBD::ID const & id) const
539 Controls::const_iterator li;
541 for (li = _controls.begin(); li != _controls.end(); ++li) {
542 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
543 if (ac && (ac->id() == id)) {
548 return boost::shared_ptr<AutomationControl>();
551 boost::shared_ptr<AutomationControl>
552 Automatable::automation_control (const Evoral::Parameter& id, bool create)
554 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
557 boost::shared_ptr<const AutomationControl>
558 Automatable::automation_control (const Evoral::Parameter& id) const
560 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
564 Automatable::clear_controls ()
566 _control_connections.drop_connections ();
567 ControlSet::clear_controls ();
571 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
573 Controls::const_iterator li;
575 next_event.when = std::numeric_limits<double>::max();
577 for (li = _controls.begin(); li != _controls.end(); ++li) {
578 boost::shared_ptr<AutomationControl> c
579 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
581 if (only_active && (!c || !c->automation_playback())) {
585 boost::shared_ptr<SlavableAutomationControl> sc
586 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
589 sc->find_next_event (now, end, next_event);
592 Evoral::ControlList::const_iterator i;
593 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
594 Evoral::ControlEvent cp (now, 0.0f);
599 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
600 i != alist->end() && (*i)->when < end; ++i) {
601 if ((*i)->when > now) {
606 if (i != alist->end() && (*i)->when < end) {
607 if ((*i)->when < next_event.when) {
608 next_event.when = (*i)->when;
613 return next_event.when != std::numeric_limits<double>::max();