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() == MidiCCAutomation) {
160 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
161 } else if (param.type() == MidiPgmChangeAutomation) {
162 return string_compose("Program [%1]", int(param.channel()) + 1);
163 } else if (param.type() == MidiPitchBenderAutomation) {
164 return string_compose("Bender [%1]", int(param.channel()) + 1);
165 } else if (param.type() == MidiChannelPressureAutomation) {
166 return string_compose("Pressure [%1]", int(param.channel()) + 1);
168 return EventTypeMap::instance().to_symbol(param);
173 Automatable::can_automate (Evoral::Parameter what)
175 _can_automate_list.insert (what);
178 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
179 * had a single automation parameter, with it's type implicit. Derived objects should
180 * pass that type and it will be used for the untyped AutomationList found.
183 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
185 Glib::Threads::Mutex::Lock lm (control_lock());
187 /* Don't clear controls, since some may be special derived Controllable classes */
189 XMLNodeList nlist = node.children();
190 XMLNodeIterator niter;
192 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
194 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
195 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
199 if ((*niter)->name() == "AutomationList") {
201 const XMLProperty* id_prop = (*niter)->property("automation-id");
203 Evoral::Parameter param = (id_prop
204 ? EventTypeMap::instance().new_parameter(id_prop->value())
207 if (param.type() == NullAutomation) {
208 warning << "Automation has null type" << endl;
213 warning << "AutomationList node without automation-id property, "
214 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
217 boost::shared_ptr<AutomationControl> existing = automation_control (param);
220 existing->alist()->set_state (**niter, 3000);
222 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
223 add_control (newcontrol);
224 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
225 newcontrol->set_list(al);
229 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
237 Automatable::get_automation_xml_state ()
239 Glib::Threads::Mutex::Lock lm (control_lock());
240 XMLNode* node = new XMLNode (Automatable::xml_node_name);
242 if (controls().empty()) {
246 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
247 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
249 node->add_child_nocopy (l->get_state ());
257 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
259 Glib::Threads::Mutex::Lock lm (control_lock());
261 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
263 if (c && (s != c->automation_state())) {
264 c->set_automation_state (s);
265 _a_session.set_dirty ();
270 Automatable::get_parameter_automation_state (Evoral::Parameter param)
272 AutoState result = Off;
274 boost::shared_ptr<AutomationControl> c = automation_control(param);
277 result = c->automation_state();
284 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
286 Glib::Threads::Mutex::Lock lm (control_lock());
288 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
290 if (c && (s != c->automation_style())) {
291 c->set_automation_style (s);
292 _a_session.set_dirty ();
297 Automatable::get_parameter_automation_style (Evoral::Parameter param)
299 Glib::Threads::Mutex::Lock lm (control_lock());
301 boost::shared_ptr<Evoral::Control> c = control(param);
302 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
305 return l->automation_style();
307 return Absolute; // whatever
312 Automatable::protect_automation ()
314 typedef set<Evoral::Parameter> ParameterSet;
315 const ParameterSet& automated_params = what_can_be_automated ();
317 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
319 boost::shared_ptr<Evoral::Control> c = control(*i);
320 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
322 switch (l->automation_state()) {
324 l->set_automation_state (Off);
327 l->set_automation_state (Play);
336 Automatable::transport_located (framepos_t now)
338 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
340 boost::shared_ptr<AutomationControl> c
341 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
343 boost::shared_ptr<AutomationList> l
344 = boost::dynamic_pointer_cast<AutomationList>(c->list());
347 l->start_write_pass (now);
354 Automatable::transport_stopped (framepos_t now)
356 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
358 boost::shared_ptr<AutomationControl> c
359 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
361 boost::shared_ptr<AutomationList> l
362 = boost::dynamic_pointer_cast<AutomationList>(c->list());
365 /* Stop any active touch gesture just before we mark the write pass
366 as finished. If we don't do this, the transport can end up stopped with
367 an AutomationList thinking that a touch is still in progress and,
368 when the transport is re-started, a touch will magically
369 be happening without it ever have being started in the usual way.
371 l->stop_touch (true, now);
372 l->write_pass_finished (now);
374 if (l->automation_playback()) {
375 c->set_value(c->list()->eval(now));
378 if (l->automation_state() == Write) {
379 l->set_automation_state (Touch);
386 boost::shared_ptr<Evoral::Control>
387 Automatable::control_factory(const Evoral::Parameter& param)
389 boost::shared_ptr<AutomationList> list(new AutomationList(param));
390 Evoral::Control* control = NULL;
391 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
392 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
394 control = new MidiTrack::MidiControl(mt, param);
396 warning << "MidiCCAutomation for non-MidiTrack" << endl;
398 } else if (param.type() == PluginAutomation) {
399 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
401 control = new PluginInsert::PluginControl(pi, param);
403 warning << "PluginAutomation for non-Plugin" << endl;
405 } else if (param.type() == GainAutomation) {
406 Amp* amp = dynamic_cast<Amp*>(this);
408 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
410 warning << "GainAutomation for non-Amp" << endl;
412 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
413 Pannable* pannable = dynamic_cast<Pannable*>(this);
415 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
417 warning << "PanAutomation for non-Pannable" << endl;
422 control = new AutomationControl(_a_session, param);
425 control->set_list(list);
426 return boost::shared_ptr<Evoral::Control>(control);
429 boost::shared_ptr<AutomationControl>
430 Automatable::automation_control (const Evoral::Parameter& id, bool create)
432 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
435 boost::shared_ptr<const AutomationControl>
436 Automatable::automation_control (const Evoral::Parameter& id) const
438 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
442 Automatable::clear_controls ()
444 _control_connections.drop_connections ();
445 ControlSet::clear_controls ();
449 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
453 /* this is a the default fallback for this virtual method. Derived Automatables
454 are free to override this to display the values of their parameters/controls
458 // Hack to display CC as integer value, rather than double
459 if (ac->parameter().type() == MidiCCAutomation) {
460 s << lrint (ac->get_value());
462 s << std::fixed << std::setprecision(3) << ac->get_value();