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>
25 #include <pbd/error.h>
26 #include <pbd/enumwriter.h>
27 #include <midi++/names.h>
28 #include <ardour/session.h>
29 #include <ardour/automatable.h>
30 #include <ardour/midi_track.h>
35 using namespace ARDOUR;
38 nframes_t Automatable::_automation_interval = 0;
40 Automatable::Automatable(Session& _session, const string& name)
41 : SessionObject(_session, name)
42 , _last_automation_snapshot(0)
46 Automatable::old_set_automation_state (const XMLNode& node)
48 const XMLProperty *prop;
50 if ((prop = node.property ("path")) != 0) {
51 load_automation (prop->value());
53 warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg;
56 if ((prop = node.property ("visible")) != 0) {
60 _visible_controls.clear ();
62 sstr << prop->value();
68 mark_automation_visible (Parameter(PluginAutomation, what), true);
72 _last_automation_snapshot = 0;
78 Automatable::load_automation (const string& path)
82 if (path[0] == '/') { // legacy
85 fullpath = _session.automation_dir();
88 ifstream in (fullpath.c_str());
91 warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
95 Glib::Mutex::Lock lm (_control_lock);
96 set<Parameter> tosave;
99 _last_automation_snapshot = 0;
106 in >> port; if (!in) break;
107 in >> when; if (!in) goto bad;
108 in >> value; if (!in) goto bad;
110 /* FIXME: this is legacy and only used for plugin inserts? I think? */
111 boost::shared_ptr<Evoral::Control> c = control (Parameter(PluginAutomation, port), true);
112 c->list()->add (when, value);
113 tosave.insert (Parameter(PluginAutomation, port));
119 error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg;
125 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
127 Parameter param = ac->parameter();
129 _can_automate_list.insert(param);
131 // Sync everything (derived classes) up to initial values
132 auto_state_changed(param);
136 Automatable::what_has_visible_data (set<Parameter>& s) const
138 Glib::Mutex::Lock lm (_control_lock);
139 set<Parameter>::const_iterator li;
141 for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
147 Automatable::describe_parameter (Parameter param)
149 /* derived classes like PluginInsert should override this */
151 if (param == Parameter(GainAutomation)) {
153 } else if (param.type() == PanAutomation) {
154 /* ID's are zero-based, present them as 1-based */
155 return (string_compose(_("Pan %1"), param.id() + 1));
156 } else if (param.type() == MidiCCAutomation) {
157 return string_compose("CC %1 (%2) [%3]",
158 param.id() + 1, 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() == MidiChannelAftertouchAutomation) {
164 return string_compose("Aftertouch [%1]", int(param.channel()) + 1);
166 return param.symbol();
171 Automatable::can_automate (Parameter what)
173 _can_automate_list.insert (what);
177 Automatable::mark_automation_visible (Parameter what, bool yn)
180 _visible_controls.insert (what);
182 set<Parameter>::iterator i;
184 if ((i = _visible_controls.find (what)) != _visible_controls.end()) {
185 _visible_controls.erase (i);
190 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
191 * had a single automation parameter, with it's type implicit. Derived objects should
192 * pass that type and it will be used for the untyped AutomationList found.
195 Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
197 Glib::Mutex::Lock lm (_control_lock);
199 /* Don't clear controls, since some may be special derived Controllable classes */
201 _visible_controls.clear ();
203 XMLNodeList nlist = node.children();
204 XMLNodeIterator niter;
206 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
208 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
209 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
213 if ((*niter)->name() == "AutomationList") {
215 const XMLProperty* id_prop = (*niter)->property("automation-id");
217 Parameter param = (id_prop ? Parameter(id_prop->value()) : legacy_param);
219 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
222 warning << "AutomationList node without automation-id property, "
223 << "using default: " << legacy_param.symbol() << endmsg;
224 al->set_parameter(legacy_param);
227 boost::shared_ptr<Evoral::Control> existing = control(param);
229 existing->set_list(al);
231 add_control(control_factory(al));
234 error << "Expected AutomationList node, got '" << (*niter)->name() << endmsg;
238 _last_automation_snapshot = 0;
244 Automatable::get_automation_state ()
246 Glib::Mutex::Lock lm (_control_lock);
247 XMLNode* node = new XMLNode (X_("Automation"));
249 if (_controls.empty()) {
253 for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
254 boost::shared_ptr<AutomationList> l
255 = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
256 node->add_child_nocopy (l->get_state ());
263 Automatable::set_parameter_automation_state (Parameter param, AutoState s)
265 Glib::Mutex::Lock lm (_control_lock);
267 boost::shared_ptr<Evoral::Control> c = control (param, true);
268 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
270 if (s != l->automation_state()) {
271 l->set_automation_state (s);
272 _session.set_dirty ();
277 Automatable::get_parameter_automation_state (Parameter param, bool lock)
279 AutoState result = Off;
282 _control_lock.lock();
284 boost::shared_ptr<Evoral::Control> c = control(param);
285 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
288 result = l->automation_state();
291 _control_lock.unlock();
297 Automatable::set_parameter_automation_style (Parameter param, AutoStyle s)
299 Glib::Mutex::Lock lm (_control_lock);
301 boost::shared_ptr<Evoral::Control> c = control(param, true);
302 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
304 if (s != l->automation_style()) {
305 l->set_automation_style (s);
306 _session.set_dirty ();
311 Automatable::get_parameter_automation_style (Parameter param)
313 Glib::Mutex::Lock lm (_control_lock);
315 boost::shared_ptr<Evoral::Control> c = control(param);
316 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
319 return l->automation_style();
321 return Absolute; // whatever
326 Automatable::protect_automation ()
328 typedef set<Evoral::Parameter> ParameterSet;
329 ParameterSet automated_params;
331 what_has_data (automated_params);
333 for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
335 boost::shared_ptr<Evoral::Control> c = control(*i);
336 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
338 switch (l->automation_state()) {
340 l->set_automation_state (Off);
343 l->set_automation_state (Play);
352 Automatable::automation_snapshot (nframes_t now, bool force)
354 if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
356 for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) {
357 boost::shared_ptr<AutomationControl> c
358 = boost::dynamic_pointer_cast<AutomationControl>(i->second);
359 if (c->automation_write()) {
360 c->list()->rt_add (now, i->second->user_value());
364 _last_automation_snapshot = now;
369 Automatable::transport_stopped (nframes_t now)
371 for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
373 boost::shared_ptr<AutomationControl> c
374 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
375 boost::shared_ptr<AutomationList> l
376 = boost::dynamic_pointer_cast<AutomationList>(c->list());
378 c->list()->reposition_for_rt_add (now);
380 if (c->automation_state() != Off) {
381 c->set_value(c->list()->eval(now));
386 boost::shared_ptr<Evoral::Control>
387 Automatable::control_factory(boost::shared_ptr<Evoral::ControlList> list) const
389 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(list);
391 if (l->parameter().type() >= MidiCCAutomation
392 && l->parameter().type() <= MidiChannelAftertouchAutomation) {
393 return boost::shared_ptr<Evoral::Control>(new MidiTrack::MidiControl((MidiTrack*)this, l));
395 return boost::shared_ptr<Evoral::Control>(new AutomationControl(_session, l));
399 boost::shared_ptr<Evoral::ControlList>
400 Automatable::control_list_factory(const Evoral::Parameter& param) const
402 return boost::shared_ptr<Evoral::ControlList>(new AutomationList(param));