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.
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/midi_track.h"
32 #include "ardour/pan_controllable.h"
33 #include "ardour/pannable.h"
34 #include "ardour/plugin.h"
35 #include "ardour/plugin_insert.h"
36 #include "ardour/session.h"
37 #include "ardour/uri_map.h"
38 #include "ardour/value_as_string.h"
43 using namespace ARDOUR;
46 const string Automatable::xml_node_name = X_("Automation");
48 Automatable::Automatable(Session& session)
53 Automatable::Automatable (const Automatable& other)
55 , _a_session (other._a_session)
57 Glib::Threads::Mutex::Lock lm (other._control_lock);
59 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
60 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
65 Automatable::~Automatable ()
68 Glib::Threads::Mutex::Lock lm (_control_lock);
70 for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
71 boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
77 Automatable::old_set_automation_state (const XMLNode& node)
79 const XMLProperty *prop;
81 if ((prop = node.property ("path")) != 0) {
82 load_automation (prop->value());
84 warning << _("Automation node has no path property") << endmsg;
91 Automatable::load_automation (const string& path)
95 if (Glib::path_is_absolute (path)) { // legacy
98 fullpath = _a_session.automation_dir();
101 ifstream in (fullpath.c_str());
104 warning << string_compose(_("cannot open %2 to load automation data (%3)")
105 , fullpath, strerror (errno)) << endmsg;
109 Glib::Threads::Mutex::Lock lm (control_lock());
110 set<Evoral::Parameter> tosave;
118 in >> port; if (!in) break;
119 in >> when; if (!in) goto bad;
120 in >> value; if (!in) goto bad;
122 Evoral::Parameter param(PluginAutomation, 0, port);
123 /* FIXME: this is legacy and only used for plugin inserts? I think? */
124 boost::shared_ptr<Evoral::Control> c = control (param, true);
125 c->list()->add (when, value);
126 tosave.insert (param);
132 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
138 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
140 Evoral::Parameter param = ac->parameter();
142 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
145 al->automation_state_changed.connect_same_thread (
147 boost::bind (&Automatable::automation_list_automation_state_changed,
148 this, ac->parameter(), _1));
151 ControlSet::add_control (ac);
154 _can_automate_list.insert (param);
155 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
160 Automatable::describe_parameter (Evoral::Parameter param)
162 /* derived classes like PluginInsert should override this */
164 if (param == Evoral::Parameter(GainAutomation)) {
166 } else if (param.type() == TrimAutomation) {
168 } else if (param.type() == MuteAutomation) {
170 } else if (param.type() == MidiCCAutomation) {
171 return string_compose("Controller %1 [%2]", param.id(), int(param.channel()) + 1);
172 } else if (param.type() == MidiPgmChangeAutomation) {
173 return string_compose("Program [%1]", int(param.channel()) + 1);
174 } else if (param.type() == MidiPitchBenderAutomation) {
175 return string_compose("Bender [%1]", int(param.channel()) + 1);
176 } else if (param.type() == MidiChannelPressureAutomation) {
177 return string_compose("Pressure [%1]", int(param.channel()) + 1);
179 } else if (param.type() == PluginPropertyAutomation) {
180 return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
183 return EventTypeMap::instance().to_symbol(param);
188 Automatable::can_automate (Evoral::Parameter what)
190 _can_automate_list.insert (what);
193 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
194 * had a single automation parameter, with it's type implicit. Derived objects should
195 * pass that type and it will be used for the untyped AutomationList found.
198 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
200 Glib::Threads::Mutex::Lock lm (control_lock());
202 /* Don't clear controls, since some may be special derived Controllable classes */
204 XMLNodeList nlist = node.children();
205 XMLNodeIterator niter;
207 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
209 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, ¶m) != 1) {
210 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
214 if ((*niter)->name() == "AutomationList") {
216 const XMLProperty* id_prop = (*niter)->property("automation-id");
218 Evoral::Parameter param = (id_prop
219 ? EventTypeMap::instance().from_symbol(id_prop->value())
222 if (param.type() == NullAutomation) {
223 warning << "Automation has null type" << endl;
228 warning << "AutomationList node without automation-id property, "
229 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
232 boost::shared_ptr<AutomationControl> existing = automation_control (param);
235 existing->alist()->set_state (**niter, 3000);
237 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
238 add_control (newcontrol);
239 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
240 newcontrol->set_list(al);
244 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
252 Automatable::get_automation_xml_state ()
254 Glib::Threads::Mutex::Lock lm (control_lock());
255 XMLNode* node = new XMLNode (Automatable::xml_node_name);
257 if (controls().empty()) {
261 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
262 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
263 if (l && !l->empty()) {
264 node->add_child_nocopy (l->get_state ());
272 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
274 Glib::Threads::Mutex::Lock lm (control_lock());
276 boost::shared_ptr<AutomationControl> c = automation_control (param, true);
278 if (c && (s != c->automation_state())) {
279 c->set_automation_state (s);
280 _a_session.set_dirty ();
281 AutomationStateChanged(); /* Emit signal */
286 Automatable::get_parameter_automation_state (Evoral::Parameter param)
288 AutoState result = Off;
290 boost::shared_ptr<AutomationControl> c = automation_control(param);
293 result = c->automation_state();
300 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
302 Glib::Threads::Mutex::Lock lm (control_lock());
304 boost::shared_ptr<AutomationControl> c = automation_control(param, true);
306 if (c && (s != c->automation_style())) {
307 c->set_automation_style (s);
308 _a_session.set_dirty ();
313 Automatable::get_parameter_automation_style (Evoral::Parameter param)
315 Glib::Threads::Mutex::Lock lm (control_lock());
317 boost::shared_ptr<Evoral::Control> c = control(param);
318 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
321 return l->automation_style();
323 return Absolute; // whatever
328 Automatable::protect_automation ()
330 typedef set<Evoral::Parameter> ParameterSet;
331 const ParameterSet& automated_params = what_can_be_automated ();
333 for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
335 boost::shared_ptr<Evoral::Control> c = control(*i);
336 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
338 switch (l->automation_state()) {
340 l->set_automation_state (Off);
343 l->set_automation_state (Play);
352 Automatable::transport_located (framepos_t now)
354 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
356 boost::shared_ptr<AutomationControl> c
357 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
359 boost::shared_ptr<AutomationList> l
360 = boost::dynamic_pointer_cast<AutomationList>(c->list());
363 l->start_write_pass (now);
370 Automatable::transport_stopped (framepos_t now)
372 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
373 boost::shared_ptr<AutomationControl> c =
374 boost::dynamic_pointer_cast<AutomationControl>(li->second);
379 boost::shared_ptr<AutomationList> l =
380 boost::dynamic_pointer_cast<AutomationList>(c->list());
385 /* Stop any active touch gesture just before we mark the write pass
386 as finished. If we don't do this, the transport can end up stopped with
387 an AutomationList thinking that a touch is still in progress and,
388 when the transport is re-started, a touch will magically
389 be happening without it ever have being started in the usual way.
391 l->stop_touch (true, now);
392 l->write_pass_finished (now, Config->get_automation_thinning_factor());
394 if (l->automation_playback()) {
395 c->set_value(c->list()->eval(now));
398 if (l->automation_state() == Write) {
399 l->set_automation_state (Touch);
404 boost::shared_ptr<Evoral::Control>
405 Automatable::control_factory(const Evoral::Parameter& param)
407 Evoral::Control* control = NULL;
408 bool make_list = true;
409 ParameterDescriptor desc(param);
410 boost::shared_ptr<AutomationList> list;
411 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
412 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
414 control = new MidiTrack::MidiControl(mt, param);
415 make_list = false; // No list, this is region "automation"
417 } else if (param.type() == PluginAutomation) {
418 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
420 pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
421 control = new PluginInsert::PluginControl(pi, param, desc);
423 warning << "PluginAutomation for non-Plugin" << endl;
425 } else if (param.type() == PluginPropertyAutomation) {
426 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
428 desc = pi->plugin(0)->get_property_descriptor(param.id());
429 if (desc.datatype != Variant::NOTHING) {
430 if (!Variant::type_is_numeric(desc.datatype)) {
431 make_list = false; // Can't automate non-numeric data yet
433 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
435 control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
438 warning << "PluginPropertyAutomation for non-Plugin" << endl;
440 } else if (param.type() == GainAutomation) {
441 Amp* amp = dynamic_cast<Amp*>(this);
443 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
445 warning << "GainAutomation for non-Amp" << endl;
447 } else if (param.type() == TrimAutomation) {
448 Amp* amp = dynamic_cast<Amp*>(this);
450 control = new Amp::GainControl(X_("trimcontrol"), _a_session, amp, param);
452 warning << "TrimAutomation for non-Amp" << endl;
454 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
455 Pannable* pannable = dynamic_cast<Pannable*>(this);
457 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
459 warning << "PanAutomation for non-Pannable" << endl;
463 if (make_list && !list) {
464 list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
468 control = new AutomationControl(_a_session, param, desc, list);
471 return boost::shared_ptr<Evoral::Control>(control);
474 boost::shared_ptr<AutomationControl>
475 Automatable::automation_control (const Evoral::Parameter& id, bool create)
477 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
480 boost::shared_ptr<const AutomationControl>
481 Automatable::automation_control (const Evoral::Parameter& id) const
483 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
487 Automatable::clear_controls ()
489 _control_connections.drop_connections ();
490 ControlSet::clear_controls ();
494 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
496 return ARDOUR::value_as_string(ac->desc(), ac->get_value());