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.
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/midi_track.h"
32 #include "ardour/pan_controllable.h"
33 #include "ardour/pannable.h"
34 #include "ardour/plugin.h"
35 #include "ardour/plugin_insert.h"
36 #include "ardour/session.h"
37 #include "ardour/uri_map.h"
42 using namespace ARDOUR;
45 const string Automatable::xml_node_name = X_("Automation");
47 Automatable::Automatable(Session& session)
52 Automatable::Automatable (const Automatable& other)
54 , _a_session (other._a_session)
56 Glib::Threads::Mutex::Lock lm (other._control_lock);
58 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
59 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
64 Automatable::~Automatable ()
67 Glib::Threads::Mutex::Lock lm (_control_lock);
69 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
70 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
76 Automatable::old_set_automation_state (const XMLNode& node)
78 const XMLProperty *prop;
80 if ((prop = node.property ("path")) != 0) {
81 load_automation (prop->value());
83 warning << _("Automation node has no path property") << endmsg;
90 Automatable::load_automation (const string& path)
94 if (Glib::path_is_absolute (path)) { // legacy
97 fullpath = _a_session.automation_dir();
100 ifstream in (fullpath.c_str());
103 warning << string_compose(_("cannot open %2 to load automation data (%3)")
104 , fullpath, strerror (errno)) << endmsg;
108 Glib::Threads::Mutex::Lock lm (control_lock());
109 set<Evoral::Parameter> tosave;
117 in >> port; if (!in) break;
118 in >> when; if (!in) goto bad;
119 in >> value; if (!in) goto bad;
121 Evoral::Parameter param(PluginAutomation, 0, port);
122 /* FIXME: this is legacy and only used for plugin inserts? I think? */
123 boost::shared_ptr<Evoral::Control> c = control (param, true);
124 c->list()->add (when, value);
125 tosave.insert (param);
131 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
137 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
139 Evoral::Parameter param = ac->parameter();
141 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
144 al->automation_state_changed.connect_same_thread (
146 boost::bind (&Automatable::automation_list_automation_state_changed,
147 this, ac->parameter(), _1));
150 ControlSet::add_control (ac);
153 _can_automate_list.insert (param);
154 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
159 Automatable::describe_parameter (Evoral::Parameter param)
161 /* derived classes like PluginInsert should override this */
163 if (param == Evoral::Parameter(GainAutomation)) {
165 } else if (param.type() == MuteAutomation) {
167 } else if (param.type() == MidiCCAutomation) {
168 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
169 } else if (param.type() == MidiPgmChangeAutomation) {
170 return string_compose("Program [%1]", int(param.channel()) + 1);
171 } else if (param.type() == MidiPitchBenderAutomation) {
172 return string_compose("Bender [%1]", int(param.channel()) + 1);
173 } else if (param.type() == MidiChannelPressureAutomation) {
174 return string_compose("Pressure [%1]", int(param.channel()) + 1);
175 } else if (param.type() == PluginPropertyAutomation) {
176 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
178 return EventTypeMap::instance().to_symbol(param);
183 Automatable::can_automate (Evoral::Parameter what)
185 _can_automate_list.insert (what);
188 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
189 * had a single automation parameter, with it's type implicit. Derived objects should
190 * pass that type and it will be used for the untyped AutomationList found.
193 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
195 Glib::Threads::Mutex::Lock lm (control_lock());
197 /* Don't clear controls, since some may be special derived Controllable classes */
199 XMLNodeList nlist = node.children();
200 XMLNodeIterator niter;
202 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
204 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
205 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
209 if ((*niter)->name() == "AutomationList") {
211 const XMLProperty* id_prop = (*niter)->property("automation-id");
213 Evoral::Parameter param = (id_prop
214 ? EventTypeMap::instance().new_parameter(id_prop->value())
217 if (param.type() == NullAutomation) {
218 warning << "Automation has null type" << endl;
223 warning << "AutomationList node without automation-id property, "
224 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
227 boost::shared_ptr<AutomationControl> existing = automation_control (param);
230 existing->alist()->set_state (**niter, 3000);
232 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
233 add_control (newcontrol);
234 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
235 newcontrol->set_list(al);
239 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
247 Automatable::get_automation_xml_state ()
249 Glib::Threads::Mutex::Lock lm (control_lock());
250 XMLNode* node = new XMLNode (Automatable::xml_node_name);
252 if (controls().empty()) {
256 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
257 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
258 if (l && !l->empty()) {
259 node->add_child_nocopy (l->get_state ());
267 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
269 Glib::Threads::Mutex::Lock lm (control_lock());
271 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
273 if (c && (s != c->automation_state())) {
274 c->set_automation_state (s);
275 _a_session.set_dirty ();
280 Automatable::get_parameter_automation_state (Evoral::Parameter param)
282 AutoState result = Off;
284 boost::shared_ptr<AutomationControl> c = automation_control(param);
287 result = c->automation_state();
294 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
296 Glib::Threads::Mutex::Lock lm (control_lock());
298 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
300 if (c && (s != c->automation_style())) {
301 c->set_automation_style (s);
302 _a_session.set_dirty ();
307 Automatable::get_parameter_automation_style (Evoral::Parameter param)
309 Glib::Threads::Mutex::Lock lm (control_lock());
311 boost::shared_ptr<Evoral::Control> c = control(param);
312 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
315 return l->automation_style();
317 return Absolute; // whatever
322 Automatable::protect_automation ()
324 typedef set<Evoral::Parameter> ParameterSet;
325 const ParameterSet& automated_params = what_can_be_automated ();
327 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
329 boost::shared_ptr<Evoral::Control> c = control(*i);
330 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
332 switch (l->automation_state()) {
334 l->set_automation_state (Off);
337 l->set_automation_state (Play);
346 Automatable::transport_located (framepos_t now)
348 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
350 boost::shared_ptr<AutomationControl> c
351 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
353 boost::shared_ptr<AutomationList> l
354 = boost::dynamic_pointer_cast<AutomationList>(c->list());
357 l->start_write_pass (now);
364 Automatable::transport_stopped (framepos_t now)
366 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
368 boost::shared_ptr<AutomationControl> c
369 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
371 boost::shared_ptr<AutomationList> l
372 = boost::dynamic_pointer_cast<AutomationList>(c->list());
375 /* Stop any active touch gesture just before we mark the write pass
376 as finished. If we don't do this, the transport can end up stopped with
377 an AutomationList thinking that a touch is still in progress and,
378 when the transport is re-started, a touch will magically
379 be happening without it ever have being started in the usual way.
381 l->stop_touch (true, now);
382 l->write_pass_finished (now);
384 if (l->automation_playback()) {
385 c->set_value(c->list()->eval(now));
388 if (l->automation_state() == Write) {
389 l->set_automation_state (Touch);
396 boost::shared_ptr<Evoral::Control>
397 Automatable::control_factory(const Evoral::Parameter& param)
399 boost::shared_ptr<AutomationList> list(new AutomationList(param));
400 Evoral::Control* control = NULL;
401 ParameterDescriptor desc(param);
402 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
403 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
405 control = new MidiTrack::MidiControl(mt, param);
406 list.reset(); // No list, this is region "automation"
408 warning << "MidiCCAutomation for non-MidiTrack" << endl;
410 } else if (param.type() == PluginAutomation) {
411 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
413 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
414 control = new PluginInsert::PluginControl(pi, param, desc);
416 warning << "PluginAutomation for non-Plugin" << endl;
418 } else if (param.type() == PluginPropertyAutomation) {
419 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
421 desc = pi->plugin(0)->get_property_descriptor(param.id());
422 if (desc.datatype != Variant::VOID) {
423 if (!Variant::type_is_numeric(desc.datatype)) {
424 list.reset(); // Can't automate non-numeric data yet
426 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
429 warning << "PluginPropertyAutomation for non-Plugin" << endl;
431 } else if (param.type() == GainAutomation) {
432 Amp* amp = dynamic_cast<Amp*>(this);
434 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
436 warning << "GainAutomation for non-Amp" << endl;
438 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
439 Pannable* pannable = dynamic_cast<Pannable*>(this);
441 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
443 warning << "PanAutomation for non-Pannable" << endl;
448 control = new AutomationControl(_a_session, param, desc);
451 control->set_list(list);
452 return boost::shared_ptr<Evoral::Control>(control);
455 boost::shared_ptr<AutomationControl>
456 Automatable::automation_control (const Evoral::Parameter& id, bool create)
458 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
461 boost::shared_ptr<const AutomationControl>
462 Automatable::automation_control (const Evoral::Parameter& id) const
464 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
468 Automatable::clear_controls ()
470 _control_connections.drop_connections ();
471 ControlSet::clear_controls ();
475 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
479 /* this is a the default fallback for this virtual method. Derived Automatables
480 are free to override this to display the values of their parameters/controls
484 // Hack to display CC as integer value, rather than double
485 if (ac->parameter().type() == MidiCCAutomation) {
486 s << lrint (ac->get_value());
488 s << std::fixed << std::setprecision(3) << ac->get_value();