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)
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 (
146 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
149 ControlSet::add_control (ac);
150 _can_automate_list.insert (param);
152 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
156 Automatable::describe_parameter (Evoral::Parameter param)
158 /* derived classes like PluginInsert should override this */
160 if (param == Evoral::Parameter(GainAutomation)) {
162 } else if (param.type() == MidiCCAutomation) {
163 return string_compose("%1: %2 [%3]",
164 param.id(), midi_name(param.id()), int(param.channel()) + 1);
165 } else if (param.type() == MidiPgmChangeAutomation) {
166 return string_compose("Program [%1]", int(param.channel()) + 1);
167 } else if (param.type() == MidiPitchBenderAutomation) {
168 return string_compose("Bender [%1]", int(param.channel()) + 1);
169 } else if (param.type() == MidiChannelPressureAutomation) {
170 return string_compose("Pressure [%1]", int(param.channel()) + 1);
172 return EventTypeMap::instance().to_symbol(param);
177 Automatable::can_automate (Evoral::Parameter what)
179 _can_automate_list.insert (what);
182 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
183 * had a single automation parameter, with it's type implicit. Derived objects should
184 * pass that type and it will be used for the untyped AutomationList found.
187 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
189 Glib::Threads::Mutex::Lock lm (control_lock());
191 /* Don't clear controls, since some may be special derived Controllable classes */
193 XMLNodeList nlist = node.children();
194 XMLNodeIterator niter;
196 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
198 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
199 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
203 if ((*niter)->name() == "AutomationList") {
205 const XMLProperty* id_prop = (*niter)->property("automation-id");
207 Evoral::Parameter param = (id_prop
208 ? EventTypeMap::instance().new_parameter(id_prop->value())
211 if (param.type() == NullAutomation) {
212 warning << "Automation has null type" << endl;
217 warning << "AutomationList node without automation-id property, "
218 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
221 boost::shared_ptr<AutomationControl> existing = automation_control (param);
224 existing->alist()->set_state (**niter, 3000);
226 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
227 add_control (newcontrol);
228 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
229 newcontrol->set_list(al);
233 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
241 Automatable::get_automation_xml_state ()
243 Glib::Threads::Mutex::Lock lm (control_lock());
244 XMLNode* node = new XMLNode (Automatable::xml_node_name);
246 if (controls().empty()) {
250 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
251 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
253 node->add_child_nocopy (l->get_state ());
261 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
263 Glib::Threads::Mutex::Lock lm (control_lock());
265 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
267 if (c && (s != c->automation_state())) {
268 c->set_automation_state (s);
269 _a_session.set_dirty ();
274 Automatable::get_parameter_automation_state (Evoral::Parameter param)
276 AutoState result = Off;
278 boost::shared_ptr<AutomationControl> c = automation_control(param);
281 result = c->automation_state();
288 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
290 Glib::Threads::Mutex::Lock lm (control_lock());
292 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
294 if (c && (s != c->automation_style())) {
295 c->set_automation_style (s);
296 _a_session.set_dirty ();
301 Automatable::get_parameter_automation_style (Evoral::Parameter param)
303 Glib::Threads::Mutex::Lock lm (control_lock());
305 boost::shared_ptr<Evoral::Control> c = control(param);
306 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
309 return l->automation_style();
311 return Absolute; // whatever
316 Automatable::protect_automation ()
318 typedef set<Evoral::Parameter> ParameterSet;
319 const ParameterSet& automated_params = what_can_be_automated ();
321 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
323 boost::shared_ptr<Evoral::Control> c = control(*i);
324 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
326 switch (l->automation_state()) {
328 l->set_automation_state (Off);
331 l->set_automation_state (Play);
340 Automatable::transport_located (framepos_t now)
342 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
344 boost::shared_ptr<AutomationControl> c
345 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
347 boost::shared_ptr<AutomationList> l
348 = boost::dynamic_pointer_cast<AutomationList>(c->list());
351 l->start_write_pass (now);
358 Automatable::transport_stopped (framepos_t now)
360 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
362 boost::shared_ptr<AutomationControl> c
363 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
365 boost::shared_ptr<AutomationList> l
366 = boost::dynamic_pointer_cast<AutomationList>(c->list());
369 /* Stop any active touch gesture just before we mark the write pass
370 as finished. If we don't do this, the transport can end up stopped with
371 an AutomationList thinking that a touch is still in progress and,
372 when the transport is re-started, a touch will magically
373 be happening without it ever have being started in the usual way.
375 l->stop_touch (true, now);
376 l->write_pass_finished (now);
378 if (l->automation_playback()) {
379 c->set_value(c->list()->eval(now));
382 if (l->automation_state() == Write) {
383 l->set_automation_state (Touch);
390 boost::shared_ptr<Evoral::Control>
391 Automatable::control_factory(const Evoral::Parameter& param)
393 boost::shared_ptr<AutomationList> list(new AutomationList(param));
394 Evoral::Control* control = NULL;
395 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
396 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
398 control = new MidiTrack::MidiControl(mt, param);
400 warning << "MidiCCAutomation for non-MidiTrack" << endl;
402 } else if (param.type() == PluginAutomation) {
403 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
405 control = new PluginInsert::PluginControl(pi, param);
407 warning << "PluginAutomation for non-Plugin" << endl;
409 } else if (param.type() == GainAutomation) {
410 Amp* amp = dynamic_cast<Amp*>(this);
412 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
414 warning << "GainAutomation for non-Amp" << endl;
416 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
417 Pannable* pannable = dynamic_cast<Pannable*>(this);
419 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
421 warning << "PanAutomation for non-Pannable" << endl;
426 control = new AutomationControl(_a_session, param);
429 control->set_list(list);
430 return boost::shared_ptr<Evoral::Control>(control);
433 boost::shared_ptr<AutomationControl>
434 Automatable::automation_control (const Evoral::Parameter& id, bool create)
436 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
439 boost::shared_ptr<const AutomationControl>
440 Automatable::automation_control (const Evoral::Parameter& id) const
442 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
446 Automatable::clear_controls ()
448 _control_connections.drop_connections ();
449 ControlSet::clear_controls ();
453 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
457 /* this is a the default fallback for this virtual method. Derived Automatables
458 are free to override this to display the values of their parameters/controls
462 // Hack to display CC as integer value, rather than double
463 if (ac->parameter().type() == MidiCCAutomation) {
464 s << lrint (ac->get_value());
466 s << std::fixed << std::setprecision(3) << ac->get_value();