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) {
369 boost::shared_ptr<AutomationControl> c
370 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
372 boost::shared_ptr<AutomationList> l
373 = boost::dynamic_pointer_cast<AutomationList>(c->list());
376 /* Stop any active touch gesture just before we mark the write pass
377 as finished. If we don't do this, the transport can end up stopped with
378 an AutomationList thinking that a touch is still in progress and,
379 when the transport is re-started, a touch will magically
380 be happening without it ever have being started in the usual way.
382 l->stop_touch (true, now);
383 l->write_pass_finished (now);
385 if (l->automation_playback()) {
386 c->set_value(c->list()->eval(now));
389 if (l->automation_state() == Write) {
390 l->set_automation_state (Touch);
397 boost::shared_ptr<Evoral::Control>
398 Automatable::control_factory(const Evoral::Parameter& param)
400 boost::shared_ptr<AutomationList> list(new AutomationList(param));
401 Evoral::Control* control = NULL;
402 ParameterDescriptor desc(param);
403 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
404 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
406 control = new MidiTrack::MidiControl(mt, param);
407 list.reset(); // No list, this is region "automation"
409 warning << "MidiCCAutomation for non-MidiTrack" << endl;
411 } else if (param.type() == PluginAutomation) {
412 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
414 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
415 control = new PluginInsert::PluginControl(pi, param, desc);
417 warning << "PluginAutomation for non-Plugin" << endl;
419 } else if (param.type() == PluginPropertyAutomation) {
420 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
422 desc = pi->plugin(0)->get_property_descriptor(param.id());
423 if (desc.datatype != Variant::NOTHING) {
424 if (!Variant::type_is_numeric(desc.datatype)) {
425 list.reset(); // Can't automate non-numeric data yet
427 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
430 warning << "PluginPropertyAutomation for non-Plugin" << endl;
432 } else if (param.type() == GainAutomation) {
433 Amp* amp = dynamic_cast<Amp*>(this);
435 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
437 warning << "GainAutomation for non-Amp" << endl;
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;
449 control = new AutomationControl(_a_session, param, desc);
452 control->set_list(list);
453 return boost::shared_ptr<Evoral::Control>(control);
456 boost::shared_ptr<AutomationControl>
457 Automatable::automation_control (const Evoral::Parameter& id, bool create)
459 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
462 boost::shared_ptr<const AutomationControl>
463 Automatable::automation_control (const Evoral::Parameter& id) const
465 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
469 Automatable::clear_controls ()
471 _control_connections.drop_connections ();
472 ControlSet::clear_controls ();
476 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
478 return ARDOUR::value_as_string(ac->desc(), ac->get_value());