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
)
59 changed_when_thawed
= false;
66 max_xval
= 0; // means "no limit"
67 default_value
= defval
;
69 rt_insertion_point
= events
.end();
70 lookup_cache
.left
= -1;
71 lookup_cache
.range
.first
= events
.end();
74 AutomationListCreated(this);
77 AutomationList::AutomationList (const AutomationList
& other
)
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
;
90 rt_insertion_point
= events
.end();
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
)
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
;
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
));
137 AutomationListCreated(this);
140 AutomationList::AutomationList (const XMLNode
& node
)
143 changed_when_thawed
= false;
147 max_xval
= 0; // means "no limit"
151 rt_insertion_point
= events
.end();
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
) {
171 AutomationList::operator== (const AutomationList
& other
)
173 return events
== other
.events
;
177 AutomationList::operator= (const AutomationList
& other
)
179 if (this != &other
) {
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();
196 maybe_signal_changed ();
203 AutomationList::maybe_signal_changed ()
208 changed_when_thawed
= true;
215 AutomationList::set_automation_state (AutoState s
)
219 automation_state_changed (); /* EMIT SIGNAL */
224 AutomationList::set_automation_style (AutoStyle s
)
228 automation_style_changed (); /* EMIT SIGNAL */
233 AutomationList::start_touch ()
240 AutomationList::stop_touch (bool mark
, double when
)
246 /* get the value of the next point after "when", and replicate
247 it directly after when, unless of course its already there.
251 AutomationList::const_iterator i
;
253 for (i
= const_begin(); i
!= const_end(); ++i
) {
254 if ((*i
)->when
>= when
) {
259 if (i
== const_end()) {
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
) {
278 AutomationList::clear ()
281 Glib::Mutex::Lock
lm (lock
);
286 maybe_signal_changed ();
290 AutomationList::x_scale (double factor
)
292 Glib::Mutex::Lock
lm (lock
);
297 AutomationList::extend_to (double when
)
299 Glib::Mutex::Lock
lm (lock
);
300 if (events
.empty() || events
.back()->when
== when
) {
303 double factor
= when
/ events
.back()->when
;
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
);
318 AutomationList::reposition_for_rt_add (double when
)
320 rt_insertion_point
= events
.end();
323 #define last_rt_insertion_point rt_insertion_point
326 AutomationList::rt_add (double when
, double value
)
328 /* this is for automation recording */
330 if ((_state
& Touch
) && !_touching
) {
334 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
337 Glib::Mutex::Lock
lm (lock
);
341 ControlEvent
cp (when
, 0.0);
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
) {
365 last_rt_insertion_point
= where
;
367 if((*where
)->when
== when
) {
368 (*where
)->value
= value
;
372 where
= events
.erase (after
, far
);
381 iterator previous
= last_rt_insertion_point
;
384 if (last_rt_insertion_point
!= events
.begin() && (*last_rt_insertion_point
)->value
== value
&& (*previous
)->value
== value
) {
385 (*last_rt_insertion_point
)->when
= when
;
392 where
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
);
394 if (where
!= events
.end()) {
395 if ((*where
)->when
== when
) {
396 (*where
)->value
= value
;
403 last_rt_insertion_point
= events
.insert (where
, point_factory (when
, value
));
404 // cerr << "\tINSERTED\n";
411 maybe_signal_changed ();
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
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
);
431 ControlEvent
cp (when
, 0.0f
);
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
;
445 if ((*insertion_point
)->when
>= when
) {
452 events
.insert (insertion_point
, point_factory (when
, value
));
453 reposition_for_rt_add (0);
460 maybe_signal_changed ();
464 AutomationList::erase (AutomationList::iterator i
)
467 Glib::Mutex::Lock
lm (lock
);
469 reposition_for_rt_add (0);
472 maybe_signal_changed ();
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);
484 maybe_signal_changed ();
488 AutomationList::reset_range (double start
, double endt
)
493 Glib::Mutex::Lock
lm (lock
);
495 ControlEvent
cp (start
, 0.0f
);
499 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) != events
.end()) {
502 e
= upper_bound (events
.begin(), events
.end(), &cp
, cmp
);
504 for (iterator i
= s
; i
!= e
; ++i
) {
505 (*i
)->value
= default_value
;
515 maybe_signal_changed ();
520 AutomationList::erase_range (double start
, double endt
)
525 Glib::Mutex::Lock
lm (lock
);
527 ControlEvent
cp (start
, 0.0f
);
531 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) != events
.end()) {
533 e
= upper_bound (events
.begin(), events
.end(), &cp
, cmp
);
535 reposition_for_rt_add (0);
543 maybe_signal_changed ();
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
)) {
568 events
.sort (sort_events_by_time
);
576 maybe_signal_changed ();
580 AutomationList::slide (iterator before
, double distance
)
583 Glib::Mutex::Lock
lm (lock
);
585 if (before
== events
.end()) {
589 while (before
!= events
.end()) {
590 (*before
)->when
+= distance
;
595 maybe_signal_changed ();
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
;
617 events
.sort (sort_events_by_time
);
625 maybe_signal_changed ();
628 std::pair
<AutomationList::iterator
,AutomationList::iterator
>
629 AutomationList::control_points_adjacent (double xval
)
631 Glib::Mutex::Lock
lm (lock
);
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()) {
653 if ((*i
)->when
> xval
) {
663 AutomationList::freeze ()
669 AutomationList::thaw ()
672 PBD::stacktrace (cerr
);
673 fatal
<< string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg
;
682 Glib::Mutex::Lock
lm (lock
);
685 events
.sort (sort_events_by_time
);
686 sort_pending
= false;
690 if (changed_when_thawed
) {
691 StateChanged(); /* EMIT SIGNAL */
696 AutomationList::set_max_xval (double x
)
702 AutomationList::mark_dirty ()
704 lookup_cache
.left
= -1;
709 AutomationList::truncate_end (double last_coordinate
)
712 Glib::Mutex::Lock
lm (lock
);
713 ControlEvent
cp (last_coordinate
, 0);
714 AutomationList::reverse_iterator i
;
717 if (events
.empty()) {
721 if (last_coordinate
== events
.back()->when
) {
725 if (last_coordinate
> events
.back()->when
) {
730 iterator foo
= events
.begin();
733 if (foo
== events
.end()) {
735 } else if (++foo
== events
.end()) {
742 /* less than 2 points: add a new point */
743 events
.push_back (point_factory (last_coordinate
, events
.back()->value
));
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
;
758 events
.push_back (point_factory (last_coordinate
, events
.back()->value
));
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
);
772 /* make i point to the last control point */
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
;
788 if ((*i
)->when
< last_coordinate
) {
792 events
.erase (i
.base());
798 events
.back()->when
= last_coordinate
;
799 events
.back()->value
= last_val
;
802 reposition_for_rt_add (0);
806 maybe_signal_changed ();
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"
826 if (overall_length
== events
.back()->when
) {
827 /* no change in overall length */
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
;
838 for (np
= 0, i
= events
.begin(); i
!= events
.end(); ++i
, ++np
) {
844 /* less than 2 points: add a new point */
845 events
.push_front (point_factory (0, events
.front()->value
));
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;
861 /* leave non-flat segment in place, add a new leading point. */
862 events
.push_front (point_factory (0, events
.front()->value
));
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" */
879 while (i
!= events
.end() && !events
.empty()) {
880 AutomationList::iterator tmp
;
885 if ((*i
)->when
> first_legal_coordinate
) {
895 /* shift all remaining points left to keep their same
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);
913 maybe_signal_changed ();
917 AutomationList::unlocked_eval (double x
)
919 return shared_eval (x
);
923 AutomationList::shared_eval (double x
)
925 pair
<AutomationEventList::iterator
,AutomationEventList::iterator
> range
;
931 npoints
= events
.size();
935 return default_value
;
938 if (x
>= events
.front()->when
) {
939 return events
.front()->value
;
941 // return default_value;
942 return events
.front()->value
;
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
));
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
);
981 /*NOTREACHED*/ /* stupid gcc */
986 AutomationList::multipoint_eval (double x
)
988 pair
<AutomationList::iterator
,AutomationList::iterator
> range
;
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);
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()) {
1018 lpos
= (*range
.first
)->when
;
1019 lval
= (*range
.first
)->value
;
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
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
;
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
; ) {
1062 nal
->events
.push_back (point_factory (**x
));
1065 reposition_for_rt_add (0);
1073 maybe_signal_changed ();
1079 AutomationList::cut_copy_clear (double start
, double end
, int op
)
1081 AutomationList
* nal
= new AutomationList (default_value
);
1083 ControlEvent
cp (start
, 0.0);
1085 bool changed
= false;
1088 Glib::Mutex::Lock
lm (lock
);
1090 if ((s
= lower_bound (events
.begin(), events
.end(), &cp
, cmp
)) == events
.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
; ) {
1109 /* adjust new points to be relative to start, which
1110 has been set to zero.
1114 nal
->events
.push_back (point_factory ((*x
)->when
- start
, (*x
)->value
));
1124 if (op
!= 2 && nal
->events
.back()->when
!= end
- start
) {
1125 nal
->events
.push_back (point_factory (end
- start
, unlocked_eval (end
)));
1129 reposition_for_rt_add (0);
1135 maybe_signal_changed ();
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
; ) {
1155 nal
->events
.push_back (point_factory (**x
));
1165 AutomationList::cut (double start
, double end
)
1167 return cut_copy_clear (start
, end
, 0);
1171 AutomationList::copy (double start
, double end
)
1173 return cut_copy_clear (start
, end
, 1);
1177 AutomationList::clear (double start
, double end
)
1179 (void) cut_copy_clear (start
, end
, 2);
1183 AutomationList::paste (AutomationList
& alist
, double pos
, float times
)
1185 if (alist
.events
.empty()) {
1190 Glib::Mutex::Lock
lm (lock
);
1194 ControlEvent
cp (pos
, 0.0);
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
1209 while (where
!= events
.end()) {
1211 if ((*where
)->when
<= end
) {
1214 events
.erase(where
);
1222 reposition_for_rt_add (0);
1226 maybe_signal_changed ();
1231 AutomationList::point_factory (double when
, double val
) const
1233 return new ControlEvent (when
, val
);
1237 AutomationList::point_factory (const ControlEvent
& other
) const
1239 return new ControlEvent (other
);
1243 AutomationList::get_state ()
1245 return state (true);
1249 AutomationList::state (bool full
)
1251 XMLNode
* root
= new XMLNode (X_("AutomationList"));
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
);
1267 root
->add_property ("state", auto_state_to_string (_state
));
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());
1283 AutomationList::serialize_events ()
1285 XMLNode
* node
= new XMLNode (X_("events"));
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
;
1293 str
<<(double) (*xx
)->value
;
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
);
1308 AutomationList::deserialize_events (const XMLNode
& node
)
1310 if (node
.children().empty()) {
1314 XMLNode
* content_node
= node
.children().front();
1316 if (content_node
->content().empty()) {
1323 stringstream
str (content_node
->content());
1339 fast_simple_add (x
, y
);
1344 error
<< _("automation list: cannot load coordinates from XML, all points ignored") << endmsg
;
1347 reposition_for_rt_add (0);
1348 maybe_signal_changed ();
1357 AutomationList::set_state (const XMLNode
& node
)
1359 XMLNodeList nlist
= node
.children();
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
);
1378 const XMLNodeList
& elist
= node
.children();
1379 XMLNodeConstIterator i
;
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
;
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
;
1399 y
= atof (prop
->value().c_str());
1401 fast_simple_add (x
, y
);
1409 if (node
.name() != X_("AutomationList") ) {
1410 error
<< string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node
.name()) << endmsg
;
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());
1423 default_value
= 0.0;
1426 if ((prop
= node
.property (X_("style"))) != 0) {
1427 _style
= string_to_auto_style (prop
->value());
1432 if ((prop
= node
.property (X_("state"))) != 0) {
1433 _state
= string_to_auto_state (prop
->value());
1438 if ((prop
= node
.property (X_("min_yval"))) != 0) {
1439 min_yval
= atof (prop
->value ());
1444 if ((prop
= node
.property (X_("max_yval"))) != 0) {
1445 max_yval
= atof (prop
->value ());
1450 if ((prop
= node
.property (X_("max_xval"))) != 0) {
1451 max_xval
= atof (prop
->value ());
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
));
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
;
1480 maybe_signal_changed ();