remove unused SHUTTLE_FRACT constant
[ardour2.git] / libs / ardour / automatable.cc
blob392938fde2fa9ee527ddf1905f795fd811976287
1 /*
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.
20 #include "ardour/ardour.h"
21 #include <fstream>
22 #include <inttypes.h>
23 #include <cstdio>
24 #include <errno.h>
26 #include <glibmm/miscutils.h>
28 #include "pbd/error.h"
29 #include "pbd/enumwriter.h"
30 #include "pbd/stacktrace.h"
32 #include "midi++/names.h"
34 #include "ardour/automatable.h"
35 #include "ardour/amp.h"
36 #include "ardour/event_type_map.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/pannable.h"
39 #include "ardour/panner.h"
40 #include "ardour/pan_controllable.h"
41 #include "ardour/plugin_insert.h"
42 #include "ardour/session.h"
44 #include "i18n.h"
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
50 framecnt_t Automatable::_automation_interval = 0;
51 const string Automatable::xml_node_name = X_("Automation");
53 Automatable::Automatable(Session& session)
54 : _a_session(session)
55 , _last_automation_snapshot(0)
59 Automatable::Automatable (const Automatable& other)
60 : ControlSet (other)
61 , _a_session (other._a_session)
62 , _last_automation_snapshot (0)
64 Glib::Mutex::Lock lm (other._control_lock);
66 for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
67 boost::shared_ptr<Evoral::Control> ac (control_factory (i->first));
68 add_control (ac);
71 int
72 Automatable::old_set_automation_state (const XMLNode& node)
74 const XMLProperty *prop;
76 if ((prop = node.property ("path")) != 0) {
77 load_automation (prop->value());
78 } else {
79 warning << _("Automation node has no path property") << endmsg;
82 if ((prop = node.property ("visible")) != 0) {
83 uint32_t what;
84 stringstream sstr;
86 _visible_controls.clear ();
88 sstr << prop->value();
89 while (1) {
90 sstr >> what;
91 if (sstr.fail()) {
92 break;
94 mark_automation_visible (Evoral::Parameter(PluginAutomation, 0, what), true);
98 _last_automation_snapshot = 0;
100 return 0;
104 Automatable::load_automation (const string& path)
106 string fullpath;
108 if (Glib::path_is_absolute (path)) { // legacy
109 fullpath = path;
110 } else {
111 fullpath = _a_session.automation_dir();
112 fullpath += path;
114 ifstream in (fullpath.c_str());
116 if (!in) {
117 warning << string_compose(_("cannot open %2 to load automation data (%3)")
118 , fullpath, strerror (errno)) << endmsg;
119 return 1;
122 Glib::Mutex::Lock lm (control_lock());
123 set<Evoral::Parameter> tosave;
124 controls().clear ();
126 _last_automation_snapshot = 0;
128 while (in) {
129 double when;
130 double value;
131 uint32_t port;
133 in >> port; if (!in) break;
134 in >> when; if (!in) goto bad;
135 in >> value; if (!in) goto bad;
137 Evoral::Parameter param(PluginAutomation, 0, port);
138 /* FIXME: this is legacy and only used for plugin inserts? I think? */
139 boost::shared_ptr<Evoral::Control> c = control (param, true);
140 c->list()->add (when, value);
141 tosave.insert (param);
144 return 0;
146 bad:
147 error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
148 controls().clear ();
149 return -1;
152 void
153 Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
155 Evoral::Parameter param = ac->parameter();
157 boost::shared_ptr<AutomationList> al = boost::dynamic_pointer_cast<AutomationList> (ac->list ());
158 assert (al);
160 al->automation_state_changed.connect_same_thread (
161 _list_connections, boost::bind (&Automatable::automation_list_automation_state_changed, this, ac->parameter(), _1)
164 ControlSet::add_control (ac);
165 _can_automate_list.insert (param);
167 automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
170 void
171 Automatable::what_has_visible_data(set<Evoral::Parameter>& s) const
173 Glib::Mutex::Lock lm (control_lock());
174 set<Evoral::Parameter>::const_iterator li;
176 for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
177 s.insert (*li);
181 string
182 Automatable::describe_parameter (Evoral::Parameter param)
184 /* derived classes like PluginInsert should override this */
186 if (param == Evoral::Parameter(GainAutomation)) {
187 return _("Fader");
188 } else if (param.type() == MidiCCAutomation) {
189 return string_compose("%1: %2 [%3]",
190 param.id(), midi_name(param.id()), int(param.channel()) + 1);
191 } else if (param.type() == MidiPgmChangeAutomation) {
192 return string_compose("Program [%1]", int(param.channel()) + 1);
193 } else if (param.type() == MidiPitchBenderAutomation) {
194 return string_compose("Bender [%1]", int(param.channel()) + 1);
195 } else if (param.type() == MidiChannelPressureAutomation) {
196 return string_compose("Pressure [%1]", int(param.channel()) + 1);
197 } else {
198 return EventTypeMap::instance().to_symbol(param);
202 void
203 Automatable::can_automate (Evoral::Parameter what)
205 _can_automate_list.insert (what);
208 void
209 Automatable::mark_automation_visible (Evoral::Parameter what, bool yn)
211 if (yn) {
212 _visible_controls.insert (what);
213 } else {
214 set<Evoral::Parameter>::iterator i;
216 if ((i = _visible_controls.find (what)) != _visible_controls.end()) {
217 _visible_controls.erase (i);
222 /** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
223 * had a single automation parameter, with it's type implicit. Derived objects should
224 * pass that type and it will be used for the untyped AutomationList found.
227 Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter legacy_param)
229 Glib::Mutex::Lock lm (control_lock());
231 /* Don't clear controls, since some may be special derived Controllable classes */
233 _visible_controls.clear ();
235 XMLNodeList nlist = node.children();
236 XMLNodeIterator niter;
238 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
240 /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
241 error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
242 continue;
245 if ((*niter)->name() == "AutomationList") {
247 const XMLProperty* id_prop = (*niter)->property("automation-id");
249 Evoral::Parameter param = (id_prop
250 ? EventTypeMap::instance().new_parameter(id_prop->value())
251 : legacy_param);
253 if (param.type() == NullAutomation) {
254 warning << "Automation has null type" << endl;
255 continue;
260 if (!id_prop) {
261 warning << "AutomationList node without automation-id property, "
262 << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
265 boost::shared_ptr<AutomationControl> existing = automation_control (param);
267 if (existing) {
268 existing->alist()->set_state (**niter, 3000);
269 } else {
270 boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
271 add_control (newcontrol);
272 boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
273 newcontrol->set_list(al);
276 } else {
277 error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
281 _last_automation_snapshot = 0;
283 return 0;
286 XMLNode&
287 Automatable::get_automation_xml_state ()
289 Glib::Mutex::Lock lm (control_lock());
290 XMLNode* node = new XMLNode (Automatable::xml_node_name);
292 if (controls().empty()) {
293 return *node;
296 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
297 boost::shared_ptr<AutomationList> l
298 = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
299 if (!l->empty()) {
300 node->add_child_nocopy (l->get_state ());
304 return *node;
307 void
308 Automatable::set_parameter_automation_state (Evoral::Parameter param, AutoState s)
310 Glib::Mutex::Lock lm (control_lock());
312 boost::shared_ptr<Evoral::Control> c = control (param, true);
313 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
315 if (s != l->automation_state()) {
316 l->set_automation_state (s);
317 _a_session.set_dirty ();
321 AutoState
322 Automatable::get_parameter_automation_state (Evoral::Parameter param)
324 AutoState result = Off;
326 boost::shared_ptr<Evoral::Control> c = control(param);
327 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
329 if (c) {
330 result = l->automation_state();
333 return result;
336 void
337 Automatable::set_parameter_automation_style (Evoral::Parameter param, AutoStyle s)
339 Glib::Mutex::Lock lm (control_lock());
341 boost::shared_ptr<Evoral::Control> c = control(param, true);
342 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
344 if (s != l->automation_style()) {
345 l->set_automation_style (s);
346 _a_session.set_dirty ();
350 AutoStyle
351 Automatable::get_parameter_automation_style (Evoral::Parameter param)
353 Glib::Mutex::Lock lm (control_lock());
355 boost::shared_ptr<Evoral::Control> c = control(param);
356 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
358 if (c) {
359 return l->automation_style();
360 } else {
361 return Absolute; // whatever
365 void
366 Automatable::protect_automation ()
368 typedef set<Evoral::Parameter> ParameterSet;
369 ParameterSet automated_params;
371 what_has_data(automated_params);
373 for (ParameterSet::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
375 boost::shared_ptr<Evoral::Control> c = control(*i);
376 boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(c->list());
378 switch (l->automation_state()) {
379 case Write:
380 l->set_automation_state (Off);
381 break;
382 case Touch:
383 l->set_automation_state (Play);
384 break;
385 default:
386 break;
391 void
392 Automatable::automation_snapshot (framepos_t now, bool force)
394 if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
396 for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
397 boost::shared_ptr<AutomationControl> c
398 = boost::dynamic_pointer_cast<AutomationControl>(i->second);
399 if (_a_session.transport_rolling() && c->automation_write()) {
400 c->list()->rt_add (now, i->second->user_double());
404 _last_automation_snapshot = now;
408 void
409 Automatable::transport_stopped (framepos_t now)
411 for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
413 boost::shared_ptr<AutomationControl> c
414 = boost::dynamic_pointer_cast<AutomationControl>(li->second);
415 if (c) {
416 boost::shared_ptr<AutomationList> l
417 = boost::dynamic_pointer_cast<AutomationList>(c->list());
419 if (l) {
420 /* Stop any active touch gesture just before we mark the write pass
421 as finished. If we don't do this, the transport can end up stopped with
422 an AutomationList thinking that a touch is still in progress and,
423 when the transport is re-started, a touch will magically
424 be happening without it ever have being started in the usual way.
426 l->stop_touch (true, now);
427 l->write_pass_finished (now);
429 if (l->automation_playback()) {
430 c->set_value(c->list()->eval(now));
433 if (l->automation_state() == Write) {
434 l->set_automation_state (Touch);
441 boost::shared_ptr<Evoral::Control>
442 Automatable::control_factory(const Evoral::Parameter& param)
444 boost::shared_ptr<AutomationList> list(new AutomationList(param));
445 Evoral::Control* control = NULL;
446 if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
447 MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
448 if (mt) {
449 control = new MidiTrack::MidiControl(mt, param);
450 } else {
451 warning << "MidiCCAutomation for non-MidiTrack" << endl;
453 } else if (param.type() == PluginAutomation) {
454 PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
455 if (pi) {
456 control = new PluginInsert::PluginControl(pi, param);
457 } else {
458 warning << "PluginAutomation for non-Plugin" << endl;
460 } else if (param.type() == GainAutomation) {
461 Amp* amp = dynamic_cast<Amp*>(this);
462 if (amp) {
463 control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
464 } else {
465 warning << "GainAutomation for non-Amp" << endl;
467 } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
468 Pannable* pannable = dynamic_cast<Pannable*>(this);
469 if (pannable) {
470 control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
471 } else {
472 warning << "PanAutomation for non-Pannable" << endl;
476 if (!control) {
477 control = new AutomationControl(_a_session, param);
480 control->set_list(list);
481 return boost::shared_ptr<Evoral::Control>(control);
484 boost::shared_ptr<AutomationControl>
485 Automatable::automation_control (const Evoral::Parameter& id, bool create)
487 return boost::dynamic_pointer_cast<AutomationControl>(Evoral::ControlSet::control(id, create));
490 boost::shared_ptr<const AutomationControl>
491 Automatable::automation_control (const Evoral::Parameter& id) const
493 return boost::dynamic_pointer_cast<const AutomationControl>(Evoral::ControlSet::control(id));
496 void
497 Automatable::clear_controls ()
499 _control_connections.drop_connections ();
500 ControlSet::clear_controls ();
503 string
504 Automatable::value_as_string (boost::shared_ptr<AutomationControl> ac) const
506 std::stringstream s;
508 /* this is a the default fallback for this virtual method. Derived Automatables
509 are free to override this to display the values of their parameters/controls
510 in different ways.
513 // Hack to display CC as integer value, rather than double
514 if (ac->parameter().type() == MidiCCAutomation) {
515 s << lrint (ac->get_value());
516 } else {
517 s << std::fixed << std::setprecision(3) << ac->get_value();
520 return s.str ();