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::transport_located (framepos_t now)
343 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
345 boost::shared_ptr<AutomationControl> c
346 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
348 boost::shared_ptr<AutomationList> l
349 = boost::dynamic_pointer_cast<AutomationList>(c->list());
352 l->start_write_pass (now);
359 Automatable::transport_stopped (framepos_t now)
361 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
362 boost::shared_ptr<AutomationControl> c =
363 boost::dynamic_pointer_cast<AutomationControl>(li->second);
368 boost::shared_ptr<AutomationList> l =
369 boost::dynamic_pointer_cast<AutomationList>(c->list());
374 /* Stop any active touch gesture just before we mark the write pass
375 as finished. If we don't do this, the transport can end up stopped with
376 an AutomationList thinking that a touch is still in progress and,
377 when the transport is re-started, a touch will magically
378 be happening without it ever have being started in the usual way.
380 const bool list_did_write = !l->in_new_write_pass ();
382 l->stop_touch (true, now);
384 c->commit_transaction (list_did_write);
386 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
388 if (l->automation_state () == Write) {
389 l->set_automation_state (Touch);
392 if (l->automation_playback ()) {
393 c->set_value_unchecked (c->list ()->eval (now));
398 boost::shared_ptr<Evoral::Control>
399 Automatable::control_factory(const Evoral::Parameter& param)
401 Evoral::Control* control = NULL;
402 bool make_list = true;
403 ParameterDescriptor desc(param);
404 boost::shared_ptr<AutomationList> list;
406 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
407 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
409 control = new MidiTrack::MidiControl(mt, param);
410 make_list = false; // No list, this is region "automation"
412 } else if (param.type() == PluginAutomation) {
413 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
415 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
416 control = new PluginInsert::PluginControl(pi, param, desc);
418 warning << "PluginAutomation for non-Plugin" << endl;
420 } else if (param.type() == PluginPropertyAutomation) {
421 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
423 desc = pi->plugin(0)->get_property_descriptor(param.id());
424 if (desc.datatype != Variant::NOTHING) {
425 if (!Variant::type_is_numeric(desc.datatype)) {
426 make_list = false; // Can't automate non-numeric data yet
428 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
430 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
433 warning << "PluginPropertyAutomation for non-Plugin" << endl;
435 } else if (param.type() == GainAutomation) {
436 control = new GainControl(_a_session, param);
437 } else if (param.type() == TrimAutomation) {
438 control = new GainControl(_a_session, param);
439 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
440 Pannable* pannable = dynamic_cast<Pannable*>(this);
442 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
444 warning << "PanAutomation for non-Pannable" << endl;
446 } else if (param.type() == RecEnableAutomation) {
447 Recordable* re = dynamic_cast<Recordable*> (this);
449 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
451 } else if (param.type() == MonitoringAutomation) {
452 Monitorable* m = dynamic_cast<Monitorable*>(this);
454 control = new MonitorControl (_a_session, X_("monitor"), *m);
456 } else if (param.type() == SoloAutomation) {
457 Soloable* s = dynamic_cast<Soloable*>(this);
458 Muteable* m = dynamic_cast<Muteable*>(this);
460 control = new SoloControl (_a_session, X_("solo"), *s, *m);
462 } else if (param.type() == MuteAutomation) {
463 Muteable* m = dynamic_cast<Muteable*>(this);
465 control = new MuteControl (_a_session, X_("mute"), *m);
469 if (make_list && !list) {
470 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
474 control = new AutomationControl(_a_session, param, desc, list);
477 return boost::shared_ptr<Evoral::Control>(control);
480 boost::shared_ptr<AutomationControl>
481 Automatable::automation_control (PBD::ID const & id) const
483 Controls::const_iterator li;
485 for (li = _controls.begin(); li != _controls.end(); ++li) {
486 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
487 if (ac && (ac->id() == id)) {
492 return boost::shared_ptr<AutomationControl>();
495 boost::shared_ptr<AutomationControl>
496 Automatable::automation_control (const Evoral::Parameter& id, bool create)
498 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
501 boost::shared_ptr<const AutomationControl>
502 Automatable::automation_control (const Evoral::Parameter& id) const
504 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
508 Automatable::clear_controls ()
510 _control_connections.drop_connections ();
511 ControlSet::clear_controls ();
515 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
517 Controls::const_iterator li;
519 next_event.when = std::numeric_limits<double>::max();
521 for (li = _controls.begin(); li != _controls.end(); ++li) {
522 boost::shared_ptr<AutomationControl> c
523 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
525 if (only_active && (!c || !c->automation_playback())) {
529 boost::shared_ptr<SlavableAutomationControl> sc
530 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
533 sc->find_next_event (now, end, next_event);
536 Evoral::ControlList::const_iterator i;
537 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
538 Evoral::ControlEvent cp (now, 0.0f);
543 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
544 i != alist->end() && (*i)->when < end; ++i) {
545 if ((*i)->when > now) {
550 if (i != alist->end() && (*i)->when < end) {
551 if ((*i)->when < next_event.when) {
552 next_event.when = (*i)->when;
557 return next_event.when != std::numeric_limits<double>::max();