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);
177 } else if (param.type() == PluginPropertyAutomation) {
178 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
181 return EventTypeMap::instance().to_symbol(param);
186 Automatable::can_automate (Evoral::Parameter what)
188 _can_automate_list.insert (what);
191 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
192 * had a single automation parameter, with it's type implicit. Derived objects should
193 * pass that type and it will be used for the untyped AutomationList found.
196 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
198 Glib::Threads::Mutex::Lock lm (control_lock());
200 /* Don't clear controls, since some may be special derived Controllable classes */
202 XMLNodeList nlist = node.children();
203 XMLNodeIterator niter;
205 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
207 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
208 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
212 if ((*niter)->name() == "AutomationList") {
214 const XMLProperty* id_prop = (*niter)->property("automation-id");
216 Evoral::Parameter param = (id_prop
217 ? EventTypeMap::instance().from_symbol(id_prop->value())
220 if (param.type() == NullAutomation) {
221 warning << "Automation has null type" << endl;
226 warning << "AutomationList node without automation-id property, "
227 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
230 boost::shared_ptr<AutomationControl> existing = automation_control (param);
233 existing->alist()->set_state (**niter, 3000);
235 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
236 add_control (newcontrol);
237 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
238 newcontrol->set_list(al);
242 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
250 Automatable::get_automation_xml_state ()
252 Glib::Threads::Mutex::Lock lm (control_lock());
253 XMLNode* node = new XMLNode (Automatable::xml_node_name);
255 if (controls().empty()) {
259 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
260 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
261 if (l && !l->empty()) {
262 node->add_child_nocopy (l->get_state ());
270 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
272 Glib::Threads::Mutex::Lock lm (control_lock());
274 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
276 if (c && (s != c->automation_state())) {
277 c->set_automation_state (s);
278 _a_session.set_dirty ();
283 Automatable::get_parameter_automation_state (Evoral::Parameter param)
285 AutoState result = Off;
287 boost::shared_ptr<AutomationControl> c = automation_control(param);
290 result = c->automation_state();
297 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
299 Glib::Threads::Mutex::Lock lm (control_lock());
301 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
303 if (c && (s != c->automation_style())) {
304 c->set_automation_style (s);
305 _a_session.set_dirty ();
310 Automatable::get_parameter_automation_style (Evoral::Parameter param)
312 Glib::Threads::Mutex::Lock lm (control_lock());
314 boost::shared_ptr<Evoral::Control> c = control(param);
315 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
318 return l->automation_style();
320 return Absolute; // whatever
325 Automatable::protect_automation ()
327 typedef set<Evoral::Parameter> ParameterSet;
328 const ParameterSet& automated_params = what_can_be_automated ();
330 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
332 boost::shared_ptr<Evoral::Control> c = control(*i);
333 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
335 switch (l->automation_state()) {
337 l->set_automation_state (Off);
340 l->set_automation_state (Play);
349 Automatable::transport_located (framepos_t now)
351 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
353 boost::shared_ptr<AutomationControl> c
354 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
356 boost::shared_ptr<AutomationList> l
357 = boost::dynamic_pointer_cast<AutomationList>(c->list());
360 l->start_write_pass (now);
367 Automatable::transport_stopped (framepos_t now)
369 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
370 boost::shared_ptr<AutomationControl> c =
371 boost::dynamic_pointer_cast<AutomationControl>(li->second);
376 boost::shared_ptr<AutomationList> l =
377 boost::dynamic_pointer_cast<AutomationList>(c->list());
382 /* Stop any active touch gesture just before we mark the write pass
383 as finished. If we don't do this, the transport can end up stopped with
384 an AutomationList thinking that a touch is still in progress and,
385 when the transport is re-started, a touch will magically
386 be happening without it ever have being started in the usual way.
388 l->stop_touch (true, now);
389 l->write_pass_finished (now, Config->get_automation_thinning_factor());
391 if (l->automation_playback()) {
392 c->set_value(c->list()->eval(now));
395 if (l->automation_state() == Write) {
396 l->set_automation_state (Touch);
401 boost::shared_ptr<Evoral::Control>
402 Automatable::control_factory(const Evoral::Parameter& param)
404 Evoral::Control* control = NULL;
405 bool make_list = true;
406 ParameterDescriptor desc(param);
407 boost::shared_ptr<AutomationList> list;
408 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
409 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
411 control = new MidiTrack::MidiControl(mt, param);
412 make_list = false; // No list, this is region "automation"
414 } else if (param.type() == PluginAutomation) {
415 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
417 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
418 control = new PluginInsert::PluginControl(pi, param, desc);
420 warning << "PluginAutomation for non-Plugin" << endl;
422 } else if (param.type() == PluginPropertyAutomation) {
423 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
425 desc = pi->plugin(0)->get_property_descriptor(param.id());
426 if (desc.datatype != Variant::NOTHING) {
427 if (!Variant::type_is_numeric(desc.datatype)) {
428 make_list = false; // Can't automate non-numeric data yet
430 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
432 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
435 warning << "PluginPropertyAutomation for non-Plugin" << endl;
437 } else if (param.type() == GainAutomation) {
438 Amp* amp = dynamic_cast<Amp*>(this);
440 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
442 warning << "GainAutomation for non-Amp" << endl;
444 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
445 Pannable* pannable = dynamic_cast<Pannable*>(this);
447 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
449 warning << "PanAutomation for non-Pannable" << endl;
453 if (make_list && !list) {
454 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
458 control = new AutomationControl(_a_session, param, desc, list);
461 return boost::shared_ptr<Evoral::Control>(control);
464 boost::shared_ptr<AutomationControl>
465 Automatable::automation_control (const Evoral::Parameter& id, bool create)
467 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
470 boost::shared_ptr<const AutomationControl>
471 Automatable::automation_control (const Evoral::Parameter& id) const
473 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
477 Automatable::clear_controls ()
479 _control_connections.drop_connections ();
480 ControlSet::clear_controls ();
484 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
486 return ARDOUR::value_as_string(ac->desc(), ac->get_value());