initial pass at "ramping back" to existing automation values at the end of a touch...
[ardour2.git] / libs / ardour / automation_event.cc
blob81347d5d403fdd0f3f70af0e054699ef5cfc19d3
1 /*
2 Copyright (C) 2002 Paul Davis
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.
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.
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.
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_event.h>
28 #include <pbd/stacktrace.h>
29 #include <pbd/localeguard.h>
31 #include "i18n.h"
33 using namespace std;
34 using namespace ARDOUR;
35 using namespace sigc;
36 using namespace PBD;
38 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
40 static bool sort_events_by_time (ControlEvent* a, ControlEvent* b)
42 return a->when < b->when;
45 #if 0
46 static void dumpit (const AutomationList& al, string prefix = "")
48 cerr << prefix << &al << endl;
49 for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
50 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
52 cerr << "\n";
54 #endif
56 AutomationList::AutomationList (double defval)
58 _frozen = 0;
59 changed_when_thawed = false;
60 _state = Off;
61 _style = Absolute;
62 _touching = false;
63 _new_touch = false;
64 min_yval = FLT_MIN;
65 max_yval = FLT_MAX;
66 max_xval = 0; // means "no limit"
67 default_value = defval;
68 _dirty = false;
69 rt_insertion_point = events.end();
70 lookup_cache.left = -1;
71 lookup_cache.range.first = events.end();
72 sort_pending = false;
74 AutomationListCreated(this);
77 AutomationList::AutomationList (const AutomationList& other)
79 _frozen = 0;
80 changed_when_thawed = false;
81 _style = other._style;
82 min_yval = other.min_yval;
83 max_yval = other.max_yval;
84 max_xval = other.max_xval;
85 default_value = other.default_value;
86 _state = other._state;
87 _touching = other._touching;
88 _new_touch = false;
89 _dirty = false;
90 rt_insertion_point = events.end();
91 lookup_cache.left = -1;
92 lookup_cache.range.first = events.end();
93 sort_pending = false;
95 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
96 /* we have to use other point_factory() because
97 its virtual and we're in a constructor.
99 events.push_back (other.point_factory (**i));
102 mark_dirty ();
103 AutomationListCreated(this);
106 AutomationList::AutomationList (const AutomationList& other, double start, double end)
108 _frozen = 0;
109 changed_when_thawed = false;
110 _style = other._style;
111 min_yval = other.min_yval;
112 max_yval = other.max_yval;
113 max_xval = other.max_xval;
114 default_value = other.default_value;
115 _state = other._state;
116 _touching = other._touching;
117 _dirty = false;
118 rt_insertion_point = events.end();
119 lookup_cache.left = -1;
120 lookup_cache.range.first = events.end();
121 sort_pending = false;
123 /* now grab the relevant points, and shift them back if necessary */
125 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
127 if (!section->empty()) {
128 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
129 events.push_back (other.point_factory ((*i)->when, (*i)->value));
133 delete section;
135 mark_dirty ();
137 AutomationListCreated(this);
140 AutomationList::AutomationList (const XMLNode& node)
142 _frozen = 0;
143 changed_when_thawed = false;
144 _touching = false;
145 min_yval = FLT_MIN;
146 max_yval = FLT_MAX;
147 max_xval = 0; // means "no limit"
148 _dirty = false;
149 _state = Off;
150 _style = Absolute;
151 rt_insertion_point = events.end();
152 lookup_cache.left = -1;
153 lookup_cache.range.first = events.end();
154 sort_pending = false;
156 set_state (node);
158 AutomationListCreated(this);
161 AutomationList::~AutomationList()
163 GoingAway ();
165 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
166 delete (*x);
170 bool
171 AutomationList::operator== (const AutomationList& other)
173 return events == other.events;
176 AutomationList&
177 AutomationList::operator= (const AutomationList& other)
179 if (this != &other) {
181 events.clear ();
183 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
184 events.push_back (point_factory (**i));
187 min_yval = other.min_yval;
188 max_yval = other.max_yval;
189 max_xval = other.max_xval;
190 default_value = other.default_value;
192 rt_insertion_point = events.end();
193 lookup_cache.range.first = events.end();
195 mark_dirty ();
196 maybe_signal_changed ();
199 return *this;
202 void
203 AutomationList::maybe_signal_changed ()
205 mark_dirty ();
207 if (_frozen) {
208 changed_when_thawed = true;
209 } else {
210 StateChanged ();
214 void
215 AutomationList::set_automation_state (AutoState s)
217 if (s != _state) {
218 _state = s;
219 automation_state_changed (); /* EMIT SIGNAL */
223 void
224 AutomationList::set_automation_style (AutoStyle s)
226 if (s != _style) {
227 _style = s;
228 automation_style_changed (); /* EMIT SIGNAL */
232 void
233 AutomationList::start_touch ()
235 _touching = true;
236 _new_touch = true;
239 void
240 AutomationList::stop_touch (bool mark, double when)
242 _touching = false;
243 _new_touch = false;
245 if (mark) {
246 /* get the value of the next point after "when", and replicate
247 it directly after when, unless of course its already there.
250 double val;
251 AutomationList::const_iterator i;
253 for (i = const_begin(); i != const_end(); ++i) {
254 if ((*i)->when >= when) {
255 break;
259 if (i == const_end()) {
260 val = default_value;
261 } else {
262 val = (*i)->value;
265 /* if the existing point is at "when", add a new one right after it,
266 otherwise add it directly where the touch ended.
269 if ((*i)->when == when) {
270 when++;
273 add (when, val);
277 void
278 AutomationList::clear ()
281 Glib::Mutex::Lock lm (lock);
282 events.clear ();
283 mark_dirty ();
286 maybe_signal_changed ();
289 void
290 AutomationList::x_scale (double factor)
292 Glib::Mutex::Lock lm (lock);
293 _x_scale (factor);
296 bool
297 AutomationList::extend_to (double when)
299 Glib::Mutex::Lock lm (lock);
300 if (events.empty() || events.back()->when == when) {
301 return false;
303 double factor = when / events.back()->when;
304 _x_scale (factor);
305 return true;
308 void AutomationList::_x_scale (double factor)
310 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
311 (*i)->when = floor ((*i)->when * factor);
314 mark_dirty ();
317 void
318 AutomationList::reposition_for_rt_add (double when)
320 rt_insertion_point = events.end();
323 #define last_rt_insertion_point rt_insertion_point
325 void
326 AutomationList::rt_add (double when, double value)
328 /* this is for automation recording */
330 if ((_state & Touch) && !_touching) {
331 return;
334 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
337 Glib::Mutex::Lock lm (lock);
339 iterator where;
340 TimeComparator cmp;
341 ControlEvent cp (when, 0.0);
342 bool done = false;
344 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
346 /* we have a previous insertion point, so we should delete
347 everything between it and the position where we are going
348 to insert this point.
351 iterator after = last_rt_insertion_point;
353 if (++after != events.end()) {
354 iterator far = after;
356 while (far != events.end()) {
357 if ((*far)->when > when) {
358 break;
360 ++far;
363 if(_new_touch) {
364 where = far;
365 last_rt_insertion_point = where;
367 if((*where)->when == when) {
368 (*where)->value = value;
369 done = true;
371 } else {
372 where = events.erase (after, far);
375 } else {
377 where = after;
381 iterator previous = last_rt_insertion_point;
382 --previous;
384 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
385 (*last_rt_insertion_point)->when = when;
386 done = true;
390 } else {
392 where = lower_bound (events.begin(), events.end(), &cp, cmp);
394 if (where != events.end()) {
395 if ((*where)->when == when) {
396 (*where)->value = value;
397 done = true;
402 if (!done) {
403 last_rt_insertion_point = events.insert (where, point_factory (when, value));
404 // cerr << "\tINSERTED\n";
407 _new_touch = false;
408 mark_dirty ();
411 maybe_signal_changed ();
414 void
415 AutomationList::fast_simple_add (double when, double value)
417 /* to be used only for loading pre-sorted data from saved state */
418 events.insert (events.end(), point_factory (when, value));
421 #undef last_rt_insertion_point
423 void
424 AutomationList::add (double when, double value)
426 /* this is for making changes from some kind of user interface or control surface (GUI, MIDI, OSC etc) */
429 Glib::Mutex::Lock lm (lock);
430 TimeComparator cmp;
431 ControlEvent cp (when, 0.0f);
432 bool insert = true;
433 iterator insertion_point;
435 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
437 /* only one point allowed per time point */
439 if ((*insertion_point)->when == when) {
440 (*insertion_point)->value = value;
441 insert = false;
442 break;
445 if ((*insertion_point)->when >= when) {
446 break;
450 if (insert) {
452 events.insert (insertion_point, point_factory (when, value));
453 reposition_for_rt_add (0);
457 mark_dirty ();
460 maybe_signal_changed ();
463 void
464 AutomationList::erase (AutomationList::iterator i)
467 Glib::Mutex::Lock lm (lock);
468 events.erase (i);
469 reposition_for_rt_add (0);
470 mark_dirty ();
472 maybe_signal_changed ();
475 void
476 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
479 Glib::Mutex::Lock lm (lock);
480 events.erase (start, end);
481 reposition_for_rt_add (0);
482 mark_dirty ();
484 maybe_signal_changed ();
487 void
488 AutomationList::reset_range (double start, double endt)
490 bool reset = false;
493 Glib::Mutex::Lock lm (lock);
494 TimeComparator cmp;
495 ControlEvent cp (start, 0.0f);
496 iterator s;
497 iterator e;
499 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
501 cp.when = endt;
502 e = upper_bound (events.begin(), events.end(), &cp, cmp);
504 for (iterator i = s; i != e; ++i) {
505 (*i)->value = default_value;
508 reset = true;
510 mark_dirty ();
514 if (reset) {
515 maybe_signal_changed ();
519 void
520 AutomationList::erase_range (double start, double endt)
522 bool erased = false;
525 Glib::Mutex::Lock lm (lock);
526 TimeComparator cmp;
527 ControlEvent cp (start, 0.0f);
528 iterator s;
529 iterator e;
531 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
532 cp.when = endt;
533 e = upper_bound (events.begin(), events.end(), &cp, cmp);
534 events.erase (s, e);
535 reposition_for_rt_add (0);
536 erased = true;
537 mark_dirty ();
542 if (erased) {
543 maybe_signal_changed ();
547 void
548 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
550 /* note: we assume higher level logic is in place to avoid this
551 reordering the time-order of control events in the list. ie. all
552 points after end are later than (end)->when.
556 Glib::Mutex::Lock lm (lock);
558 while (start != end) {
559 (*start)->when += xdelta;
560 (*start)->value += ydelta;
561 if (isnan ((*start)->value)) {
562 abort ();
564 ++start;
567 if (!_frozen) {
568 events.sort (sort_events_by_time);
569 } else {
570 sort_pending = true;
573 mark_dirty ();
576 maybe_signal_changed ();
579 void
580 AutomationList::slide (iterator before, double distance)
583 Glib::Mutex::Lock lm (lock);
585 if (before == events.end()) {
586 return;
589 while (before != events.end()) {
590 (*before)->when += distance;
591 ++before;
595 maybe_signal_changed ();
598 void
599 AutomationList::modify (iterator iter, double when, double val)
601 /* note: we assume higher level logic is in place to avoid this
602 reordering the time-order of control events in the list. ie. all
603 points after *iter are later than when.
607 Glib::Mutex::Lock lm (lock);
609 (*iter)->when = when;
610 (*iter)->value = val;
612 if (isnan (val)) {
613 abort ();
616 if (!_frozen) {
617 events.sort (sort_events_by_time);
618 } else {
619 sort_pending = true;
622 mark_dirty ();
625 maybe_signal_changed ();
628 std::pair<AutomationList::iterator,AutomationList::iterator>
629 AutomationList::control_points_adjacent (double xval)
631 Glib::Mutex::Lock lm (lock);
632 iterator i;
633 TimeComparator cmp;
634 ControlEvent cp (xval, 0.0f);
635 std::pair<iterator,iterator> ret;
637 ret.first = events.end();
638 ret.second = events.end();
640 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
642 if (ret.first == events.end()) {
643 if ((*i)->when >= xval) {
644 if (i != events.begin()) {
645 ret.first = i;
646 --ret.first;
647 } else {
648 return ret;
653 if ((*i)->when > xval) {
654 ret.second = i;
655 break;
659 return ret;
662 void
663 AutomationList::freeze ()
665 _frozen++;
668 void
669 AutomationList::thaw ()
671 if (_frozen == 0) {
672 PBD::stacktrace (cerr);
673 fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg;
674 /*NOTREACHED*/
677 if (--_frozen > 0) {
678 return;
682 Glib::Mutex::Lock lm (lock);
684 if (sort_pending) {
685 events.sort (sort_events_by_time);
686 sort_pending = false;
690 if (changed_when_thawed) {
691 StateChanged(); /* EMIT SIGNAL */
695 void
696 AutomationList::set_max_xval (double x)
698 max_xval = x;
701 void
702 AutomationList::mark_dirty ()
704 lookup_cache.left = -1;
705 _dirty = true;
708 void
709 AutomationList::truncate_end (double last_coordinate)
712 Glib::Mutex::Lock lm (lock);
713 ControlEvent cp (last_coordinate, 0);
714 AutomationList::reverse_iterator i;
715 double last_val;
717 if (events.empty()) {
718 return;
721 if (last_coordinate == events.back()->when) {
722 return;
725 if (last_coordinate > events.back()->when) {
727 /* extending end:
730 iterator foo = events.begin();
731 bool lessthantwo;
733 if (foo == events.end()) {
734 lessthantwo = true;
735 } else if (++foo == events.end()) {
736 lessthantwo = true;
737 } else {
738 lessthantwo = false;
741 if (lessthantwo) {
742 /* less than 2 points: add a new point */
743 events.push_back (point_factory (last_coordinate, events.back()->value));
744 } else {
746 /* more than 2 points: check to see if the last 2 values
747 are equal. if so, just move the position of the
748 last point. otherwise, add a new point.
751 iterator penultimate = events.end();
752 --penultimate; /* points at last point */
753 --penultimate; /* points at the penultimate point */
755 if (events.back()->value == (*penultimate)->value) {
756 events.back()->when = last_coordinate;
757 } else {
758 events.push_back (point_factory (last_coordinate, events.back()->value));
762 } else {
764 /* shortening end */
766 last_val = unlocked_eval (last_coordinate);
767 last_val = max ((double) min_yval, last_val);
768 last_val = min ((double) max_yval, last_val);
770 i = events.rbegin();
772 /* make i point to the last control point */
774 ++i;
776 /* now go backwards, removing control points that are
777 beyond the new last coordinate.
780 uint32_t sz = events.size();
782 while (i != events.rend() && sz > 2) {
783 AutomationList::reverse_iterator tmp;
785 tmp = i;
786 ++tmp;
788 if ((*i)->when < last_coordinate) {
789 break;
792 events.erase (i.base());
793 --sz;
795 i = tmp;
798 events.back()->when = last_coordinate;
799 events.back()->value = last_val;
802 reposition_for_rt_add (0);
803 mark_dirty();
806 maybe_signal_changed ();
809 void
810 AutomationList::truncate_start (double overall_length)
813 Glib::Mutex::Lock lm (lock);
814 AutomationList::iterator i;
815 double first_legal_value;
816 double first_legal_coordinate;
818 if (events.empty()) {
819 fatal << _("programming error:")
820 << "AutomationList::truncate_start() called on an empty list"
821 << endmsg;
822 /*NOTREACHED*/
823 return;
826 if (overall_length == events.back()->when) {
827 /* no change in overall length */
828 return;
831 if (overall_length > events.back()->when) {
833 /* growing at front: duplicate first point. shift all others */
835 double shift = overall_length - events.back()->when;
836 uint32_t np;
838 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
839 (*i)->when += shift;
842 if (np < 2) {
844 /* less than 2 points: add a new point */
845 events.push_front (point_factory (0, events.front()->value));
847 } else {
849 /* more than 2 points: check to see if the first 2 values
850 are equal. if so, just move the position of the
851 first point. otherwise, add a new point.
854 iterator second = events.begin();
855 ++second; /* points at the second point */
857 if (events.front()->value == (*second)->value) {
858 /* first segment is flat, just move start point back to zero */
859 events.front()->when = 0;
860 } else {
861 /* leave non-flat segment in place, add a new leading point. */
862 events.push_front (point_factory (0, events.front()->value));
866 } else {
868 /* shrinking at front */
870 first_legal_coordinate = events.back()->when - overall_length;
871 first_legal_value = unlocked_eval (first_legal_coordinate);
872 first_legal_value = max (min_yval, first_legal_value);
873 first_legal_value = min (max_yval, first_legal_value);
875 /* remove all events earlier than the new "front" */
877 i = events.begin();
879 while (i != events.end() && !events.empty()) {
880 AutomationList::iterator tmp;
882 tmp = i;
883 ++tmp;
885 if ((*i)->when > first_legal_coordinate) {
886 break;
889 events.erase (i);
891 i = tmp;
895 /* shift all remaining points left to keep their same
896 relative position
899 for (i = events.begin(); i != events.end(); ++i) {
900 (*i)->when -= first_legal_coordinate;
903 /* add a new point for the interpolated new value */
905 events.push_front (point_factory (0, first_legal_value));
908 reposition_for_rt_add (0);
910 mark_dirty();
913 maybe_signal_changed ();
916 double
917 AutomationList::unlocked_eval (double x)
919 return shared_eval (x);
922 double
923 AutomationList::shared_eval (double x)
925 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
926 int32_t npoints;
927 double lpos, upos;
928 double lval, uval;
929 double fraction;
931 npoints = events.size();
933 switch (npoints) {
934 case 0:
935 return default_value;
937 case 1:
938 if (x >= events.front()->when) {
939 return events.front()->value;
940 } else {
941 // return default_value;
942 return events.front()->value;
945 case 2:
946 if (x >= events.back()->when) {
947 return events.back()->value;
948 } else if (x == events.front()->when) {
949 return events.front()->value;
950 } else if (x < events.front()->when) {
951 // return default_value;
952 return events.front()->value;
955 lpos = events.front()->when;
956 lval = events.front()->value;
957 upos = events.back()->when;
958 uval = events.back()->value;
960 /* linear interpolation betweeen the two points
963 fraction = (double) (x - lpos) / (double) (upos - lpos);
964 return lval + (fraction * (uval - lval));
966 default:
968 if (x >= events.back()->when) {
969 return events.back()->value;
970 } else if (x == events.front()->when) {
971 return events.front()->value;
972 } else if (x < events.front()->when) {
973 // return default_value;
974 return events.front()->value;
977 return multipoint_eval (x);
978 break;
981 /*NOTREACHED*/ /* stupid gcc */
982 return 0.0;
985 double
986 AutomationList::multipoint_eval (double x)
988 pair<AutomationList::iterator,AutomationList::iterator> range;
989 double upos, lpos;
990 double uval, lval;
991 double fraction;
993 /* only do the range lookup if x is in a different range than last time
994 this was called (or if the lookup cache has been marked "dirty" (left<0)
997 if ((lookup_cache.left < 0) ||
998 ((lookup_cache.left > x) ||
999 (lookup_cache.range.first == events.end()) ||
1000 ((*lookup_cache.range.second)->when < x))) {
1002 ControlEvent cp (x, 0);
1003 TimeComparator cmp;
1005 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
1008 range = lookup_cache.range;
1010 if (range.first == range.second) {
1012 /* x does not exist within the list as a control point */
1014 lookup_cache.left = x;
1016 if (range.first != events.begin()) {
1017 --range.first;
1018 lpos = (*range.first)->when;
1019 lval = (*range.first)->value;
1020 } else {
1021 /* we're before the first point */
1022 // return default_value;
1023 return events.front()->value;
1026 if (range.second == events.end()) {
1027 /* we're after the last point */
1028 return events.back()->value;
1031 upos = (*range.second)->when;
1032 uval = (*range.second)->value;
1034 /* linear interpolation betweeen the two points
1035 on either side of x
1038 fraction = (double) (x - lpos) / (double) (upos - lpos);
1039 return lval + (fraction * (uval - lval));
1043 /* x is a control point in the data */
1044 lookup_cache.left = -1;
1045 return (*range.first)->value;
1048 AutomationList*
1049 AutomationList::cut (iterator start, iterator end)
1051 AutomationList* nal = new AutomationList (default_value);
1054 Glib::Mutex::Lock lm (lock);
1056 for (iterator x = start; x != end; ) {
1057 iterator tmp;
1059 tmp = x;
1060 ++tmp;
1062 nal->events.push_back (point_factory (**x));
1063 events.erase (x);
1065 reposition_for_rt_add (0);
1067 x = tmp;
1070 mark_dirty ();
1073 maybe_signal_changed ();
1075 return nal;
1078 AutomationList*
1079 AutomationList::cut_copy_clear (double start, double end, int op)
1081 AutomationList* nal = new AutomationList (default_value);
1082 iterator s, e;
1083 ControlEvent cp (start, 0.0);
1084 TimeComparator cmp;
1085 bool changed = false;
1088 Glib::Mutex::Lock lm (lock);
1090 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1091 return nal;
1094 cp.when = end;
1095 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1097 if (op != 2 && (*s)->when != start) {
1098 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1101 for (iterator x = s; x != e; ) {
1102 iterator tmp;
1104 tmp = x;
1105 ++tmp;
1107 changed = true;
1109 /* adjust new points to be relative to start, which
1110 has been set to zero.
1113 if (op != 2) {
1114 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1117 if (op != 1) {
1118 events.erase (x);
1121 x = tmp;
1124 if (op != 2 && nal->events.back()->when != end - start) {
1125 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1128 if (changed) {
1129 reposition_for_rt_add (0);
1132 mark_dirty ();
1135 maybe_signal_changed ();
1137 return nal;
1141 AutomationList*
1142 AutomationList::copy (iterator start, iterator end)
1144 AutomationList* nal = new AutomationList (default_value);
1147 Glib::Mutex::Lock lm (lock);
1149 for (iterator x = start; x != end; ) {
1150 iterator tmp;
1152 tmp = x;
1153 ++tmp;
1155 nal->events.push_back (point_factory (**x));
1157 x = tmp;
1161 return nal;
1164 AutomationList*
1165 AutomationList::cut (double start, double end)
1167 return cut_copy_clear (start, end, 0);
1170 AutomationList*
1171 AutomationList::copy (double start, double end)
1173 return cut_copy_clear (start, end, 1);
1176 void
1177 AutomationList::clear (double start, double end)
1179 (void) cut_copy_clear (start, end, 2);
1182 bool
1183 AutomationList::paste (AutomationList& alist, double pos, float times)
1185 if (alist.events.empty()) {
1186 return false;
1190 Glib::Mutex::Lock lm (lock);
1191 iterator where;
1192 iterator prev;
1193 double end = 0;
1194 ControlEvent cp (pos, 0.0);
1195 TimeComparator cmp;
1197 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1199 for (iterator i = alist.begin();i != alist.end(); ++i) {
1200 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1201 end = (*i)->when + pos;
1205 /* move all points after the insertion along the timeline by
1206 the correct amount.
1209 while (where != events.end()) {
1210 iterator tmp;
1211 if ((*where)->when <= end) {
1212 tmp = where;
1213 ++tmp;
1214 events.erase(where);
1215 where = tmp;
1217 } else {
1218 break;
1222 reposition_for_rt_add (0);
1223 mark_dirty ();
1226 maybe_signal_changed ();
1227 return true;
1230 ControlEvent*
1231 AutomationList::point_factory (double when, double val) const
1233 return new ControlEvent (when, val);
1236 ControlEvent*
1237 AutomationList::point_factory (const ControlEvent& other) const
1239 return new ControlEvent (other);
1242 XMLNode&
1243 AutomationList::get_state ()
1245 return state (true);
1248 XMLNode&
1249 AutomationList::state (bool full)
1251 XMLNode* root = new XMLNode (X_("AutomationList"));
1252 char buf[64];
1253 LocaleGuard lg (X_("POSIX"));
1255 root->add_property ("id", _id.to_s());
1257 snprintf (buf, sizeof (buf), "%.12g", default_value);
1258 root->add_property ("default", buf);
1259 snprintf (buf, sizeof (buf), "%.12g", min_yval);
1260 root->add_property ("min_yval", buf);
1261 snprintf (buf, sizeof (buf), "%.12g", max_yval);
1262 root->add_property ("max_yval", buf);
1263 snprintf (buf, sizeof (buf), "%.12g", max_xval);
1264 root->add_property ("max_xval", buf);
1266 if (full) {
1267 root->add_property ("state", auto_state_to_string (_state));
1268 } else {
1269 /* never save anything but Off for automation state to a template */
1270 root->add_property ("state", auto_state_to_string (Off));
1273 root->add_property ("style", auto_style_to_string (_style));
1275 if (!events.empty()) {
1276 root->add_child_nocopy (serialize_events());
1279 return *root;
1282 XMLNode&
1283 AutomationList::serialize_events ()
1285 XMLNode* node = new XMLNode (X_("events"));
1286 stringstream str;
1288 str.precision(15); //10 digits is enough digits for 24 hours at 96kHz
1290 for (iterator xx = events.begin(); xx != events.end(); ++xx) {
1291 str << (double) (*xx)->when;
1292 str << ' ';
1293 str <<(double) (*xx)->value;
1294 str << '\n';
1297 /* XML is a bit wierd */
1299 XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
1300 content_node->set_content (str.str());
1302 node->add_child_nocopy (*content_node);
1304 return *node;
1308 AutomationList::deserialize_events (const XMLNode& node)
1310 if (node.children().empty()) {
1311 return -1;
1314 XMLNode* content_node = node.children().front();
1316 if (content_node->content().empty()) {
1317 return -1;
1320 freeze ();
1321 clear ();
1323 stringstream str (content_node->content());
1325 double x;
1326 double y;
1327 bool ok = true;
1329 while (str) {
1330 str >> x;
1331 if (!str) {
1332 break;
1334 str >> y;
1335 if (!str) {
1336 ok = false;
1337 break;
1339 fast_simple_add (x, y);
1342 if (!ok) {
1343 clear ();
1344 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
1345 } else {
1346 mark_dirty ();
1347 reposition_for_rt_add (0);
1348 maybe_signal_changed ();
1351 thaw ();
1353 return 0;
1357 AutomationList::set_state (const XMLNode& node)
1359 XMLNodeList nlist = node.children();
1360 XMLNode* nsos;
1361 XMLNodeIterator niter;
1362 const XMLProperty* prop;
1364 if (node.name() == X_("events")) {
1365 /* partial state setting*/
1366 return deserialize_events (node);
1369 if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
1371 if ((nsos = node.child (X_("AutomationList")))) {
1372 /* new school in old school clothing */
1373 return set_state (*nsos);
1376 /* old school */
1378 const XMLNodeList& elist = node.children();
1379 XMLNodeConstIterator i;
1380 XMLProperty* prop;
1381 jack_nframes_t x;
1382 double y;
1384 freeze ();
1385 clear ();
1387 for (i = elist.begin(); i != elist.end(); ++i) {
1389 if ((prop = (*i)->property ("x")) == 0) {
1390 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1391 continue;
1393 x = atoi (prop->value().c_str());
1395 if ((prop = (*i)->property ("y")) == 0) {
1396 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1397 continue;
1399 y = atof (prop->value().c_str());
1401 fast_simple_add (x, y);
1404 thaw ();
1406 return 0;
1409 if (node.name() != X_("AutomationList") ) {
1410 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
1411 return -1;
1414 if ((prop = node.property ("id")) != 0) {
1415 _id = prop->value ();
1416 /* update session AL list */
1417 AutomationListCreated(this);
1420 if ((prop = node.property (X_("default"))) != 0){
1421 default_value = atof (prop->value());
1422 } else {
1423 default_value = 0.0;
1426 if ((prop = node.property (X_("style"))) != 0) {
1427 _style = string_to_auto_style (prop->value());
1428 } else {
1429 _style = Absolute;
1432 if ((prop = node.property (X_("state"))) != 0) {
1433 _state = string_to_auto_state (prop->value());
1434 } else {
1435 _state = Off;
1438 if ((prop = node.property (X_("min_yval"))) != 0) {
1439 min_yval = atof (prop->value ());
1440 } else {
1441 min_yval = FLT_MIN;
1444 if ((prop = node.property (X_("max_yval"))) != 0) {
1445 max_yval = atof (prop->value ());
1446 } else {
1447 max_yval = FLT_MAX;
1450 if ((prop = node.property (X_("max_xval"))) != 0) {
1451 max_xval = atof (prop->value ());
1452 } else {
1453 max_xval = 0; // means "no limit ;
1456 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1457 if ((*niter)->name() == X_("events")) {
1458 deserialize_events (*(*niter));
1462 return 0;
1465 void
1466 AutomationList::shift (nframes64_t pos, nframes64_t frames)
1469 Glib::Mutex::Lock lm (lock);
1471 for (iterator i = begin (); i != end (); ++i) {
1472 if ((*i)->when >= pos) {
1473 (*i)->when += frames;
1477 mark_dirty ();
1480 maybe_signal_changed ();