Strip trailing whitespace and fix other whitespace errors (e.g. space/tab mixing...
[ardour.git] / libs / ardour / automation_list.cc
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 <sigc++/bind.h>
27 #include "ardour/automation_list.h"
28 #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 sigc;
38 using namespace PBD;
39
40 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
41
42 #if 0
43 static void dumpit (const AutomationList& al, string prefix = "")
44 {
45         cerr << prefix << &al << endl;
46         for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
47                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
48         }
49         cerr << "\n";
50 }
51 #endif
52
53 AutomationList::AutomationList (Evoral::Parameter id)
54         : ControlList(id)
55 {
56         _state = Off;
57         _style = Absolute;
58         _touching = false;
59
60         create_curve_if_necessary();
61
62         assert(_parameter.type() != NullAutomation);
63         AutomationListCreated(this);
64 }
65
66 AutomationList::AutomationList (const AutomationList& other)
67         : StatefulDestructible()
68         , ControlList(other)
69 {
70         _style = other._style;
71         _state = other._state;
72         _touching = other._touching;
73
74         create_curve_if_necessary();
75
76         assert(_parameter.type() != NullAutomation);
77         AutomationListCreated(this);
78 }
79
80 AutomationList::AutomationList (const AutomationList& other, double start, double end)
81         : ControlList(other, start, end)
82 {
83         _style = other._style;
84         _state = other._state;
85         _touching = other._touching;
86
87         create_curve_if_necessary();
88
89         assert(_parameter.type() != NullAutomation);
90         AutomationListCreated(this);
91 }
92
93 /** \a id is used for legacy sessions where the type is not present
94  * in or below the <AutomationList> node.  It is used if \a id is non-null.
95  */
96 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
97         : ControlList(id)
98 {
99         _touching = false;
100         _state = Off;
101         _style = Absolute;
102
103         set_state (node);
104
105         if (id) {
106                 _parameter = id;
107         }
108
109         create_curve_if_necessary();
110
111         assert(_parameter.type() != NullAutomation);
112         AutomationListCreated(this);
113 }
114
115 AutomationList::~AutomationList()
116 {
117         GoingAway ();
118 }
119
120 boost::shared_ptr<Evoral::ControlList>
121 AutomationList::create(Evoral::Parameter id)
122 {
123         return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id));
124 }
125
126 void
127 AutomationList::create_curve_if_necessary()
128 {
129         switch (_parameter.type()) {
130         case GainAutomation:
131         case PanAutomation:
132         case FadeInAutomation:
133         case FadeOutAutomation:
134         case EnvelopeAutomation:
135                 create_curve();
136                 break;
137         default:
138                 break;
139         }
140 }
141
142 bool
143 AutomationList::operator== (const AutomationList& other)
144 {
145         return _events == other._events;
146 }
147
148 AutomationList&
149 AutomationList::operator= (const AutomationList& other)
150 {
151         if (this != &other) {
152
153                 _events.clear ();
154
155                 for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
156                         _events.push_back (new Evoral::ControlEvent (**i));
157                 }
158
159                 _min_yval = other._min_yval;
160                 _max_yval = other._max_yval;
161                 _max_xval = other._max_xval;
162                 _default_value = other._default_value;
163
164                 mark_dirty ();
165                 maybe_signal_changed ();
166         }
167
168         return *this;
169 }
170
171 void
172 AutomationList::maybe_signal_changed ()
173 {
174         ControlList::maybe_signal_changed ();
175
176         if (!_frozen) {
177                 StateChanged (); /* EMIT SIGNAL */
178         }
179 }
180
181 void
182 AutomationList::set_automation_state (AutoState s)
183 {
184         if (s != _state) {
185                 _state = s;
186                 automation_state_changed (); /* EMIT SIGNAL */
187         }
188 }
189
190 void
191 AutomationList::set_automation_style (AutoStyle s)
192 {
193         if (s != _style) {
194                 _style = s;
195                 automation_style_changed (); /* EMIT SIGNAL */
196         }
197 }
198
199 void
200 AutomationList::start_touch ()
201 {
202         _touching = true;
203         _new_value = true;
204 }
205
206 void
207 AutomationList::stop_touch ()
208 {
209         _touching = false;
210         _new_value = false;
211 }
212
213 void
214 AutomationList::freeze ()
215 {
216         _frozen++;
217 }
218
219 void
220 AutomationList::thaw ()
221 {
222         ControlList::thaw();
223
224         if (_changed_when_thawed) {
225                 StateChanged(); /* EMIT SIGNAL */
226         }
227 }
228
229 void
230 AutomationList::mark_dirty () const
231 {
232         ControlList::mark_dirty ();
233         Dirty (); /* EMIT SIGNAL */
234 }
235
236 XMLNode&
237 AutomationList::get_state ()
238 {
239         return state (true);
240 }
241
242 XMLNode&
243 AutomationList::state (bool full)
244 {
245         XMLNode* root = new XMLNode (X_("AutomationList"));
246         char buf[64];
247         LocaleGuard lg (X_("POSIX"));
248
249         root->add_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
250
251         root->add_property ("id", _id.to_s());
252
253         snprintf (buf, sizeof (buf), "%.12g", _default_value);
254         root->add_property ("default", buf);
255         snprintf (buf, sizeof (buf), "%.12g", _min_yval);
256         root->add_property ("min-yval", buf);
257         snprintf (buf, sizeof (buf), "%.12g", _max_yval);
258         root->add_property ("max-yval", buf);
259         snprintf (buf, sizeof (buf), "%.12g", _max_xval);
260         root->add_property ("max-xval", buf);
261
262         root->add_property ("interpolation-style", enum_2_string (_interpolation));
263
264         if (full) {
265                 root->add_property ("state", auto_state_to_string (_state));
266         } else {
267                 /* never save anything but Off for automation state to a template */
268                 root->add_property ("state", auto_state_to_string (Off));
269         }
270
271         root->add_property ("style", auto_style_to_string (_style));
272
273         if (!_events.empty()) {
274                 root->add_child_nocopy (serialize_events());
275         }
276
277         return *root;
278 }
279
280 XMLNode&
281 AutomationList::serialize_events ()
282 {
283         XMLNode* node = new XMLNode (X_("events"));
284         stringstream str;
285
286         str.precision(15);  //10 digits is enough digits for 24 hours at 96kHz
287
288         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
289                 str << (double) (*xx)->when;
290                 str << ' ';
291                 str <<(double) (*xx)->value;
292                 str << '\n';
293         }
294
295         /* XML is a bit wierd */
296
297         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
298         content_node->set_content (str.str());
299
300         node->add_child_nocopy (*content_node);
301
302         return *node;
303 }
304
305 int
306 AutomationList::deserialize_events (const XMLNode& node)
307 {
308         if (node.children().empty()) {
309                 return -1;
310         }
311
312         XMLNode* content_node = node.children().front();
313
314         if (content_node->content().empty()) {
315                 return -1;
316         }
317
318         freeze ();
319         clear ();
320
321         stringstream str (content_node->content());
322
323         double x;
324         double y;
325         bool ok = true;
326
327         while (str) {
328                 str >> x;
329                 if (!str) {
330                         break;
331                 }
332                 str >> y;
333                 if (!str) {
334                         ok = false;
335                         break;
336                 }
337                 fast_simple_add (x, y);
338         }
339
340         if (!ok) {
341                 clear ();
342                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
343         } else {
344                 mark_dirty ();
345                 reposition_for_rt_add (0);
346                 maybe_signal_changed ();
347         }
348
349         thaw ();
350
351         return 0;
352 }
353
354 int
355 AutomationList::set_state (const XMLNode& node)
356 {
357         XMLNodeList nlist = node.children();
358         XMLNode* nsos;
359         XMLNodeIterator niter;
360         const XMLProperty* prop;
361
362         if (node.name() == X_("events")) {
363                 /* partial state setting*/
364                 return deserialize_events (node);
365         }
366
367         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
368
369                 if ((nsos = node.child (X_("AutomationList")))) {
370                         /* new school in old school clothing */
371                         return set_state (*nsos);
372                 }
373
374                 /* old school */
375
376                 const XMLNodeList& elist = node.children();
377                 XMLNodeConstIterator i;
378                 XMLProperty* prop;
379                 nframes_t x;
380                 double y;
381
382                 freeze ();
383                 clear ();
384
385                 for (i = elist.begin(); i != elist.end(); ++i) {
386
387                         if ((prop = (*i)->property ("x")) == 0) {
388                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
389                                 continue;
390                         }
391                         x = atoi (prop->value().c_str());
392
393                         if ((prop = (*i)->property ("y")) == 0) {
394                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
395                                 continue;
396                         }
397                         y = atof (prop->value().c_str());
398
399                         fast_simple_add (x, y);
400                 }
401
402                 thaw ();
403
404                 return 0;
405         }
406
407         if (node.name() != X_("AutomationList") ) {
408                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
409                 return -1;
410         }
411
412         if ((prop = node.property ("id")) != 0) {
413                 _id = prop->value ();
414                 /* update session AL list */
415                 AutomationListCreated(this);
416         }
417
418         if ((prop = node.property (X_("automation-id"))) != 0){
419                 _parameter = EventTypeMap::instance().new_parameter(prop->value());
420         } else {
421                 warning << "Legacy session: automation list has no automation-id property.";
422         }
423
424         if ((prop = node.property (X_("interpolation-style"))) != 0) {
425                 _interpolation = (InterpolationStyle)string_2_enum(prop->value(), _interpolation);
426         } else {
427                 _interpolation = Linear;
428         }
429
430         if ((prop = node.property (X_("default"))) != 0){
431                 _default_value = atof (prop->value().c_str());
432         } else {
433                 _default_value = 0.0;
434         }
435
436         if ((prop = node.property (X_("style"))) != 0) {
437                 _style = string_to_auto_style (prop->value());
438         } else {
439                 _style = Absolute;
440         }
441
442         if ((prop = node.property (X_("state"))) != 0) {
443                 _state = string_to_auto_state (prop->value());
444         } else {
445                 _state = Off;
446         }
447
448         if ((prop = node.property (X_("min_yval"))) != 0) {
449                 _min_yval = atof (prop->value ().c_str());
450         } else {
451                 _min_yval = FLT_MIN;
452         }
453
454         if ((prop = node.property (X_("max_yval"))) != 0) {
455                 _max_yval = atof (prop->value ().c_str());
456         } else {
457                 _max_yval = FLT_MAX;
458         }
459
460         if ((prop = node.property (X_("max_xval"))) != 0) {
461                 _max_xval = atof (prop->value ().c_str());
462         } else {
463                 _max_xval = 0; // means "no limit ;
464         }
465
466         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
467                 if ((*niter)->name() == X_("events")) {
468                         deserialize_events (*(*niter));
469                 }
470         }
471
472         return 0;
473 }
474