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.
20 #include "ardour/ardour.h"
26 #include <glibmm/miscutils.h>
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/stacktrace.h"
32 #include "midi++/names.h"
34 #include "ardour/automatable.h"
35 #include "ardour/amp.h"
36 #include "ardour/event_type_map.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/pannable.h"
39 #include "ardour/panner.h"
40 #include "ardour/pan_controllable.h"
41 #include "ardour/plugin_insert.h"
42 #include "ardour/session.h"
47 using namespace ARDOUR;
50 framecnt_t Automatable::_automation_interval = 0;
51 const string Automatable::xml_node_name = X_("Automation");
53 Automatable::Automatable(Session& session)
55 , _last_automation_snapshot(0)
59 Automatable::Automatable (const Automatable& other)
61 , _a_session (other._a_session)
62 , _last_automation_snapshot (0)
64 Glib::Mutex::Lock lm (other._control_lock);
66 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
67 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
72 Automatable::old_set_automation_state (const XMLNode& node)
74 const XMLProperty *prop;
76 if ((prop = node.property ("path")) != 0) {
77 load_automation (prop->value());
79 warning << _("Automation node has no path property") << endmsg;
82 _last_automation_snapshot = 0;
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::Mutex::Lock lm (control_lock());
107 set<Evoral::Parameter> tosave;
110 _last_automation_snapshot = 0;
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::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;
236 _last_automation_snapshot = 0;
242 Automatable::get_automation_xml_state ()
244 Glib::Mutex::Lock lm (control_lock());
245 XMLNode* node = new XMLNode (Automatable::xml_node_name);
247 if (controls().empty()) {
251 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
252 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
254 node->add_child_nocopy (l->get_state ());
262 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
264 Glib::Mutex::Lock lm (control_lock());
266 boost::shared_ptr<Evoral::Control> c = control (param, true);
267 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
269 if (s != l->automation_state()) {
270 l->set_automation_state (s);
271 _a_session.set_dirty ();
276 Automatable::get_parameter_automation_state (Evoral::Parameter param)
278 AutoState result = Off;
280 boost::shared_ptr<Evoral::Control> c = control(param);
281 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
284 result = l->automation_state();
291 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
293 Glib::Mutex::Lock lm (control_lock());
295 boost::shared_ptr<Evoral::Control> c = control(param, true);
296 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
298 if (s != l->automation_style()) {
299 l->set_automation_style (s);
300 _a_session.set_dirty ();
305 Automatable::get_parameter_automation_style (Evoral::Parameter param)
307 Glib::Mutex::Lock lm (control_lock());
309 boost::shared_ptr<Evoral::Control> c = control(param);
310 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
313 return l->automation_style();
315 return Absolute; // whatever
320 Automatable::protect_automation ()
322 typedef set<Evoral::Parameter> ParameterSet;
323 const ParameterSet& automated_params = what_can_be_automated ();
325 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
327 boost::shared_ptr<Evoral::Control> c = control(*i);
328 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
330 switch (l->automation_state()) {
332 l->set_automation_state (Off);
335 l->set_automation_state (Play);
344 Automatable::automation_snapshot (framepos_t now, bool force)
346 if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
348 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
349 boost::shared_ptr<AutomationControl> c
350 = boost::dynamic_pointer_cast<AutomationControl>(i->second);
351 if (_a_session.transport_rolling() && c->automation_write()) {
352 c->list()->rt_add (now, i->second->user_double());
356 _last_automation_snapshot = now;
361 Automatable::transport_stopped (framepos_t now)
363 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
365 boost::shared_ptr<AutomationControl> c
366 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
368 boost::shared_ptr<AutomationList> l
369 = boost::dynamic_pointer_cast<AutomationList>(c->list());
372 /* Stop any active touch gesture just before we mark the write pass
373 as finished. If we don't do this, the transport can end up stopped with
374 an AutomationList thinking that a touch is still in progress and,
375 when the transport is re-started, a touch will magically
376 be happening without it ever have being started in the usual way.
378 l->stop_touch (true, now);
379 l->write_pass_finished (now);
381 if (l->automation_playback()) {
382 c->set_value(c->list()->eval(now));
385 if (l->automation_state() == Write) {
386 l->set_automation_state (Touch);
393 boost::shared_ptr<Evoral::Control>
394 Automatable::control_factory(const Evoral::Parameter& param)
396 boost::shared_ptr<AutomationList> list(new AutomationList(param));
397 Evoral::Control* control = NULL;
398 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
399 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
401 control = new MidiTrack::MidiControl(mt, param);
403 warning << "MidiCCAutomation for non-MidiTrack" << endl;
405 } else if (param.type() == PluginAutomation) {
406 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
408 control = new PluginInsert::PluginControl(pi, param);
410 warning << "PluginAutomation for non-Plugin" << endl;
412 } else if (param.type() == GainAutomation) {
413 Amp* amp = dynamic_cast<Amp*>(this);
415 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
417 warning << "GainAutomation for non-Amp" << endl;
419 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
420 Pannable* pannable = dynamic_cast<Pannable*>(this);
422 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
424 warning << "PanAutomation for non-Pannable" << endl;
429 control = new AutomationControl(_a_session, param);
432 control->set_list(list);
433 return boost::shared_ptr<Evoral::Control>(control);
436 boost::shared_ptr<AutomationControl>
437 Automatable::automation_control (const Evoral::Parameter& id, bool create)
439 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
442 boost::shared_ptr<const AutomationControl>
443 Automatable::automation_control (const Evoral::Parameter& id) const
445 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
449 Automatable::clear_controls ()
451 _control_connections.drop_connections ();
452 ControlSet::clear_controls ();
456 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
460 /* this is a the default fallback for this virtual method. Derived Automatables
461 are free to override this to display the values of their parameters/controls
465 // Hack to display CC as integer value, rather than double
466 if (ac->parameter().type() == MidiCCAutomation) {
467 s << lrint (ac->get_value());
469 s << std::fixed << std::setprecision(3) << ac->get_value();