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() == TrimAutomation) {
168 } else if (param.type() == MuteAutomation) {
170 } else if (param.type() == MidiCCAutomation) {
171 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
172 } else if (param.type() == MidiPgmChangeAutomation) {
173 return string_compose("Program [%1]", int(param.channel()) + 1);
174 } else if (param.type() == MidiPitchBenderAutomation) {
175 return string_compose("Bender [%1]", int(param.channel()) + 1);
176 } else if (param.type() == MidiChannelPressureAutomation) {
177 return string_compose("Pressure [%1]", int(param.channel()) + 1);
179 } else if (param.type() == PluginPropertyAutomation) {
180 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
183 return EventTypeMap::instance().to_symbol(param);
188 Automatable::can_automate (Evoral::Parameter what)
190 _can_automate_list.insert (what);
193 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
194 * had a single automation parameter, with it's type implicit. Derived objects should
195 * pass that type and it will be used for the untyped AutomationList found.
198 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
200 Glib::Threads::Mutex::Lock lm (control_lock());
202 /* Don't clear controls, since some may be special derived Controllable classes */
204 XMLNodeList nlist = node.children();
205 XMLNodeIterator niter;
207 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
209 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
210 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
214 if ((*niter)->name() == "AutomationList") {
216 const XMLProperty* id_prop = (*niter)->property("automation-id");
218 Evoral::Parameter param = (id_prop
219 ? EventTypeMap::instance().from_symbol(id_prop->value())
222 if (param.type() == NullAutomation) {
223 warning << "Automation has null type" << endl;
228 warning << "AutomationList node without automation-id property, "
229 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
232 boost::shared_ptr<AutomationControl> existing = automation_control (param);
235 existing->alist()->set_state (**niter, 3000);
237 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
238 add_control (newcontrol);
239 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
240 newcontrol->set_list(al);
244 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
252 Automatable::get_automation_xml_state ()
254 Glib::Threads::Mutex::Lock lm (control_lock());
255 XMLNode* node = new XMLNode (Automatable::xml_node_name);
257 if (controls().empty()) {
261 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
262 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
263 if (l && !l->empty()) {
264 node->add_child_nocopy (l->get_state ());
272 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
274 Glib::Threads::Mutex::Lock lm (control_lock());
276 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
278 if (c && (s != c->automation_state())) {
279 c->set_automation_state (s);
280 _a_session.set_dirty ();
285 Automatable::get_parameter_automation_state (Evoral::Parameter param)
287 AutoState result = Off;
289 boost::shared_ptr<AutomationControl> c = automation_control(param);
292 result = c->automation_state();
299 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
301 Glib::Threads::Mutex::Lock lm (control_lock());
303 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
305 if (c && (s != c->automation_style())) {
306 c->set_automation_style (s);
307 _a_session.set_dirty ();
312 Automatable::get_parameter_automation_style (Evoral::Parameter param)
314 Glib::Threads::Mutex::Lock lm (control_lock());
316 boost::shared_ptr<Evoral::Control> c = control(param);
317 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
320 return l->automation_style();
322 return Absolute; // whatever
327 Automatable::protect_automation ()
329 typedef set<Evoral::Parameter> ParameterSet;
330 const ParameterSet& automated_params = what_can_be_automated ();
332 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
334 boost::shared_ptr<Evoral::Control> c = control(*i);
335 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
337 switch (l->automation_state()) {
339 l->set_automation_state (Off);
342 l->set_automation_state (Play);
351 Automatable::transport_located (framepos_t now)
353 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
355 boost::shared_ptr<AutomationControl> c
356 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
358 boost::shared_ptr<AutomationList> l
359 = boost::dynamic_pointer_cast<AutomationList>(c->list());
362 l->start_write_pass (now);
369 Automatable::transport_stopped (framepos_t now)
371 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
372 boost::shared_ptr<AutomationControl> c =
373 boost::dynamic_pointer_cast<AutomationControl>(li->second);
378 boost::shared_ptr<AutomationList> l =
379 boost::dynamic_pointer_cast<AutomationList>(c->list());
384 /* Stop any active touch gesture just before we mark the write pass
385 as finished. If we don't do this, the transport can end up stopped with
386 an AutomationList thinking that a touch is still in progress and,
387 when the transport is re-started, a touch will magically
388 be happening without it ever have being started in the usual way.
390 l->stop_touch (true, now);
391 l->write_pass_finished (now, Config->get_automation_thinning_factor());
393 if (l->automation_playback()) {
394 c->set_value(c->list()->eval(now));
397 if (l->automation_state() == Write) {
398 l->set_automation_state (Touch);
403 boost::shared_ptr<Evoral::Control>
404 Automatable::control_factory(const Evoral::Parameter& param)
406 Evoral::Control* control = NULL;
407 bool make_list = true;
408 ParameterDescriptor desc(param);
409 boost::shared_ptr<AutomationList> list;
410 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
411 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
413 control = new MidiTrack::MidiControl(mt, param);
414 make_list = false; // No list, this is region "automation"
416 } else if (param.type() == PluginAutomation) {
417 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
419 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
420 control = new PluginInsert::PluginControl(pi, param, desc);
422 warning << "PluginAutomation for non-Plugin" << endl;
424 } else if (param.type() == PluginPropertyAutomation) {
425 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
427 desc = pi->plugin(0)->get_property_descriptor(param.id());
428 if (desc.datatype != Variant::NOTHING) {
429 if (!Variant::type_is_numeric(desc.datatype)) {
430 make_list = false; // Can't automate non-numeric data yet
432 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
434 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
437 warning << "PluginPropertyAutomation for non-Plugin" << endl;
439 } else if (param.type() == GainAutomation) {
440 Amp* amp = dynamic_cast<Amp*>(this);
442 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
444 warning << "GainAutomation for non-Amp" << endl;
446 } else if (param.type() == TrimAutomation) {
447 Amp* amp = dynamic_cast<Amp*>(this);
449 control = new Amp::GainControl(X_("trimcontrol"), _a_session, amp, param);
451 warning << "TrimAutomation for non-Amp" << endl;
453 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
454 Pannable* pannable = dynamic_cast<Pannable*>(this);
456 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
458 warning << "PanAutomation for non-Pannable" << endl;
462 if (make_list && !list) {
463 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
467 control = new AutomationControl(_a_session, param, desc, list);
470 return boost::shared_ptr<Evoral::Control>(control);
473 boost::shared_ptr<AutomationControl>
474 Automatable::automation_control (const Evoral::Parameter& id, bool create)
476 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
479 boost::shared_ptr<const AutomationControl>
480 Automatable::automation_control (const Evoral::Parameter& id) const
482 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
486 Automatable::clear_controls ()
488 _control_connections.drop_connections ();
489 ControlSet::clear_controls ();
493 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
495 return ARDOUR::value_as_string(ac->desc(), ac->get_value());