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());
368 if (l && l->automation_state () == Write) {
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);
400 if (list_did_write) {
401 c->commit_transaction ();
403 l->write_pass_finished (now, Config->get_automation_thinning_factor());
405 if (l->automation_playback()) {
406 c->set_value(c->list()->eval(now));
409 if (l->automation_state() == Write) {
410 l->set_automation_state (Touch);
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 Amp* amp = dynamic_cast<Amp*>(this);
454 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
456 warning << "GainAutomation for non-Amp" << endl;
458 } else if (param.type() == TrimAutomation) {
459 Amp* amp = dynamic_cast<Amp*>(this);
461 control = new Amp::GainControl(X_("trimcontrol"), _a_session, amp, param);
463 warning << "TrimAutomation 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;
474 if (make_list && !list) {
475 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
479 control = new AutomationControl(_a_session, param, desc, list);
482 return boost::shared_ptr<Evoral::Control>(control);
485 boost::shared_ptr<AutomationControl>
486 Automatable::automation_control (const Evoral::Parameter& id, bool create)
488 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
491 boost::shared_ptr<const AutomationControl>
492 Automatable::automation_control (const Evoral::Parameter& id) const
494 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
498 Automatable::clear_controls ()
500 _control_connections.drop_connections ();
501 ControlSet::clear_controls ();
505 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
507 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
511 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
513 Controls::const_iterator li;
515 next_event.when = std::numeric_limits<double>::max();
517 for (li = _controls.begin(); li != _controls.end(); ++li) {
518 boost::shared_ptr<AutomationControl> c
519 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
521 if (only_active && (!c || !c->automation_playback())) {
525 Evoral::ControlList::const_iterator i;
526 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
527 Evoral::ControlEvent cp (now, 0.0f);
532 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
533 i != alist->end() && (*i)->when < end; ++i) {
534 if ((*i)->when > now) {
539 if (i != alist->end() && (*i)->when < end) {
540 if ((*i)->when < next_event.when) {
541 next_event.when = (*i)->when;
546 return next_event.when != std::numeric_limits<double>::max();