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/gain_control.h"
32 #include "ardour/monitor_control.h"
33 #include "ardour/midi_track.h"
34 #include "ardour/pan_controllable.h"
35 #include "ardour/pannable.h"
36 #include "ardour/plugin.h"
37 #include "ardour/plugin_insert.h"
38 #include "ardour/record_enable_control.h"
39 #include "ardour/session.h"
40 #include "ardour/uri_map.h"
41 #include "ardour/value_as_string.h"
46 using namespace ARDOUR;
49 const string Automatable::xml_node_name = X_("Automation");
51 Automatable::Automatable(Session& session)
56 Automatable::Automatable (const Automatable& other)
58 , _a_session (other._a_session)
60 Glib::Threads::Mutex::Lock lm (other._control_lock);
62 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
63 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
68 Automatable::~Automatable ()
71 Glib::Threads::Mutex::Lock lm (_control_lock);
73 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
74 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
80 Automatable::old_set_automation_state (const XMLNode& node)
82 XMLProperty const * prop;
84 if ((prop = node.property ("path")) != 0) {
85 load_automation (prop->value());
87 warning << _("Automation node has no path property") << endmsg;
94 Automatable::load_automation (const string& path)
98 if (Glib::path_is_absolute (path)) { // legacy
101 fullpath = _a_session.automation_dir();
105 FILE * in = g_fopen (fullpath.c_str (), "rb");
108 warning << string_compose(_("cannot open %2 to load automation data (%3)")
109 , fullpath, strerror (errno)) << endmsg;
113 Glib::Threads::Mutex::Lock lm (control_lock());
114 set<Evoral::Parameter> tosave;
122 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
129 Evoral::Parameter param(PluginAutomation, 0, port);
130 /* FIXME: this is legacy and only used for plugin inserts? I think? */
131 boost::shared_ptr<Evoral::Control> c = control (param, true);
132 c->list()->add (when, value);
133 tosave.insert (param);
140 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
147 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
149 Evoral::Parameter param = ac->parameter();
151 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
153 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
155 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
156 al->automation_state_changed.connect_same_thread (
158 boost::bind (&Automatable::automation_list_automation_state_changed,
159 this, ac->parameter(), _1));
162 ControlSet::add_control (ac);
164 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
165 _can_automate_list.insert (param);
166 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
171 Automatable::describe_parameter (Evoral::Parameter param)
173 /* derived classes like PluginInsert should override this */
175 if (param == Evoral::Parameter(GainAutomation)) {
177 } else if (param.type() == TrimAutomation) {
179 } else if (param.type() == MuteAutomation) {
181 } else if (param.type() == MidiCCAutomation) {
182 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
183 } else if (param.type() == MidiPgmChangeAutomation) {
184 return string_compose("Program [%1]", int(param.channel()) + 1);
185 } else if (param.type() == MidiPitchBenderAutomation) {
186 return string_compose("Bender [%1]", int(param.channel()) + 1);
187 } else if (param.type() == MidiChannelPressureAutomation) {
188 return string_compose("Pressure [%1]", int(param.channel()) + 1);
190 } else if (param.type() == PluginPropertyAutomation) {
191 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
194 return EventTypeMap::instance().to_symbol(param);
199 Automatable::can_automate (Evoral::Parameter what)
201 _can_automate_list.insert (what);
204 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
205 * had a single automation parameter, with it's type implicit. Derived objects should
206 * pass that type and it will be used for the untyped AutomationList found.
209 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
211 Glib::Threads::Mutex::Lock lm (control_lock());
213 /* Don't clear controls, since some may be special derived Controllable classes */
215 XMLNodeList nlist = node.children();
216 XMLNodeIterator niter;
218 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
220 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
221 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
225 if ((*niter)->name() == "AutomationList") {
227 XMLProperty const * id_prop = (*niter)->property("automation-id");
229 Evoral::Parameter param = (id_prop
230 ? EventTypeMap::instance().from_symbol(id_prop->value())
233 if (param.type() == NullAutomation) {
234 warning << "Automation has null type" << endl;
238 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
239 warning << "Ignored automation data for non-automatable parameter" << endl;
244 warning << "AutomationList node without automation-id property, "
245 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
248 boost::shared_ptr<AutomationControl> existing = automation_control (param);
251 existing->alist()->set_state (**niter, 3000);
253 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
254 add_control (newcontrol);
255 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
256 newcontrol->set_list(al);
260 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
268 Automatable::get_automation_xml_state ()
270 Glib::Threads::Mutex::Lock lm (control_lock());
271 XMLNode* node = new XMLNode (Automatable::xml_node_name);
273 if (controls().empty()) {
277 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
278 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
279 if (l && !l->empty()) {
280 node->add_child_nocopy (l->get_state ());
288 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
290 Glib::Threads::Mutex::Lock lm (control_lock());
292 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
294 if (c && (s != c->automation_state())) {
295 c->set_automation_state (s);
296 _a_session.set_dirty ();
297 AutomationStateChanged(); /* Emit signal */
302 Automatable::get_parameter_automation_state (Evoral::Parameter param)
304 AutoState result = Off;
306 boost::shared_ptr<AutomationControl> c = automation_control(param);
309 result = c->automation_state();
316 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
318 Glib::Threads::Mutex::Lock lm (control_lock());
320 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
322 if (c && (s != c->automation_style())) {
323 c->set_automation_style (s);
324 _a_session.set_dirty ();
329 Automatable::get_parameter_automation_style (Evoral::Parameter param)
331 Glib::Threads::Mutex::Lock lm (control_lock());
333 boost::shared_ptr<Evoral::Control> c = control(param);
334 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
337 return l->automation_style();
339 return Absolute; // whatever
344 Automatable::protect_automation ()
346 typedef set<Evoral::Parameter> ParameterSet;
347 const ParameterSet& automated_params = what_can_be_automated ();
349 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
351 boost::shared_ptr<Evoral::Control> c = control(*i);
352 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
354 switch (l->automation_state()) {
356 l->set_automation_state (Off);
359 l->set_automation_state (Play);
368 Automatable::transport_located (framepos_t now)
370 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
372 boost::shared_ptr<AutomationControl> c
373 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
375 boost::shared_ptr<AutomationList> l
376 = boost::dynamic_pointer_cast<AutomationList>(c->list());
379 l->start_write_pass (now);
386 Automatable::transport_stopped (framepos_t now)
388 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
389 boost::shared_ptr<AutomationControl> c =
390 boost::dynamic_pointer_cast<AutomationControl>(li->second);
395 boost::shared_ptr<AutomationList> l =
396 boost::dynamic_pointer_cast<AutomationList>(c->list());
401 /* Stop any active touch gesture just before we mark the write pass
402 as finished. If we don't do this, the transport can end up stopped with
403 an AutomationList thinking that a touch is still in progress and,
404 when the transport is re-started, a touch will magically
405 be happening without it ever have being started in the usual way.
407 const bool list_did_write = !l->in_new_write_pass ();
409 l->stop_touch (true, now);
411 c->commit_transaction (list_did_write);
413 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
415 if (l->automation_state () == Write) {
416 l->set_automation_state (Touch);
419 if (l->automation_playback ()) {
420 c->set_value_unchecked (c->list ()->eval (now));
425 boost::shared_ptr<Evoral::Control>
426 Automatable::control_factory(const Evoral::Parameter& param)
428 Evoral::Control* control = NULL;
429 bool make_list = true;
430 ParameterDescriptor desc(param);
431 boost::shared_ptr<AutomationList> list;
433 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
434 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
436 control = new MidiTrack::MidiControl(mt, param);
437 make_list = false; // No list, this is region "automation"
439 } else if (param.type() == PluginAutomation) {
440 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
442 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
443 control = new PluginInsert::PluginControl(pi, param, desc);
445 warning << "PluginAutomation for non-Plugin" << endl;
447 } else if (param.type() == PluginPropertyAutomation) {
448 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
450 desc = pi->plugin(0)->get_property_descriptor(param.id());
451 if (desc.datatype != Variant::NOTHING) {
452 if (!Variant::type_is_numeric(desc.datatype)) {
453 make_list = false; // Can't automate non-numeric data yet
455 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
457 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
460 warning << "PluginPropertyAutomation for non-Plugin" << endl;
462 } else if (param.type() == GainAutomation) {
463 control = new GainControl(_a_session, param);
464 } else if (param.type() == TrimAutomation) {
465 control = new GainControl(_a_session, param);
466 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
467 Pannable* pannable = dynamic_cast<Pannable*>(this);
469 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
471 warning << "PanAutomation for non-Pannable" << endl;
473 } else if (param.type() == RecEnableAutomation) {
474 Recordable* re = dynamic_cast<Recordable*> (this);
476 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
478 } else if (param.type() == MonitoringAutomation) {
479 Monitorable* m = dynamic_cast<Monitorable*>(this);
481 control = new MonitorControl (_a_session, X_("monitor"), *m);
483 } else if (param.type() == SoloAutomation) {
484 Soloable* s = dynamic_cast<Soloable*>(this);
485 Muteable* m = dynamic_cast<Muteable*>(this);
487 control = new SoloControl (_a_session, X_("solo"), *s, *m);
489 } else if (param.type() == MuteAutomation) {
490 Muteable* m = dynamic_cast<Muteable*>(this);
492 control = new MuteControl (_a_session, X_("mute"), *m);
496 if (make_list && !list) {
497 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
501 control = new AutomationControl(_a_session, param, desc, list);
504 return boost::shared_ptr<Evoral::Control>(control);
507 boost::shared_ptr<AutomationControl>
508 Automatable::automation_control (const Evoral::Parameter& id, bool create)
510 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
513 boost::shared_ptr<const AutomationControl>
514 Automatable::automation_control (const Evoral::Parameter& id) const
516 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
520 Automatable::clear_controls ()
522 _control_connections.drop_connections ();
523 ControlSet::clear_controls ();
527 Automatable::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
529 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
533 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
535 Controls::const_iterator li;
537 next_event.when = std::numeric_limits<double>::max();
539 for (li = _controls.begin(); li != _controls.end(); ++li) {
540 boost::shared_ptr<AutomationControl> c
541 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
543 if (only_active && (!c || !c->automation_playback())) {
547 Evoral::ControlList::const_iterator i;
548 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
549 Evoral::ControlEvent cp (now, 0.0f);
554 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
555 i != alist->end() && (*i)->when < end; ++i) {
556 if ((*i)->when > now) {
561 if (i != alist->end() && (*i)->when < end) {
562 if ((*i)->when < next_event.when) {
563 next_event.when = (*i)->when;
568 return next_event.when != std::numeric_limits<double>::max();