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);
335 l->set_automation_state (Play);
344 Automatable::non_realtime_locate (framepos_t now)
346 bool rolling = _a_session.transport_rolling ();
348 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
350 boost::shared_ptr<AutomationControl> c
351 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
353 boost::shared_ptr<AutomationList> l
354 = boost::dynamic_pointer_cast<AutomationList>(c->list());
360 bool am_touching = c->touching ();
361 if (rolling && am_touching) {
362 /* when locating while rolling, and writing automation,
363 * start a new write pass.
364 * compare to compare to non_realtime_transport_stop()
366 const bool list_did_write = !l->in_new_write_pass ();
367 c->stop_touch (-1); // time is irrelevant
369 c->commit_transaction (list_did_write);
370 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
372 if (l->automation_state () == Write) {
373 l->set_automation_state (Touch);
375 if (l->automation_playback ()) {
376 c->set_value_unchecked (c->list ()->eval (now));
380 l->start_write_pass (now);
382 if (rolling && am_touching) {
383 c->start_touch (now);
390 Automatable::non_realtime_transport_stop (framepos_t now, bool /*flush_processors*/)
392 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
393 boost::shared_ptr<AutomationControl> c =
394 boost::dynamic_pointer_cast<AutomationControl>(li->second);
399 boost::shared_ptr<AutomationList> l =
400 boost::dynamic_pointer_cast<AutomationList>(c->list());
405 /* Stop any active touch gesture just before we mark the write pass
406 as finished. If we don't do this, the transport can end up stopped with
407 an AutomationList thinking that a touch is still in progress and,
408 when the transport is re-started, a touch will magically
409 be happening without it ever have being started in the usual way.
411 const bool list_did_write = !l->in_new_write_pass ();
416 c->commit_transaction (list_did_write);
418 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
420 if (l->automation_state () == Write) {
421 l->set_automation_state (Touch);
424 if (l->automation_playback ()) {
425 c->set_value_unchecked (c->list ()->eval (now));
431 Automatable::automation_run (framepos_t start, pframes_t nframes)
433 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
434 boost::shared_ptr<AutomationControl> c =
435 boost::dynamic_pointer_cast<AutomationControl>(li->second);
439 c->automation_run (start, nframes);
443 boost::shared_ptr<Evoral::Control>
444 Automatable::control_factory(const Evoral::Parameter& param)
446 Evoral::Control* control = NULL;
447 bool make_list = true;
448 ParameterDescriptor desc(param);
449 boost::shared_ptr<AutomationList> list;
451 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
452 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
454 control = new MidiTrack::MidiControl(mt, param);
455 make_list = false; // No list, this is region "automation"
457 } else if (param.type() == PluginAutomation) {
458 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
460 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
461 control = new PluginInsert::PluginControl(pi, param, desc);
463 warning << "PluginAutomation for non-Plugin" << endl;
465 } else if (param.type() == PluginPropertyAutomation) {
466 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
468 desc = pi->plugin(0)->get_property_descriptor(param.id());
469 if (desc.datatype != Variant::NOTHING) {
470 if (!Variant::type_is_numeric(desc.datatype)) {
471 make_list = false; // Can't automate non-numeric data yet
473 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
475 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
478 warning << "PluginPropertyAutomation for non-Plugin" << endl;
480 } else if (param.type() == GainAutomation) {
481 control = new GainControl(_a_session, param);
482 } else if (param.type() == TrimAutomation) {
483 control = new GainControl(_a_session, param);
484 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
485 Pannable* pannable = dynamic_cast<Pannable*>(this);
487 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
489 warning << "PanAutomation for non-Pannable" << endl;
491 } else if (param.type() == RecEnableAutomation) {
492 Recordable* re = dynamic_cast<Recordable*> (this);
494 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
496 } else if (param.type() == MonitoringAutomation) {
497 Monitorable* m = dynamic_cast<Monitorable*>(this);
499 control = new MonitorControl (_a_session, X_("monitor"), *m);
501 } else if (param.type() == SoloAutomation) {
502 Soloable* s = dynamic_cast<Soloable*>(this);
503 Muteable* m = dynamic_cast<Muteable*>(this);
505 control = new SoloControl (_a_session, X_("solo"), *s, *m);
507 } else if (param.type() == MuteAutomation) {
508 Muteable* m = dynamic_cast<Muteable*>(this);
510 control = new MuteControl (_a_session, X_("mute"), *m);
514 if (make_list && !list) {
515 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
519 control = new AutomationControl(_a_session, param, desc, list);
522 return boost::shared_ptr<Evoral::Control>(control);
525 boost::shared_ptr<AutomationControl>
526 Automatable::automation_control (PBD::ID const & id) const
528 Controls::const_iterator li;
530 for (li = _controls.begin(); li != _controls.end(); ++li) {
531 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (li->second);
532 if (ac && (ac->id() == id)) {
537 return boost::shared_ptr<AutomationControl>();
540 boost::shared_ptr<AutomationControl>
541 Automatable::automation_control (const Evoral::Parameter& id, bool create)
543 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
546 boost::shared_ptr<const AutomationControl>
547 Automatable::automation_control (const Evoral::Parameter& id) const
549 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
553 Automatable::clear_controls ()
555 _control_connections.drop_connections ();
556 ControlSet::clear_controls ();
560 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
562 Controls::const_iterator li;
564 next_event.when = std::numeric_limits<double>::max();
566 for (li = _controls.begin(); li != _controls.end(); ++li) {
567 boost::shared_ptr<AutomationControl> c
568 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
570 if (only_active && (!c || !c->automation_playback())) {
574 boost::shared_ptr<SlavableAutomationControl> sc
575 = boost::dynamic_pointer_cast<SlavableAutomationControl>(li->second);
578 sc->find_next_event (now, end, next_event);
581 Evoral::ControlList::const_iterator i;
582 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
583 Evoral::ControlEvent cp (now, 0.0f);
588 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
589 i != alist->end() && (*i)->when < end; ++i) {
590 if ((*i)->when > now) {
595 if (i != alist->end() && (*i)->when < end) {
596 if ((*i)->when < next_event.when) {
597 next_event.when = (*i)->when;
602 return next_event.when != std::numeric_limits<double>::max();