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 if ((prop = node.property ("visible")) != 0) {
86 _visible_controls.clear ();
88 sstr << prop->value();
94 mark_automation_visible (Evoral::Parameter(PluginAutomation, 0, what), true);
98 _last_automation_snapshot = 0;
104 Automatable::load_automation (const string& path)
108 if (Glib::path_is_absolute (path)) { // legacy
111 fullpath = _a_session.automation_dir();
114 ifstream in (fullpath.c_str());
117 warning << string_compose(_("cannot open %2 to load automation data (%3)")
118 , fullpath, strerror (errno)) << endmsg;
122 Glib::Mutex::Lock lm (control_lock());
123 set<Evoral::Parameter> tosave;
126 _last_automation_snapshot = 0;
133 in >> port; if (!in) break;
134 in >> when; if (!in) goto bad;
135 in >> value; if (!in) goto bad;
137 Evoral::Parameter param(PluginAutomation, 0, port);
138 /* FIXME: this is legacy and only used for plugin inserts? I think? */
139 boost::shared_ptr<Evoral::Control> c = control (param, true);
140 c->list()->add (when, value);
141 tosave.insert (param);
147 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
153 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
155 Evoral::Parameter param = ac->parameter();
157 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
160 al->automation_state_changed.connect_same_thread (
161 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
164 ControlSet::add_control (ac);
165 _can_automate_list.insert (param);
167 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
171 Automatable::what_has_visible_data(set<Evoral::Parameter>& s) const
173 Glib::Mutex::Lock lm (control_lock());
174 set<Evoral::Parameter>::const_iterator li;
176 for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
182 Automatable::describe_parameter (Evoral::Parameter param)
184 /* derived classes like PluginInsert should override this */
186 if (param == Evoral::Parameter(GainAutomation)) {
188 } else if (param.type() == MidiCCAutomation) {
189 return string_compose("%1: %2 [%3]",
190 param.id(), midi_name(param.id()), int(param.channel()) + 1);
191 } else if (param.type() == MidiPgmChangeAutomation) {
192 return string_compose("Program [%1]", int(param.channel()) + 1);
193 } else if (param.type() == MidiPitchBenderAutomation) {
194 return string_compose("Bender [%1]", int(param.channel()) + 1);
195 } else if (param.type() == MidiChannelPressureAutomation) {
196 return string_compose("Pressure [%1]", int(param.channel()) + 1);
198 return EventTypeMap::instance().to_symbol(param);
203 Automatable::can_automate (Evoral::Parameter what)
205 _can_automate_list.insert (what);
209 Automatable::mark_automation_visible (Evoral::Parameter what, bool yn)
212 _visible_controls.insert (what);
214 set<Evoral::Parameter>::iterator i;
216 if ((i = _visible_controls.find (what)) != _visible_controls.end()) {
217 _visible_controls.erase (i);
222 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
223 * had a single automation parameter, with it's type implicit. Derived objects should
224 * pass that type and it will be used for the untyped AutomationList found.
227 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
229 Glib::Mutex::Lock lm (control_lock());
231 /* Don't clear controls, since some may be special derived Controllable classes */
233 _visible_controls.clear ();
235 XMLNodeList nlist = node.children();
236 XMLNodeIterator niter;
238 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
240 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
241 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
245 if ((*niter)->name() == "AutomationList") {
247 const XMLProperty* id_prop = (*niter)->property("automation-id");
249 Evoral::Parameter param = (id_prop
250 ? EventTypeMap::instance().new_parameter(id_prop->value())
253 if (param.type() == NullAutomation) {
254 warning << "Automation has null type" << endl;
259 warning << "AutomationList node without automation-id property, "
260 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
263 boost::shared_ptr<AutomationControl> existing = automation_control (param);
266 existing->alist()->set_state (**niter, 3000);
268 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
269 add_control (newcontrol);
270 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
271 newcontrol->set_list(al);
275 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
279 _last_automation_snapshot = 0;
285 Automatable::get_automation_xml_state ()
287 Glib::Mutex::Lock lm (control_lock());
288 XMLNode* node = new XMLNode (Automatable::xml_node_name);
290 if (controls().empty()) {
294 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
295 boost::shared_ptr<AutomationList> l
296 = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
298 node->add_child_nocopy (l->get_state ());
306 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
308 Glib::Mutex::Lock lm (control_lock());
310 boost::shared_ptr<Evoral::Control> c = control (param, true);
311 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
313 if (s != l->automation_state()) {
314 l->set_automation_state (s);
315 _a_session.set_dirty ();
320 Automatable::get_parameter_automation_state (Evoral::Parameter param)
322 AutoState result = Off;
324 boost::shared_ptr<Evoral::Control> c = control(param);
325 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
328 result = l->automation_state();
335 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
337 Glib::Mutex::Lock lm (control_lock());
339 boost::shared_ptr<Evoral::Control> c = control(param, true);
340 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
342 if (s != l->automation_style()) {
343 l->set_automation_style (s);
344 _a_session.set_dirty ();
349 Automatable::get_parameter_automation_style (Evoral::Parameter param)
351 Glib::Mutex::Lock lm (control_lock());
353 boost::shared_ptr<Evoral::Control> c = control(param);
354 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
357 return l->automation_style();
359 return Absolute; // whatever
364 Automatable::protect_automation ()
366 typedef set<Evoral::Parameter> ParameterSet;
367 ParameterSet automated_params;
369 what_has_data(automated_params);
371 for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
373 boost::shared_ptr<Evoral::Control> c = control(*i);
374 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
376 switch (l->automation_state()) {
378 l->set_automation_state (Off);
381 l->set_automation_state (Play);
390 Automatable::automation_snapshot (framepos_t now, bool force)
392 if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
394 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
395 boost::shared_ptr<AutomationControl> c
396 = boost::dynamic_pointer_cast<AutomationControl>(i->second);
397 if (_a_session.transport_rolling() && c->automation_write()) {
398 c->list()->rt_add (now, i->second->user_double());
402 _last_automation_snapshot = now;
407 Automatable::transport_stopped (framepos_t now)
409 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
411 boost::shared_ptr<AutomationControl> c
412 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
414 boost::shared_ptr<AutomationList> l
415 = boost::dynamic_pointer_cast<AutomationList>(c->list());
418 /* Stop any active touch gesture just before we mark the write pass
419 as finished. If we don't do this, the transport can end up stopped with
420 an AutomationList thinking that a touch is still in progress and,
421 when the transport is re-started, a touch will magically
422 be happening without it ever have being started in the usual way.
424 l->stop_touch (true, now);
425 l->write_pass_finished (now);
427 if (l->automation_playback()) {
428 c->set_value(c->list()->eval(now));
431 if (l->automation_state() == Write) {
432 l->set_automation_state (Touch);
439 boost::shared_ptr<Evoral::Control>
440 Automatable::control_factory(const Evoral::Parameter& param)
442 boost::shared_ptr<AutomationList> list(new AutomationList(param));
443 Evoral::Control* control = NULL;
444 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
445 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
447 control = new MidiTrack::MidiControl(mt, param);
449 warning << "MidiCCAutomation for non-MidiTrack" << endl;
451 } else if (param.type() == PluginAutomation) {
452 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
454 control = new PluginInsert::PluginControl(pi, param);
456 warning << "PluginAutomation for non-Plugin" << endl;
458 } else if (param.type() == GainAutomation) {
459 Amp* amp = dynamic_cast<Amp*>(this);
461 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
463 warning << "GainAutomation for non-Amp" << endl;
465 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
466 Pannable* pannable = dynamic_cast<Pannable*>(this);
468 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
470 warning << "PanAutomation for non-Pannable" << endl;
475 control = new AutomationControl(_a_session, param);
478 control->set_list(list);
479 return boost::shared_ptr<Evoral::Control>(control);
482 boost::shared_ptr<AutomationControl>
483 Automatable::automation_control (const Evoral::Parameter& id, bool create)
485 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
488 boost::shared_ptr<const AutomationControl>
489 Automatable::automation_control (const Evoral::Parameter& id) const
491 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
495 Automatable::clear_controls ()
497 _control_connections.drop_connections ();
498 ControlSet::clear_controls ();
502 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
506 /* this is a the default fallback for this virtual method. Derived Automatables
507 are free to override this to display the values of their parameters/controls
511 // Hack to display CC as integer value, rather than double
512 if (ac->parameter().type() == MidiCCAutomation) {
513 s << lrint (ac->get_value());
515 s << std::fixed << std::setprecision(3) << ac->get_value();