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 ());
152 al->automation_state_changed.connect_same_thread (
154 boost::bind (&Automatable::automation_list_automation_state_changed,
155 this, ac->parameter(), _1));
158 ControlSet::add_control (ac);
161 _can_automate_list.insert (param);
162 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
167 Automatable::describe_parameter (Evoral::Parameter param)
169 /* derived classes like PluginInsert should override this */
171 if (param == Evoral::Parameter(GainAutomation)) {
173 } else if (param.type() == TrimAutomation) {
175 } else if (param.type() == MuteAutomation) {
177 } else if (param.type() == MidiCCAutomation) {
178 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
179 } else if (param.type() == MidiPgmChangeAutomation) {
180 return string_compose("Program [%1]", int(param.channel()) + 1);
181 } else if (param.type() == MidiPitchBenderAutomation) {
182 return string_compose("Bender [%1]", int(param.channel()) + 1);
183 } else if (param.type() == MidiChannelPressureAutomation) {
184 return string_compose("Pressure [%1]", int(param.channel()) + 1);
186 } else if (param.type() == PluginPropertyAutomation) {
187 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
190 return EventTypeMap::instance().to_symbol(param);
195 Automatable::can_automate (Evoral::Parameter what)
197 _can_automate_list.insert (what);
200 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
201 * had a single automation parameter, with it's type implicit. Derived objects should
202 * pass that type and it will be used for the untyped AutomationList found.
205 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
207 Glib::Threads::Mutex::Lock lm (control_lock());
209 /* Don't clear controls, since some may be special derived Controllable classes */
211 XMLNodeList nlist = node.children();
212 XMLNodeIterator niter;
214 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
216 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
217 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
221 if ((*niter)->name() == "AutomationList") {
223 XMLProperty const * id_prop = (*niter)->property("automation-id");
225 Evoral::Parameter param = (id_prop
226 ? EventTypeMap::instance().from_symbol(id_prop->value())
229 if (param.type() == NullAutomation) {
230 warning << "Automation has null type" << endl;
235 warning << "AutomationList node without automation-id property, "
236 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
239 boost::shared_ptr<AutomationControl> existing = automation_control (param);
242 existing->alist()->set_state (**niter, 3000);
244 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
245 add_control (newcontrol);
246 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
247 newcontrol->set_list(al);
251 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
259 Automatable::get_automation_xml_state ()
261 Glib::Threads::Mutex::Lock lm (control_lock());
262 XMLNode* node = new XMLNode (Automatable::xml_node_name);
264 if (controls().empty()) {
268 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
269 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
270 if (l && !l->empty()) {
271 node->add_child_nocopy (l->get_state ());
279 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
281 Glib::Threads::Mutex::Lock lm (control_lock());
283 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
285 if (c && (s != c->automation_state())) {
286 c->set_automation_state (s);
287 _a_session.set_dirty ();
288 AutomationStateChanged(); /* Emit signal */
293 Automatable::get_parameter_automation_state (Evoral::Parameter param)
295 AutoState result = Off;
297 boost::shared_ptr<AutomationControl> c = automation_control(param);
300 result = c->automation_state();
307 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
309 Glib::Threads::Mutex::Lock lm (control_lock());
311 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
313 if (c && (s != c->automation_style())) {
314 c->set_automation_style (s);
315 _a_session.set_dirty ();
320 Automatable::get_parameter_automation_style (Evoral::Parameter param)
322 Glib::Threads::Mutex::Lock lm (control_lock());
324 boost::shared_ptr<Evoral::Control> c = control(param);
325 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
328 return l->automation_style();
330 return Absolute; // whatever
335 Automatable::protect_automation ()
337 typedef set<Evoral::Parameter> ParameterSet;
338 const ParameterSet& automated_params = what_can_be_automated ();
340 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
342 boost::shared_ptr<Evoral::Control> c = control(*i);
343 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
345 switch (l->automation_state()) {
347 l->set_automation_state (Off);
350 l->set_automation_state (Play);
359 Automatable::transport_located (framepos_t now)
361 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
363 boost::shared_ptr<AutomationControl> c
364 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
366 boost::shared_ptr<AutomationList> l
367 = boost::dynamic_pointer_cast<AutomationList>(c->list());
370 l->start_write_pass (now);
377 Automatable::transport_stopped (framepos_t now)
379 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
380 boost::shared_ptr<AutomationControl> c =
381 boost::dynamic_pointer_cast<AutomationControl>(li->second);
386 boost::shared_ptr<AutomationList> l =
387 boost::dynamic_pointer_cast<AutomationList>(c->list());
392 /* Stop any active touch gesture just before we mark the write pass
393 as finished. If we don't do this, the transport can end up stopped with
394 an AutomationList thinking that a touch is still in progress and,
395 when the transport is re-started, a touch will magically
396 be happening without it ever have being started in the usual way.
398 const bool list_did_write = !l->in_new_write_pass ();
400 l->stop_touch (true, now);
402 c->commit_transaction (list_did_write);
404 l->write_pass_finished (now, Config->get_automation_thinning_factor ());
406 if (l->automation_state () == Write) {
407 l->set_automation_state (Touch);
410 if (l->automation_playback ()) {
411 c->set_value_unchecked (c->list ()->eval (now));
416 boost::shared_ptr<Evoral::Control>
417 Automatable::control_factory(const Evoral::Parameter& param)
419 Evoral::Control* control = NULL;
420 bool make_list = true;
421 ParameterDescriptor desc(param);
422 boost::shared_ptr<AutomationList> list;
423 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
424 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
426 control = new MidiTrack::MidiControl(mt, param);
427 make_list = false; // No list, this is region "automation"
429 } else if (param.type() == PluginAutomation) {
430 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
432 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
433 control = new PluginInsert::PluginControl(pi, param, desc);
435 warning << "PluginAutomation for non-Plugin" << endl;
437 } else if (param.type() == PluginPropertyAutomation) {
438 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
440 desc = pi->plugin(0)->get_property_descriptor(param.id());
441 if (desc.datatype != Variant::NOTHING) {
442 if (!Variant::type_is_numeric(desc.datatype)) {
443 make_list = false; // Can't automate non-numeric data yet
445 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
447 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
450 warning << "PluginPropertyAutomation for non-Plugin" << endl;
452 } else if (param.type() == GainAutomation) {
453 control = new GainControl(_a_session, param);
454 } else if (param.type() == TrimAutomation) {
455 control = new GainControl(_a_session, param);
456 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
457 Pannable* pannable = dynamic_cast<Pannable*>(this);
459 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
461 warning << "PanAutomation for non-Pannable" << endl;
465 if (make_list && !list) {
466 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
470 control = new AutomationControl(_a_session, param, desc, list);
473 return boost::shared_ptr<Evoral::Control>(control);
476 boost::shared_ptr<AutomationControl>
477 Automatable::automation_control (const Evoral::Parameter& id, bool create)
479 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
482 boost::shared_ptr<const AutomationControl>
483 Automatable::automation_control (const Evoral::Parameter& id) const
485 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
489 Automatable::clear_controls ()
491 _control_connections.drop_connections ();
492 ControlSet::clear_controls ();
496 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
498 return ARDOUR::value_as_string(ac->desc(), ac->get_value());
502 Automatable::find_next_event (double now, double end, Evoral::ControlEvent& next_event, bool only_active) const
504 Controls::const_iterator li;
506 next_event.when = std::numeric_limits<double>::max();
508 for (li = _controls.begin(); li != _controls.end(); ++li) {
509 boost::shared_ptr<AutomationControl> c
510 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
512 if (only_active && (!c || !c->automation_playback())) {
516 Evoral::ControlList::const_iterator i;
517 boost::shared_ptr<const Evoral::ControlList> alist (li->second->list());
518 Evoral::ControlEvent cp (now, 0.0f);
523 for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
524 i != alist->end() && (*i)->when < end; ++i) {
525 if ((*i)->when > now) {
530 if (i != alist->end() && (*i)->when < end) {
531 if ((*i)->when < next_event.when) {
532 next_event.when = (*i)->when;
537 return next_event.when != std::numeric_limits<double>::max();