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_insert.h"
35 #include "ardour/session.h"
40 using namespace ARDOUR;
43 const string Automatable::xml_node_name = X_("Automation");
45 Automatable::Automatable(Session& session)
50 Automatable::Automatable (const Automatable& other)
52 , _a_session (other._a_session)
54 Glib::Threads::Mutex::Lock lm (other._control_lock);
56 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
57 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
62 Automatable::~Automatable ()
65 Glib::Threads::Mutex::Lock lm (_control_lock);
67 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
68 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
74 Automatable::old_set_automation_state (const XMLNode& node)
76 const XMLProperty *prop;
78 if ((prop = node.property ("path")) != 0) {
79 load_automation (prop->value());
81 warning << _("Automation node has no path property") << endmsg;
88 Automatable::load_automation (const string& path)
92 if (Glib::path_is_absolute (path)) { // legacy
95 fullpath = _a_session.automation_dir();
98 ifstream in (fullpath.c_str());
101 warning << string_compose(_("cannot open %2 to load automation data (%3)")
102 , fullpath, strerror (errno)) << endmsg;
106 Glib::Threads::Mutex::Lock lm (control_lock());
107 set<Evoral::Parameter> tosave;
115 in >> port; if (!in) break;
116 in >> when; if (!in) goto bad;
117 in >> value; if (!in) goto bad;
119 Evoral::Parameter param(PluginAutomation, 0, port);
120 /* FIXME: this is legacy and only used for plugin inserts? I think? */
121 boost::shared_ptr<Evoral::Control> c = control (param, true);
122 c->list()->add (when, value);
123 tosave.insert (param);
129 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
135 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
137 Evoral::Parameter param = ac->parameter();
139 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
142 al->automation_state_changed.connect_same_thread (
143 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
146 ControlSet::add_control (ac);
147 _can_automate_list.insert (param);
149 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
153 Automatable::describe_parameter (Evoral::Parameter param)
155 /* derived classes like PluginInsert should override this */
157 if (param == Evoral::Parameter(GainAutomation)) {
159 } else if (param.type() == MuteAutomation) {
161 } else if (param.type() == MidiCCAutomation) {
162 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
163 } else if (param.type() == MidiPgmChangeAutomation) {
164 return string_compose("Program [%1]", int(param.channel()) + 1);
165 } else if (param.type() == MidiPitchBenderAutomation) {
166 return string_compose("Bender [%1]", int(param.channel()) + 1);
167 } else if (param.type() == MidiChannelPressureAutomation) {
168 return string_compose("Pressure [%1]", int(param.channel()) + 1);
170 return EventTypeMap::instance().to_symbol(param);
175 Automatable::can_automate (Evoral::Parameter what)
177 _can_automate_list.insert (what);
180 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
181 * had a single automation parameter, with it's type implicit. Derived objects should
182 * pass that type and it will be used for the untyped AutomationList found.
185 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
187 Glib::Threads::Mutex::Lock lm (control_lock());
189 /* Don't clear controls, since some may be special derived Controllable classes */
191 XMLNodeList nlist = node.children();
192 XMLNodeIterator niter;
194 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
196 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
197 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
201 if ((*niter)->name() == "AutomationList") {
203 const XMLProperty* id_prop = (*niter)->property("automation-id");
205 Evoral::Parameter param = (id_prop
206 ? EventTypeMap::instance().new_parameter(id_prop->value())
209 if (param.type() == NullAutomation) {
210 warning << "Automation has null type" << endl;
215 warning << "AutomationList node without automation-id property, "
216 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
219 boost::shared_ptr<AutomationControl> existing = automation_control (param);
222 existing->alist()->set_state (**niter, 3000);
224 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
225 add_control (newcontrol);
226 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
227 newcontrol->set_list(al);
231 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
239 Automatable::get_automation_xml_state ()
241 Glib::Threads::Mutex::Lock lm (control_lock());
242 XMLNode* node = new XMLNode (Automatable::xml_node_name);
244 if (controls().empty()) {
248 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
249 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
251 node->add_child_nocopy (l->get_state ());
259 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
261 Glib::Threads::Mutex::Lock lm (control_lock());
263 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
265 if (c && (s != c->automation_state())) {
266 c->set_automation_state (s);
267 _a_session.set_dirty ();
272 Automatable::get_parameter_automation_state (Evoral::Parameter param)
274 AutoState result = Off;
276 boost::shared_ptr<AutomationControl> c = automation_control(param);
279 result = c->automation_state();
286 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
288 Glib::Threads::Mutex::Lock lm (control_lock());
290 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
292 if (c && (s != c->automation_style())) {
293 c->set_automation_style (s);
294 _a_session.set_dirty ();
299 Automatable::get_parameter_automation_style (Evoral::Parameter param)
301 Glib::Threads::Mutex::Lock lm (control_lock());
303 boost::shared_ptr<Evoral::Control> c = control(param);
304 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
307 return l->automation_style();
309 return Absolute; // whatever
314 Automatable::protect_automation ()
316 typedef set<Evoral::Parameter> ParameterSet;
317 const ParameterSet& automated_params = what_can_be_automated ();
319 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
321 boost::shared_ptr<Evoral::Control> c = control(*i);
322 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
324 switch (l->automation_state()) {
326 l->set_automation_state (Off);
329 l->set_automation_state (Play);
338 Automatable::transport_located (framepos_t now)
340 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
342 boost::shared_ptr<AutomationControl> c
343 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
345 boost::shared_ptr<AutomationList> l
346 = boost::dynamic_pointer_cast<AutomationList>(c->list());
349 l->start_write_pass (now);
356 Automatable::transport_stopped (framepos_t now)
358 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
360 boost::shared_ptr<AutomationControl> c
361 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
363 boost::shared_ptr<AutomationList> l
364 = boost::dynamic_pointer_cast<AutomationList>(c->list());
367 /* Stop any active touch gesture just before we mark the write pass
368 as finished. If we don't do this, the transport can end up stopped with
369 an AutomationList thinking that a touch is still in progress and,
370 when the transport is re-started, a touch will magically
371 be happening without it ever have being started in the usual way.
373 l->stop_touch (true, now);
374 l->write_pass_finished (now);
376 if (l->automation_playback()) {
377 c->set_value(c->list()->eval(now));
380 if (l->automation_state() == Write) {
381 l->set_automation_state (Touch);
388 boost::shared_ptr<Evoral::Control>
389 Automatable::control_factory(const Evoral::Parameter& param)
391 boost::shared_ptr<AutomationList> list(new AutomationList(param));
392 Evoral::Control* control = NULL;
393 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
394 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
396 control = new MidiTrack::MidiControl(mt, param);
398 warning << "MidiCCAutomation for non-MidiTrack" << endl;
400 } else if (param.type() == PluginAutomation) {
401 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
403 control = new PluginInsert::PluginControl(pi, param);
405 warning << "PluginAutomation for non-Plugin" << endl;
407 } else if (param.type() == GainAutomation) {
408 Amp* amp = dynamic_cast<Amp*>(this);
410 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
412 warning << "GainAutomation for non-Amp" << endl;
414 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
415 Pannable* pannable = dynamic_cast<Pannable*>(this);
417 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
419 warning << "PanAutomation for non-Pannable" << endl;
424 control = new AutomationControl(_a_session, param);
427 control->set_list(list);
428 return boost::shared_ptr<Evoral::Control>(control);
431 boost::shared_ptr<AutomationControl>
432 Automatable::automation_control (const Evoral::Parameter& id, bool create)
434 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
437 boost::shared_ptr<const AutomationControl>
438 Automatable::automation_control (const Evoral::Parameter& id) const
440 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
444 Automatable::clear_controls ()
446 _control_connections.drop_connections ();
447 ControlSet::clear_controls ();
451 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
455 /* this is a the default fallback for this virtual method. Derived Automatables
456 are free to override this to display the values of their parameters/controls
460 // Hack to display CC as integer value, rather than double
461 if (ac->parameter().type() == MidiCCAutomation) {
462 s << lrint (ac->get_value());
464 s << std::fixed << std::setprecision(3) << ac->get_value();