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"
38 #include "ardour/value_as_string.h"
43 using namespace ARDOUR;
46 const string Automatable::xml_node_name = X_("Automation");
48 Automatable::Automatable(Session& session)
53 Automatable::Automatable (const Automatable& other)
55 , _a_session (other._a_session)
57 Glib::Threads::Mutex::Lock lm (other._control_lock);
59 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
60 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
65 Automatable::~Automatable ()
68 Glib::Threads::Mutex::Lock lm (_control_lock);
70 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
71 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
77 Automatable::old_set_automation_state (const XMLNode& node)
79 const XMLProperty *prop;
81 if ((prop = node.property ("path")) != 0) {
82 load_automation (prop->value());
84 warning << _("Automation node has no path property") << endmsg;
91 Automatable::load_automation (const string& path)
95 if (Glib::path_is_absolute (path)) { // legacy
98 fullpath = _a_session.automation_dir();
101 ifstream in (fullpath.c_str());
104 warning << string_compose(_("cannot open %2 to load automation data (%3)")
105 , fullpath, strerror (errno)) << endmsg;
109 Glib::Threads::Mutex::Lock lm (control_lock());
110 set<Evoral::Parameter> tosave;
118 in >> port; if (!in) break;
119 in >> when; if (!in) goto bad;
120 in >> value; if (!in) goto bad;
122 Evoral::Parameter param(PluginAutomation, 0, port);
123 /* FIXME: this is legacy and only used for plugin inserts? I think? */
124 boost::shared_ptr<Evoral::Control> c = control (param, true);
125 c->list()->add (when, value);
126 tosave.insert (param);
132 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
138 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
140 Evoral::Parameter param = ac->parameter();
142 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
145 al->automation_state_changed.connect_same_thread (
147 boost::bind (&Automatable::automation_list_automation_state_changed,
148 this, ac->parameter(), _1));
151 ControlSet::add_control (ac);
154 _can_automate_list.insert (param);
155 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
160 Automatable::describe_parameter (Evoral::Parameter param)
162 /* derived classes like PluginInsert should override this */
164 if (param == Evoral::Parameter(GainAutomation)) {
166 } else if (param.type() == MuteAutomation) {
168 } else if (param.type() == MidiCCAutomation) {
169 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
170 } else if (param.type() == MidiPgmChangeAutomation) {
171 return string_compose("Program [%1]", int(param.channel()) + 1);
172 } else if (param.type() == MidiPitchBenderAutomation) {
173 return string_compose("Bender [%1]", int(param.channel()) + 1);
174 } else if (param.type() == MidiChannelPressureAutomation) {
175 return string_compose("Pressure [%1]", int(param.channel()) + 1);
176 } else if (param.type() == PluginPropertyAutomation) {
177 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
179 return EventTypeMap::instance().to_symbol(param);
184 Automatable::can_automate (Evoral::Parameter what)
186 _can_automate_list.insert (what);
189 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
190 * had a single automation parameter, with it's type implicit. Derived objects should
191 * pass that type and it will be used for the untyped AutomationList found.
194 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
196 Glib::Threads::Mutex::Lock lm (control_lock());
198 /* Don't clear controls, since some may be special derived Controllable classes */
200 XMLNodeList nlist = node.children();
201 XMLNodeIterator niter;
203 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
205 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
206 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
210 if ((*niter)->name() == "AutomationList") {
212 const XMLProperty* id_prop = (*niter)->property("automation-id");
214 Evoral::Parameter param = (id_prop
215 ? EventTypeMap::instance().new_parameter(id_prop->value())
218 if (param.type() == NullAutomation) {
219 warning << "Automation has null type" << endl;
224 warning << "AutomationList node without automation-id property, "
225 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
228 boost::shared_ptr<AutomationControl> existing = automation_control (param);
231 existing->alist()->set_state (**niter, 3000);
233 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
234 add_control (newcontrol);
235 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
236 newcontrol->set_list(al);
240 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
248 Automatable::get_automation_xml_state ()
250 Glib::Threads::Mutex::Lock lm (control_lock());
251 XMLNode* node = new XMLNode (Automatable::xml_node_name);
253 if (controls().empty()) {
257 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
258 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
259 if (l && !l->empty()) {
260 node->add_child_nocopy (l->get_state ());
268 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
270 Glib::Threads::Mutex::Lock lm (control_lock());
272 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
274 if (c && (s != c->automation_state())) {
275 c->set_automation_state (s);
276 _a_session.set_dirty ();
281 Automatable::get_parameter_automation_state (Evoral::Parameter param)
283 AutoState result = Off;
285 boost::shared_ptr<AutomationControl> c = automation_control(param);
288 result = c->automation_state();
295 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
297 Glib::Threads::Mutex::Lock lm (control_lock());
299 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
301 if (c && (s != c->automation_style())) {
302 c->set_automation_style (s);
303 _a_session.set_dirty ();
308 Automatable::get_parameter_automation_style (Evoral::Parameter param)
310 Glib::Threads::Mutex::Lock lm (control_lock());
312 boost::shared_ptr<Evoral::Control> c = control(param);
313 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
316 return l->automation_style();
318 return Absolute; // whatever
323 Automatable::protect_automation ()
325 typedef set<Evoral::Parameter> ParameterSet;
326 const ParameterSet& automated_params = what_can_be_automated ();
328 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
330 boost::shared_ptr<Evoral::Control> c = control(*i);
331 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
333 switch (l->automation_state()) {
335 l->set_automation_state (Off);
338 l->set_automation_state (Play);
347 Automatable::transport_located (framepos_t now)
349 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
351 boost::shared_ptr<AutomationControl> c
352 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
354 boost::shared_ptr<AutomationList> l
355 = boost::dynamic_pointer_cast<AutomationList>(c->list());
358 l->start_write_pass (now);
365 Automatable::transport_stopped (framepos_t now)
367 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
368 boost::shared_ptr<AutomationControl> c =
369 boost::dynamic_pointer_cast<AutomationControl>(li->second);
374 boost::shared_ptr<AutomationList> l =
375 boost::dynamic_pointer_cast<AutomationList>(c->list());
380 /* Stop any active touch gesture just before we mark the write pass
381 as finished. If we don't do this, the transport can end up stopped with
382 an AutomationList thinking that a touch is still in progress and,
383 when the transport is re-started, a touch will magically
384 be happening without it ever have being started in the usual way.
386 l->stop_touch (true, now);
387 l->write_pass_finished (now, Config->get_automation_thinning_factor());
389 if (l->automation_playback()) {
390 c->set_value(c->list()->eval(now));
393 if (l->automation_state() == Write) {
394 l->set_automation_state (Touch);
399 boost::shared_ptr<Evoral::Control>
400 Automatable::control_factory(const Evoral::Parameter& param)
402 boost::shared_ptr<AutomationList> list(new AutomationList(param));
403 Evoral::Control* control = NULL;
404 ParameterDescriptor desc(param);
405 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
406 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
408 control = new MidiTrack::MidiControl(mt, param);
409 list.reset(); // No list, this is region "automation"
411 warning << "MidiCCAutomation for non-MidiTrack" << endl;
413 } else if (param.type() == PluginAutomation) {
414 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
416 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
417 control = new PluginInsert::PluginControl(pi, param, desc);
419 warning << "PluginAutomation for non-Plugin" << endl;
421 } else if (param.type() == PluginPropertyAutomation) {
422 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
424 desc = pi->plugin(0)->get_property_descriptor(param.id());
425 if (desc.datatype != Variant::NOTHING) {
426 if (!Variant::type_is_numeric(desc.datatype)) {
427 list.reset(); // Can't automate non-numeric data yet
429 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
432 warning << "PluginPropertyAutomation for non-Plugin" << endl;
434 } else if (param.type() == GainAutomation) {
435 Amp* amp = dynamic_cast<Amp*>(this);
437 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
439 warning << "GainAutomation for non-Amp" << endl;
441 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
442 Pannable* pannable = dynamic_cast<Pannable*>(this);
444 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
446 warning << "PanAutomation for non-Pannable" << endl;
451 control = new AutomationControl(_a_session, param, desc);
454 control->set_list(list);
455 return boost::shared_ptr<Evoral::Control>(control);
458 boost::shared_ptr<AutomationControl>
459 Automatable::automation_control (const Evoral::Parameter& id, bool create)
461 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
464 boost::shared_ptr<const AutomationControl>
465 Automatable::automation_control (const Evoral::Parameter& id) const
467 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
471 Automatable::clear_controls ()
473 _control_connections.drop_connections ();
474 ControlSet::clear_controls ();
478 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
480 return ARDOUR::value_as_string(ac->desc(), ac->get_value());