3ffc040b62a01b475b197dc266b098a67671f404
[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         WritePassStarted.connect_same_thread (_writepass_connection, boost::bind (&AutomationList::snapshot_history, this, false));
166 }
167
168 AutomationList&
169 AutomationList::operator= (const AutomationList& other)
170 {
171         if (this != &other) {
172                 ControlList::freeze ();
173                 /* ControlList::operator= calls copy_events() which calls
174                  * mark_dirty() and maybe_signal_changed()
175                  */
176                 ControlList::operator= (other);
177                 _state = other._state;
178                 _touching = other._touching;
179                 ControlList::thaw ();
180         }
181
182         return *this;
183 }
184
185 void
186 AutomationList::maybe_signal_changed ()
187 {
188         ControlList::maybe_signal_changed ();
189
190         if (!ControlList::frozen()) {
191                 StateChanged (); /* EMIT SIGNAL */
192         }
193 }
194
195 AutoState
196 AutomationList::automation_state() const
197 {
198         Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock);
199         return _state;
200 }
201
202 void
203 AutomationList::set_automation_state (AutoState s)
204 {
205         {
206                 Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock);
207
208                 if (s == _state) {
209                         return;
210                 }
211                 _state = s;
212                 if (s == Write && _desc.toggled) {
213                         snapshot_history (true);
214                 }
215         }
216
217         automation_state_changed (s); /* EMIT SIGNAL */
218 }
219
220 Evoral::ControlList::InterpolationStyle
221 AutomationList::default_interpolation () const
222 {
223         switch (_parameter.type()) {
224                 case GainAutomation:
225                 case BusSendLevel:
226                 case EnvelopeAutomation:
227 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
228                         /* use old, wrong linear gain interpolation */
229                         return ControlList::Linear;
230 #endif
231                         return ControlList::Exponential;
232                         break;
233                 case TrimAutomation:
234                         return ControlList::Logarithmic;
235                         break;
236                 default:
237                         break;
238         }
239         /* based on Evoral::ParameterDescriptor log,toggle,.. */
240         return ControlList::default_interpolation ();
241 }
242
243 void
244 AutomationList::start_write_pass (double when)
245 {
246         snapshot_history (true);
247         ControlList::start_write_pass (when);
248 }
249
250 void
251 AutomationList::write_pass_finished (double when, double thinning_factor)
252 {
253         ControlList::write_pass_finished (when, thinning_factor);
254 }
255
256 void
257 AutomationList::start_touch (double when)
258 {
259         if (_state == Touch) {
260                 start_write_pass (when);
261         }
262
263         g_atomic_int_set (&_touching, 1);
264 }
265
266 void
267 AutomationList::stop_touch (double)
268 {
269         if (g_atomic_int_get (&_touching) == 0) {
270                 /* this touch has already been stopped (probably by Automatable::transport_stopped),
271                    so we've nothing to do.
272                 */
273                 return;
274         }
275
276         g_atomic_int_set (&_touching, 0);
277 }
278
279 /* _before may be owned by the undo stack,
280  * so we have to be careful about doing this.
281  *
282  * ::before () transfers ownership, setting _before to 0
283  */
284 void
285 AutomationList::clear_history ()
286 {
287         delete _before;
288         _before = 0;
289 }
290
291 void
292 AutomationList::snapshot_history (bool need_lock)
293 {
294         if (!in_new_write_pass ()) {
295                 return;
296         }
297         delete _before;
298         _before = &state (true, need_lock);
299 }
300
301
302 void
303 AutomationList::thaw ()
304 {
305         ControlList::thaw();
306
307         if (_changed_when_thawed) {
308                 _changed_when_thawed = false;
309                 StateChanged(); /* EMIT SIGNAL */
310         }
311 }
312
313 bool
314 AutomationList::paste (const ControlList& alist, double pos, DoubleBeatsFramesConverter const& bfc)
315 {
316         AutomationType src_type = (AutomationType)alist.parameter().type();
317         AutomationType dst_type = (AutomationType)_parameter.type();
318
319         if (parameter_is_midi (src_type) == parameter_is_midi (dst_type)) {
320                 return ControlList::paste (alist, pos);
321         }
322         bool to_frame = parameter_is_midi (src_type);
323
324         ControlList cl (alist);
325         cl.clear ();
326         for (const_iterator i = alist.begin ();i != alist.end (); ++i) {
327                 double when = (*i)->when;
328                 if (to_frame) {
329                         when = bfc.to ((*i)->when);
330                 } else {
331                         when = bfc.from ((*i)->when);
332                 }
333                 cl.fast_simple_add (when, (*i)->value);
334         }
335         return ControlList::paste (cl, pos);
336 }
337
338 Command*
339 AutomationList::memento_command (XMLNode* before, XMLNode* after)
340 {
341         return new MementoCommand<AutomationList> (*this, before, after);
342 }
343
344 XMLNode&
345 AutomationList::get_state ()
346 {
347         return state (true, true);
348 }
349
350 XMLNode&
351 AutomationList::state (bool full, bool need_lock)
352 {
353         XMLNode* root = new XMLNode (X_("AutomationList"));
354
355         root->set_property ("automation-id", EventTypeMap::instance().to_symbol(_parameter));
356         root->set_property ("id", id());
357
358 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
359         /* force new enums to existing ones in session-file */
360         Evoral::ControlList::InterpolationStyle is = _interpolation;
361         switch (is) {
362                 case ControlList::Exponential:
363                 case ControlList::Logarithmic:
364                         is = ControlList::Linear;
365                         break;
366                 default:
367                         break;
368         }
369         root->set_property ("interpolation-style", is);
370 #else
371         root->set_property ("interpolation-style", _interpolation);
372 #endif
373
374         if (full) {
375                 /* never serialize state with Write enabled - too dangerous
376                    for the user's data
377                 */
378                 if (_state != Write) {
379                         root->set_property ("state", _state);
380                 } else {
381                         if (_events.empty ()) {
382                                 root->set_property ("state", Off);
383                         } else {
384                                 root->set_property ("state", Touch);
385                         }
386                 }
387         } else {
388                 /* never save anything but Off for automation state to a template */
389                 root->set_property ("state", Off);
390         }
391
392         if (!_events.empty()) {
393                 root->add_child_nocopy (serialize_events (need_lock));
394         }
395
396         return *root;
397 }
398
399 XMLNode&
400 AutomationList::serialize_events (bool need_lock)
401 {
402         XMLNode* node = new XMLNode (X_("events"));
403         stringstream str;
404
405         Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock, Glib::Threads::NOT_LOCK);
406         if (need_lock) {
407                 lm.acquire ();
408         }
409         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
410                 str << PBD::to_string ((*xx)->when);
411                 str << ' ';
412                 str << PBD::to_string ((*xx)->value);
413                 str << '\n';
414         }
415
416         /* XML is a bit wierd */
417
418         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
419         content_node->set_content (str.str());
420
421         node->add_child_nocopy (*content_node);
422
423         return *node;
424 }
425
426 int
427 AutomationList::deserialize_events (const XMLNode& node)
428 {
429         if (node.children().empty()) {
430                 return -1;
431         }
432
433         XMLNode* content_node = node.children().front();
434
435         if (content_node->content().empty()) {
436                 return -1;
437         }
438
439         ControlList::freeze ();
440         clear ();
441
442         stringstream str (content_node->content());
443
444         std::string x_str;
445         std::string y_str;
446         double x;
447         double y;
448         bool ok = true;
449
450         while (str) {
451                 str >> x_str;
452                 if (!str || !PBD::string_to<double> (x_str, x)) {
453                         break;
454                 }
455                 str >> y_str;
456                 if (!str || !PBD::string_to<double> (y_str, y)) {
457                         ok = false;
458                         break;
459                 }
460                 y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
461                 fast_simple_add (x, y);
462         }
463
464         if (!ok) {
465                 clear ();
466                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
467         } else {
468                 mark_dirty ();
469                 maybe_signal_changed ();
470         }
471
472         thaw ();
473
474         return 0;
475 }
476
477 int
478 AutomationList::set_state (const XMLNode& node, int version)
479 {
480         XMLNodeList nlist = node.children();
481         XMLNode* nsos;
482         XMLNodeIterator niter;
483
484         if (node.name() == X_("events")) {
485                 /* partial state setting*/
486                 return deserialize_events (node);
487         }
488
489         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
490
491                 if ((nsos = node.child (X_("AutomationList")))) {
492                         /* new school in old school clothing */
493                         return set_state (*nsos, version);
494                 }
495
496                 /* old school */
497
498                 const XMLNodeList& elist = node.children();
499                 XMLNodeConstIterator i;
500
501                 ControlList::freeze ();
502                 clear ();
503
504                 for (i = elist.begin(); i != elist.end(); ++i) {
505
506                         pframes_t x;
507                         if (!(*i)->get_property ("x", x)) {
508                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
509                                 continue;
510                         }
511
512                         double y;
513                         if (!(*i)->get_property ("y", y)) {
514                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
515                                 continue;
516                         }
517
518                         y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
519                         fast_simple_add (x, y);
520                 }
521
522                 thaw ();
523
524                 return 0;
525         }
526
527         if (node.name() != X_("AutomationList") ) {
528                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
529                 return -1;
530         }
531
532         if (set_id (node)) {
533                 /* update session AL list */
534                 AutomationListCreated(this);
535         }
536
537         std::string value;
538         if (node.get_property (X_("automation-id"), value)) {
539                 _parameter = EventTypeMap::instance().from_symbol(value);
540         } else {
541                 warning << "Legacy session: automation list has no automation-id property." << endmsg;
542         }
543
544         if (!node.get_property (X_("interpolation-style"), _interpolation)) {
545                 _interpolation = default_interpolation ();
546         }
547 #ifndef XXX_NEW_INTERPOLATON__BREAK_SESSION_FORMAT_XXX
548         /* internally force logarithmic and Trim params to use Log-scale */
549         if (_desc.logarithmic || _parameter.type() == TrimAutomation) {
550                 _interpolation = ControlList::Logarithmic;
551         }
552 #endif
553
554         if (node.get_property (X_("state"), _state)) {
555                 if (_state == Write) {
556                         _state = Off;
557                 }
558                 automation_state_changed (_state);
559         } else {
560                 _state = Off;
561         }
562
563         bool have_events = false;
564
565         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
566                 if ((*niter)->name() == X_("events")) {
567                         deserialize_events (*(*niter));
568                         have_events = true;
569                 }
570         }
571
572         if (!have_events) {
573                 /* there was no Events child node; clear any current events */
574                 freeze ();
575                 clear ();
576                 mark_dirty ();
577                 maybe_signal_changed ();
578                 thaw ();
579         }
580
581         return 0;
582 }
583
584 bool
585 AutomationList::operator!= (AutomationList const & other) const
586 {
587         return (
588                 static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
589                 _state != other._state ||
590                 _touching != other._touching
591                 );
592 }
593
594 PBD::PropertyBase *
595 AutomationListProperty::clone () const
596 {
597         return new AutomationListProperty (
598                 this->property_id(),
599                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())),
600                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get()))
601                 );
602 }