correctly handle touch/write automation data merging when it occurs before the start...
[ardour2.git] / libs / ardour / automation_event.cc
blob6013122357fe1f364dbd23d4d0d914968d592849
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)
57 : _touch_saved_point (-1.0, -1.0)
59 _frozen = 0;
60 changed_when_thawed = false;
61 _state = Auto_Off;
62 _style = Auto_Absolute;
63 _touching = false;
64 _new_touch = false;
65 min_yval = FLT_MIN;
66 max_yval = FLT_MAX;
67 max_xval = 0; // means "no limit"
68 default_value = defval;
69 _dirty = false;
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)
78 : _touch_saved_point (-1.0, -1.0)
80 _frozen = 0;
81 changed_when_thawed = false;
82 _style = other._style;
83 min_yval = other.min_yval;
84 max_yval = other.max_yval;
85 max_xval = other.max_xval;
86 default_value = other.default_value;
87 _state = other._state;
88 _touching = other._touching;
89 _new_touch = false;
90 _dirty = false;
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)
107 : _touch_saved_point (-1.0, -1.0)
109 _frozen = 0;
110 changed_when_thawed = false;
111 _style = other._style;
112 min_yval = other.min_yval;
113 max_yval = other.max_yval;
114 max_xval = other.max_xval;
115 default_value = other.default_value;
116 _state = other._state;
117 _touching = other._touching;
118 _dirty = false;
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)
141 : _touch_saved_point (-1.0, -1.0)
143 _frozen = 0;
144 changed_when_thawed = false;
145 _touching = false;
146 min_yval = FLT_MIN;
147 max_yval = FLT_MAX;
148 max_xval = 0; // means "no limit"
149 _dirty = false;
150 _state = Auto_Off;
151 _style = Auto_Absolute;
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);
169 for (AutomationEventList::iterator x = nascent_events.begin(); x != nascent_events.end(); ++x) {
170 delete (*x);
174 bool
175 AutomationList::operator== (const AutomationList& other)
177 return events == other.events;
180 AutomationList&
181 AutomationList::operator= (const AutomationList& other)
183 if (this != &other) {
185 events.clear ();
187 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
188 events.push_back (point_factory (**i));
191 min_yval = other.min_yval;
192 max_yval = other.max_yval;
193 max_xval = other.max_xval;
194 default_value = other.default_value;
196 lookup_cache.range.first = events.end();
198 mark_dirty ();
199 maybe_signal_changed ();
202 return *this;
205 void
206 AutomationList::maybe_signal_changed ()
208 mark_dirty ();
210 if (_frozen) {
211 changed_when_thawed = true;
212 } else {
213 cerr << " SC, events holds " << events.size() << endl;
214 StateChanged ();
218 void
219 AutomationList::set_automation_state (AutoState s)
221 if (s != _state) {
222 _state = s;
223 automation_state_changed (); /* EMIT SIGNAL */
227 void
228 AutomationList::set_automation_style (AutoStyle s)
230 if (s != _style) {
231 _style = s;
232 automation_style_changed (); /* EMIT SIGNAL */
236 void
237 AutomationList::start_touch ()
239 _touching = true;
240 _new_touch = true;
241 _touch_saved_point.when = -1.0;
244 void
245 AutomationList::stop_touch (bool mark, double when)
247 _touching = false;
248 _new_touch = false;
251 void
252 AutomationList::clear ()
255 Glib::Mutex::Lock lm (lock);
256 events.clear ();
257 mark_dirty ();
260 maybe_signal_changed ();
263 void
264 AutomationList::x_scale (double factor)
266 Glib::Mutex::Lock lm (lock);
267 _x_scale (factor);
270 bool
271 AutomationList::extend_to (double when)
273 Glib::Mutex::Lock lm (lock);
274 if (events.empty() || events.back()->when == when) {
275 return false;
277 double factor = when / events.back()->when;
278 _x_scale (factor);
279 return true;
282 void AutomationList::_x_scale (double factor)
284 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
285 (*i)->when = floor ((*i)->when * factor);
288 mark_dirty ();
291 void
292 AutomationList::reposition_for_rt_add (double when)
294 merge_nascent ();
297 void
298 AutomationList::rt_add (double when, double value)
300 /* this is for automation recording */
302 if ((_state & Auto_Touch) && !_touching) {
303 return;
306 assert (when > nascent_events.back()->when);
307 nascent_events.push_back (point_factory (when, value));
310 void
311 AutomationList::merge_nascent ()
314 Glib::Mutex::Lock lm (lock);
316 if (nascent_events.empty()) {
317 return;
320 iterator i;
321 iterator range_begin = events.end();
322 iterator range_end = events.end();
323 bool inserted = false;
325 double lower = nascent_events.front()->when;
326 double upper = nascent_events.back()->when;
327 bool preexisting = !events.empty();
328 double clamp_time;
329 double clamp_value;
331 if (preexisting) {
332 clamp_time = upper + 1; // XXX FIX ME
333 clamp_value = unlocked_eval (clamp_time);
336 for (i = events.begin(); i != events.end(); ++i) {
338 /* find the range that overaps with nascent events,
339 and insert the contents of nascent events.
342 if ((*i)->when >= lower) {
344 if (range_begin == events.end()) {
345 range_begin = i;
348 if ((*i)->when > clamp_time) {
349 range_end = i;
350 break;
355 if (range_begin == events.end()) {
357 if (range_end == events.end()) {
359 /* nascent events are all after any events in the existing list. insert
360 at the back of the list. no clamp point is required.
363 events.insert (events.end(), nascent_events.begin(), nascent_events.end());
365 } else {
367 /* nascent events are all before any events in the existing list. insert
368 at the front of the list, with the clamp value to pull us back to
369 any existing points.
372 events.insert (events.begin(), nascent_events.begin(), nascent_events.end());
373 if (preexisting) {
374 events.insert (events.begin(), point_factory (clamp_time, clamp_value));
378 } else {
379 /* nascent events begin somewhere within in the existing list. insert them,
380 along with the clamp point if necessary. then delete the existing events
381 that correspond to the range of nascent events.
384 events.insert (range_end, nascent_events.begin(), nascent_events.end());
385 if (preexisting) {
386 events.insert (range_end, point_factory (clamp_time, clamp_value));
388 events.erase (range_begin, range_end);
391 nascent_events.clear ();
394 maybe_signal_changed ();
397 void
398 AutomationList::fast_simple_add (double when, double value)
400 /* to be used only for loading pre-sorted data from saved state */
401 events.insert (events.end(), point_factory (when, value));
404 void
405 AutomationList::add (double when, double value)
407 /* this is for making changes from some kind of user interface or control surface (GUI, MIDI, OSC etc) */
410 Glib::Mutex::Lock lm (lock);
411 TimeComparator cmp;
412 ControlEvent cp (when, 0.0f);
413 bool insert = true;
414 iterator insertion_point;
416 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
418 /* only one point allowed per time point */
420 if ((*insertion_point)->when == when) {
421 (*insertion_point)->value = value;
422 insert = false;
423 break;
426 if ((*insertion_point)->when >= when) {
427 break;
431 if (insert) {
432 events.insert (insertion_point, point_factory (when, value));
435 mark_dirty ();
438 maybe_signal_changed ();
441 void
442 AutomationList::erase (AutomationList::iterator i)
445 Glib::Mutex::Lock lm (lock);
446 events.erase (i);
447 mark_dirty ();
449 maybe_signal_changed ();
452 void
453 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
456 Glib::Mutex::Lock lm (lock);
457 events.erase (start, end);
458 mark_dirty ();
460 maybe_signal_changed ();
463 void
464 AutomationList::reset_range (double start, double endt)
466 bool reset = false;
469 Glib::Mutex::Lock lm (lock);
470 TimeComparator cmp;
471 ControlEvent cp (start, 0.0f);
472 iterator s;
473 iterator e;
475 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
477 cp.when = endt;
478 e = upper_bound (events.begin(), events.end(), &cp, cmp);
480 for (iterator i = s; i != e; ++i) {
481 (*i)->value = default_value;
484 reset = true;
486 mark_dirty ();
490 if (reset) {
491 maybe_signal_changed ();
495 void
496 AutomationList::erase_range (double start, double endt)
498 bool erased = false;
501 Glib::Mutex::Lock lm (lock);
502 TimeComparator cmp;
503 ControlEvent cp (start, 0.0f);
504 iterator s;
505 iterator e;
507 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
508 cp.when = endt;
509 e = upper_bound (events.begin(), events.end(), &cp, cmp);
510 events.erase (s, e);
511 erased = true;
512 mark_dirty ();
517 if (erased) {
518 maybe_signal_changed ();
522 void
523 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
525 /* note: we assume higher level logic is in place to avoid this
526 reordering the time-order of control events in the list. ie. all
527 points after end are later than (end)->when.
531 Glib::Mutex::Lock lm (lock);
533 while (start != end) {
534 (*start)->when += xdelta;
535 (*start)->value += ydelta;
536 if (isnan ((*start)->value)) {
537 abort ();
539 ++start;
542 if (!_frozen) {
543 events.sort (sort_events_by_time);
544 } else {
545 sort_pending = true;
548 mark_dirty ();
551 maybe_signal_changed ();
554 void
555 AutomationList::slide (iterator before, double distance)
558 Glib::Mutex::Lock lm (lock);
560 if (before == events.end()) {
561 return;
564 while (before != events.end()) {
565 (*before)->when += distance;
566 ++before;
570 maybe_signal_changed ();
573 void
574 AutomationList::modify (iterator iter, double when, double val)
576 /* note: we assume higher level logic is in place to avoid this
577 reordering the time-order of control events in the list. ie. all
578 points after *iter are later than when.
582 Glib::Mutex::Lock lm (lock);
584 (*iter)->when = when;
585 (*iter)->value = val;
587 if (isnan (val)) {
588 abort ();
591 if (!_frozen) {
592 events.sort (sort_events_by_time);
593 } else {
594 sort_pending = true;
597 mark_dirty ();
600 maybe_signal_changed ();
603 std::pair<AutomationList::iterator,AutomationList::iterator>
604 AutomationList::control_points_adjacent (double xval)
606 Glib::Mutex::Lock lm (lock);
607 iterator i;
608 TimeComparator cmp;
609 ControlEvent cp (xval, 0.0f);
610 std::pair<iterator,iterator> ret;
612 ret.first = events.end();
613 ret.second = events.end();
615 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
617 if (ret.first == events.end()) {
618 if ((*i)->when >= xval) {
619 if (i != events.begin()) {
620 ret.first = i;
621 --ret.first;
622 } else {
623 return ret;
628 if ((*i)->when > xval) {
629 ret.second = i;
630 break;
634 return ret;
637 void
638 AutomationList::freeze ()
640 _frozen++;
643 void
644 AutomationList::thaw ()
646 if (_frozen == 0) {
647 PBD::stacktrace (cerr);
648 fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg;
649 /*NOTREACHED*/
652 if (--_frozen > 0) {
653 return;
657 Glib::Mutex::Lock lm (lock);
659 if (sort_pending) {
660 events.sort (sort_events_by_time);
661 sort_pending = false;
665 if (changed_when_thawed) {
666 cerr << " thaw SC, events holds " << events.size() << endl;
667 StateChanged(); /* EMIT SIGNAL */
671 void
672 AutomationList::set_max_xval (double x)
674 max_xval = x;
677 void
678 AutomationList::mark_dirty ()
680 lookup_cache.left = -1;
681 _dirty = true;
684 void
685 AutomationList::truncate_end (double last_coordinate)
688 Glib::Mutex::Lock lm (lock);
689 ControlEvent cp (last_coordinate, 0);
690 AutomationList::reverse_iterator i;
691 double last_val;
693 if (events.empty()) {
694 return;
697 if (last_coordinate == events.back()->when) {
698 return;
701 if (last_coordinate > events.back()->when) {
703 /* extending end:
706 iterator foo = events.begin();
707 bool lessthantwo;
709 if (foo == events.end()) {
710 lessthantwo = true;
711 } else if (++foo == events.end()) {
712 lessthantwo = true;
713 } else {
714 lessthantwo = false;
717 if (lessthantwo) {
718 /* less than 2 points: add a new point */
719 events.push_back (point_factory (last_coordinate, events.back()->value));
720 } else {
722 /* more than 2 points: check to see if the last 2 values
723 are equal. if so, just move the position of the
724 last point. otherwise, add a new point.
727 iterator penultimate = events.end();
728 --penultimate; /* points at last point */
729 --penultimate; /* points at the penultimate point */
731 if (events.back()->value == (*penultimate)->value) {
732 events.back()->when = last_coordinate;
733 } else {
734 events.push_back (point_factory (last_coordinate, events.back()->value));
738 } else {
740 /* shortening end */
742 last_val = unlocked_eval (last_coordinate);
743 last_val = max ((double) min_yval, last_val);
744 last_val = min ((double) max_yval, last_val);
746 i = events.rbegin();
748 /* make i point to the last control point */
750 ++i;
752 /* now go backwards, removing control points that are
753 beyond the new last coordinate.
756 uint32_t sz = events.size();
758 while (i != events.rend() && sz > 2) {
759 AutomationList::reverse_iterator tmp;
761 tmp = i;
762 ++tmp;
764 if ((*i)->when < last_coordinate) {
765 break;
768 events.erase (i.base());
769 --sz;
771 i = tmp;
774 events.back()->when = last_coordinate;
775 events.back()->value = last_val;
778 mark_dirty();
781 maybe_signal_changed ();
784 void
785 AutomationList::truncate_start (double overall_length)
788 Glib::Mutex::Lock lm (lock);
789 AutomationList::iterator i;
790 double first_legal_value;
791 double first_legal_coordinate;
793 if (events.empty()) {
794 fatal << _("programming error:")
795 << "AutomationList::truncate_start() called on an empty list"
796 << endmsg;
797 /*NOTREACHED*/
798 return;
801 if (overall_length == events.back()->when) {
802 /* no change in overall length */
803 return;
806 if (overall_length > events.back()->when) {
808 /* growing at front: duplicate first point. shift all others */
810 double shift = overall_length - events.back()->when;
811 uint32_t np;
813 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
814 (*i)->when += shift;
817 if (np < 2) {
819 /* less than 2 points: add a new point */
820 events.push_front (point_factory (0, events.front()->value));
822 } else {
824 /* more than 2 points: check to see if the first 2 values
825 are equal. if so, just move the position of the
826 first point. otherwise, add a new point.
829 iterator second = events.begin();
830 ++second; /* points at the second point */
832 if (events.front()->value == (*second)->value) {
833 /* first segment is flat, just move start point back to zero */
834 events.front()->when = 0;
835 } else {
836 /* leave non-flat segment in place, add a new leading point. */
837 events.push_front (point_factory (0, events.front()->value));
841 } else {
843 /* shrinking at front */
845 first_legal_coordinate = events.back()->when - overall_length;
846 first_legal_value = unlocked_eval (first_legal_coordinate);
847 first_legal_value = max (min_yval, first_legal_value);
848 first_legal_value = min (max_yval, first_legal_value);
850 /* remove all events earlier than the new "front" */
852 i = events.begin();
854 while (i != events.end() && !events.empty()) {
855 AutomationList::iterator tmp;
857 tmp = i;
858 ++tmp;
860 if ((*i)->when > first_legal_coordinate) {
861 break;
864 events.erase (i);
866 i = tmp;
870 /* shift all remaining points left to keep their same
871 relative position
874 for (i = events.begin(); i != events.end(); ++i) {
875 (*i)->when -= first_legal_coordinate;
878 /* add a new point for the interpolated new value */
880 events.push_front (point_factory (0, first_legal_value));
883 mark_dirty();
886 maybe_signal_changed ();
889 double
890 AutomationList::unlocked_eval (double x)
892 return shared_eval (x);
895 double
896 AutomationList::shared_eval (double x)
898 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
899 int32_t npoints;
900 double lpos, upos;
901 double lval, uval;
902 double fraction;
904 npoints = events.size();
906 switch (npoints) {
907 case 0:
908 return default_value;
910 case 1:
911 if (x >= events.front()->when) {
912 return events.front()->value;
913 } else {
914 // return default_value;
915 return events.front()->value;
918 case 2:
919 if (x >= events.back()->when) {
920 return events.back()->value;
921 } else if (x == events.front()->when) {
922 return events.front()->value;
923 } else if (x < events.front()->when) {
924 // return default_value;
925 return events.front()->value;
928 lpos = events.front()->when;
929 lval = events.front()->value;
930 upos = events.back()->when;
931 uval = events.back()->value;
933 /* linear interpolation betweeen the two points
936 fraction = (double) (x - lpos) / (double) (upos - lpos);
937 return lval + (fraction * (uval - lval));
939 default:
941 if (x >= events.back()->when) {
942 return events.back()->value;
943 } else if (x == events.front()->when) {
944 return events.front()->value;
945 } else if (x < events.front()->when) {
946 // return default_value;
947 return events.front()->value;
950 return multipoint_eval (x);
951 break;
954 /*NOTREACHED*/ /* stupid gcc */
955 return 0.0;
958 double
959 AutomationList::multipoint_eval (double x)
961 pair<AutomationList::iterator,AutomationList::iterator> range;
962 double upos, lpos;
963 double uval, lval;
964 double fraction;
966 /* only do the range lookup if x is in a different range than last time
967 this was called (or if the lookup cache has been marked "dirty" (left<0)
970 if ((lookup_cache.left < 0) ||
971 ((lookup_cache.left > x) ||
972 (lookup_cache.range.first == events.end()) ||
973 ((*lookup_cache.range.second)->when < x))) {
975 ControlEvent cp (x, 0);
976 TimeComparator cmp;
978 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
981 range = lookup_cache.range;
983 if (range.first == range.second) {
985 /* x does not exist within the list as a control point */
987 lookup_cache.left = x;
989 if (range.first != events.begin()) {
990 --range.first;
991 lpos = (*range.first)->when;
992 lval = (*range.first)->value;
993 } else {
994 /* we're before the first point */
995 // return default_value;
996 return events.front()->value;
999 if (range.second == events.end()) {
1000 /* we're after the last point */
1001 return events.back()->value;
1004 upos = (*range.second)->when;
1005 uval = (*range.second)->value;
1007 /* linear interpolation betweeen the two points
1008 on either side of x
1011 fraction = (double) (x - lpos) / (double) (upos - lpos);
1012 return lval + (fraction * (uval - lval));
1016 /* x is a control point in the data */
1017 lookup_cache.left = -1;
1018 return (*range.first)->value;
1021 AutomationList*
1022 AutomationList::cut (iterator start, iterator end)
1024 AutomationList* nal = new AutomationList (default_value);
1027 Glib::Mutex::Lock lm (lock);
1029 for (iterator x = start; x != end; ) {
1030 iterator tmp;
1032 tmp = x;
1033 ++tmp;
1035 nal->events.push_back (point_factory (**x));
1036 events.erase (x);
1038 x = tmp;
1041 mark_dirty ();
1044 maybe_signal_changed ();
1046 return nal;
1049 AutomationList*
1050 AutomationList::cut_copy_clear (double start, double end, int op)
1052 AutomationList* nal = new AutomationList (default_value);
1053 iterator s, e;
1054 ControlEvent cp (start, 0.0);
1055 TimeComparator cmp;
1056 bool changed = false;
1059 Glib::Mutex::Lock lm (lock);
1061 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1062 return nal;
1065 cp.when = end;
1066 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1068 if (op != 2 && (*s)->when != start) {
1069 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1072 for (iterator x = s; x != e; ) {
1073 iterator tmp;
1075 tmp = x;
1076 ++tmp;
1078 changed = true;
1080 /* adjust new points to be relative to start, which
1081 has been set to zero.
1084 if (op != 2) {
1085 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1088 if (op != 1) {
1089 events.erase (x);
1092 x = tmp;
1095 if (op != 2 && nal->events.back()->when != end - start) {
1096 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1099 mark_dirty ();
1102 maybe_signal_changed ();
1104 return nal;
1108 AutomationList*
1109 AutomationList::copy (iterator start, iterator end)
1111 AutomationList* nal = new AutomationList (default_value);
1114 Glib::Mutex::Lock lm (lock);
1116 for (iterator x = start; x != end; ) {
1117 iterator tmp;
1119 tmp = x;
1120 ++tmp;
1122 nal->events.push_back (point_factory (**x));
1124 x = tmp;
1128 return nal;
1131 AutomationList*
1132 AutomationList::cut (double start, double end)
1134 return cut_copy_clear (start, end, 0);
1137 AutomationList*
1138 AutomationList::copy (double start, double end)
1140 return cut_copy_clear (start, end, 1);
1143 void
1144 AutomationList::clear (double start, double end)
1146 (void) cut_copy_clear (start, end, 2);
1149 bool
1150 AutomationList::paste (AutomationList& alist, double pos, float times)
1152 if (alist.events.empty()) {
1153 return false;
1157 Glib::Mutex::Lock lm (lock);
1158 iterator where;
1159 iterator prev;
1160 double end = 0;
1161 ControlEvent cp (pos, 0.0);
1162 TimeComparator cmp;
1164 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1166 for (iterator i = alist.begin();i != alist.end(); ++i) {
1167 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1168 end = (*i)->when + pos;
1172 /* move all points after the insertion along the timeline by
1173 the correct amount.
1176 while (where != events.end()) {
1177 iterator tmp;
1178 if ((*where)->when <= end) {
1179 tmp = where;
1180 ++tmp;
1181 events.erase(where);
1182 where = tmp;
1184 } else {
1185 break;
1189 mark_dirty ();
1192 maybe_signal_changed ();
1193 return true;
1196 ControlEvent*
1197 AutomationList::point_factory (double when, double val) const
1199 return new ControlEvent (when, val);
1202 ControlEvent*
1203 AutomationList::point_factory (const ControlEvent& other) const
1205 return new ControlEvent (other);
1208 XMLNode&
1209 AutomationList::get_state ()
1211 return state (true);
1214 XMLNode&
1215 AutomationList::state (bool full)
1217 XMLNode* root = new XMLNode (X_("AutomationList"));
1218 char buf[64];
1219 LocaleGuard lg (X_("POSIX"));
1221 root->add_property ("id", _id.to_s());
1223 snprintf (buf, sizeof (buf), "%.12g", default_value);
1224 root->add_property ("default", buf);
1225 snprintf (buf, sizeof (buf), "%.12g", min_yval);
1226 root->add_property ("min_yval", buf);
1227 snprintf (buf, sizeof (buf), "%.12g", max_yval);
1228 root->add_property ("max_yval", buf);
1229 snprintf (buf, sizeof (buf), "%.12g", max_xval);
1230 root->add_property ("max_xval", buf);
1232 if (full) {
1233 root->add_property ("state", auto_state_to_string (_state));
1234 } else {
1235 /* never save anything but Off for automation state to a template */
1236 root->add_property ("state", auto_state_to_string (Auto_Off));
1239 root->add_property ("style", auto_style_to_string (_style));
1241 if (!events.empty()) {
1242 root->add_child_nocopy (serialize_events());
1245 return *root;
1248 XMLNode&
1249 AutomationList::serialize_events ()
1251 XMLNode* node = new XMLNode (X_("events"));
1252 stringstream str;
1254 str.precision(15); //10 digits is enough digits for 24 hours at 96kHz
1256 for (iterator xx = events.begin(); xx != events.end(); ++xx) {
1257 str << (double) (*xx)->when;
1258 str << ' ';
1259 str <<(double) (*xx)->value;
1260 str << '\n';
1263 /* XML is a bit wierd */
1265 XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
1266 content_node->set_content (str.str());
1268 node->add_child_nocopy (*content_node);
1270 return *node;
1274 AutomationList::deserialize_events (const XMLNode& node)
1276 if (node.children().empty()) {
1277 return -1;
1280 XMLNode* content_node = node.children().front();
1282 if (content_node->content().empty()) {
1283 return -1;
1286 freeze ();
1287 clear ();
1289 stringstream str (content_node->content());
1291 double x;
1292 double y;
1293 bool ok = true;
1295 while (str) {
1296 str >> x;
1297 if (!str) {
1298 break;
1300 str >> y;
1301 if (!str) {
1302 ok = false;
1303 break;
1305 fast_simple_add (x, y);
1308 if (!ok) {
1309 clear ();
1310 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
1311 } else {
1312 mark_dirty ();
1313 maybe_signal_changed ();
1316 thaw ();
1318 return 0;
1322 AutomationList::set_state (const XMLNode& node)
1324 XMLNodeList nlist = node.children();
1325 XMLNode* nsos;
1326 XMLNodeIterator niter;
1327 const XMLProperty* prop;
1329 if (node.name() == X_("events")) {
1330 /* partial state setting*/
1331 return deserialize_events (node);
1334 if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
1336 if ((nsos = node.child (X_("AutomationList")))) {
1337 /* new school in old school clothing */
1338 return set_state (*nsos);
1341 /* old school */
1343 const XMLNodeList& elist = node.children();
1344 XMLNodeConstIterator i;
1345 XMLProperty* prop;
1346 jack_nframes_t x;
1347 double y;
1349 freeze ();
1350 clear ();
1352 for (i = elist.begin(); i != elist.end(); ++i) {
1354 if ((prop = (*i)->property ("x")) == 0) {
1355 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1356 continue;
1358 x = atoi (prop->value().c_str());
1360 if ((prop = (*i)->property ("y")) == 0) {
1361 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1362 continue;
1364 y = atof (prop->value().c_str());
1366 fast_simple_add (x, y);
1369 thaw ();
1371 return 0;
1374 if (node.name() != X_("AutomationList") ) {
1375 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
1376 return -1;
1379 if ((prop = node.property ("id")) != 0) {
1380 _id = prop->value ();
1381 /* update session AL list */
1382 AutomationListCreated(this);
1385 if ((prop = node.property (X_("default"))) != 0){
1386 default_value = atof (prop->value());
1387 } else {
1388 default_value = 0.0;
1391 if ((prop = node.property (X_("style"))) != 0) {
1392 _style = string_to_auto_style (prop->value());
1393 } else {
1394 _style = Auto_Absolute;
1397 if ((prop = node.property (X_("state"))) != 0) {
1398 _state = string_to_auto_state (prop->value());
1399 } else {
1400 _state = Auto_Off;
1403 if ((prop = node.property (X_("min_yval"))) != 0) {
1404 min_yval = atof (prop->value ());
1405 } else {
1406 min_yval = FLT_MIN;
1409 if ((prop = node.property (X_("max_yval"))) != 0) {
1410 max_yval = atof (prop->value ());
1411 } else {
1412 max_yval = FLT_MAX;
1415 if ((prop = node.property (X_("max_xval"))) != 0) {
1416 max_xval = atof (prop->value ());
1417 } else {
1418 max_xval = 0; // means "no limit ;
1421 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1422 if ((*niter)->name() == X_("events")) {
1423 deserialize_events (*(*niter));
1427 return 0;
1430 void
1431 AutomationList::shift (nframes64_t pos, nframes64_t frames)
1434 Glib::Mutex::Lock lm (lock);
1436 for (iterator i = begin (); i != end (); ++i) {
1437 if ((*i)->when >= pos) {
1438 (*i)->when += frames;
1442 mark_dirty ();
1445 maybe_signal_changed ();