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);
189 } else if (param.type() == MidiNotePressureAutomation) {
190 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
192 } else if (param.type() == PluginPropertyAutomation) {
193 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
196 return EventTypeMap::instance().to_symbol(param);
201 Automatable::can_automate (Evoral::Parameter what)
203 _can_automate_list.insert (what);
206 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
207 * had a single automation parameter, with it's type implicit. Derived objects should
208 * pass that type and it will be used for the untyped AutomationList found.
211 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
213 Glib::Threads::Mutex::Lock lm (control_lock());
215 /* Don't clear controls, since some may be special derived Controllable classes */
217 XMLNodeList nlist = node.children();
218 XMLNodeIterator niter;
220 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
222 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
223 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
227 if ((*niter)->name() == "AutomationList") {
229 XMLProperty const * id_prop = (*niter)->property("automation-id");
231 Evoral::Parameter param = (id_prop
232 ? EventTypeMap::instance().from_symbol(id_prop->value())
235 if (param.type() == NullAutomation) {
236 warning << "Automation has null type" << endl;
240 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
241 warning << "Ignored automation data for non-automatable parameter" << endl;
246 warning << "AutomationList node without automation-id property, "
247 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
250 boost::shared_ptr<AutomationControl> existing = automation_control (param);
253 existing->alist()->set_state (**niter, 3000);
255 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
256 add_control (newcontrol);
257 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
258 newcontrol->set_list(al);
262 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
270 Automatable::get_automation_xml_state ()
272 Glib::Threads::Mutex::Lock lm (control_lock());
273 XMLNode* node = new XMLNode (Automatable::xml_node_name);
275 if (controls().empty()) {
279 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
280 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
281 if (l && !l->empty()) {
282 node->add_child_nocopy (l->get_state ());
290 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
292 Glib::Threads::Mutex::Lock lm (control_lock());
294 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
296 if (c && (s != c->automation_state())) {
297 c->set_automation_state (s);
298 _a_session.set_dirty ();
299 AutomationStateChanged(); /* Emit signal */
304 Automatable::get_parameter_automation_state (Evoral::Parameter param)
306 AutoState result = Off;
308 boost::shared_ptr<AutomationControl> c = automation_control(param);
311 result = c->automation_state();
318 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
320 Glib::Threads::Mutex::Lock lm (control_lock());
322 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
324 if (c && (s != c->automation_style())) {
325 c->set_automation_style (s);
326 _a_session.set_dirty ();
331 Automatable::get_parameter_automation_style (Evoral::Parameter param)
333 Glib::Threads::Mutex::Lock lm (control_lock());
335 boost::shared_ptr<Evoral::Control> c = control(param);
336 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
339 return l->automation_style();
341 return Absolute; // whatever
346 Automatable::protect_automation ()
348 typedef set<Evoral::Parameter> ParameterSet;
349 const ParameterSet& automated_params = what_can_be_automated ();
351 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
353 boost::shared_ptr<Evoral::Control> c = control(*i);
354 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
356 switch (l->automation_state()) {
358 l->set_automation_state (Off);
361 l->set_automation_state (Play);
370 Automatable::transport_located (framepos_t now)
372 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
374 boost::shared_ptr<AutomationControl> c
375 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
377 boost::shared_ptr<AutomationList> l
378 = boost::dynamic_pointer_cast<AutomationList>(c->list());
381 l->start_write_pass (now);
388 Automatable::transport_stopped (framepos_t now)
390 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
391 boost::shared_ptr<AutomationControl> c =
392 boost::dynamic_pointer_cast<AutomationControl>(li->second);
397 boost::shared_ptr<AutomationList> l =
398 boost::dynamic_pointer_cast<AutomationList>(c->list());
403 /* Stop any active touch gesture just before we mark the write pass
404 as finished. If we don't do this, the transport can end up stopped with
405 an AutomationList thinking that a touch is still in progress and,
406 when the transport is re-started, a touch will magically
407 be happening without it ever have being started in the usual way.
409 const bool list_did_write = !l->in_new_write_pass ();
411 l->stop_touch (true, now);
413 c->commit_transaction (list_did_write);
415 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
417 if (l->automation_state () == Write) {
418 l->set_automation_state (Touch);
421 if (l->automation_playback ()) {
422 c->set_value_unchecked (c->list ()->eval (now));
427 boost::shared_ptr<Evoral::Control>
428 Automatable::control_factory(const Evoral::Parameter& param)
430 Evoral::Control* control = NULL;
431 bool make_list = true;
432 ParameterDescriptor desc(param);
433 boost::shared_ptr<AutomationList> list;
435 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
436 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
438 control = new MidiTrack::MidiControl(mt, param);
439 make_list = false; // No list, this is region "automation"
441 } else if (param.type() == PluginAutomation) {
442 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
444 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
445 control = new PluginInsert::PluginControl(pi, param, desc);
447 warning << "PluginAutomation for non-Plugin" << endl;
449 } else if (param.type() == PluginPropertyAutomation) {
450 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
452 desc = pi->plugin(0)->get_property_descriptor(param.id());
453 if (desc.datatype != Variant::NOTHING) {
454 if (!Variant::type_is_numeric(desc.datatype)) {
455 make_list = false; // Can't automate non-numeric data yet
457 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
459 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
462 warning << "PluginPropertyAutomation for non-Plugin" << endl;
464 } else if (param.type() == GainAutomation) {
465 control = new GainControl(_a_session, param);
466 } else if (param.type() == TrimAutomation) {
467 control = new GainControl(_a_session, param);
468 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
469 Pannable* pannable = dynamic_cast<Pannable*>(this);
471 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
473 warning << "PanAutomation for non-Pannable" << endl;
475 } else if (param.type() == RecEnableAutomation) {
476 Recordable* re = dynamic_cast<Recordable*> (this);
478 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
480 } else if (param.type() == MonitoringAutomation) {
481 Monitorable* m = dynamic_cast<Monitorable*>(this);
483 control = new MonitorControl (_a_session, X_("monitor"), *m);
485 } else if (param.type() == SoloAutomation) {
486 Soloable* s = dynamic_cast<Soloable*>(this);
487 Muteable* m = dynamic_cast<Muteable*>(this);
489 control = new SoloControl (_a_session, X_("solo"), *s, *m);
491 } else if (param.type() == MuteAutomation) {
492 Muteable* m = dynamic_cast<Muteable*>(this);
494 control = new MuteControl (_a_session, X_("mute"), *m);
498 if (make_list && !list) {
499 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
503 control = new AutomationControl(_a_session, param, desc, list);
506 return boost::shared_ptr<Evoral::Control>(control);
509 boost::shared_ptr<AutomationControl>
510 Automatable::automation_control (const Evoral::Parameter& id, bool create)
512 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
515 boost::shared_ptr<const AutomationControl>
516 Automatable::automation_control (const Evoral::Parameter& id) const
518 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
522 Automatable::clear_controls ()
524 _control_connections.drop_connections ();
525 ControlSet::clear_controls ();
529 Automatable::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
531 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
535 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
537 Controls::const_iterator li;
539 next_event.when = std::numeric_limits<double>::max();
541 for (li = _controls.begin(); li != _controls.end(); ++li) {
542 boost::shared_ptr<AutomationControl> c
543 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
545 if (only_active && (!c || !c->automation_playback())) {
549 Evoral::ControlList::const_iterator i;
550 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
551 Evoral::ControlEvent cp (now, 0.0f);
556 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
557 i != alist->end() && (*i)->when < end; ++i) {
558 if ((*i)->when > now) {
563 if (i != alist->end() && (*i)->when < end) {
564 if ((*i)->when < next_event.when) {
565 next_event.when = (*i)->when;
570 return next_event.when != std::numeric_limits<double>::max();