Compatibility with old out-of-range automation-lane data
[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/beats_frames_converter.h"
28 #include "ardour/event_type_map.h"
29 #include "ardour/parameter_descriptor.h"
30 #include "ardour/parameter_types.h"
31 #include "ardour/evoral_types_convert.h"
32 #include "ardour/types_convert.h"
33 #include "evoral/Curve.hpp"
34 #include "pbd/memento_command.h"
35 #include "pbd/stacktrace.h"
36 #include "pbd/enumwriter.h"
37 #include "pbd/types_convert.h"
38
39 #include "pbd/i18n.h"
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44
45 PBD::Signal1<void,AutomationList *> AutomationList::AutomationListCreated;
46
47 #if 0
48 static void dumpit (const AutomationList& al, string prefix = "")
49 {
50         cerr << prefix << &al << endl;
51         for (AutomationList::const_iterator i = al.begin(); i != al.end(); ++i) {
52                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
53         }
54         cerr << "\n";
55 }
56 #endif
57 AutomationList::AutomationList (const Evoral::Parameter& id, const Evoral::ParameterDescriptor& desc)
58         : ControlList(id, desc)
59         , _before (0)
60 {
61         _state = Off;
62         g_atomic_int_set (&_touching, 0);
63         _interpolation = default_interpolation ();
64
65         create_curve_if_necessary();
66
67         assert(_parameter.type() != NullAutomation);
68         AutomationListCreated(this);
69 }
70
71 AutomationList::AutomationList (const Evoral::Parameter& id)
72         : ControlList(id, ARDOUR::ParameterDescriptor(id))
73         , _before (0)
74 {
75         _state = Off;
76         g_atomic_int_set (&_touching, 0);
77         _interpolation = default_interpolation ();
78
79         create_curve_if_necessary();
80
81         assert(_parameter.type() != NullAutomation);
82         AutomationListCreated(this);
83 }
84
85 AutomationList::AutomationList (const AutomationList& other)
86         : ControlList(other)
87         , StatefulDestructible()
88         , _before (0)
89 {
90         _state = other._state;
91         g_atomic_int_set (&_touching, other.touching());
92
93         create_curve_if_necessary();
94
95         assert(_parameter.type() != NullAutomation);
96         AutomationListCreated(this);
97 }
98
99 AutomationList::AutomationList (const AutomationList& other, double start, double end)
100         : ControlList(other, start, end)
101         , _before (0)
102 {
103         _state = other._state;
104         g_atomic_int_set (&_touching, other.touching());
105
106         create_curve_if_necessary();
107
108         assert(_parameter.type() != NullAutomation);
109         AutomationListCreated(this);
110 }
111
112 /** @param id is used for legacy sessions where the type is not present
113  * in or below the AutomationList node.  It is used if @param id is non-null.
114  */
115 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
116         : ControlList(id, ARDOUR::ParameterDescriptor(id))
117         , _before (0)
118 {
119         g_atomic_int_set (&_touching, 0);
120         _interpolation = default_interpolation ();
121         _state = Off;
122
123         set_state (node, Stateful::loading_state_version);
124
125         if (id) {
126                 _parameter = id;
127         }
128
129         create_curve_if_necessary();
130
131         assert(_parameter.type() != NullAutomation);
132         AutomationListCreated(this);
133 }
134
135 AutomationList::~AutomationList()
136 {
137         delete _before;
138 }
139
140 boost::shared_ptr<Evoral::ControlList>
141 AutomationList::create(const Evoral::Parameter&           id,
142                        const Evoral::ParameterDescriptor& desc)
143 {
144         return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id, desc));
145 }
146
147 void
148 AutomationList::create_curve_if_necessary()
149 {
150         switch (_parameter.type()) {
151         case GainAutomation:
152         case TrimAutomation:
153         case PanAzimuthAutomation:
154         case PanElevationAutomation:
155         case PanWidthAutomation:
156         case FadeInAutomation:
157         case FadeOutAutomation:
158         case EnvelopeAutomation:
159                 create_curve();
160                 break;
161         default:
162                 break;
163         }
164 }
165
166 AutomationList&
167 AutomationList::operator= (const AutomationList& other)
168 {
169         if (this != &other) {
170
171                 ControlList::operator= (other);
172                 _state = other._state;
173                 _touching = other._touching;
174
175                 mark_dirty ();
176                 maybe_signal_changed ();
177         }
178
179         return *this;
180 }
181
182 void
183 AutomationList::maybe_signal_changed ()
184 {
185         ControlList::maybe_signal_changed ();
186
187         if (!ControlList::frozen()) {
188                 StateChanged (); /* EMIT SIGNAL */
189         }
190 }
191
192 void
193 AutomationList::set_automation_state (AutoState s)
194 {
195         if (s != _state) {
196                 _state = s;
197                 delete _before;
198                 if (s == Write && _desc.toggled) {
199                         _before = &get_state ();
200                 } else {
201                         _before = 0;
202                 }
203                 automation_state_changed (s); /* EMIT SIGNAL */
204         }
205 }
206
207 Evoral::ControlList::InterpolationStyle
208 AutomationList::default_interpolation () const
209 {
210         switch (_parameter.type()) {
211                 case GainAutomation:
212                 case BusSendLevel:
213                 case EnvelopeAutomation:
214 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
215                         /* use old, wrong linear gain interpolation */
216                         return ControlList::Linear;
217 #endif
218                         return ControlList::Exponential;
219                         break;
220                 case TrimAutomation:
221                         return ControlList::Logarithmic;
222                         break;
223                 default:
224                         break;
225         }
226         /* based on Evoral::ParameterDescriptor log,toggle,.. */
227         return ControlList::default_interpolation ();
228 }
229
230 void
231 AutomationList::start_write_pass (double when)
232 {
233         delete _before;
234         if (in_new_write_pass ()) {
235                 _before = &get_state ();
236         } else {
237                 _before = 0;
238         }
239         ControlList::start_write_pass (when);
240 }
241
242 void
243 AutomationList::write_pass_finished (double when, double thinning_factor)
244 {
245         ControlList::write_pass_finished (when, thinning_factor);
246 }
247
248 void
249 AutomationList::start_touch (double when)
250 {
251         if (_state == Touch) {
252                 start_write_pass (when);
253         }
254
255         g_atomic_int_set (&_touching, 1);
256 }
257
258 void
259 AutomationList::stop_touch (bool mark, double)
260 {
261         if (g_atomic_int_get (&_touching) == 0) {
262                 /* this touch has already been stopped (probably by Automatable::transport_stopped),
263                    so we've nothing to do.
264                 */
265                 return;
266         }
267
268         g_atomic_int_set (&_touching, 0);
269
270         if (_state == Touch) {
271
272                 if (mark) {
273
274                         /* XXX need to mark the last added point with the
275                          * current time
276                          */
277                 }
278         }
279 }
280
281 /* _before may be owned by the undo stack,
282  * so we have to be careful about doing this.
283  *
284  * ::before () transfers ownership, setting _before to 0
285  */
286 void
287 AutomationList::clear_history ()
288 {
289         delete _before;
290         _before = 0;
291 }
292
293 void
294 AutomationList::thaw ()
295 {
296         ControlList::thaw();
297
298         if (_changed_when_thawed) {
299                 _changed_when_thawed = false;
300                 StateChanged(); /* EMIT SIGNAL */
301         }
302 }
303
304 bool
305 AutomationList::paste (const ControlList& alist, double pos, DoubleBeatsFramesConverter const& bfc)
306 {
307         AutomationType src_type = (AutomationType)alist.parameter().type();
308         AutomationType dst_type = (AutomationType)_parameter.type();
309
310         if (parameter_is_midi (src_type) == parameter_is_midi (dst_type)) {
311                 return ControlList::paste (alist, pos);
312         }
313         bool to_frame = parameter_is_midi (src_type);
314
315         ControlList cl (alist);
316         cl.clear ();
317         for (const_iterator i = alist.begin ();i != alist.end (); ++i) {
318                 double when = (*i)->when;
319                 if (to_frame) {
320                         when = bfc.to ((*i)->when);
321                 } else {
322                         when = bfc.from ((*i)->when);
323                 }
324                 cl.fast_simple_add (when, (*i)->value);
325         }
326         return ControlList::paste (cl, pos);
327 }
328
329 Command*
330 AutomationList::memento_command (XMLNode* before, XMLNode* after)
331 {
332         return new MementoCommand<AutomationList> (*this, before, after);
333 }
334
335 XMLNode&
336 AutomationList::get_state ()
337 {
338         return state (true);
339 }
340
341 XMLNode&
342 AutomationList::state (bool full)
343 {
344         XMLNode* root = new XMLNode (X_("AutomationList"));
345
346         root->set_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
347         root->set_property ("id", id());
348
349 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
350         /* force new enums to existing ones in session-file */
351         Evoral::ControlList::InterpolationStyle is = _interpolation;
352         switch (is) {
353                 case ControlList::Exponential:
354                 case ControlList::Logarithmic:
355                         is = ControlList::Linear;
356                         break;
357                 default:
358                         break;
359         }
360         root->set_property ("interpolation-style", is);
361 #else
362         root->set_property ("interpolation-style", _interpolation);
363 #endif
364
365         if (full) {
366                 /* never serialize state with Write enabled - too dangerous
367                    for the user's data
368                 */
369                 if (_state != Write) {
370                         root->set_property ("state", _state);
371                 } else {
372                         if (_events.empty ()) {
373                                 root->set_property ("state", Off);
374                         } else {
375                                 root->set_property ("state", Touch);
376                         }
377                 }
378         } else {
379                 /* never save anything but Off for automation state to a template */
380                 root->set_property ("state", Off);
381         }
382
383         if (!_events.empty()) {
384                 root->add_child_nocopy (serialize_events());
385         }
386
387         return *root;
388 }
389
390 XMLNode&
391 AutomationList::serialize_events ()
392 {
393         XMLNode* node = new XMLNode (X_("events"));
394         stringstream str;
395
396         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
397                 str << PBD::to_string ((*xx)->when);
398                 str << ' ';
399                 str << PBD::to_string ((*xx)->value);
400                 str << '\n';
401         }
402
403         /* XML is a bit wierd */
404
405         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
406         content_node->set_content (str.str());
407
408         node->add_child_nocopy (*content_node);
409
410         return *node;
411 }
412
413 int
414 AutomationList::deserialize_events (const XMLNode& node)
415 {
416         if (node.children().empty()) {
417                 return -1;
418         }
419
420         XMLNode* content_node = node.children().front();
421
422         if (content_node->content().empty()) {
423                 return -1;
424         }
425
426         ControlList::freeze ();
427         clear ();
428
429         stringstream str (content_node->content());
430
431         std::string x_str;
432         std::string y_str;
433         double x;
434         double y;
435         bool ok = true;
436
437         while (str) {
438                 str >> x_str;
439                 if (!str || !PBD::string_to<double> (x_str, x)) {
440                         break;
441                 }
442                 str >> y_str;
443                 if (!str || !PBD::string_to<double> (y_str, y)) {
444                         ok = false;
445                         break;
446                 }
447                 y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
448                 fast_simple_add (x, y);
449         }
450
451         if (!ok) {
452                 clear ();
453                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
454         } else {
455                 mark_dirty ();
456                 maybe_signal_changed ();
457         }
458
459         thaw ();
460
461         return 0;
462 }
463
464 int
465 AutomationList::set_state (const XMLNode& node, int version)
466 {
467         XMLNodeList nlist = node.children();
468         XMLNode* nsos;
469         XMLNodeIterator niter;
470
471         if (node.name() == X_("events")) {
472                 /* partial state setting*/
473                 return deserialize_events (node);
474         }
475
476         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
477
478                 if ((nsos = node.child (X_("AutomationList")))) {
479                         /* new school in old school clothing */
480                         return set_state (*nsos, version);
481                 }
482
483                 /* old school */
484
485                 const XMLNodeList& elist = node.children();
486                 XMLNodeConstIterator i;
487
488                 ControlList::freeze ();
489                 clear ();
490
491                 for (i = elist.begin(); i != elist.end(); ++i) {
492
493                         pframes_t x;
494                         if (!(*i)->get_property ("x", x)) {
495                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
496                                 continue;
497                         }
498
499                         double y;
500                         if (!(*i)->get_property ("y", y)) {
501                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
502                                 continue;
503                         }
504
505                         y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
506                         fast_simple_add (x, y);
507                 }
508
509                 thaw ();
510
511                 return 0;
512         }
513
514         if (node.name() != X_("AutomationList") ) {
515                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
516                 return -1;
517         }
518
519         if (set_id (node)) {
520                 /* update session AL list */
521                 AutomationListCreated(this);
522         }
523
524         std::string value;
525         if (node.get_property (X_("automation-id"), value)) {
526                 _parameter = EventTypeMap::instance().from_symbol(value);
527         } else {
528                 warning << "Legacy session: automation list has no automation-id property." << endmsg;
529         }
530
531         if (!node.get_property (X_("interpolation-style"), _interpolation)) {
532                 _interpolation = default_interpolation ();
533         }
534 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
535         /* internally force logarithmic and Trim params to use Log-scale */
536         if (_desc.logarithmic || _parameter.type() == TrimAutomation) {
537                 _interpolation = ControlList::Logarithmic;
538         }
539 #endif
540
541         if (node.get_property (X_("state"), _state)) {
542                 if (_state == Write) {
543                         _state = Off;
544                 }
545                 automation_state_changed (_state);
546         } else {
547                 _state = Off;
548         }
549
550         bool have_events = false;
551
552         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
553                 if ((*niter)->name() == X_("events")) {
554                         deserialize_events (*(*niter));
555                         have_events = true;
556                 }
557         }
558
559         if (!have_events) {
560                 /* there was no Events child node; clear any current events */
561                 freeze ();
562                 clear ();
563                 mark_dirty ();
564                 maybe_signal_changed ();
565                 thaw ();
566         }
567
568         return 0;
569 }
570
571 bool
572 AutomationList::operator!= (AutomationList const & other) const
573 {
574         return (
575                 static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
576                 _state != other._state ||
577                 _touching != other._touching
578                 );
579 }
580
581 PBD::PropertyBase *
582 AutomationListProperty::clone () const
583 {
584         return new AutomationListProperty (
585                 this->property_id(),
586                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())),
587                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get()))
588                 );
589 }
590