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/midi_track.h"
33 #include "ardour/pan_controllable.h"
34 #include "ardour/pannable.h"
35 #include "ardour/plugin.h"
36 #include "ardour/plugin_insert.h"
37 #include "ardour/session.h"
38 #include "ardour/uri_map.h"
39 #include "ardour/value_as_string.h"
44 using namespace ARDOUR;
47 const string Automatable::xml_node_name = X_("Automation");
49 Automatable::Automatable(Session& session)
54 Automatable::Automatable (const Automatable& other)
56 , _a_session (other._a_session)
58 Glib::Threads::Mutex::Lock lm (other._control_lock);
60 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
61 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
66 Automatable::~Automatable ()
69 Glib::Threads::Mutex::Lock lm (_control_lock);
71 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
72 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
78 Automatable::old_set_automation_state (const XMLNode& node)
80 XMLProperty const * prop;
82 if ((prop = node.property ("path")) != 0) {
83 load_automation (prop->value());
85 warning << _("Automation node has no path property") << endmsg;
92 Automatable::load_automation (const string& path)
96 if (Glib::path_is_absolute (path)) { // legacy
99 fullpath = _a_session.automation_dir();
103 FILE * in = g_fopen (fullpath.c_str (), "rb");
106 warning << string_compose(_("cannot open %2 to load automation data (%3)")
107 , fullpath, strerror (errno)) << endmsg;
111 Glib::Threads::Mutex::Lock lm (control_lock());
112 set<Evoral::Parameter> tosave;
120 if (3 != fscanf (in, "%d %lf %lf", &port, &when, &value)) {
127 Evoral::Parameter param(PluginAutomation, 0, port);
128 /* FIXME: this is legacy and only used for plugin inserts? I think? */
129 boost::shared_ptr<Evoral::Control> c = control (param, true);
130 c->list()->add (when, value);
131 tosave.insert (param);
138 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
145 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
147 Evoral::Parameter param = ac->parameter();
149 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
151 boost::shared_ptr<AutomationControl> actl (boost::dynamic_pointer_cast<AutomationControl> (ac));
153 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
154 al->automation_state_changed.connect_same_thread (
156 boost::bind (&Automatable::automation_list_automation_state_changed,
157 this, ac->parameter(), _1));
160 ControlSet::add_control (ac);
162 if ((!actl || !(actl->flags() & Controllable::NotAutomatable)) && al) {
163 _can_automate_list.insert (param);
164 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
169 Automatable::describe_parameter (Evoral::Parameter param)
171 /* derived classes like PluginInsert should override this */
173 if (param == Evoral::Parameter(GainAutomation)) {
175 } else if (param.type() == TrimAutomation) {
177 } else if (param.type() == MuteAutomation) {
179 } else if (param.type() == MidiCCAutomation) {
180 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
181 } else if (param.type() == MidiPgmChangeAutomation) {
182 return string_compose("Program [%1]", int(param.channel()) + 1);
183 } else if (param.type() == MidiPitchBenderAutomation) {
184 return string_compose("Bender [%1]", int(param.channel()) + 1);
185 } else if (param.type() == MidiChannelPressureAutomation) {
186 return string_compose("Pressure [%1]", int(param.channel()) + 1);
188 } else if (param.type() == PluginPropertyAutomation) {
189 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
192 return EventTypeMap::instance().to_symbol(param);
197 Automatable::can_automate (Evoral::Parameter what)
199 _can_automate_list.insert (what);
202 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
203 * had a single automation parameter, with it's type implicit. Derived objects should
204 * pass that type and it will be used for the untyped AutomationList found.
207 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
209 Glib::Threads::Mutex::Lock lm (control_lock());
211 /* Don't clear controls, since some may be special derived Controllable classes */
213 XMLNodeList nlist = node.children();
214 XMLNodeIterator niter;
216 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
218 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
219 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
223 if ((*niter)->name() == "AutomationList") {
225 XMLProperty const * id_prop = (*niter)->property("automation-id");
227 Evoral::Parameter param = (id_prop
228 ? EventTypeMap::instance().from_symbol(id_prop->value())
231 if (param.type() == NullAutomation) {
232 warning << "Automation has null type" << endl;
237 warning << "AutomationList node without automation-id property, "
238 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
241 boost::shared_ptr<AutomationControl> existing = automation_control (param);
244 existing->alist()->set_state (**niter, 3000);
246 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
247 add_control (newcontrol);
248 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
249 newcontrol->set_list(al);
253 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
261 Automatable::get_automation_xml_state ()
263 Glib::Threads::Mutex::Lock lm (control_lock());
264 XMLNode* node = new XMLNode (Automatable::xml_node_name);
266 if (controls().empty()) {
270 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
271 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
272 if (l && !l->empty()) {
273 node->add_child_nocopy (l->get_state ());
281 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
283 Glib::Threads::Mutex::Lock lm (control_lock());
285 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
287 if (c && (s != c->automation_state())) {
288 c->set_automation_state (s);
289 _a_session.set_dirty ();
290 AutomationStateChanged(); /* Emit signal */
295 Automatable::get_parameter_automation_state (Evoral::Parameter param)
297 AutoState result = Off;
299 boost::shared_ptr<AutomationControl> c = automation_control(param);
302 result = c->automation_state();
309 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
311 Glib::Threads::Mutex::Lock lm (control_lock());
313 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
315 if (c && (s != c->automation_style())) {
316 c->set_automation_style (s);
317 _a_session.set_dirty ();
322 Automatable::get_parameter_automation_style (Evoral::Parameter param)
324 Glib::Threads::Mutex::Lock lm (control_lock());
326 boost::shared_ptr<Evoral::Control> c = control(param);
327 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
330 return l->automation_style();
332 return Absolute; // whatever
337 Automatable::protect_automation ()
339 typedef set<Evoral::Parameter> ParameterSet;
340 const ParameterSet& automated_params = what_can_be_automated ();
342 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
344 boost::shared_ptr<Evoral::Control> c = control(*i);
345 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
347 switch (l->automation_state()) {
349 l->set_automation_state (Off);
352 l->set_automation_state (Play);
361 Automatable::transport_located (framepos_t now)
363 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
365 boost::shared_ptr<AutomationControl> c
366 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
368 boost::shared_ptr<AutomationList> l
369 = boost::dynamic_pointer_cast<AutomationList>(c->list());
372 l->start_write_pass (now);
379 Automatable::transport_stopped (framepos_t now)
381 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
382 boost::shared_ptr<AutomationControl> c =
383 boost::dynamic_pointer_cast<AutomationControl>(li->second);
388 boost::shared_ptr<AutomationList> l =
389 boost::dynamic_pointer_cast<AutomationList>(c->list());
394 /* Stop any active touch gesture just before we mark the write pass
395 as finished. If we don't do this, the transport can end up stopped with
396 an AutomationList thinking that a touch is still in progress and,
397 when the transport is re-started, a touch will magically
398 be happening without it ever have being started in the usual way.
400 const bool list_did_write = !l->in_new_write_pass ();
402 l->stop_touch (true, now);
404 c->commit_transaction (list_did_write);
406 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
408 if (l->automation_state () == Write) {
409 l->set_automation_state (Touch);
412 if (l->automation_playback ()) {
413 c->set_value_unchecked (c->list ()->eval (now));
418 boost::shared_ptr<Evoral::Control>
419 Automatable::control_factory(const Evoral::Parameter& param)
421 Evoral::Control* control = NULL;
422 bool make_list = true;
423 ParameterDescriptor desc(param);
424 boost::shared_ptr<AutomationList> list;
425 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
426 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
428 control = new MidiTrack::MidiControl(mt, param);
429 make_list = false; // No list, this is region "automation"
431 } else if (param.type() == PluginAutomation) {
432 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
434 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
435 control = new PluginInsert::PluginControl(pi, param, desc);
437 warning << "PluginAutomation for non-Plugin" << endl;
439 } else if (param.type() == PluginPropertyAutomation) {
440 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
442 desc = pi->plugin(0)->get_property_descriptor(param.id());
443 if (desc.datatype != Variant::NOTHING) {
444 if (!Variant::type_is_numeric(desc.datatype)) {
445 make_list = false; // Can't automate non-numeric data yet
447 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
449 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
452 warning << "PluginPropertyAutomation for non-Plugin" << endl;
454 } else if (param.type() == GainAutomation) {
455 control = new GainControl(_a_session, param);
456 } else if (param.type() == TrimAutomation) {
457 control = new GainControl(_a_session, param);
458 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
459 Pannable* pannable = dynamic_cast<Pannable*>(this);
461 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
463 warning << "PanAutomation for non-Pannable" << endl;
467 if (make_list && !list) {
468 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
472 control = new AutomationControl(_a_session, param, desc, list);
475 return boost::shared_ptr<Evoral::Control>(control);
478 boost::shared_ptr<AutomationControl>
479 Automatable::automation_control (const Evoral::Parameter& id, bool create)
481 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
484 boost::shared_ptr<const AutomationControl>
485 Automatable::automation_control (const Evoral::Parameter& id) const
487 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
491 Automatable::clear_controls ()
493 _control_connections.drop_connections ();
494 ControlSet::clear_controls ();
498 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
500 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
504 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
506 Controls::const_iterator li;
508 next_event.when = std::numeric_limits<double>::max();
510 for (li = _controls.begin(); li != _controls.end(); ++li) {
511 boost::shared_ptr<AutomationControl> c
512 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
514 if (only_active && (!c || !c->automation_playback())) {
518 Evoral::ControlList::const_iterator i;
519 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
520 Evoral::ControlEvent cp (now, 0.0f);
525 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
526 i != alist->end() && (*i)->when < end; ++i) {
527 if ((*i)->when > now) {
532 if (i != alist->end() && (*i)->when < end) {
533 if ((*i)->when < next_event.when) {
534 next_event.when = (*i)->when;
539 return next_event.when != std::numeric_limits<double>::max();