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 "midi++/names.h"
30 #include "ardour/amp.h"
31 #include "ardour/automatable.h"
32 #include "ardour/event_type_map.h"
33 #include "ardour/midi_track.h"
34 #include "ardour/pan_controllable.h"
35 #include "ardour/pannable.h"
36 #include "ardour/plugin_insert.h"
37 #include "ardour/session.h"
42 using namespace ARDOUR;
45 const string Automatable::xml_node_name = X_("Automation");
47 Automatable::Automatable(Session& session)
52 Automatable::Automatable (const Automatable& other)
54 , _a_session (other._a_session)
56 Glib::Threads::Mutex::Lock lm (other._control_lock);
58 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
59 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
64 Automatable::~Automatable ()
67 Glib::Threads::Mutex::Lock lm (_control_lock);
69 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
70 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
76 Automatable::old_set_automation_state (const XMLNode& node)
78 const XMLProperty *prop;
80 if ((prop = node.property ("path")) != 0) {
81 load_automation (prop->value());
83 warning << _("Automation node has no path property") << endmsg;
90 Automatable::load_automation (const string& path)
94 if (Glib::path_is_absolute (path)) { // legacy
97 fullpath = _a_session.automation_dir();
100 ifstream in (fullpath.c_str());
103 warning << string_compose(_("cannot open %2 to load automation data (%3)")
104 , fullpath, strerror (errno)) << endmsg;
108 Glib::Threads::Mutex::Lock lm (control_lock());
109 set<Evoral::Parameter> tosave;
117 in >> port; if (!in) break;
118 in >> when; if (!in) goto bad;
119 in >> value; if (!in) goto bad;
121 Evoral::Parameter param(PluginAutomation, 0, port);
122 /* FIXME: this is legacy and only used for plugin inserts? I think? */
123 boost::shared_ptr<Evoral::Control> c = control (param, true);
124 c->list()->add (when, value);
125 tosave.insert (param);
131 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
137 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
139 Evoral::Parameter param = ac->parameter();
141 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
144 al->automation_state_changed.connect_same_thread (
145 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
148 ControlSet::add_control (ac);
149 _can_automate_list.insert (param);
151 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
155 Automatable::describe_parameter (Evoral::Parameter param)
157 /* derived classes like PluginInsert should override this */
159 if (param == Evoral::Parameter(GainAutomation)) {
161 } else if (param.type() == MidiCCAutomation) {
162 return string_compose("%1: %2 [%3]",
163 param.id(), midi_name(param.id()), int(param.channel()) + 1);
164 } else if (param.type() == MidiPgmChangeAutomation) {
165 return string_compose("Program [%1]", int(param.channel()) + 1);
166 } else if (param.type() == MidiPitchBenderAutomation) {
167 return string_compose("Bender [%1]", int(param.channel()) + 1);
168 } else if (param.type() == MidiChannelPressureAutomation) {
169 return string_compose("Pressure [%1]", int(param.channel()) + 1);
171 return EventTypeMap::instance().to_symbol(param);
176 Automatable::can_automate (Evoral::Parameter what)
178 _can_automate_list.insert (what);
181 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
182 * had a single automation parameter, with it's type implicit. Derived objects should
183 * pass that type and it will be used for the untyped AutomationList found.
186 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
188 Glib::Threads::Mutex::Lock lm (control_lock());
190 /* Don't clear controls, since some may be special derived Controllable classes */
192 XMLNodeList nlist = node.children();
193 XMLNodeIterator niter;
195 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
197 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
198 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
202 if ((*niter)->name() == "AutomationList") {
204 const XMLProperty* id_prop = (*niter)->property("automation-id");
206 Evoral::Parameter param = (id_prop
207 ? EventTypeMap::instance().new_parameter(id_prop->value())
210 if (param.type() == NullAutomation) {
211 warning << "Automation has null type" << endl;
216 warning << "AutomationList node without automation-id property, "
217 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
220 boost::shared_ptr<AutomationControl> existing = automation_control (param);
223 existing->alist()->set_state (**niter, 3000);
225 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
226 add_control (newcontrol);
227 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
228 newcontrol->set_list(al);
232 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
240 Automatable::get_automation_xml_state ()
242 Glib::Threads::Mutex::Lock lm (control_lock());
243 XMLNode* node = new XMLNode (Automatable::xml_node_name);
245 if (controls().empty()) {
249 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
250 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
252 node->add_child_nocopy (l->get_state ());
260 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
262 Glib::Threads::Mutex::Lock lm (control_lock());
264 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
266 if (c && (s != c->automation_state())) {
267 c->set_automation_state (s);
268 _a_session.set_dirty ();
273 Automatable::get_parameter_automation_state (Evoral::Parameter param)
275 AutoState result = Off;
277 boost::shared_ptr<AutomationControl> c = automation_control(param);
280 result = c->automation_state();
287 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
289 Glib::Threads::Mutex::Lock lm (control_lock());
291 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
293 if (c && (s != c->automation_style())) {
294 c->set_automation_style (s);
295 _a_session.set_dirty ();
300 Automatable::get_parameter_automation_style (Evoral::Parameter param)
302 Glib::Threads::Mutex::Lock lm (control_lock());
304 boost::shared_ptr<Evoral::Control> c = control(param);
305 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
308 return l->automation_style();
310 return Absolute; // whatever
315 Automatable::protect_automation ()
317 typedef set<Evoral::Parameter> ParameterSet;
318 const ParameterSet& automated_params = what_can_be_automated ();
320 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
322 boost::shared_ptr<Evoral::Control> c = control(*i);
323 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
325 switch (l->automation_state()) {
327 l->set_automation_state (Off);
330 l->set_automation_state (Play);
339 Automatable::transport_located (framepos_t now)
341 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
343 boost::shared_ptr<AutomationControl> c
344 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
346 boost::shared_ptr<AutomationList> l
347 = boost::dynamic_pointer_cast<AutomationList>(c->list());
350 l->start_write_pass (now);
357 Automatable::transport_stopped (framepos_t now)
359 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
361 boost::shared_ptr<AutomationControl> c
362 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
364 boost::shared_ptr<AutomationList> l
365 = boost::dynamic_pointer_cast<AutomationList>(c->list());
368 /* Stop any active touch gesture just before we mark the write pass
369 as finished. If we don't do this, the transport can end up stopped with
370 an AutomationList thinking that a touch is still in progress and,
371 when the transport is re-started, a touch will magically
372 be happening without it ever have being started in the usual way.
374 l->stop_touch (true, now);
375 l->write_pass_finished (now);
377 if (l->automation_playback()) {
378 c->set_value(c->list()->eval(now));
381 if (l->automation_state() == Write) {
382 l->set_automation_state (Touch);
389 boost::shared_ptr<Evoral::Control>
390 Automatable::control_factory(const Evoral::Parameter& param)
392 boost::shared_ptr<AutomationList> list(new AutomationList(param));
393 Evoral::Control* control = NULL;
394 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
395 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
397 control = new MidiTrack::MidiControl(mt, param);
399 warning << "MidiCCAutomation for non-MidiTrack" << endl;
401 } else if (param.type() == PluginAutomation) {
402 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
404 control = new PluginInsert::PluginControl(pi, param);
406 warning << "PluginAutomation for non-Plugin" << endl;
408 } else if (param.type() == GainAutomation) {
409 Amp* amp = dynamic_cast<Amp*>(this);
411 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
413 warning << "GainAutomation for non-Amp" << endl;
415 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
416 Pannable* pannable = dynamic_cast<Pannable*>(this);
418 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
420 warning << "PanAutomation for non-Pannable" << endl;
425 control = new AutomationControl(_a_session, param);
428 control->set_list(list);
429 return boost::shared_ptr<Evoral::Control>(control);
432 boost::shared_ptr<AutomationControl>
433 Automatable::automation_control (const Evoral::Parameter& id, bool create)
435 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
438 boost::shared_ptr<const AutomationControl>
439 Automatable::automation_control (const Evoral::Parameter& id) const
441 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
445 Automatable::clear_controls ()
447 _control_connections.drop_connections ();
448 ControlSet::clear_controls ();
452 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
456 /* this is a the default fallback for this virtual method. Derived Automatables
457 are free to override this to display the values of their parameters/controls
461 // Hack to display CC as integer value, rather than double
462 if (ac->parameter().type() == MidiCCAutomation) {
463 s << lrint (ac->get_value());
465 s << std::fixed << std::setprecision(3) << ac->get_value();