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 "ardour/amp.h"
29 #include "ardour/automatable.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/midi_track.h"
32 #include "ardour/pan_controllable.h"
33 #include "ardour/pannable.h"
34 #include "ardour/plugin_insert.h"
35 #include "ardour/session.h"
40 using namespace ARDOUR;
43 const string Automatable::xml_node_name = X_("Automation");
45 Automatable::Automatable(Session& session)
50 Automatable::Automatable (const Automatable& other)
52 , _a_session (other._a_session)
54 Glib::Threads::Mutex::Lock lm (other._control_lock);
56 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
57 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
62 Automatable::~Automatable ()
65 Glib::Threads::Mutex::Lock lm (_control_lock);
67 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
68 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
74 Automatable::old_set_automation_state (const XMLNode& node)
76 const XMLProperty *prop;
78 if ((prop = node.property ("path")) != 0) {
79 load_automation (prop->value());
81 warning << _("Automation node has no path property") << endmsg;
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::Threads::Mutex::Lock lm (control_lock());
107 set<Evoral::Parameter> tosave;
115 in >> port; if (!in) break;
116 in >> when; if (!in) goto bad;
117 in >> value; if (!in) goto bad;
119 Evoral::Parameter param(PluginAutomation, 0, port);
120 /* FIXME: this is legacy and only used for plugin inserts? I think? */
121 boost::shared_ptr<Evoral::Control> c = control (param, true);
122 c->list()->add (when, value);
123 tosave.insert (param);
129 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
135 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
137 Evoral::Parameter param = ac->parameter();
139 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
142 al->automation_state_changed.connect_same_thread (
144 boost::bind (&Automatable::automation_list_automation_state_changed,
145 this, ac->parameter(), _1));
148 ControlSet::add_control (ac);
149 _can_automate_list.insert (param);
152 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
157 Automatable::describe_parameter (Evoral::Parameter param)
159 /* derived classes like PluginInsert should override this */
161 if (param == Evoral::Parameter(GainAutomation)) {
163 } else if (param.type() == MuteAutomation) {
165 } else if (param.type() == MidiCCAutomation) {
166 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
167 } else if (param.type() == MidiPgmChangeAutomation) {
168 return string_compose("Program [%1]", int(param.channel()) + 1);
169 } else if (param.type() == MidiPitchBenderAutomation) {
170 return string_compose("Bender [%1]", int(param.channel()) + 1);
171 } else if (param.type() == MidiChannelPressureAutomation) {
172 return string_compose("Pressure [%1]", int(param.channel()) + 1);
174 return EventTypeMap::instance().to_symbol(param);
179 Automatable::can_automate (Evoral::Parameter what)
181 _can_automate_list.insert (what);
184 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
185 * had a single automation parameter, with it's type implicit. Derived objects should
186 * pass that type and it will be used for the untyped AutomationList found.
189 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
191 Glib::Threads::Mutex::Lock lm (control_lock());
193 /* Don't clear controls, since some may be special derived Controllable classes */
195 XMLNodeList nlist = node.children();
196 XMLNodeIterator niter;
198 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
200 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
201 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
205 if ((*niter)->name() == "AutomationList") {
207 const XMLProperty* id_prop = (*niter)->property("automation-id");
209 Evoral::Parameter param = (id_prop
210 ? EventTypeMap::instance().new_parameter(id_prop->value())
213 if (param.type() == NullAutomation) {
214 warning << "Automation has null type" << endl;
219 warning << "AutomationList node without automation-id property, "
220 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
223 boost::shared_ptr<AutomationControl> existing = automation_control (param);
226 existing->alist()->set_state (**niter, 3000);
228 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
229 add_control (newcontrol);
230 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
231 newcontrol->set_list(al);
235 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
243 Automatable::get_automation_xml_state ()
245 Glib::Threads::Mutex::Lock lm (control_lock());
246 XMLNode* node = new XMLNode (Automatable::xml_node_name);
248 if (controls().empty()) {
252 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
253 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
255 node->add_child_nocopy (l->get_state ());
263 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
265 Glib::Threads::Mutex::Lock lm (control_lock());
267 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
269 if (c && (s != c->automation_state())) {
270 c->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<AutomationControl> c = automation_control(param);
283 result = c->automation_state();
290 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
292 Glib::Threads::Mutex::Lock lm (control_lock());
294 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
296 if (c && (s != c->automation_style())) {
297 c->set_automation_style (s);
298 _a_session.set_dirty ();
303 Automatable::get_parameter_automation_style (Evoral::Parameter param)
305 Glib::Threads::Mutex::Lock lm (control_lock());
307 boost::shared_ptr<Evoral::Control> c = control(param);
308 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
311 return l->automation_style();
313 return Absolute; // whatever
318 Automatable::protect_automation ()
320 typedef set<Evoral::Parameter> ParameterSet;
321 const ParameterSet& automated_params = what_can_be_automated ();
323 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
325 boost::shared_ptr<Evoral::Control> c = control(*i);
326 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
328 switch (l->automation_state()) {
330 l->set_automation_state (Off);
333 l->set_automation_state (Play);
342 Automatable::transport_located (framepos_t now)
344 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
346 boost::shared_ptr<AutomationControl> c
347 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
349 boost::shared_ptr<AutomationList> l
350 = boost::dynamic_pointer_cast<AutomationList>(c->list());
353 l->start_write_pass (now);
360 Automatable::transport_stopped (framepos_t now)
362 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
364 boost::shared_ptr<AutomationControl> c
365 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
367 boost::shared_ptr<AutomationList> l
368 = boost::dynamic_pointer_cast<AutomationList>(c->list());
371 /* Stop any active touch gesture just before we mark the write pass
372 as finished. If we don't do this, the transport can end up stopped with
373 an AutomationList thinking that a touch is still in progress and,
374 when the transport is re-started, a touch will magically
375 be happening without it ever have being started in the usual way.
377 l->stop_touch (true, now);
378 l->write_pass_finished (now);
380 if (l->automation_playback()) {
381 c->set_value(c->list()->eval(now));
384 if (l->automation_state() == Write) {
385 l->set_automation_state (Touch);
392 boost::shared_ptr<Evoral::Control>
393 Automatable::control_factory(const Evoral::Parameter& param)
395 boost::shared_ptr<AutomationList> list(new AutomationList(param));
396 Evoral::Control* control = NULL;
397 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
398 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
400 control = new MidiTrack::MidiControl(mt, param);
401 list.reset(); // No list, this is region "automation"
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();