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;
239 warning << "AutomationList node without automation-id property, "
240 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
243 boost::shared_ptr<AutomationControl> existing = automation_control (param);
246 existing->alist()->set_state (**niter, 3000);
248 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
249 add_control (newcontrol);
250 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
251 newcontrol->set_list(al);
255 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
263 Automatable::get_automation_xml_state ()
265 Glib::Threads::Mutex::Lock lm (control_lock());
266 XMLNode* node = new XMLNode (Automatable::xml_node_name);
268 if (controls().empty()) {
272 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
273 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
274 if (l && !l->empty()) {
275 node->add_child_nocopy (l->get_state ());
283 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
285 Glib::Threads::Mutex::Lock lm (control_lock());
287 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
289 if (c && (s != c->automation_state())) {
290 c->set_automation_state (s);
291 _a_session.set_dirty ();
292 AutomationStateChanged(); /* Emit signal */
297 Automatable::get_parameter_automation_state (Evoral::Parameter param)
299 AutoState result = Off;
301 boost::shared_ptr<AutomationControl> c = automation_control(param);
304 result = c->automation_state();
311 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
313 Glib::Threads::Mutex::Lock lm (control_lock());
315 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
317 if (c && (s != c->automation_style())) {
318 c->set_automation_style (s);
319 _a_session.set_dirty ();
324 Automatable::get_parameter_automation_style (Evoral::Parameter param)
326 Glib::Threads::Mutex::Lock lm (control_lock());
328 boost::shared_ptr<Evoral::Control> c = control(param);
329 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
332 return l->automation_style();
334 return Absolute; // whatever
339 Automatable::protect_automation ()
341 typedef set<Evoral::Parameter> ParameterSet;
342 const ParameterSet& automated_params = what_can_be_automated ();
344 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
346 boost::shared_ptr<Evoral::Control> c = control(*i);
347 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
349 switch (l->automation_state()) {
351 l->set_automation_state (Off);
354 l->set_automation_state (Play);
363 Automatable::transport_located (framepos_t now)
365 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
367 boost::shared_ptr<AutomationControl> c
368 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
370 boost::shared_ptr<AutomationList> l
371 = boost::dynamic_pointer_cast<AutomationList>(c->list());
374 l->start_write_pass (now);
381 Automatable::transport_stopped (framepos_t now)
383 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
384 boost::shared_ptr<AutomationControl> c =
385 boost::dynamic_pointer_cast<AutomationControl>(li->second);
390 boost::shared_ptr<AutomationList> l =
391 boost::dynamic_pointer_cast<AutomationList>(c->list());
396 /* Stop any active touch gesture just before we mark the write pass
397 as finished. If we don't do this, the transport can end up stopped with
398 an AutomationList thinking that a touch is still in progress and,
399 when the transport is re-started, a touch will magically
400 be happening without it ever have being started in the usual way.
402 const bool list_did_write = !l->in_new_write_pass ();
404 l->stop_touch (true, now);
406 c->commit_transaction (list_did_write);
408 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
410 if (l->automation_state () == Write) {
411 l->set_automation_state (Touch);
414 if (l->automation_playback ()) {
415 c->set_value_unchecked (c->list ()->eval (now));
420 boost::shared_ptr<Evoral::Control>
421 Automatable::control_factory(const Evoral::Parameter& param)
423 Evoral::Control* control = NULL;
424 bool make_list = true;
425 ParameterDescriptor desc(param);
426 boost::shared_ptr<AutomationList> list;
428 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
429 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
431 control = new MidiTrack::MidiControl(mt, param);
432 make_list = false; // No list, this is region "automation"
434 } else if (param.type() == PluginAutomation) {
435 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
437 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
438 control = new PluginInsert::PluginControl(pi, param, desc);
440 warning << "PluginAutomation for non-Plugin" << endl;
442 } else if (param.type() == PluginPropertyAutomation) {
443 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
445 desc = pi->plugin(0)->get_property_descriptor(param.id());
446 if (desc.datatype != Variant::NOTHING) {
447 if (!Variant::type_is_numeric(desc.datatype)) {
448 make_list = false; // Can't automate non-numeric data yet
450 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
452 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
455 warning << "PluginPropertyAutomation for non-Plugin" << endl;
457 } else if (param.type() == GainAutomation) {
458 control = new GainControl(_a_session, param);
459 } else if (param.type() == TrimAutomation) {
460 control = new GainControl(_a_session, param);
461 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
462 Pannable* pannable = dynamic_cast<Pannable*>(this);
464 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
466 warning << "PanAutomation for non-Pannable" << endl;
468 } else if (param.type() == RecEnableAutomation) {
469 Recordable* re = dynamic_cast<Recordable*> (this);
471 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
473 } else if (param.type() == MonitoringAutomation) {
474 Monitorable* m = dynamic_cast<Monitorable*>(this);
476 control = new MonitorControl (_a_session, X_("monitor"), *m);
478 } else if (param.type() == SoloAutomation) {
479 Soloable* s = dynamic_cast<Soloable*>(this);
480 Muteable* m = dynamic_cast<Muteable*>(this);
482 control = new SoloControl (_a_session, X_("solo"), *s, *m);
484 } else if (param.type() == MuteAutomation) {
485 Muteable* m = dynamic_cast<Muteable*>(this);
487 control = new MuteControl (_a_session, X_("mute"), *m);
491 if (make_list && !list) {
492 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
496 control = new AutomationControl(_a_session, param, desc, list);
499 return boost::shared_ptr<Evoral::Control>(control);
502 boost::shared_ptr<AutomationControl>
503 Automatable::automation_control (const Evoral::Parameter& id, bool create)
505 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
508 boost::shared_ptr<const AutomationControl>
509 Automatable::automation_control (const Evoral::Parameter& id) const
511 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
515 Automatable::clear_controls ()
517 _control_connections.drop_connections ();
518 ControlSet::clear_controls ();
522 Automatable::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
524 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
528 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
530 Controls::const_iterator li;
532 next_event.when = std::numeric_limits<double>::max();
534 for (li = _controls.begin(); li != _controls.end(); ++li) {
535 boost::shared_ptr<AutomationControl> c
536 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
538 if (only_active && (!c || !c->automation_playback())) {
542 Evoral::ControlList::const_iterator i;
543 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
544 Evoral::ControlEvent cp (now, 0.0f);
549 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
550 i != alist->end() && (*i)->when < end; ++i) {
551 if ((*i)->when > now) {
556 if (i != alist->end() && (*i)->when < end) {
557 if ((*i)->when < next_event.when) {
558 next_event.when = (*i)->when;
563 return next_event.when != std::numeric_limits<double>::max();