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)
60 , _a_session (other._a_session)
62 Glib::Threads::Mutex::Lock lm (other._control_lock);
64 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
65 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
70 Automatable::~Automatable ()
73 Glib::Threads::Mutex::Lock lm (_control_lock);
75 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
76 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
82 Automatable::old_set_automation_state (const XMLNode& node)
84 XMLProperty const * prop;
86 if ((prop = node.property ("path")) != 0) {
87 load_automation (prop->value());
89 warning << _("Automation node has no path property") << endmsg;
96 Automatable::load_automation (const string& path)
100 if (Glib::path_is_absolute (path)) { // legacy
103 fullpath = _a_session.automation_dir();
107 FILE * in = g_fopen (fullpath.c_str (), "rb");
110 warning << string_compose(_("cannot open %2 to load automation data (%3)")
111 , fullpath, strerror (errno)) << endmsg;
115 Glib::Threads::Mutex::Lock lm (control_lock());
116 set<Evoral::Parameter> tosave;
124 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
131 Evoral::Parameter param(PluginAutomation, 0, port);
132 /* FIXME: this is legacy and only used for plugin inserts? I think? */
133 boost::shared_ptr<Evoral::Control> c = control (param, true);
134 c->list()->add (when, value);
135 tosave.insert (param);
142 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
149 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
151 Evoral::Parameter param = ac->parameter();
153 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
155 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
157 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
158 al->automation_state_changed.connect_same_thread (
160 boost::bind (&Automatable::automation_list_automation_state_changed,
161 this, ac->parameter(), _1));
164 ControlSet::add_control (ac);
166 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
167 _can_automate_list.insert (param);
168 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
173 Automatable::describe_parameter (Evoral::Parameter param)
175 /* derived classes like PluginInsert should override this */
177 if (param == Evoral::Parameter(GainAutomation)) {
179 } else if (param.type() == TrimAutomation) {
181 } else if (param.type() == MuteAutomation) {
183 } else if (param.type() == MidiCCAutomation) {
184 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
185 } else if (param.type() == MidiPgmChangeAutomation) {
186 return string_compose("Program [%1]", int(param.channel()) + 1);
187 } else if (param.type() == MidiPitchBenderAutomation) {
188 return string_compose("Bender [%1]", int(param.channel()) + 1);
189 } else if (param.type() == MidiChannelPressureAutomation) {
190 return string_compose("Pressure [%1]", int(param.channel()) + 1);
191 } else if (param.type() == MidiNotePressureAutomation) {
192 return string_compose("PolyPressure [%1]", int(param.channel()) + 1);
194 } else if (param.type() == PluginPropertyAutomation) {
195 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
198 return EventTypeMap::instance().to_symbol(param);
203 Automatable::can_automate (Evoral::Parameter what)
205 _can_automate_list.insert (what);
208 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
209 * had a single automation parameter, with it's type implicit. Derived objects should
210 * pass that type and it will be used for the untyped AutomationList found.
213 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
215 Glib::Threads::Mutex::Lock lm (control_lock());
217 /* Don't clear controls, since some may be special derived Controllable classes */
219 XMLNodeList nlist = node.children();
220 XMLNodeIterator niter;
222 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
224 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
225 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
229 if ((*niter)->name() == "AutomationList") {
231 XMLProperty const * id_prop = (*niter)->property("automation-id");
233 Evoral::Parameter param = (id_prop
234 ? EventTypeMap::instance().from_symbol(id_prop->value())
237 if (param.type() == NullAutomation) {
238 warning << "Automation has null type" << endl;
242 if (_can_automate_list.find (param) == _can_automate_list.end ()) {
243 warning << "Ignored automation data for non-automatable parameter" << endl;
248 warning << "AutomationList node without automation-id property, "
249 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
252 boost::shared_ptr<AutomationControl> existing = automation_control (param);
255 existing->alist()->set_state (**niter, 3000);
257 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
258 add_control (newcontrol);
259 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
260 newcontrol->set_list(al);
264 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
272 Automatable::get_automation_xml_state ()
274 Glib::Threads::Mutex::Lock lm (control_lock());
275 XMLNode* node = new XMLNode (Automatable::xml_node_name);
277 if (controls().empty()) {
281 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
282 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
284 node->add_child_nocopy (l->get_state ());
292 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
294 Glib::Threads::Mutex::Lock lm (control_lock());
296 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
298 if (c && (s != c->automation_state())) {
299 c->set_automation_state (s);
300 _a_session.set_dirty ();
301 AutomationStateChanged(); /* Emit signal */
306 Automatable::get_parameter_automation_state (Evoral::Parameter param)
308 AutoState result = Off;
310 boost::shared_ptr<AutomationControl> c = automation_control(param);
313 result = c->automation_state();
320 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
322 Glib::Threads::Mutex::Lock lm (control_lock());
324 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
326 if (c && (s != c->automation_style())) {
327 c->set_automation_style (s);
328 _a_session.set_dirty ();
333 Automatable::get_parameter_automation_style (Evoral::Parameter param)
335 Glib::Threads::Mutex::Lock lm (control_lock());
337 boost::shared_ptr<Evoral::Control> c = control(param);
338 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
341 return l->automation_style();
343 return Absolute; // whatever
348 Automatable::protect_automation ()
350 typedef set<Evoral::Parameter> ParameterSet;
351 const ParameterSet& automated_params = what_can_be_automated ();
353 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
355 boost::shared_ptr<Evoral::Control> c = control(*i);
356 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
358 switch (l->automation_state()) {
360 l->set_automation_state (Off);
363 l->set_automation_state (Play);
372 Automatable::transport_located (framepos_t now)
374 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
376 boost::shared_ptr<AutomationControl> c
377 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
379 boost::shared_ptr<AutomationList> l
380 = boost::dynamic_pointer_cast<AutomationList>(c->list());
383 l->start_write_pass (now);
390 Automatable::transport_stopped (framepos_t now)
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 ();
413 l->stop_touch (true, now);
415 c->commit_transaction (list_did_write);
417 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
419 if (l->automation_state () == Write) {
420 l->set_automation_state (Touch);
423 if (l->automation_playback ()) {
424 c->set_value_unchecked (c->list ()->eval (now));
429 boost::shared_ptr<Evoral::Control>
430 Automatable::control_factory(const Evoral::Parameter& param)
432 Evoral::Control* control = NULL;
433 bool make_list = true;
434 ParameterDescriptor desc(param);
435 boost::shared_ptr<AutomationList> list;
437 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
438 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
440 control = new MidiTrack::MidiControl(mt, param);
441 make_list = false; // No list, this is region "automation"
443 } else if (param.type() == PluginAutomation) {
444 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
446 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
447 control = new PluginInsert::PluginControl(pi, param, desc);
449 warning << "PluginAutomation for non-Plugin" << endl;
451 } else if (param.type() == PluginPropertyAutomation) {
452 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
454 desc = pi->plugin(0)->get_property_descriptor(param.id());
455 if (desc.datatype != Variant::NOTHING) {
456 if (!Variant::type_is_numeric(desc.datatype)) {
457 make_list = false; // Can't automate non-numeric data yet
459 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
461 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
464 warning << "PluginPropertyAutomation for non-Plugin" << endl;
466 } else if (param.type() == GainAutomation) {
467 control = new GainControl(_a_session, param);
468 } else if (param.type() == TrimAutomation) {
469 control = new GainControl(_a_session, param);
470 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
471 Pannable* pannable = dynamic_cast<Pannable*>(this);
473 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
475 warning << "PanAutomation for non-Pannable" << endl;
477 } else if (param.type() == RecEnableAutomation) {
478 Recordable* re = dynamic_cast<Recordable*> (this);
480 control = new RecordEnableControl (_a_session, X_("recenable"), *re);
482 } else if (param.type() == MonitoringAutomation) {
483 Monitorable* m = dynamic_cast<Monitorable*>(this);
485 control = new MonitorControl (_a_session, X_("monitor"), *m);
487 } else if (param.type() == SoloAutomation) {
488 Soloable* s = dynamic_cast<Soloable*>(this);
489 Muteable* m = dynamic_cast<Muteable*>(this);
491 control = new SoloControl (_a_session, X_("solo"), *s, *m);
493 } else if (param.type() == MuteAutomation) {
494 Muteable* m = dynamic_cast<Muteable*>(this);
496 control = new MuteControl (_a_session, X_("mute"), *m);
500 if (make_list && !list) {
501 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
505 control = new AutomationControl(_a_session, param, desc, list);
508 return boost::shared_ptr<Evoral::Control>(control);
511 boost::shared_ptr<AutomationControl>
512 Automatable::automation_control (const Evoral::Parameter& id, bool create)
514 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
517 boost::shared_ptr<const AutomationControl>
518 Automatable::automation_control (const Evoral::Parameter& id) const
520 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
524 Automatable::clear_controls ()
526 _control_connections.drop_connections ();
527 ControlSet::clear_controls ();
531 Automatable::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
533 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
537 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
539 Controls::const_iterator li;
541 next_event.when = std::numeric_limits<double>::max();
543 for (li = _controls.begin(); li != _controls.end(); ++li) {
544 boost::shared_ptr<AutomationControl> c
545 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
547 if (only_active && (!c || !c->automation_playback())) {
551 Evoral::ControlList::const_iterator i;
552 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
553 Evoral::ControlEvent cp (now, 0.0f);
558 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
559 i != alist->end() && (*i)->when < end; ++i) {
560 if ((*i)->when > now) {
565 if (i != alist->end() && (*i)->when < end) {
566 if ((*i)->when < next_event.when) {
567 next_event.when = (*i)->when;
572 return next_event.when != std::numeric_limits<double>::max();