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 framecnt_t Automatable::_automation_interval = 0;
46 const string Automatable::xml_node_name = X_("Automation");
48 Automatable::Automatable(Session& session)
50 , _last_automation_snapshot(0)
54 Automatable::Automatable (const Automatable& other)
56 , _a_session (other._a_session)
57 , _last_automation_snapshot (0)
59 Glib::Mutex::Lock lm (other._control_lock);
61 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
62 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
67 Automatable::old_set_automation_state (const XMLNode& node)
69 const XMLProperty *prop;
71 if ((prop = node.property ("path")) != 0) {
72 load_automation (prop->value());
74 warning << _("Automation node has no path property") << endmsg;
77 _last_automation_snapshot = 0;
83 Automatable::load_automation (const string& path)
87 if (Glib::path_is_absolute (path)) { // legacy
90 fullpath = _a_session.automation_dir();
93 ifstream in (fullpath.c_str());
96 warning << string_compose(_("cannot open %2 to load automation data (%3)")
97 , fullpath, strerror (errno)) << endmsg;
101 Glib::Mutex::Lock lm (control_lock());
102 set<Evoral::Parameter> tosave;
105 _last_automation_snapshot = 0;
112 in >> port; if (!in) break;
113 in >> when; if (!in) goto bad;
114 in >> value; if (!in) goto bad;
116 Evoral::Parameter param(PluginAutomation, 0, port);
117 /* FIXME: this is legacy and only used for plugin inserts? I think? */
118 boost::shared_ptr<Evoral::Control> c = control (param, true);
119 c->list()->add (when, value);
120 tosave.insert (param);
126 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
132 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
134 Evoral::Parameter param = ac->parameter();
136 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
139 al->automation_state_changed.connect_same_thread (
140 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
143 ControlSet::add_control (ac);
144 _can_automate_list.insert (param);
146 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
150 Automatable::describe_parameter (Evoral::Parameter param)
152 /* derived classes like PluginInsert should override this */
154 if (param == Evoral::Parameter(GainAutomation)) {
156 } else if (param.type() == MidiCCAutomation) {
157 return string_compose("%1: %2 [%3]",
158 param.id(), midi_name(param.id()), int(param.channel()) + 1);
159 } else if (param.type() == MidiPgmChangeAutomation) {
160 return string_compose("Program [%1]", int(param.channel()) + 1);
161 } else if (param.type() == MidiPitchBenderAutomation) {
162 return string_compose("Bender [%1]", int(param.channel()) + 1);
163 } else if (param.type() == MidiChannelPressureAutomation) {
164 return string_compose("Pressure [%1]", int(param.channel()) + 1);
166 return EventTypeMap::instance().to_symbol(param);
171 Automatable::can_automate (Evoral::Parameter what)
173 _can_automate_list.insert (what);
176 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
177 * had a single automation parameter, with it's type implicit. Derived objects should
178 * pass that type and it will be used for the untyped AutomationList found.
181 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
183 Glib::Mutex::Lock lm (control_lock());
185 /* Don't clear controls, since some may be special derived Controllable classes */
187 XMLNodeList nlist = node.children();
188 XMLNodeIterator niter;
190 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
192 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
193 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
197 if ((*niter)->name() == "AutomationList") {
199 const XMLProperty* id_prop = (*niter)->property("automation-id");
201 Evoral::Parameter param = (id_prop
202 ? EventTypeMap::instance().new_parameter(id_prop->value())
205 if (param.type() == NullAutomation) {
206 warning << "Automation has null type" << endl;
211 warning << "AutomationList node without automation-id property, "
212 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
215 boost::shared_ptr<AutomationControl> existing = automation_control (param);
218 existing->alist()->set_state (**niter, 3000);
220 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
221 add_control (newcontrol);
222 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
223 newcontrol->set_list(al);
227 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
231 _last_automation_snapshot = 0;
237 Automatable::get_automation_xml_state ()
239 Glib::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::Mutex::Lock lm (control_lock());
261 boost::shared_ptr<Evoral::Control> c = control (param, true);
262 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
264 if (s != l->automation_state()) {
265 l->set_automation_state (s);
266 _a_session.set_dirty ();
271 Automatable::get_parameter_automation_state (Evoral::Parameter param)
273 AutoState result = Off;
275 boost::shared_ptr<Evoral::Control> c = control(param);
276 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
279 result = l->automation_state();
286 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
288 Glib::Mutex::Lock lm (control_lock());
290 boost::shared_ptr<Evoral::Control> c = control(param, true);
291 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
293 if (s != l->automation_style()) {
294 l->set_automation_style (s);
295 _a_session.set_dirty ();
300 Automatable::get_parameter_automation_style (Evoral::Parameter param)
302 Glib::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::automation_snapshot (framepos_t now, bool force)
341 if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
343 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
344 boost::shared_ptr<AutomationControl> c
345 = boost::dynamic_pointer_cast<AutomationControl>(i->second);
346 if (_a_session.transport_rolling() && c->automation_write()) {
347 c->list()->rt_add (now, i->second->user_double());
351 _last_automation_snapshot = 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();