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;
243 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
244 warning << "Ignored automation data for non-automatable parameter" << endl;
249 warning << "AutomationList node without automation-id property, "
250 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
253 boost::shared_ptr<AutomationControl> existing = automation_control (param);
256 existing->alist()->set_state (**niter, 3000);
258 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
259 add_control (newcontrol);
260 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
261 newcontrol->set_list(al);
265 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
273 Automatable::get_automation_xml_state ()
275 Glib::Threads::Mutex::Lock lm (control_lock());
276 XMLNode* node = new XMLNode (Automatable::xml_node_name);
278 if (controls().empty()) {
282 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
283 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
285 node->add_child_nocopy (l->get_state ());
293 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
295 Glib::Threads::Mutex::Lock lm (control_lock());
297 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
299 if (c && (s != c->automation_state())) {
300 c->set_automation_state (s);
301 _a_session.set_dirty ();
302 AutomationStateChanged(); /* Emit signal */
307 Automatable::get_parameter_automation_state (Evoral::Parameter param)
309 AutoState result = Off;
311 boost::shared_ptr<AutomationControl> c = automation_control(param);
314 result = c->automation_state();
321 Automatable::protect_automation ()
323 typedef set<Evoral::Parameter> ParameterSet;
324 const ParameterSet& automated_params = what_can_be_automated ();
326 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
328 boost::shared_ptr<Evoral::Control> c = control(*i);
329 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
331 switch (l->automation_state()) {
333 l->set_automation_state (Off);
338 l->set_automation_state (Play);
347 Automatable::non_realtime_locate (samplepos_t now)
349 bool rolling = _a_session.transport_rolling ();
351 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
353 boost::shared_ptr<AutomationControl> c
354 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
356 boost::shared_ptr<AutomationList> l
357 = boost::dynamic_pointer_cast<AutomationList>(c->list());
363 bool am_touching = c->touching ();
364 if (rolling && am_touching) {
365 /* when locating while rolling, and writing automation,
366 * start a new write pass.
367 * compare to compare to non_realtime_transport_stop()
369 const bool list_did_write = !l->in_new_write_pass ();
370 c->stop_touch (-1); // time is irrelevant
372 c->commit_transaction (list_did_write);
373 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
375 if (l->automation_state () == Write) {
376 l->set_automation_state (Touch);
378 if (l->automation_playback ()) {
379 c->set_value_unchecked (c->list ()->eval (now));
383 l->start_write_pass (now);
385 if (rolling && am_touching) {
386 c->start_touch (now);
393 Automatable::non_realtime_transport_stop (samplepos_t now, bool /*flush_processors*/)
395 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
396 boost::shared_ptr<AutomationControl> c =
397 boost::dynamic_pointer_cast<AutomationControl>(li->second);
402 boost::shared_ptr<AutomationList> l =
403 boost::dynamic_pointer_cast<AutomationList>(c->list());
408 /* Stop any active touch gesture just before we mark the write pass
409 as finished. If we don't do this, the transport can end up stopped with
410 an AutomationList thinking that a touch is still in progress and,
411 when the transport is re-started, a touch will magically
412 be happening without it ever have being started in the usual way.
414 const bool list_did_write = !l->in_new_write_pass ();
419 c->commit_transaction (list_did_write);
421 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
423 if (l->automation_state () == Write) {
424 l->set_automation_state (Touch);
427 if (l->automation_playback ()) {
428 c->set_value_unchecked (c->list ()->eval (now));
434 Automatable::automation_run (samplepos_t start, pframes_t nframes)
436 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
437 boost::shared_ptr<AutomationControl> c =
438 boost::dynamic_pointer_cast<AutomationControl>(li->second);
442 c->automation_run (start, nframes);
446 boost::shared_ptr<Evoral::Control>
447 Automatable::control_factory(const Evoral::Parameter& param)
449 Evoral::Control* control = NULL;
450 bool make_list = true;
451 ParameterDescriptor desc(param);
452 boost::shared_ptr<AutomationList> list;
454 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
455 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
457 control = new MidiTrack::MidiControl(mt, param);
458 make_list = false; // No list, this is region "automation"
460 } else if (param.type() == PluginAutomation) {
461 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
463 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
464 control = new PluginInsert::PluginControl(pi, param, desc);
466 warning << "PluginAutomation for non-Plugin" << endl;
468 } else if (param.type() == PluginPropertyAutomation) {
469 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
471 desc = pi->plugin(0)->get_property_descriptor(param.id());
472 if (desc.datatype != Variant::NOTHING) {
473 if (!Variant::type_is_numeric(desc.datatype)) {
474 make_list = false; // Can't automate non-numeric data yet
476 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
478 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
481 warning << "PluginPropertyAutomation for non-Plugin" << endl;
483 } else if (param.type() == GainAutomation) {
484 control = new GainControl(_a_session, param);
485 } else if (param.type() == TrimAutomation) {
486 control = new GainControl(_a_session, param);
487 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
488 Pannable* pannable = dynamic_cast<Pannable*>(this);
490 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
492 warning << "PanAutomation for non-Pannable" << endl;
494 } else if (param.type() == RecEnableAutomation) {
495 Recordable* re = dynamic_cast<Recordable*> (this);
497 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
499 } else if (param.type() == MonitoringAutomation) {
500 Monitorable* m = dynamic_cast<Monitorable*>(this);
502 control = new MonitorControl (_a_session, X_("monitor"), *m);
504 } else if (param.type() == SoloAutomation) {
505 Soloable* s = dynamic_cast<Soloable*>(this);
506 Muteable* m = dynamic_cast<Muteable*>(this);
508 control = new SoloControl (_a_session, X_("solo"), *s, *m);
510 } else if (param.type() == MuteAutomation) {
511 Muteable* m = dynamic_cast<Muteable*>(this);
513 control = new MuteControl (_a_session, X_("mute"), *m);
517 if (make_list && !list) {
518 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
522 control = new AutomationControl(_a_session, param, desc, list);
525 return boost::shared_ptr<Evoral::Control>(control);
528 boost::shared_ptr<AutomationControl>
529 Automatable::automation_control (PBD::ID const & id) const
531 Controls::const_iterator li;
533 for (li = _controls.begin(); li != _controls.end(); ++li) {
534 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
535 if (ac && (ac->id() == id)) {
540 return boost::shared_ptr<AutomationControl>();
543 boost::shared_ptr<AutomationControl>
544 Automatable::automation_control (const Evoral::Parameter& id, bool create)
546 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
549 boost::shared_ptr<const AutomationControl>
550 Automatable::automation_control (const Evoral::Parameter& id) const
552 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
556 Automatable::clear_controls ()
558 _control_connections.drop_connections ();
559 ControlSet::clear_controls ();
563 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
565 Controls::const_iterator li;
567 next_event.when = std::numeric_limits<double>::max();
569 for (li = _controls.begin(); li != _controls.end(); ++li) {
570 boost::shared_ptr<AutomationControl> c
571 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
573 if (only_active && (!c || !c->automation_playback())) {
577 boost::shared_ptr<SlavableAutomationControl> sc
578 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
581 sc->find_next_event (now, end, next_event);
584 Evoral::ControlList::const_iterator i;
585 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
586 Evoral::ControlEvent cp (now, 0.0f);
591 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
592 i != alist->end() && (*i)->when < end; ++i) {
593 if ((*i)->when > now) {
598 if (i != alist->end() && (*i)->when < end) {
599 if ((*i)->when < next_event.when) {
600 next_event.when = (*i)->when;
605 return next_event.when != std::numeric_limits<double>::max();