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.
23 #include "pbd/gstdio_compat.h"
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.h"
35 #include "ardour/plugin_insert.h"
36 #include "ardour/session.h"
37 #include "ardour/uri_map.h"
38 #include "ardour/value_as_string.h"
43 using namespace ARDOUR;
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();
102 FILE * in = g_fopen (fullpath.c_str (), "rb");
105 warning << string_compose(_("cannot open %2 to load automation data (%3)")
106 , fullpath, strerror (errno)) << endmsg;
110 Glib::Threads::Mutex::Lock lm (control_lock());
111 set<Evoral::Parameter> tosave;
119 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
126 Evoral::Parameter param(PluginAutomation, 0, port);
127 /* FIXME: this is legacy and only used for plugin inserts? I think? */
128 boost::shared_ptr<Evoral::Control> c = control (param, true);
129 c->list()->add (when, value);
130 tosave.insert (param);
137 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
144 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
146 Evoral::Parameter param = ac->parameter();
148 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
151 al->automation_state_changed.connect_same_thread (
153 boost::bind (&Automatable::automation_list_automation_state_changed,
154 this, ac->parameter(), _1));
157 ControlSet::add_control (ac);
160 _can_automate_list.insert (param);
161 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
166 Automatable::describe_parameter (Evoral::Parameter param)
168 /* derived classes like PluginInsert should override this */
170 if (param == Evoral::Parameter(GainAutomation)) {
172 } else if (param.type() == TrimAutomation) {
174 } else if (param.type() == MuteAutomation) {
176 } else if (param.type() == MidiCCAutomation) {
177 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
178 } else if (param.type() == MidiPgmChangeAutomation) {
179 return string_compose("Program [%1]", int(param.channel()) + 1);
180 } else if (param.type() == MidiPitchBenderAutomation) {
181 return string_compose("Bender [%1]", int(param.channel()) + 1);
182 } else if (param.type() == MidiChannelPressureAutomation) {
183 return string_compose("Pressure [%1]", int(param.channel()) + 1);
185 } else if (param.type() == PluginPropertyAutomation) {
186 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
189 return EventTypeMap::instance().to_symbol(param);
194 Automatable::can_automate (Evoral::Parameter what)
196 _can_automate_list.insert (what);
199 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
200 * had a single automation parameter, with it's type implicit. Derived objects should
201 * pass that type and it will be used for the untyped AutomationList found.
204 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
206 Glib::Threads::Mutex::Lock lm (control_lock());
208 /* Don't clear controls, since some may be special derived Controllable classes */
210 XMLNodeList nlist = node.children();
211 XMLNodeIterator niter;
213 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
215 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
216 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
220 if ((*niter)->name() == "AutomationList") {
222 const XMLProperty* id_prop = (*niter)->property("automation-id");
224 Evoral::Parameter param = (id_prop
225 ? EventTypeMap::instance().from_symbol(id_prop->value())
228 if (param.type() == NullAutomation) {
229 warning << "Automation has null type" << endl;
234 warning << "AutomationList node without automation-id property, "
235 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
238 boost::shared_ptr<AutomationControl> existing = automation_control (param);
241 existing->alist()->set_state (**niter, 3000);
243 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
244 add_control (newcontrol);
245 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
246 newcontrol->set_list(al);
250 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
258 Automatable::get_automation_xml_state ()
260 Glib::Threads::Mutex::Lock lm (control_lock());
261 XMLNode* node = new XMLNode (Automatable::xml_node_name);
263 if (controls().empty()) {
267 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
268 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
269 if (l && !l->empty()) {
270 node->add_child_nocopy (l->get_state ());
278 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
280 Glib::Threads::Mutex::Lock lm (control_lock());
282 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
284 if (c && (s != c->automation_state())) {
285 c->set_automation_state (s);
286 _a_session.set_dirty ();
287 AutomationStateChanged(); /* Emit signal */
292 Automatable::get_parameter_automation_state (Evoral::Parameter param)
294 AutoState result = Off;
296 boost::shared_ptr<AutomationControl> c = automation_control(param);
299 result = c->automation_state();
306 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
308 Glib::Threads::Mutex::Lock lm (control_lock());
310 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
312 if (c && (s != c->automation_style())) {
313 c->set_automation_style (s);
314 _a_session.set_dirty ();
319 Automatable::get_parameter_automation_style (Evoral::Parameter param)
321 Glib::Threads::Mutex::Lock lm (control_lock());
323 boost::shared_ptr<Evoral::Control> c = control(param);
324 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
327 return l->automation_style();
329 return Absolute; // whatever
334 Automatable::protect_automation ()
336 typedef set<Evoral::Parameter> ParameterSet;
337 const ParameterSet& automated_params = what_can_be_automated ();
339 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
341 boost::shared_ptr<Evoral::Control> c = control(*i);
342 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
344 switch (l->automation_state()) {
346 l->set_automation_state (Off);
349 l->set_automation_state (Play);
358 Automatable::transport_located (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 l->start_write_pass (now);
376 Automatable::transport_stopped (framepos_t now)
378 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
379 boost::shared_ptr<AutomationControl> c =
380 boost::dynamic_pointer_cast<AutomationControl>(li->second);
385 boost::shared_ptr<AutomationList> l =
386 boost::dynamic_pointer_cast<AutomationList>(c->list());
391 /* Stop any active touch gesture just before we mark the write pass
392 as finished. If we don't do this, the transport can end up stopped with
393 an AutomationList thinking that a touch is still in progress and,
394 when the transport is re-started, a touch will magically
395 be happening without it ever have being started in the usual way.
397 const bool list_did_write = !l->in_new_write_pass ();
399 l->stop_touch (true, now);
401 c->commit_transaction (list_did_write);
403 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
405 if (l->automation_state () == Write) {
406 l->set_automation_state (Touch);
409 if (l->automation_playback ()) {
410 c->set_value_unchecked (c->list ()->eval (now));
415 boost::shared_ptr<Evoral::Control>
416 Automatable::control_factory(const Evoral::Parameter& param)
418 Evoral::Control* control = NULL;
419 bool make_list = true;
420 ParameterDescriptor desc(param);
421 boost::shared_ptr<AutomationList> list;
422 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
423 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
425 control = new MidiTrack::MidiControl(mt, param);
426 make_list = false; // No list, this is region "automation"
428 } else if (param.type() == PluginAutomation) {
429 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
431 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
432 control = new PluginInsert::PluginControl(pi, param, desc);
434 warning << "PluginAutomation for non-Plugin" << endl;
436 } else if (param.type() == PluginPropertyAutomation) {
437 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
439 desc = pi->plugin(0)->get_property_descriptor(param.id());
440 if (desc.datatype != Variant::NOTHING) {
441 if (!Variant::type_is_numeric(desc.datatype)) {
442 make_list = false; // Can't automate non-numeric data yet
444 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
446 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
449 warning << "PluginPropertyAutomation for non-Plugin" << endl;
451 } else if (param.type() == GainAutomation) {
452 control = new Amp::GainControl(_a_session, param);
453 } else if (param.type() == TrimAutomation) {
454 control = new Amp::GainControl(_a_session, param);
455 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
456 Pannable* pannable = dynamic_cast<Pannable*>(this);
458 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
460 warning << "PanAutomation for non-Pannable" << endl;
464 if (make_list && !list) {
465 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
469 control = new AutomationControl(_a_session, param, desc, list);
472 return boost::shared_ptr<Evoral::Control>(control);
475 boost::shared_ptr<AutomationControl>
476 Automatable::automation_control (const Evoral::Parameter& id, bool create)
478 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
481 boost::shared_ptr<const AutomationControl>
482 Automatable::automation_control (const Evoral::Parameter& id) const
484 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
488 Automatable::clear_controls ()
490 _control_connections.drop_connections ();
491 ControlSet::clear_controls ();
495 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
497 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
501 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
503 Controls::const_iterator li;
505 next_event.when = std::numeric_limits<double>::max();
507 for (li = _controls.begin(); li != _controls.end(); ++li) {
508 boost::shared_ptr<AutomationControl> c
509 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
511 if (only_active && (!c || !c->automation_playback())) {
515 Evoral::ControlList::const_iterator i;
516 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
517 Evoral::ControlEvent cp (now, 0.0f);
522 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
523 i != alist->end() && (*i)->when < end; ++i) {
524 if ((*i)->when > now) {
529 if (i != alist->end() && (*i)->when < end) {
530 if ((*i)->when < next_event.when) {
531 next_event.when = (*i)->when;
536 return next_event.when != std::numeric_limits<double>::max();