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 const string Automatable::xml_node_name = X_("Automation");
53 Automatable::Automatable(Session& session)
58 Automatable::Automatable (const Automatable& other)
60 , _a_session (other._a_session)
62 Glib::Threads::Mutex::Lock lm (other._control_lock);
64 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
65 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
70 Automatable::~Automatable ()
72 Glib::Threads::Mutex::Lock lm (_control_lock);
73 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
74 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
79 Automatable::old_set_automation_state (const XMLNode& node)
81 XMLProperty const * prop;
83 if ((prop = node.property ("path")) != 0) {
84 load_automation (prop->value());
86 warning << _("Automation node has no path property") << endmsg;
93 Automatable::load_automation (const string& path)
97 if (Glib::path_is_absolute (path)) { // legacy
100 fullpath = _a_session.automation_dir();
104 FILE * in = g_fopen (fullpath.c_str (), "rb");
107 warning << string_compose(_("cannot open %2 to load automation data (%3)")
108 , fullpath, strerror (errno)) << endmsg;
112 Glib::Threads::Mutex::Lock lm (control_lock());
113 set<Evoral::Parameter> tosave;
121 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
128 Evoral::Parameter param(PluginAutomation, 0, port);
129 /* FIXME: this is legacy and only used for plugin inserts? I think? */
130 boost::shared_ptr<Evoral::Control> c = control (param, true);
131 c->list()->add (when, value);
132 tosave.insert (param);
139 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
146 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
148 Evoral::Parameter param = ac->parameter();
150 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
152 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
154 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
155 al->automation_state_changed.connect_same_thread (
157 boost::bind (&Automatable::automation_list_automation_state_changed,
158 this, ac->parameter(), _1));
161 ControlSet::add_control (ac);
163 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
164 _can_automate_list.insert (param);
165 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
170 Automatable::describe_parameter (Evoral::Parameter param)
172 /* derived classes like PluginInsert should override this */
174 if (param == Evoral::Parameter(GainAutomation)) {
176 } else if (param.type() == TrimAutomation) {
178 } else if (param.type() == MuteAutomation) {
180 } else if (param.type() == MidiCCAutomation) {
181 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
182 } else if (param.type() == MidiPgmChangeAutomation) {
183 return string_compose("Program [%1]", int(param.channel()) + 1);
184 } else if (param.type() == MidiPitchBenderAutomation) {
185 return string_compose("Bender [%1]", int(param.channel()) + 1);
186 } else if (param.type() == MidiChannelPressureAutomation) {
187 return string_compose("Pressure [%1]", int(param.channel()) + 1);
188 } else if (param.type() == MidiNotePressureAutomation) {
189 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
191 } else if (param.type() == PluginPropertyAutomation) {
192 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
195 return EventTypeMap::instance().to_symbol(param);
200 Automatable::can_automate (Evoral::Parameter what)
202 _can_automate_list.insert (what);
205 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
206 * had a single automation parameter, with it's type implicit. Derived objects should
207 * pass that type and it will be used for the untyped AutomationList found.
210 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
212 Glib::Threads::Mutex::Lock lm (control_lock());
214 /* Don't clear controls, since some may be special derived Controllable classes */
216 XMLNodeList nlist = node.children();
217 XMLNodeIterator niter;
219 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
221 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
222 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
226 if ((*niter)->name() == "AutomationList") {
228 XMLProperty const * id_prop = (*niter)->property("automation-id");
230 Evoral::Parameter param = (id_prop
231 ? EventTypeMap::instance().from_symbol(id_prop->value())
234 if (param.type() == NullAutomation) {
235 warning << "Automation has null type" << endl;
239 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
240 warning << "Ignored automation data for non-automatable parameter" << endl;
245 warning << "AutomationList node without automation-id property, "
246 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
249 boost::shared_ptr<AutomationControl> existing = automation_control (param);
252 existing->alist()->set_state (**niter, 3000);
254 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
255 add_control (newcontrol);
256 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
257 newcontrol->set_list(al);
261 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
269 Automatable::get_automation_xml_state ()
271 Glib::Threads::Mutex::Lock lm (control_lock());
272 XMLNode* node = new XMLNode (Automatable::xml_node_name);
274 if (controls().empty()) {
278 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
279 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
281 node->add_child_nocopy (l->get_state ());
289 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
291 Glib::Threads::Mutex::Lock lm (control_lock());
293 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
295 if (c && (s != c->automation_state())) {
296 c->set_automation_state (s);
297 _a_session.set_dirty ();
298 AutomationStateChanged(); /* Emit signal */
303 Automatable::get_parameter_automation_state (Evoral::Parameter param)
305 AutoState result = Off;
307 boost::shared_ptr<AutomationControl> c = automation_control(param);
310 result = c->automation_state();
317 Automatable::protect_automation ()
319 typedef set<Evoral::Parameter> ParameterSet;
320 const ParameterSet& automated_params = what_can_be_automated ();
322 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
324 boost::shared_ptr<Evoral::Control> c = control(*i);
325 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
327 switch (l->automation_state()) {
329 l->set_automation_state (Off);
332 l->set_automation_state (Play);
341 Automatable::non_realtime_locate (framepos_t now)
343 bool rolling = _a_session.transport_rolling ();
345 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
347 boost::shared_ptr<AutomationControl> c
348 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
350 boost::shared_ptr<AutomationList> l
351 = boost::dynamic_pointer_cast<AutomationList>(c->list());
357 bool am_touching = c->touching ();
358 if (rolling && am_touching) {
359 /* when locating while rolling, and writing automation,
360 * start a new write pass.
361 * compare to compare to non_realtime_transport_stop()
363 const bool list_did_write = !l->in_new_write_pass ();
364 c->stop_touch (-1); // time is irrelevant
366 c->commit_transaction (list_did_write);
367 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
369 if (l->automation_state () == Write) {
370 l->set_automation_state (Touch);
372 if (l->automation_playback ()) {
373 c->set_value_unchecked (c->list ()->eval (now));
377 l->start_write_pass (now);
379 if (rolling && am_touching) {
380 c->start_touch (now);
387 Automatable::non_realtime_transport_stop (framepos_t now, bool /*flush_processors*/)
389 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
390 boost::shared_ptr<AutomationControl> c =
391 boost::dynamic_pointer_cast<AutomationControl>(li->second);
396 boost::shared_ptr<AutomationList> l =
397 boost::dynamic_pointer_cast<AutomationList>(c->list());
402 /* Stop any active touch gesture just before we mark the write pass
403 as finished. If we don't do this, the transport can end up stopped with
404 an AutomationList thinking that a touch is still in progress and,
405 when the transport is re-started, a touch will magically
406 be happening without it ever have being started in the usual way.
408 const bool list_did_write = !l->in_new_write_pass ();
412 c->commit_transaction (list_did_write);
414 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
416 if (l->automation_state () == Write) {
417 l->set_automation_state (Touch);
420 if (l->automation_playback ()) {
421 c->set_value_unchecked (c->list ()->eval (now));
427 Automatable::automation_run (framepos_t start, pframes_t nframes)
429 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
430 boost::shared_ptr<AutomationControl> c =
431 boost::dynamic_pointer_cast<AutomationControl>(li->second);
435 c->automation_run (start, nframes);
439 boost::shared_ptr<Evoral::Control>
440 Automatable::control_factory(const Evoral::Parameter& param)
442 Evoral::Control* control = NULL;
443 bool make_list = true;
444 ParameterDescriptor desc(param);
445 boost::shared_ptr<AutomationList> list;
447 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
448 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
450 control = new MidiTrack::MidiControl(mt, param);
451 make_list = false; // No list, this is region "automation"
453 } else if (param.type() == PluginAutomation) {
454 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
456 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
457 control = new PluginInsert::PluginControl(pi, param, desc);
459 warning << "PluginAutomation for non-Plugin" << endl;
461 } else if (param.type() == PluginPropertyAutomation) {
462 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
464 desc = pi->plugin(0)->get_property_descriptor(param.id());
465 if (desc.datatype != Variant::NOTHING) {
466 if (!Variant::type_is_numeric(desc.datatype)) {
467 make_list = false; // Can't automate non-numeric data yet
469 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
471 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
474 warning << "PluginPropertyAutomation for non-Plugin" << endl;
476 } else if (param.type() == GainAutomation) {
477 control = new GainControl(_a_session, param);
478 } else if (param.type() == TrimAutomation) {
479 control = new GainControl(_a_session, param);
480 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
481 Pannable* pannable = dynamic_cast<Pannable*>(this);
483 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
485 warning << "PanAutomation for non-Pannable" << endl;
487 } else if (param.type() == RecEnableAutomation) {
488 Recordable* re = dynamic_cast<Recordable*> (this);
490 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
492 } else if (param.type() == MonitoringAutomation) {
493 Monitorable* m = dynamic_cast<Monitorable*>(this);
495 control = new MonitorControl (_a_session, X_("monitor"), *m);
497 } else if (param.type() == SoloAutomation) {
498 Soloable* s = dynamic_cast<Soloable*>(this);
499 Muteable* m = dynamic_cast<Muteable*>(this);
501 control = new SoloControl (_a_session, X_("solo"), *s, *m);
503 } else if (param.type() == MuteAutomation) {
504 Muteable* m = dynamic_cast<Muteable*>(this);
506 control = new MuteControl (_a_session, X_("mute"), *m);
510 if (make_list && !list) {
511 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
515 control = new AutomationControl(_a_session, param, desc, list);
518 return boost::shared_ptr<Evoral::Control>(control);
521 boost::shared_ptr<AutomationControl>
522 Automatable::automation_control (PBD::ID const & id) const
524 Controls::const_iterator li;
526 for (li = _controls.begin(); li != _controls.end(); ++li) {
527 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
528 if (ac && (ac->id() == id)) {
533 return boost::shared_ptr<AutomationControl>();
536 boost::shared_ptr<AutomationControl>
537 Automatable::automation_control (const Evoral::Parameter& id, bool create)
539 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
542 boost::shared_ptr<const AutomationControl>
543 Automatable::automation_control (const Evoral::Parameter& id) const
545 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
549 Automatable::clear_controls ()
551 _control_connections.drop_connections ();
552 ControlSet::clear_controls ();
556 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
558 Controls::const_iterator li;
560 next_event.when = std::numeric_limits<double>::max();
562 for (li = _controls.begin(); li != _controls.end(); ++li) {
563 boost::shared_ptr<AutomationControl> c
564 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
566 if (only_active && (!c || !c->automation_playback())) {
570 boost::shared_ptr<SlavableAutomationControl> sc
571 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
574 sc->find_next_event (now, end, next_event);
577 Evoral::ControlList::const_iterator i;
578 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
579 Evoral::ControlEvent cp (now, 0.0f);
584 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
585 i != alist->end() && (*i)->when < end; ++i) {
586 if ((*i)->when > now) {
591 if (i != alist->end() && (*i)->when < end) {
592 if ((*i)->when < next_event.when) {
593 next_event.when = (*i)->when;
598 return next_event.when != std::numeric_limits<double>::max();