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"
41 #include "ardour/uri_map.h"
43 #include "ardour/value_as_string.h"
48 using namespace ARDOUR;
51 const string Automatable::xml_node_name = X_("Automation");
53 Automatable::Automatable(Session& session)
58 Automatable::Automatable (const Automatable& other)
61 , _a_session (other._a_session)
63 Glib::Threads::Mutex::Lock lm (other._control_lock);
65 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
66 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
71 Automatable::~Automatable ()
73 Glib::Threads::Mutex::Lock lm (_control_lock);
74 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
75 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());
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::protect_automation ()
320 typedef set<Evoral::Parameter> ParameterSet;
321 const ParameterSet& automated_params = what_can_be_automated ();
323 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
325 boost::shared_ptr<Evoral::Control> c = control(*i);
326 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
328 switch (l->automation_state()) {
330 l->set_automation_state (Off);
333 l->set_automation_state (Play);
342 Automatable::non_realtime_locate (framepos_t now)
344 bool rolling = _a_session.transport_rolling ();
346 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
348 boost::shared_ptr<AutomationControl> c
349 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
351 boost::shared_ptr<AutomationList> l
352 = boost::dynamic_pointer_cast<AutomationList>(c->list());
358 bool am_touching = c->touching ();
359 if (rolling && am_touching) {
360 /* when locating while rolling, and writing automation,
361 * start a new write pass.
362 * compare to compare to non_realtime_transport_stop()
364 const bool list_did_write = !l->in_new_write_pass ();
365 c->stop_touch (-1); // time is irrelevant
367 c->commit_transaction (list_did_write);
368 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
370 if (l->automation_state () == Write) {
371 l->set_automation_state (Touch);
373 if (l->automation_playback ()) {
374 c->set_value_unchecked (c->list ()->eval (now));
378 l->start_write_pass (now);
380 if (rolling && am_touching) {
381 c->start_touch (now);
388 Automatable::non_realtime_transport_stop (framepos_t now, bool /*flush_processors*/)
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 ();
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));
428 Automatable::automation_run (framepos_t start, pframes_t nframes)
430 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
431 boost::shared_ptr<AutomationControl> c =
432 boost::dynamic_pointer_cast<AutomationControl>(li->second);
436 c->automation_run (start, nframes);
440 boost::shared_ptr<Evoral::Control>
441 Automatable::control_factory(const Evoral::Parameter& param)
443 Evoral::Control* control = NULL;
444 bool make_list = true;
445 ParameterDescriptor desc(param);
446 boost::shared_ptr<AutomationList> list;
448 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
449 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
451 control = new MidiTrack::MidiControl(mt, param);
452 make_list = false; // No list, this is region "automation"
454 } else if (param.type() == PluginAutomation) {
455 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
457 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
458 control = new PluginInsert::PluginControl(pi, param, desc);
460 warning << "PluginAutomation for non-Plugin" << endl;
462 } else if (param.type() == PluginPropertyAutomation) {
463 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
465 desc = pi->plugin(0)->get_property_descriptor(param.id());
466 if (desc.datatype != Variant::NOTHING) {
467 if (!Variant::type_is_numeric(desc.datatype)) {
468 make_list = false; // Can't automate non-numeric data yet
470 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
472 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
475 warning << "PluginPropertyAutomation for non-Plugin" << endl;
477 } else if (param.type() == GainAutomation) {
478 control = new GainControl(_a_session, param);
479 } else if (param.type() == TrimAutomation) {
480 control = new GainControl(_a_session, param);
481 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
482 Pannable* pannable = dynamic_cast<Pannable*>(this);
484 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
486 warning << "PanAutomation for non-Pannable" << endl;
488 } else if (param.type() == RecEnableAutomation) {
489 Recordable* re = dynamic_cast<Recordable*> (this);
491 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
493 } else if (param.type() == MonitoringAutomation) {
494 Monitorable* m = dynamic_cast<Monitorable*>(this);
496 control = new MonitorControl (_a_session, X_("monitor"), *m);
498 } else if (param.type() == SoloAutomation) {
499 Soloable* s = dynamic_cast<Soloable*>(this);
500 Muteable* m = dynamic_cast<Muteable*>(this);
502 control = new SoloControl (_a_session, X_("solo"), *s, *m);
504 } else if (param.type() == MuteAutomation) {
505 Muteable* m = dynamic_cast<Muteable*>(this);
507 control = new MuteControl (_a_session, X_("mute"), *m);
511 if (make_list && !list) {
512 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
516 control = new AutomationControl(_a_session, param, desc, list);
519 return boost::shared_ptr<Evoral::Control>(control);
522 boost::shared_ptr<AutomationControl>
523 Automatable::automation_control (PBD::ID const & id) const
525 Controls::const_iterator li;
527 for (li = _controls.begin(); li != _controls.end(); ++li) {
528 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
529 if (ac && (ac->id() == id)) {
534 return boost::shared_ptr<AutomationControl>();
537 boost::shared_ptr<AutomationControl>
538 Automatable::automation_control (const Evoral::Parameter& id, bool create)
540 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
543 boost::shared_ptr<const AutomationControl>
544 Automatable::automation_control (const Evoral::Parameter& id) const
546 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
550 Automatable::clear_controls ()
552 _control_connections.drop_connections ();
553 ControlSet::clear_controls ();
557 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
559 Controls::const_iterator li;
561 next_event.when = std::numeric_limits<double>::max();
563 for (li = _controls.begin(); li != _controls.end(); ++li) {
564 boost::shared_ptr<AutomationControl> c
565 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
567 if (only_active && (!c || !c->automation_playback())) {
571 boost::shared_ptr<SlavableAutomationControl> sc
572 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
575 sc->find_next_event (now, end, next_event);
578 Evoral::ControlList::const_iterator i;
579 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
580 Evoral::ControlEvent cp (now, 0.0f);
585 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
586 i != alist->end() && (*i)->when < end; ++i) {
587 if ((*i)->when > now) {
592 if (i != alist->end() && (*i)->when < end) {
593 if ((*i)->when < next_event.when) {
594 next_event.when = (*i)->when;
599 return next_event.when != std::numeric_limits<double>::max();