globally remove all trailing whitespace from ardour code base.
[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 "ardour/automation_list.h"
27 #include "ardour/event_type_map.h"
28 #include "ardour/parameter_descriptor.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
58         create_curve_if_necessary();
59
60         assert(_parameter.type() != NullAutomation);
61         AutomationListCreated(this);
62 }
63
64 AutomationList::AutomationList (const Evoral::Parameter& id)
65         : ControlList(id, ARDOUR::ParameterDescriptor(id))
66 {
67         _state = Off;
68         _style = Absolute;
69         g_atomic_int_set (&_touching, 0);
70
71         create_curve_if_necessary();
72
73         assert(_parameter.type() != NullAutomation);
74         AutomationListCreated(this);
75 }
76
77 AutomationList::AutomationList (const AutomationList& other)
78         : StatefulDestructible()
79         , ControlList(other)
80 {
81         _style = other._style;
82         _state = other._state;
83         g_atomic_int_set (&_touching, other.touching());
84
85         create_curve_if_necessary();
86
87         assert(_parameter.type() != NullAutomation);
88         AutomationListCreated(this);
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
98         create_curve_if_necessary();
99
100         assert(_parameter.type() != NullAutomation);
101         AutomationListCreated(this);
102 }
103
104 /** @param id is used for legacy sessions where the type is not present
105  * in or below the AutomationList node.  It is used if @param id is non-null.
106  */
107 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
108         : ControlList(id, ARDOUR::ParameterDescriptor(id))
109 {
110         g_atomic_int_set (&_touching, 0);
111         _state = Off;
112         _style = Absolute;
113
114         set_state (node, Stateful::loading_state_version);
115
116         if (id) {
117                 _parameter = id;
118         }
119
120         create_curve_if_necessary();
121
122         assert(_parameter.type() != NullAutomation);
123         AutomationListCreated(this);
124 }
125
126 AutomationList::~AutomationList()
127 {
128 }
129
130 boost::shared_ptr<Evoral::ControlList>
131 AutomationList::create(const Evoral::Parameter&           id,
132                        const Evoral::ParameterDescriptor& desc)
133 {
134         return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id, desc));
135 }
136
137 void
138 AutomationList::create_curve_if_necessary()
139 {
140         switch (_parameter.type()) {
141         case GainAutomation:
142         case TrimAutomation:
143         case PanAzimuthAutomation:
144         case PanElevationAutomation:
145         case PanWidthAutomation:
146         case FadeInAutomation:
147         case FadeOutAutomation:
148         case EnvelopeAutomation:
149                 create_curve();
150                 break;
151         default:
152                 break;
153         }
154 }
155
156 AutomationList&
157 AutomationList::operator= (const AutomationList& other)
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 ();
168                 maybe_signal_changed ();
169         }
170
171         return *this;
172 }
173
174 void
175 AutomationList::maybe_signal_changed ()
176 {
177         ControlList::maybe_signal_changed ();
178
179         if (!ControlList::frozen()) {
180                 StateChanged (); /* EMIT SIGNAL */
181         }
182 }
183
184 void
185 AutomationList::set_automation_state (AutoState s)
186 {
187         if (s != _state) {
188                 _state = s;
189                 automation_state_changed (s); /* EMIT SIGNAL */
190         }
191 }
192
193 void
194 AutomationList::set_automation_style (AutoStyle s)
195 {
196         if (s != _style) {
197                 _style = s;
198                 automation_style_changed (); /* EMIT SIGNAL */
199         }
200 }
201
202 void
203 AutomationList::start_touch (double when)
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
236 AutomationList::thaw ()
237 {
238         ControlList::thaw();
239
240         if (_changed_when_thawed) {
241                 _changed_when_thawed = false;
242                 StateChanged(); /* EMIT SIGNAL */
243         }
244 }
245
246 XMLNode&
247 AutomationList::get_state ()
248 {
249         return state (true);
250 }
251
252 XMLNode&
253 AutomationList::state (bool full)
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                         if (_events.empty ()) {
280                                 root->add_property ("state", auto_state_to_string (Off));
281                         } else {
282                                 root->add_property ("state", auto_state_to_string (Touch));
283                         }
284                 }
285         } else {
286                 /* never save anything but Off for automation state to a template */
287                 root->add_property ("state", auto_state_to_string (Off));
288         }
289
290         root->add_property ("style", auto_style_to_string (_style));
291
292         if (!_events.empty()) {
293                 root->add_child_nocopy (serialize_events());
294         }
295
296         return *root;
297 }
298
299 XMLNode&
300 AutomationList::serialize_events ()
301 {
302         XMLNode* node = new XMLNode (X_("events"));
303         stringstream str;
304
305         str.precision(15);  //10 digits is enough digits for 24 hours at 96kHz
306
307         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
308                 str << (double) (*xx)->when;
309                 str << ' ';
310                 str <<(double) (*xx)->value;
311                 str << '\n';
312         }
313
314         /* XML is a bit wierd */
315
316         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
317         content_node->set_content (str.str());
318
319         node->add_child_nocopy (*content_node);
320
321         return *node;
322 }
323
324 int
325 AutomationList::deserialize_events (const XMLNode& node)
326 {
327         if (node.children().empty()) {
328                 return -1;
329         }
330
331         XMLNode* content_node = node.children().front();
332
333         if (content_node->content().empty()) {
334                 return -1;
335         }
336
337         ControlList::freeze ();
338         clear ();
339
340         stringstream str (content_node->content());
341
342         double x;
343         double y;
344         bool ok = true;
345
346         while (str) {
347                 str >> x;
348                 if (!str) {
349                         break;
350                 }
351                 str >> y;
352                 if (!str) {
353                         ok = false;
354                         break;
355                 }
356                 fast_simple_add (x, y);
357         }
358
359         if (!ok) {
360                 clear ();
361                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
362         } else {
363                 mark_dirty ();
364                 maybe_signal_changed ();
365         }
366
367         thaw ();
368
369         return 0;
370 }
371
372 int
373 AutomationList::set_state (const XMLNode& node, int version)
374 {
375         LocaleGuard lg (X_("C"));
376         XMLNodeList nlist = node.children();
377         XMLNode* nsos;
378         XMLNodeIterator niter;
379         const XMLProperty* prop;
380
381         if (node.name() == X_("events")) {
382                 /* partial state setting*/
383                 return deserialize_events (node);
384         }
385
386         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
387
388                 if ((nsos = node.child (X_("AutomationList")))) {
389                         /* new school in old school clothing */
390                         return set_state (*nsos, version);
391                 }
392
393                 /* old school */
394
395                 const XMLNodeList& elist = node.children();
396                 XMLNodeConstIterator i;
397                 XMLProperty* prop;
398                 pframes_t x;
399                 double y;
400
401                 ControlList::freeze ();
402                 clear ();
403
404                 for (i = elist.begin(); i != elist.end(); ++i) {
405
406                         if ((prop = (*i)->property ("x")) == 0) {
407                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
408                                 continue;
409                         }
410                         x = atoi (prop->value().c_str());
411
412                         if ((prop = (*i)->property ("y")) == 0) {
413                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
414                                 continue;
415                         }
416                         y = atof (prop->value().c_str());
417
418                         fast_simple_add (x, y);
419                 }
420
421                 thaw ();
422
423                 return 0;
424         }
425
426         if (node.name() != X_("AutomationList") ) {
427                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
428                 return -1;
429         }
430
431         if (set_id (node)) {
432                 /* update session AL list */
433                 AutomationListCreated(this);
434         }
435
436         if ((prop = node.property (X_("automation-id"))) != 0){
437                 _parameter = EventTypeMap::instance().from_symbol(prop->value());
438         } else {
439                 warning << "Legacy session: automation list has no automation-id property." << endmsg;
440         }
441
442         if ((prop = node.property (X_("interpolation-style"))) != 0) {
443                 _interpolation = (InterpolationStyle)string_2_enum(prop->value(), _interpolation);
444         } else {
445                 _interpolation = Linear;
446         }
447
448         if ((prop = node.property (X_("default"))) != 0){
449                 _default_value = atof (prop->value().c_str());
450         } else {
451                 _default_value = 0.0;
452         }
453
454         if ((prop = node.property (X_("style"))) != 0) {
455                 _style = string_to_auto_style (prop->value());
456         } else {
457                 _style = Absolute;
458         }
459
460         if ((prop = node.property (X_("state"))) != 0) {
461                 _state = string_to_auto_state (prop->value());
462                 if (_state == Write) {
463                         _state = Off;
464                 }
465                 automation_state_changed(_state);
466         } else {
467                 _state = Off;
468         }
469
470         if ((prop = node.property (X_("min-yval"))) != 0) {
471                 _min_yval = atof (prop->value ().c_str());
472         } else {
473                 _min_yval = FLT_MIN;
474         }
475
476         if ((prop = node.property (X_("max-yval"))) != 0) {
477                 _max_yval = atof (prop->value ().c_str());
478         } else {
479                 _max_yval = FLT_MAX;
480         }
481
482         bool have_events = false;
483
484         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
485                 if ((*niter)->name() == X_("events")) {
486                         deserialize_events (*(*niter));
487                         have_events = true;
488                 }
489         }
490
491         if (!have_events) {
492                 /* there was no Events child node; clear any current events */
493                 freeze ();
494                 clear ();
495                 mark_dirty ();
496                 maybe_signal_changed ();
497                 thaw ();
498         }
499
500         return 0;
501 }
502
503 bool
504 AutomationList::operator!= (AutomationList const & other) const
505 {
506         return (
507                 static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
508                 _state != other._state ||
509                 _style != other._style ||
510                 _touching != other._touching
511                 );
512 }
513
514 PBD::PropertyBase *
515 AutomationListProperty::clone () const
516 {
517         return new AutomationListProperty (
518                 this->property_id(),
519                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())),
520                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get()))
521                 );
522 }
523