ardour
automation_list.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002 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 <set>
21 #include <climits>
22 #include <float.h>
23 #include <cmath>
24 #include <sstream>
25 #include <algorithm>
26 #include "ardour/automation_list.h"
27 #include "ardour/event_type_map.h"
29 #include "evoral/Curve.hpp"
30 #include "pbd/stacktrace.h"
31 #include "pbd/enumwriter.h"
32 
33 #include "i18n.h"
34 
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38 
39 PBD::Signal1<void,AutomationList *> AutomationList::AutomationListCreated;
40 
41 #if 0
42 static void dumpit (const AutomationList& al, string prefix = "")
43 {
44  cerr << prefix << &al << endl;
45  for (AutomationList::const_iterator i = al.begin(); i != al.end(); ++i) {
46  cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
47  }
48  cerr << "\n";
49 }
50 #endif
51 AutomationList::AutomationList (const Evoral::Parameter& id, const Evoral::ParameterDescriptor& desc)
52  : ControlList(id, desc)
53 {
54  _state = Off;
55  _style = Absolute;
56  g_atomic_int_set (&_touching, 0);
57 
59 
60  assert(_parameter.type() != NullAutomation);
62 }
63 
66 {
67  _state = Off;
68  _style = Absolute;
69  g_atomic_int_set (&_touching, 0);
70 
72 
73  assert(_parameter.type() != NullAutomation);
75 }
76 
79  , ControlList(other)
80 {
81  _style = other._style;
82  _state = other._state;
83  g_atomic_int_set (&_touching, other.touching());
84 
86 
87  assert(_parameter.type() != NullAutomation);
89 }
90 
91 AutomationList::AutomationList (const AutomationList& other, double start, double end)
92  : ControlList(other, start, end)
93 {
94  _style = other._style;
95  _state = other._state;
96  g_atomic_int_set (&_touching, other.touching());
97 
99 
100  assert(_parameter.type() != NullAutomation);
101  AutomationListCreated(this);
102 }
103 
109 {
110  g_atomic_int_set (&_touching, 0);
111  _state = Off;
112  _style = Absolute;
113 
115 
116  if (id) {
117  _parameter = id;
118  }
119 
121 
122  assert(_parameter.type() != NullAutomation);
123  AutomationListCreated(this);
124 }
125 
127 {
128 }
129 
132  const Evoral::ParameterDescriptor& desc)
133 {
135 }
136 
137 void
139 {
140  switch (_parameter.type()) {
141  case GainAutomation:
142  case TrimAutomation:
145  case PanWidthAutomation:
146  case FadeInAutomation:
147  case FadeOutAutomation:
148  case EnvelopeAutomation:
149  create_curve();
150  break;
151  default:
152  break;
153  }
154 }
155 
158 {
159  if (this != &other) {
160 
161 
162  ControlList::operator= (other);
163  _state = other._state;
164  _style = other._style;
165  _touching = other._touching;
166 
167  mark_dirty ();
169  }
170 
171  return *this;
172 }
173 
174 void
176 {
177  ControlList::maybe_signal_changed ();
178 
179  if (!ControlList::frozen()) {
180  StateChanged (); /* EMIT SIGNAL */
181  }
182 }
183 
184 void
186 {
187  if (s != _state) {
188  _state = s;
189  automation_state_changed (s); /* EMIT SIGNAL */
190  }
191 }
192 
193 void
195 {
196  if (s != _style) {
197  _style = s;
198  automation_style_changed (); /* EMIT SIGNAL */
199  }
200 }
201 
202 void
204 {
205  if (_state == Touch) {
206  start_write_pass (when);
207  }
208 
209  g_atomic_int_set (&_touching, 1);
210 }
211 
212 void
213 AutomationList::stop_touch (bool mark, double)
214 {
215  if (g_atomic_int_get (&_touching) == 0) {
216  /* this touch has already been stopped (probably by Automatable::transport_stopped),
217  so we've nothing to do.
218  */
219  return;
220  }
221 
222  g_atomic_int_set (&_touching, 0);
223 
224  if (_state == Touch) {
225 
226  if (mark) {
227 
228  /* XXX need to mark the last added point with the
229  * current time
230  */
231  }
232  }
233 }
234 
235 void
237 {
238  ControlList::thaw();
239 
240  if (_changed_when_thawed) {
241  _changed_when_thawed = false;
242  StateChanged(); /* EMIT SIGNAL */
243  }
244 }
245 
246 XMLNode&
248 {
249  return state (true);
250 }
251 
252 XMLNode&
254 {
255  XMLNode* root = new XMLNode (X_("AutomationList"));
256  char buf[64];
257  LocaleGuard lg (X_("C"));
258 
259  root->add_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
260 
261  root->add_property ("id", id().to_s());
262 
263  snprintf (buf, sizeof (buf), "%.12g", _default_value);
264  root->add_property ("default", buf);
265  snprintf (buf, sizeof (buf), "%.12g", _min_yval);
266  root->add_property ("min-yval", buf);
267  snprintf (buf, sizeof (buf), "%.12g", _max_yval);
268  root->add_property ("max-yval", buf);
269 
270  root->add_property ("interpolation-style", enum_2_string (_interpolation));
271 
272  if (full) {
273  /* never serialize state with Write enabled - too dangerous
274  for the user's data
275  */
276  if (_state != Write) {
277  root->add_property ("state", auto_state_to_string (_state));
278  } else {
279  root->add_property ("state", auto_state_to_string (Off));
280  }
281  } else {
282  /* never save anything but Off for automation state to a template */
283  root->add_property ("state", auto_state_to_string (Off));
284  }
285 
286  root->add_property ("style", auto_style_to_string (_style));
287 
288  if (!_events.empty()) {
290  }
291 
292  return *root;
293 }
294 
295 XMLNode&
297 {
298  XMLNode* node = new XMLNode (X_("events"));
299  stringstream str;
300 
301  str.precision(15); //10 digits is enough digits for 24 hours at 96kHz
302 
303  for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
304  str << (double) (*xx)->when;
305  str << ' ';
306  str <<(double) (*xx)->value;
307  str << '\n';
308  }
309 
310  /* XML is a bit wierd */
311 
312  XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
313  content_node->set_content (str.str());
314 
315  node->add_child_nocopy (*content_node);
316 
317  return *node;
318 }
319 
320 int
322 {
323  if (node.children().empty()) {
324  return -1;
325  }
326 
327  XMLNode* content_node = node.children().front();
328 
329  if (content_node->content().empty()) {
330  return -1;
331  }
332 
333  ControlList::freeze ();
334  clear ();
335 
336  stringstream str (content_node->content());
337 
338  double x;
339  double y;
340  bool ok = true;
341 
342  while (str) {
343  str >> x;
344  if (!str) {
345  break;
346  }
347  str >> y;
348  if (!str) {
349  ok = false;
350  break;
351  }
352  fast_simple_add (x, y);
353  }
354 
355  if (!ok) {
356  clear ();
357  error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
358  } else {
359  mark_dirty ();
361  }
362 
363  thaw ();
364 
365  return 0;
366 }
367 
368 int
369 AutomationList::set_state (const XMLNode& node, int version)
370 {
371  LocaleGuard lg (X_("C"));
372  XMLNodeList nlist = node.children();
373  XMLNode* nsos;
374  XMLNodeIterator niter;
375  const XMLProperty* prop;
376 
377  if (node.name() == X_("events")) {
378  /* partial state setting*/
379  return deserialize_events (node);
380  }
381 
382  if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
383 
384  if ((nsos = node.child (X_("AutomationList")))) {
385  /* new school in old school clothing */
386  return set_state (*nsos, version);
387  }
388 
389  /* old school */
390 
391  const XMLNodeList& elist = node.children();
393  XMLProperty* prop;
394  pframes_t x;
395  double y;
396 
397  ControlList::freeze ();
398  clear ();
399 
400  for (i = elist.begin(); i != elist.end(); ++i) {
401 
402  if ((prop = (*i)->property ("x")) == 0) {
403  error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
404  continue;
405  }
406  x = atoi (prop->value().c_str());
407 
408  if ((prop = (*i)->property ("y")) == 0) {
409  error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
410  continue;
411  }
412  y = atof (prop->value().c_str());
413 
414  fast_simple_add (x, y);
415  }
416 
417  thaw ();
418 
419  return 0;
420  }
421 
422  if (node.name() != X_("AutomationList") ) {
423  error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
424  return -1;
425  }
426 
427  if (set_id (node)) {
428  /* update session AL list */
429  AutomationListCreated(this);
430  }
431 
432  if ((prop = node.property (X_("automation-id"))) != 0){
434  } else {
435  warning << "Legacy session: automation list has no automation-id property." << endmsg;
436  }
437 
438  if ((prop = node.property (X_("interpolation-style"))) != 0) {
440  } else {
442  }
443 
444  if ((prop = node.property (X_("default"))) != 0){
445  _default_value = atof (prop->value().c_str());
446  } else {
447  _default_value = 0.0;
448  }
449 
450  if ((prop = node.property (X_("style"))) != 0) {
451  _style = string_to_auto_style (prop->value());
452  } else {
453  _style = Absolute;
454  }
455 
456  if ((prop = node.property (X_("state"))) != 0) {
457  _state = string_to_auto_state (prop->value());
458  if (_state == Write) {
459  _state = Off;
460  }
462  } else {
463  _state = Off;
464  }
465 
466  if ((prop = node.property (X_("min-yval"))) != 0) {
467  _min_yval = atof (prop->value ().c_str());
468  } else {
469  _min_yval = FLT_MIN;
470  }
471 
472  if ((prop = node.property (X_("max-yval"))) != 0) {
473  _max_yval = atof (prop->value ().c_str());
474  } else {
475  _max_yval = FLT_MAX;
476  }
477 
478  bool have_events = false;
479 
480  for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
481  if ((*niter)->name() == X_("events")) {
482  deserialize_events (*(*niter));
483  have_events = true;
484  }
485  }
486 
487  if (!have_events) {
488  /* there was no Events child node; clear any current events */
489  freeze ();
490  clear ();
491  mark_dirty ();
493  thaw ();
494  }
495 
496  return 0;
497 }
498 
499 bool
501 {
502  return (
503  static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
504  _state != other._state ||
505  _style != other._style ||
506  _touching != other._touching
507  );
508 }
509 
512 {
513  return new AutomationListProperty (
514  this->property_id(),
517  );
518 }
519 
void set_automation_state(AutoState)
XMLNodeList::iterator XMLNodeIterator
Definition: xml++.h:48
int atoi(const string &s)
Definition: convert.cc:140
virtual boost::shared_ptr< ControlList > create(const Evoral::Parameter &id, const Evoral::ParameterDescriptor &desc)
const std::string & content() const
Definition: xml++.h:107
const std::string & value() const
Definition: xml++.h:159
int deserialize_events(const XMLNode &)
AutomationListProperty(PBD::PropertyDescriptor< boost::shared_ptr< AutomationList > > d, Ptr p)
#define enum_2_string(e)
Definition: enumwriter.h:97
XMLNode & state(bool full)
const std::string & name() const
Definition: xml++.h:104
int set_state(const XMLNode &, int version)
AutomationList & operator=(const AutomationList &)
virtual void freeze()
void mark_dirty() const
uint32_t pframes_t
Definition: types.h:61
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
AutoStyle string_to_auto_style(std::string)
Definition: utils.cc:609
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
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
AutoStyle
Definition: types.h:155
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
#define _(Text)
Definition: i18n.h:11
PropertyID property_id() const
#define X_(Text)
Definition: i18n.h:13
XMLProperty * property(const char *)
Definition: xml++.cc:413
std::string auto_state_to_string(AutoState)
Definition: utils.cc:585
#define string_2_enum(str, e)
Definition: enumwriter.h:98
void stop_touch(bool mark, double when)
PBD::PropertyBase * clone() const
AutomationList(const Evoral::Parameter &id, const Evoral::ParameterDescriptor &desc)
Definition: amp.h:29
const PBD::ID & id() const
Definition: stateful.h:68
void set_automation_style(AutoStyle m)
bool set_id(const XMLNode &)
Definition: stateful.cc:381
void fast_simple_add(double when, double value)
static EventTypeMap & instance()
Evoral::Parameter from_symbol(const std::string &str) const
static int loading_state_version
Definition: stateful.h:90
PBD::Signal0< void > automation_style_changed
T * get() const
Definition: shared_ptr.hpp:268
void start_touch(double when)
bool operator!=(const AutomationList &) const
XMLProperty * add_property(const char *name, const std::string &value)
PBD::Signal0< void > StateChanged
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
LIBEVORAL_API uint64_t ControlList
Definition: debug.cpp:5
Definition: xml++.h:95
const std::string & set_content(const std::string &)
Definition: xml++.cc:295
uint32_t type() const
Definition: Parameter.hpp:47
Definition: debug.h:30
XMLNode * child(const char *) const
Definition: xml++.cc:309
std::string auto_style_to_string(AutoStyle)
Definition: utils.cc:623
AutoState string_to_auto_state(std::string)
Definition: utils.cc:567
static PBD::Signal1< void, AutomationList * > AutomationListCreated
XMLNodeList::const_iterator XMLNodeConstIterator
Definition: xml++.h:49
EventList::iterator iterator
Definition: ControlList.hpp:82
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
double atof(const string &s)
Definition: convert.cc:158
PBD::Signal1< void, AutoState > automation_state_changed
InterpolationStyle _interpolation
AutoState
Definition: types.h:145