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.
26 #include <sigc++/bind.h>
27 #include <ardour/automation_event.h>
28 #include <pbd/stacktrace.h>
29 #include <pbd/localeguard.h>
34 using namespace ARDOUR
;
38 sigc::signal
<void,AutomationList
*> AutomationList::AutomationListCreated
;
40 static bool sort_events_by_time (ControlEvent
* a
, ControlEvent
* b
)
42 return a
->when
< b
->when
;
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
;
56 AutomationList::AutomationList (double defval
)
57 : _touch_saved_point (-1.0, -1.0)
60 changed_when_thawed
= false;
62 _style
= Auto_Absolute
;
67 max_xval
= 0; // means "no limit"
68 default_value
= defval
;
70 lookup_cache
.left
= -1;
71 lookup_cache
.range
.first
= events
.end();
74 AutomationListCreated(this);
77 AutomationList::AutomationList (const AutomationList
& other
)
78 : _touch_saved_point (-1.0, -1.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
;
91 lookup_cache
.left
= -1;
92 lookup_cache
.range
.first
= events
.end();
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
));
103 AutomationListCreated(this);
106 AutomationList::AutomationList (const AutomationList
& other
, double start
, double end
)
107 : _touch_saved_point (-1.0, -1.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
;
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
));
137 AutomationListCreated(this);
140 AutomationList::AutomationList (const XMLNode
& node
)
141 : _touch_saved_point (-1.0, -1.0)
144 changed_when_thawed
= false;
148 max_xval
= 0; // means "no limit"
151 _style
= Auto_Absolute
;
152 lookup_cache
.left
= -1;
153 lookup_cache
.range
.first
= events
.end();
154 sort_pending
= false;
158 AutomationListCreated(this);
161 AutomationList::~AutomationList()
165 for (AutomationEventList::iterator x
= events
.begin(); x
!= events
.end(); ++x
) {
169 for (AutomationEventList::iterator x
= nascent_events
.begin(); x
!= nascent_events
.end(); ++x
) {
175 AutomationList::operator== (const AutomationList
& other
)
177 return events
== other
.events
;
181 AutomationList::operator= (const AutomationList
& other
)
183 if (this != &other
) {
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();
199 maybe_signal_changed ();
206 AutomationList::maybe_signal_changed ()
211 changed_when_thawed
= true;
213 cerr
<< " SC, events holds " << events
.size() << endl
;
219 AutomationList::set_automation_state (AutoState s
)
223 automation_state_changed (); /* EMIT SIGNAL */
228 AutomationList::set_automation_style (AutoStyle s
)
232 automation_style_changed (); /* EMIT SIGNAL */
237 AutomationList::start_touch ()
241 _touch_saved_point
.when
= -1.0;
245 AutomationList::stop_touch (bool mark
, double when
)
252 AutomationList::clear ()
255 Glib::Mutex::Lock
lm (lock
);
260 maybe_signal_changed ();
264 AutomationList::x_scale (double factor
)
266 Glib::Mutex::Lock
lm (lock
);
271 AutomationList::extend_to (double when
)
273 Glib::Mutex::Lock
lm (lock
);
274 if (events
.empty() || events
.back()->when
== when
) {
277 double factor
= when
/ events
.back()->when
;
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
);
292 AutomationList::reposition_for_rt_add (double when
)
298 AutomationList::rt_add (double when
, double value
)
300 /* this is for automation recording */
302 if ((_state
& Auto_Touch
) && !_touching
) {
306 assert (when
> nascent_events
.back()->when
);
307 nascent_events
.push_back (point_factory (when
, value
));
311 AutomationList::merge_nascent ()
314 Glib::Mutex::Lock
lm (lock
);
316 if (nascent_events
.empty()) {
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();
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()) {
348 if ((*i
)->when
> clamp_time
) {
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());
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
372 events
.insert (events
.begin(), nascent_events
.begin(), nascent_events
.end());
374 events
.insert (events
.begin(), point_factory (clamp_time
, clamp_value
));
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());
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 ();
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
));
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
);
412 ControlEvent
cp (when
, 0.0f
);
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
;
426 if ((*insertion_point
)->when
>= when
) {
432 events
.insert (insertion_point
, point_factory (when
, value
));
438 maybe_signal_changed ();
442 AutomationList::erase (AutomationList::iterator i
)
445 Glib::Mutex::Lock
lm (lock
);
449 maybe_signal_changed ();
453 AutomationList::erase (AutomationList::iterator start
, AutomationList::iterator end
)
456 Glib::Mutex::Lock
lm (lock
);
457 events
.erase (start
, end
);
460 maybe_signal_changed ();
464 AutomationList::reset_range (double start
, double endt
)
469 Glib::Mutex::Lock
lm (lock
);
471 ControlEvent
cp (start
, 0.0f
);
475 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) != events
.end()) {
478 e
= upper_bound (events
.begin(), events
.end(), &cp
, cmp
);
480 for (iterator i
= s
; i
!= e
; ++i
) {
481 (*i
)->value
= default_value
;
491 maybe_signal_changed ();
496 AutomationList::erase_range (double start
, double endt
)
501 Glib::Mutex::Lock
lm (lock
);
503 ControlEvent
cp (start
, 0.0f
);
507 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) != events
.end()) {
509 e
= upper_bound (events
.begin(), events
.end(), &cp
, cmp
);
518 maybe_signal_changed ();
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
)) {
543 events
.sort (sort_events_by_time
);
551 maybe_signal_changed ();
555 AutomationList::slide (iterator before
, double distance
)
558 Glib::Mutex::Lock
lm (lock
);
560 if (before
== events
.end()) {
564 while (before
!= events
.end()) {
565 (*before
)->when
+= distance
;
570 maybe_signal_changed ();
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
;
592 events
.sort (sort_events_by_time
);
600 maybe_signal_changed ();
603 std::pair
<AutomationList::iterator
,AutomationList::iterator
>
604 AutomationList::control_points_adjacent (double xval
)
606 Glib::Mutex::Lock
lm (lock
);
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()) {
628 if ((*i
)->when
> xval
) {
638 AutomationList::freeze ()
644 AutomationList::thaw ()
647 PBD::stacktrace (cerr
);
648 fatal
<< string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg
;
657 Glib::Mutex::Lock
lm (lock
);
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 */
672 AutomationList::set_max_xval (double x
)
678 AutomationList::mark_dirty ()
680 lookup_cache
.left
= -1;
685 AutomationList::truncate_end (double last_coordinate
)
688 Glib::Mutex::Lock
lm (lock
);
689 ControlEvent
cp (last_coordinate
, 0);
690 AutomationList::reverse_iterator i
;
693 if (events
.empty()) {
697 if (last_coordinate
== events
.back()->when
) {
701 if (last_coordinate
> events
.back()->when
) {
706 iterator foo
= events
.begin();
709 if (foo
== events
.end()) {
711 } else if (++foo
== events
.end()) {
718 /* less than 2 points: add a new point */
719 events
.push_back (point_factory (last_coordinate
, events
.back()->value
));
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
;
734 events
.push_back (point_factory (last_coordinate
, events
.back()->value
));
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
);
748 /* make i point to the last control point */
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
;
764 if ((*i
)->when
< last_coordinate
) {
768 events
.erase (i
.base());
774 events
.back()->when
= last_coordinate
;
775 events
.back()->value
= last_val
;
781 maybe_signal_changed ();
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"
801 if (overall_length
== events
.back()->when
) {
802 /* no change in overall length */
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
;
813 for (np
= 0, i
= events
.begin(); i
!= events
.end(); ++i
, ++np
) {
819 /* less than 2 points: add a new point */
820 events
.push_front (point_factory (0, events
.front()->value
));
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;
836 /* leave non-flat segment in place, add a new leading point. */
837 events
.push_front (point_factory (0, events
.front()->value
));
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" */
854 while (i
!= events
.end() && !events
.empty()) {
855 AutomationList::iterator tmp
;
860 if ((*i
)->when
> first_legal_coordinate
) {
870 /* shift all remaining points left to keep their same
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
));
886 maybe_signal_changed ();
890 AutomationList::unlocked_eval (double x
)
892 return shared_eval (x
);
896 AutomationList::shared_eval (double x
)
898 pair
<AutomationEventList::iterator
,AutomationEventList::iterator
> range
;
904 npoints
= events
.size();
908 return default_value
;
911 if (x
>= events
.front()->when
) {
912 return events
.front()->value
;
914 // return default_value;
915 return events
.front()->value
;
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
));
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
);
954 /*NOTREACHED*/ /* stupid gcc */
959 AutomationList::multipoint_eval (double x
)
961 pair
<AutomationList::iterator
,AutomationList::iterator
> range
;
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);
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()) {
991 lpos
= (*range
.first
)->when
;
992 lval
= (*range
.first
)->value
;
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
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
;
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
; ) {
1035 nal
->events
.push_back (point_factory (**x
));
1044 maybe_signal_changed ();
1050 AutomationList::cut_copy_clear (double start
, double end
, int op
)
1052 AutomationList
* nal
= new AutomationList (default_value
);
1054 ControlEvent
cp (start
, 0.0);
1056 bool changed
= false;
1059 Glib::Mutex::Lock
lm (lock
);
1061 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) == events
.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
; ) {
1080 /* adjust new points to be relative to start, which
1081 has been set to zero.
1085 nal
->events
.push_back (point_factory ((*x
)->when
- start
, (*x
)->value
));
1095 if (op
!= 2 && nal
->events
.back()->when
!= end
- start
) {
1096 nal
->events
.push_back (point_factory (end
- start
, unlocked_eval (end
)));
1102 maybe_signal_changed ();
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
; ) {
1122 nal
->events
.push_back (point_factory (**x
));
1132 AutomationList::cut (double start
, double end
)
1134 return cut_copy_clear (start
, end
, 0);
1138 AutomationList::copy (double start
, double end
)
1140 return cut_copy_clear (start
, end
, 1);
1144 AutomationList::clear (double start
, double end
)
1146 (void) cut_copy_clear (start
, end
, 2);
1150 AutomationList::paste (AutomationList
& alist
, double pos
, float times
)
1152 if (alist
.events
.empty()) {
1157 Glib::Mutex::Lock
lm (lock
);
1161 ControlEvent
cp (pos
, 0.0);
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
1176 while (where
!= events
.end()) {
1178 if ((*where
)->when
<= end
) {
1181 events
.erase(where
);
1192 maybe_signal_changed ();
1197 AutomationList::point_factory (double when
, double val
) const
1199 return new ControlEvent (when
, val
);
1203 AutomationList::point_factory (const ControlEvent
& other
) const
1205 return new ControlEvent (other
);
1209 AutomationList::get_state ()
1211 return state (true);
1215 AutomationList::state (bool full
)
1217 XMLNode
* root
= new XMLNode (X_("AutomationList"));
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
);
1233 root
->add_property ("state", auto_state_to_string (_state
));
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());
1249 AutomationList::serialize_events ()
1251 XMLNode
* node
= new XMLNode (X_("events"));
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
;
1259 str
<<(double) (*xx
)->value
;
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
);
1274 AutomationList::deserialize_events (const XMLNode
& node
)
1276 if (node
.children().empty()) {
1280 XMLNode
* content_node
= node
.children().front();
1282 if (content_node
->content().empty()) {
1289 stringstream
str (content_node
->content());
1305 fast_simple_add (x
, y
);
1310 error
<< _("automation list: cannot load coordinates from XML, all points ignored") << endmsg
;
1313 maybe_signal_changed ();
1322 AutomationList::set_state (const XMLNode
& node
)
1324 XMLNodeList nlist
= node
.children();
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
);
1343 const XMLNodeList
& elist
= node
.children();
1344 XMLNodeConstIterator i
;
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
;
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
;
1364 y
= atof (prop
->value().c_str());
1366 fast_simple_add (x
, y
);
1374 if (node
.name() != X_("AutomationList") ) {
1375 error
<< string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node
.name()) << endmsg
;
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());
1388 default_value
= 0.0;
1391 if ((prop
= node
.property (X_("style"))) != 0) {
1392 _style
= string_to_auto_style (prop
->value());
1394 _style
= Auto_Absolute
;
1397 if ((prop
= node
.property (X_("state"))) != 0) {
1398 _state
= string_to_auto_state (prop
->value());
1403 if ((prop
= node
.property (X_("min_yval"))) != 0) {
1404 min_yval
= atof (prop
->value ());
1409 if ((prop
= node
.property (X_("max_yval"))) != 0) {
1410 max_yval
= atof (prop
->value ());
1415 if ((prop
= node
.property (X_("max_xval"))) != 0) {
1416 max_xval
= atof (prop
->value ());
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
));
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
;
1445 maybe_signal_changed ();