Update libardour GPL boilerplate and (C) from git log
[ardour.git] / libs / ardour / automation_list.cc
1 /*
2  * Copyright (C) 2008-2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2008-2014 David Robillard <d@drobilla.net>
4  * Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2012-2017 Robin Gareus <robin@gareus.org>
6  * Copyright (C) 2015 Nick Mainsbridge <mainsbridge@gmail.com>
7  * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include <set>
25 #include <climits>
26 #include <float.h>
27 #include <cmath>
28 #include <sstream>
29 #include <algorithm>
30 #include "ardour/automation_list.h"
31 #include "ardour/beats_samples_converter.h"
32 #include "ardour/event_type_map.h"
33 #include "ardour/parameter_descriptor.h"
34 #include "ardour/parameter_types.h"
35 #include "ardour/evoral_types_convert.h"
36 #include "ardour/types_convert.h"
37 #include "evoral/Curve.hpp"
38 #include "pbd/memento_command.h"
39 #include "pbd/stacktrace.h"
40 #include "pbd/enumwriter.h"
41 #include "pbd/types_convert.h"
42
43 #include "pbd/i18n.h"
44
45 using namespace std;
46 using namespace ARDOUR;
47 using namespace PBD;
48
49 PBD::Signal1<void,AutomationList *> AutomationList::AutomationListCreated;
50
51 #if 0
52 static void dumpit (const AutomationList& al, string prefix = "")
53 {
54         cerr << prefix << &al << endl;
55         for (AutomationList::const_iterator i = al.begin(); i != al.end(); ++i) {
56                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
57         }
58         cerr << "\n";
59 }
60 #endif
61 AutomationList::AutomationList (const Evoral::Parameter& id, const Evoral::ParameterDescriptor& desc)
62         : ControlList(id, desc)
63         , _before (0)
64 {
65         _state = Off;
66         g_atomic_int_set (&_touching, 0);
67         _interpolation = default_interpolation ();
68
69         create_curve_if_necessary();
70
71         assert(_parameter.type() != NullAutomation);
72         AutomationListCreated(this);
73 }
74
75 AutomationList::AutomationList (const Evoral::Parameter& id)
76         : ControlList(id, ARDOUR::ParameterDescriptor(id))
77         , _before (0)
78 {
79         _state = Off;
80         g_atomic_int_set (&_touching, 0);
81         _interpolation = default_interpolation ();
82
83         create_curve_if_necessary();
84
85         assert(_parameter.type() != NullAutomation);
86         AutomationListCreated(this);
87 }
88
89 AutomationList::AutomationList (const AutomationList& other)
90         : ControlList(other)
91         , StatefulDestructible()
92         , _before (0)
93 {
94         _state = other._state;
95         g_atomic_int_set (&_touching, other.touching());
96
97         create_curve_if_necessary();
98
99         assert(_parameter.type() != NullAutomation);
100         AutomationListCreated(this);
101 }
102
103 AutomationList::AutomationList (const AutomationList& other, double start, double end)
104         : ControlList(other, start, end)
105         , _before (0)
106 {
107         _state = other._state;
108         g_atomic_int_set (&_touching, other.touching());
109
110         create_curve_if_necessary();
111
112         assert(_parameter.type() != NullAutomation);
113         AutomationListCreated(this);
114 }
115
116 /** @param id is used for legacy sessions where the type is not present
117  * in or below the AutomationList node.  It is used if @param id is non-null.
118  */
119 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
120         : ControlList(id, ARDOUR::ParameterDescriptor(id))
121         , _before (0)
122 {
123         g_atomic_int_set (&_touching, 0);
124         _interpolation = default_interpolation ();
125         _state = Off;
126
127         set_state (node, Stateful::loading_state_version);
128
129         if (id) {
130                 _parameter = id;
131         }
132
133         create_curve_if_necessary();
134
135         assert(_parameter.type() != NullAutomation);
136         AutomationListCreated(this);
137 }
138
139 AutomationList::~AutomationList()
140 {
141         delete _before;
142 }
143
144 boost::shared_ptr<Evoral::ControlList>
145 AutomationList::create(const Evoral::Parameter&           id,
146                        const Evoral::ParameterDescriptor& desc)
147 {
148         return boost::shared_ptr<Evoral::ControlList>(new AutomationList(id, desc));
149 }
150
151 void
152 AutomationList::create_curve_if_necessary()
153 {
154         switch (_parameter.type()) {
155         case GainAutomation:
156         case TrimAutomation:
157         case PanAzimuthAutomation:
158         case PanElevationAutomation:
159         case PanWidthAutomation:
160         case FadeInAutomation:
161         case FadeOutAutomation:
162         case EnvelopeAutomation:
163                 create_curve();
164                 break;
165         default:
166                 break;
167         }
168
169         WritePassStarted.connect_same_thread (_writepass_connection, boost::bind (&AutomationList::snapshot_history, this, false));
170 }
171
172 AutomationList&
173 AutomationList::operator= (const AutomationList& other)
174 {
175         if (this != &other) {
176                 ControlList::freeze ();
177                 /* ControlList::operator= calls copy_events() which calls
178                  * mark_dirty() and maybe_signal_changed()
179                  */
180                 ControlList::operator= (other);
181                 _state = other._state;
182                 _touching = other._touching;
183                 ControlList::thaw ();
184         }
185
186         return *this;
187 }
188
189 void
190 AutomationList::maybe_signal_changed ()
191 {
192         ControlList::maybe_signal_changed ();
193
194         if (!ControlList::frozen()) {
195                 StateChanged (); /* EMIT SIGNAL */
196         }
197 }
198
199 AutoState
200 AutomationList::automation_state() const
201 {
202         Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock);
203         return _state;
204 }
205
206 void
207 AutomationList::set_automation_state (AutoState s)
208 {
209         {
210                 Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock);
211
212                 if (s == _state) {
213                         return;
214                 }
215                 _state = s;
216                 if (s == Write && _desc.toggled) {
217                         snapshot_history (true);
218                 }
219         }
220
221         automation_state_changed (s); /* EMIT SIGNAL */
222 }
223
224 Evoral::ControlList::InterpolationStyle
225 AutomationList::default_interpolation () const
226 {
227         switch (_parameter.type()) {
228                 case GainAutomation:
229                 case BusSendLevel:
230                 case EnvelopeAutomation:
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, DoubleBeatsSamplesConverter 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_sample = 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_sample) {
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 save_auto_state, 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         root->set_property ("interpolation-style", _interpolation);
358
359         if (save_auto_state) {
360                 /* never serialize state with Write enabled - too dangerous
361                    for the user's data
362                 */
363                 if (_state != Write) {
364                         root->set_property ("state", _state);
365                 } else {
366                         if (_events.empty ()) {
367                                 root->set_property ("state", Off);
368                         } else {
369                                 root->set_property ("state", Touch);
370                         }
371                 }
372         } else {
373                 /* never save anything but Off for automation state to a template */
374                 root->set_property ("state", Off);
375         }
376
377         if (!_events.empty()) {
378                 root->add_child_nocopy (serialize_events (need_lock));
379         }
380
381         return *root;
382 }
383
384 XMLNode&
385 AutomationList::serialize_events (bool need_lock)
386 {
387         XMLNode* node = new XMLNode (X_("events"));
388         stringstream str;
389
390         Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock, Glib::Threads::NOT_LOCK);
391         if (need_lock) {
392                 lm.acquire ();
393         }
394         for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
395                 str << PBD::to_string ((*xx)->when);
396                 str << ' ';
397                 str << PBD::to_string ((*xx)->value);
398                 str << '\n';
399         }
400
401         /* XML is a bit wierd */
402
403         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
404         content_node->set_content (str.str());
405
406         node->add_child_nocopy (*content_node);
407
408         return *node;
409 }
410
411 int
412 AutomationList::deserialize_events (const XMLNode& node)
413 {
414         if (node.children().empty()) {
415                 return -1;
416         }
417
418         XMLNode* content_node = node.children().front();
419
420         if (content_node->content().empty()) {
421                 return -1;
422         }
423
424         ControlList::freeze ();
425         clear ();
426
427         stringstream str (content_node->content());
428
429         std::string x_str;
430         std::string y_str;
431         double x;
432         double y;
433         bool ok = true;
434
435         while (str) {
436                 str >> x_str;
437                 if (!str || !PBD::string_to<double> (x_str, x)) {
438                         break;
439                 }
440                 str >> y_str;
441                 if (!str || !PBD::string_to<double> (y_str, y)) {
442                         ok = false;
443                         break;
444                 }
445                 y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
446                 fast_simple_add (x, y);
447         }
448
449         if (!ok) {
450                 clear ();
451                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
452         } else {
453                 mark_dirty ();
454                 maybe_signal_changed ();
455         }
456
457         thaw ();
458
459         return 0;
460 }
461
462 int
463 AutomationList::set_state (const XMLNode& node, int version)
464 {
465         XMLNodeList nlist = node.children();
466         XMLNode* nsos;
467         XMLNodeIterator niter;
468
469         if (node.name() == X_("events")) {
470                 /* partial state setting*/
471                 return deserialize_events (node);
472         }
473
474         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
475
476                 if ((nsos = node.child (X_("AutomationList")))) {
477                         /* new school in old school clothing */
478                         return set_state (*nsos, version);
479                 }
480
481                 /* old school */
482
483                 const XMLNodeList& elist = node.children();
484                 XMLNodeConstIterator i;
485
486                 ControlList::freeze ();
487                 clear ();
488
489                 for (i = elist.begin(); i != elist.end(); ++i) {
490
491                         pframes_t x;
492                         if (!(*i)->get_property ("x", x)) {
493                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
494                                 continue;
495                         }
496
497                         double y;
498                         if (!(*i)->get_property ("y", y)) {
499                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
500                                 continue;
501                         }
502
503                         y = std::min ((double)_desc.upper, std::max ((double)_desc.lower, y));
504                         fast_simple_add (x, y);
505                 }
506
507                 thaw ();
508
509                 return 0;
510         }
511
512         if (node.name() != X_("AutomationList") ) {
513                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
514                 return -1;
515         }
516
517         if (set_id (node)) {
518                 /* update session AL list */
519                 AutomationListCreated(this);
520         }
521
522         std::string value;
523         if (node.get_property (X_("automation-id"), value)) {
524                 _parameter = EventTypeMap::instance().from_symbol(value);
525         } else {
526                 warning << "Legacy session: automation list has no automation-id property." << endmsg;
527         }
528
529         if (!node.get_property (X_("interpolation-style"), _interpolation)) {
530                 _interpolation = default_interpolation ();
531         }
532
533         if (node.get_property (X_("state"), _state)) {
534                 if (_state == Write) {
535                         _state = Off;
536                 }
537                 automation_state_changed (_state);
538         } else {
539                 _state = Off;
540         }
541
542         bool have_events = false;
543
544         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
545                 if ((*niter)->name() == X_("events")) {
546                         deserialize_events (*(*niter));
547                         have_events = true;
548                 }
549         }
550
551         if (!have_events) {
552                 /* there was no Events child node; clear any current events */
553                 freeze ();
554                 clear ();
555                 mark_dirty ();
556                 maybe_signal_changed ();
557                 thaw ();
558         }
559
560         return 0;
561 }
562
563 bool
564 AutomationList::operator!= (AutomationList const & other) const
565 {
566         return (
567                 static_cast<ControlList const &> (*this) != static_cast<ControlList const &> (other) ||
568                 _state != other._state ||
569                 _touching != other._touching
570                 );
571 }
572
573 PBD::PropertyBase *
574 AutomationListProperty::clone () const
575 {
576         return new AutomationListProperty (
577                 this->property_id(),
578                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_old.get())),
579                 boost::shared_ptr<AutomationList> (new AutomationList (*this->_current.get()))
580                 );
581 }