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 if (list_did_write) {
402 c->commit_transaction ();
405 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
407 if (l->automation_state () == Write) {
408 l->set_automation_state (Touch);
411 if (l->automation_playback ()) {
412 c->set_value_unchecked (c->list ()->eval (now));
417 boost::shared_ptr<Evoral::Control>
418 Automatable::control_factory(const Evoral::Parameter& param)
420 Evoral::Control* control = NULL;
421 bool make_list = true;
422 ParameterDescriptor desc(param);
423 boost::shared_ptr<AutomationList> list;
424 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
425 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
427 control = new MidiTrack::MidiControl(mt, param);
428 make_list = false; // No list, this is region "automation"
430 } else if (param.type() == PluginAutomation) {
431 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
433 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
434 control = new PluginInsert::PluginControl(pi, param, desc);
436 warning << "PluginAutomation for non-Plugin" << endl;
438 } else if (param.type() == PluginPropertyAutomation) {
439 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
441 desc = pi->plugin(0)->get_property_descriptor(param.id());
442 if (desc.datatype != Variant::NOTHING) {
443 if (!Variant::type_is_numeric(desc.datatype)) {
444 make_list = false; // Can't automate non-numeric data yet
446 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
448 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
451 warning << "PluginPropertyAutomation for non-Plugin" << endl;
453 } else if (param.type() == GainAutomation) {
454 Amp* amp = dynamic_cast<Amp*>(this);
456 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
458 warning << "GainAutomation for non-Amp" << endl;
460 } else if (param.type() == TrimAutomation) {
461 Amp* amp = dynamic_cast<Amp*>(this);
463 control = new Amp::GainControl(X_("trimcontrol"), _a_session, amp, param);
465 warning << "TrimAutomation for non-Amp" << endl;
467 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
468 Pannable* pannable = dynamic_cast<Pannable*>(this);
470 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
472 warning << "PanAutomation for non-Pannable" << endl;
476 if (make_list && !list) {
477 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
481 control = new AutomationControl(_a_session, param, desc, list);
484 return boost::shared_ptr<Evoral::Control>(control);
487 boost::shared_ptr<AutomationControl>
488 Automatable::automation_control (const Evoral::Parameter& id, bool create)
490 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
493 boost::shared_ptr<const AutomationControl>
494 Automatable::automation_control (const Evoral::Parameter& id) const
496 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
500 Automatable::clear_controls ()
502 _control_connections.drop_connections ();
503 ControlSet::clear_controls ();
507 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
509 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
513 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
515 Controls::const_iterator li;
517 next_event.when = std::numeric_limits<double>::max();
519 for (li = _controls.begin(); li != _controls.end(); ++li) {
520 boost::shared_ptr<AutomationControl> c
521 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
523 if (only_active && (!c || !c->automation_playback())) {
527 Evoral::ControlList::const_iterator i;
528 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
529 Evoral::ControlEvent cp (now, 0.0f);
534 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
535 i != alist->end() && (*i)->when < end; ++i) {
536 if ((*i)->when > now) {
541 if (i != alist->end() && (*i)->when < end) {
542 if ((*i)->when < next_event.when) {
543 next_event.when = (*i)->when;
548 return next_event.when != std::numeric_limits<double>::max();