ardour
automatable.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2001,2007 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #include <fstream>
21 #include <cstdio>
22 #include <errno.h>
23 
24 #include <glibmm/miscutils.h>
25 
26 #include "pbd/error.h"
27 
28 #include "ardour/amp.h"
29 #include "ardour/automatable.h"
30 #include "ardour/event_type_map.h"
31 #include "ardour/midi_track.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"
39 
40 #include "i18n.h"
41 
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace PBD;
45 
46 const string Automatable::xml_node_name = X_("Automation");
47 
48 Automatable::Automatable(Session& session)
49  : _a_session(session)
50 {
51 }
52 
54  : ControlSet (other)
55  , _a_session (other._a_session)
56 {
58 
59  for (Controls::const_iterator i = other._controls.begin(); i != other._controls.end(); ++i) {
61  add_control (ac);
62  }
63 }
64 
66 {
67  {
69 
70  for (Controls::const_iterator li = _controls.begin(); li != _controls.end(); ++li) {
71  boost::dynamic_pointer_cast<AutomationControl>(li->second)->drop_references ();
72  }
73  }
74 }
75 
76 int
78 {
79  const XMLProperty *prop;
80 
81  if ((prop = node.property ("path")) != 0) {
82  load_automation (prop->value());
83  } else {
84  warning << _("Automation node has no path property") << endmsg;
85  }
86 
87  return 0;
88 }
89 
90 int
91 Automatable::load_automation (const string& path)
92 {
93  string fullpath;
94 
95  if (Glib::path_is_absolute (path)) { // legacy
96  fullpath = path;
97  } else {
98  fullpath = _a_session.automation_dir();
99  fullpath += path;
100  }
101  ifstream in (fullpath.c_str());
102 
103  if (!in) {
104  warning << string_compose(_("cannot open %2 to load automation data (%3)")
105  , fullpath, strerror (errno)) << endmsg;
106  return 1;
107  }
108 
110  set<Evoral::Parameter> tosave;
111  controls().clear ();
112 
113  while (in) {
114  double when;
115  double value;
116  uint32_t port;
117 
118  in >> port; if (!in) break;
119  in >> when; if (!in) goto bad;
120  in >> value; if (!in) goto bad;
121 
122  Evoral::Parameter param(PluginAutomation, 0, port);
123  /* FIXME: this is legacy and only used for plugin inserts? I think? */
125  c->list()->add (when, value);
126  tosave.insert (param);
127  }
128 
129  return 0;
130 
131  bad:
132  error << string_compose(_("cannot load automation data from %2"), fullpath) << endmsg;
133  controls().clear ();
134  return -1;
135 }
136 
137 void
139 {
140  Evoral::Parameter param = ac->parameter();
141 
143 
144  if (al) {
145  al->automation_state_changed.connect_same_thread (
148  this, ac->parameter(), _1));
149  }
150 
151  ControlSet::add_control (ac);
152 
153  if (al) {
154  _can_automate_list.insert (param);
155  automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
156  }
157 }
158 
159 string
161 {
162  /* derived classes like PluginInsert should override this */
163 
164  if (param == Evoral::Parameter(GainAutomation)) {
165  return _("Fader");
166  } else if (param.type() == TrimAutomation) {
167  return _("Trim");
168  } else if (param.type() == MuteAutomation) {
169  return _("Mute");
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);
178 #ifdef LV2_SUPPORT
179  } else if (param.type() == PluginPropertyAutomation) {
180  return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
181 #endif
182  } else {
183  return EventTypeMap::instance().to_symbol(param);
184  }
185 }
186 
187 void
189 {
190  _can_automate_list.insert (what);
191 }
192 
197 int
199 {
201 
202  /* Don't clear controls, since some may be special derived Controllable classes */
203 
204  XMLNodeList nlist = node.children();
205  XMLNodeIterator niter;
206 
207  for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
208 
209  /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
210  error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
211  continue;
212  }*/
213 
214  if ((*niter)->name() == "AutomationList") {
215 
216  const XMLProperty* id_prop = (*niter)->property("automation-id");
217 
218  Evoral::Parameter param = (id_prop
220  : legacy_param);
221 
222  if (param.type() == NullAutomation) {
223  warning << "Automation has null type" << endl;
224  continue;
225  }
226 
227  if (!id_prop) {
228  warning << "AutomationList node without automation-id property, "
229  << "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
230  }
231 
233 
234  if (existing) {
235  existing->alist()->set_state (**niter, 3000);
236  } else {
238  add_control (newcontrol);
239  boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
240  newcontrol->set_list(al);
241  }
242 
243  } else {
244  error << "Expected AutomationList node, got '" << (*niter)->name() << "'" << endmsg;
245  }
246  }
247 
248  return 0;
249 }
250 
251 XMLNode&
253 {
256 
257  if (controls().empty()) {
258  return *node;
259  }
260 
261  for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
263  if (l && !l->empty()) {
264  node->add_child_nocopy (l->get_state ());
265  }
266  }
267 
268  return *node;
269 }
270 
271 void
273 {
275 
277 
278  if (c && (s != c->automation_state())) {
279  c->set_automation_state (s);
281  AutomationStateChanged(); /* Emit signal */
282  }
283 }
284 
285 AutoState
287 {
288  AutoState result = Off;
289 
291 
292  if (c) {
293  result = c->automation_state();
294  }
295 
296  return result;
297 }
298 
299 void
301 {
303 
305 
306  if (c && (s != c->automation_style())) {
307  c->set_automation_style (s);
309  }
310 }
311 
312 AutoStyle
314 {
316 
319 
320  if (c) {
321  return l->automation_style();
322  } else {
323  return Absolute; // whatever
324  }
325 }
326 
327 void
329 {
330  typedef set<Evoral::Parameter> ParameterSet;
331  const ParameterSet& automated_params = what_can_be_automated ();
332 
333  for (ParameterSet::const_iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
334 
337 
338  switch (l->automation_state()) {
339  case Write:
341  break;
342  case Touch:
344  break;
345  default:
346  break;
347  }
348  }
349 }
350 
351 void
353 {
354  for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
355 
358  if (c) {
361 
362  if (l) {
363  l->start_write_pass (now);
364  }
365  }
366  }
367 }
368 
369 void
371 {
372  for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
375  if (!c) {
376  continue;
377  }
378 
381  if (!l) {
382  continue;
383  }
384 
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.
390  */
391  l->stop_touch (true, now);
392  l->write_pass_finished (now, Config->get_automation_thinning_factor());
393 
394  if (l->automation_playback()) {
395  c->set_value(c->list()->eval(now));
396  }
397 
398  if (l->automation_state() == Write) {
400  }
401  }
402 }
403 
406 {
407  Evoral::Control* control = NULL;
408  bool make_list = true;
409  ParameterDescriptor desc(param);
411  if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
412  MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
413  if (mt) {
414  control = new MidiTrack::MidiControl(mt, param);
415  make_list = false; // No list, this is region "automation"
416  }
417  } else if (param.type() == PluginAutomation) {
418  PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
419  if (pi) {
420  pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
421  control = new PluginInsert::PluginControl(pi, param, desc);
422  } else {
423  warning << "PluginAutomation for non-Plugin" << endl;
424  }
425  } else if (param.type() == PluginPropertyAutomation) {
426  PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
427  if (pi) {
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
432  } else {
433  list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
434  }
435  control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
436  }
437  } else {
438  warning << "PluginPropertyAutomation for non-Plugin" << endl;
439  }
440  } else if (param.type() == GainAutomation) {
441  Amp* amp = dynamic_cast<Amp*>(this);
442  if (amp) {
443  control = new Amp::GainControl(X_("gaincontrol"), _a_session, amp, param);
444  } else {
445  warning << "GainAutomation for non-Amp" << endl;
446  }
447  } else if (param.type() == TrimAutomation) {
448  Amp* amp = dynamic_cast<Amp*>(this);
449  if (amp) {
450  control = new Amp::GainControl(X_("trimcontrol"), _a_session, amp, param);
451  } else {
452  warning << "TrimAutomation for non-Amp" << endl;
453  }
454  } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
455  Pannable* pannable = dynamic_cast<Pannable*>(this);
456  if (pannable) {
457  control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
458  } else {
459  warning << "PanAutomation for non-Pannable" << endl;
460  }
461  }
462 
463  if (make_list && !list) {
464  list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
465  }
466 
467  if (!control) {
468  control = new AutomationControl(_a_session, param, desc, list);
469  }
470 
472 }
473 
476 {
478 }
479 
482 {
484 }
485 
486 void
488 {
490  ControlSet::clear_controls ();
491 }
492 
493 string
495 {
496  return ARDOUR::value_as_string(ac->desc(), ac->get_value());
497 }
void set_automation_state(AutoState)
virtual void transport_located(framepos_t now)
Definition: automatable.cc:352
AutoState automation_state() const
std::set< Evoral::Parameter > _can_automate_list
Definition: automatable.h:96
XMLNodeList::iterator XMLNodeIterator
Definition: xml++.h:48
std::string automation_dir() const
Automation data.
void set_parameter_automation_style(Evoral::Parameter param, AutoStyle)
Definition: automatable.cc:300
void set_list(boost::shared_ptr< ControlList >)
Definition: Control.cpp:69
void write_pass_finished(double when, double thinning_factor=0.0)
boost::shared_ptr< Evoral::Control > control_factory(const Evoral::Parameter &id)
Definition: automatable.cc:405
const std::string & value() const
Definition: xml++.h:159
AutoStyle get_parameter_automation_style(Evoral::Parameter param)
Definition: automatable.cc:313
virtual void transport_stopped(framepos_t now)
Definition: automatable.cc:370
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
int set_state(const XMLNode &, int version)
std::string value_as_string(const ARDOUR::ParameterDescriptor &desc, double v)
boost::shared_ptr< Control > control(const Parameter &id, bool create_if_missing=false)
Definition: ControlSet.cpp:73
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
void start_write_pass(double time)
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
boost::shared_ptr< Plugin > plugin(uint32_t num=0) const
static URIMap & instance()
Definition: uri_map.cc:66
virtual void set_parameter_automation_state(Evoral::Parameter param, AutoState)
Definition: automatable.cc:272
virtual void add_control(boost::shared_ptr< Evoral::Control >)
Definition: automatable.cc:138
AutoStyle
Definition: types.h:155
AutoStyle automation_style() const
AutoStyle automation_style() const
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
Glib::Threads::Mutex _control_lock
Definition: ControlSet.hpp:76
#define _(Text)
Definition: i18n.h:11
#define X_(Text)
Definition: i18n.h:13
XMLProperty * property(const char *)
Definition: xml++.cc:413
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
Glib::Threads::Mutex & control_lock() const
Definition: ControlSet.hpp:70
virtual void automation_list_automation_state_changed(Evoral::Parameter, AutoState)
Definition: automatable.h:91
virtual std::string value_as_string(boost::shared_ptr< AutomationControl >) const
Definition: automatable.cc:494
void stop_touch(bool mark, double when)
const ParameterDescriptor & desc() const
virtual std::string describe_parameter(Evoral::Parameter param)
Definition: automatable.cc:160
AutoState automation_state() const
void can_automate(Evoral::Parameter)
Definition: automatable.cc:188
Definition: amp.h:29
const std::set< Evoral::Parameter > & what_can_be_automated() const
Definition: automatable.h:76
bool automation_playback() const
PBD::ScopedConnectionList _control_connections
connections to our controls' signals
Definition: automatable.h:101
virtual ~Automatable()
Definition: automatable.cc:65
int old_set_automation_state(const XMLNode &)
Definition: automatable.cc:77
PBD::Signal0< void > AutomationStateChanged
Definition: automatable.h:84
int64_t framepos_t
Definition: types.h:66
static EventTypeMap & instance()
Evoral::Parameter from_symbol(const std::string &str) const
int set_automation_xml_state(const XMLNode &, Evoral::Parameter default_param)
Definition: automatable.cc:198
const Parameter & parameter() const
Definition: Control.hpp:69
Session & _a_session
Definition: automatable.h:87
std::string to_symbol(const Evoral::Parameter &param) const
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
uint32_t id() const
Definition: Parameter.hpp:49
int load_automation(const std::string &path)
Definition: automatable.cc:91
Definition: xml++.h:95
boost::shared_ptr< ControlList > list()
Definition: Control.hpp:66
Controls & controls()
Definition: ControlSet.hpp:58
Automatable(Session &)
Definition: automatable.cc:48
uint32_t type() const
Definition: Parameter.hpp:47
Definition: debug.h:30
boost::shared_ptr< AutomationControl > automation_control(const Evoral::Parameter &id, bool create_if_missing=false)
Definition: automatable.cc:475
void set_automation_state(AutoState as)
XMLNode & get_automation_xml_state()
Definition: automatable.cc:252
boost::shared_ptr< AutomationList > alist() const
Nothing (void)
Definition: variant.h:40
AutoState get_parameter_automation_state(Evoral::Parameter param)
Definition: automatable.cc:286
void set_automation_style(AutoStyle as)
uint8_t channel() const
Definition: Parameter.hpp:48
Variant::Type datatype
for properties
PBD::ScopedConnectionList _list_connections
Definition: ControlSet.hpp:79
static bool type_is_numeric(Type type)
Definition: variant.h:179
static const std::string xml_node_name
Definition: automatable.h:79
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
bool empty() const
PBD::Signal1< void, AutoState > automation_state_changed
AutoState
Definition: types.h:145