Merged with trunk R1141
[ardour.git] / libs / ardour / automation_event.cc
1 /*
2     Copyright (C) 2002 Paul Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <set>
22 #include <climits>
23 #include <float.h>
24 #include <cmath>
25 #include <sstream>
26 #include <algorithm>
27 #include <sigc++/bind.h>
28 #include <ardour/automation_event.h>
29 #include <pbd/convert.h>
30
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace ARDOUR;
35 using namespace sigc;
36 using namespace PBD;
37
38 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
39
40 #if 0
41 static void dumpit (const AutomationList& al, string prefix = "")
42 {
43         cerr << prefix << &al << endl;
44         for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
45                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
46         }
47         cerr << "\n";
48 }
49 #endif
50
51 AutomationList::AutomationList (double defval)
52 {
53         _frozen = false;
54         changed_when_thawed = false;
55         _state = Off;
56         _style = Absolute;
57         _touching = false;
58         min_yval = FLT_MIN;
59         max_yval = FLT_MAX;
60         max_xval = 0; // means "no limit" 
61         default_value = defval;
62         _dirty = false;
63         rt_insertion_point = events.end();
64         lookup_cache.left = -1;
65         lookup_cache.range.first = events.end();
66
67         AutomationListCreated(this);
68 }
69
70 AutomationList::AutomationList (const AutomationList& other)
71 {
72         _frozen = false;
73         changed_when_thawed = false;
74         _style = other._style;
75         min_yval = other.min_yval;
76         max_yval = other.max_yval;
77         max_xval = other.max_xval;
78         default_value = other.default_value;
79         _state = other._state;
80         _touching = other._touching;
81         _dirty = false;
82         rt_insertion_point = events.end();
83         lookup_cache.left = -1;
84         lookup_cache.range.first = events.end();
85
86         for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
87                 /* we have to use other point_factory() because
88                    its virtual and we're in a constructor.
89                 */
90                 events.push_back (other.point_factory (**i));
91         }
92
93         mark_dirty ();
94         AutomationListCreated(this);
95 }
96
97 AutomationList::AutomationList (const AutomationList& other, double start, double end)
98 {
99         _frozen = false;
100         changed_when_thawed = false;
101         _style = other._style;
102         min_yval = other.min_yval;
103         max_yval = other.max_yval;
104         max_xval = other.max_xval;
105         default_value = other.default_value;
106         _state = other._state;
107         _touching = other._touching;
108         _dirty = false;
109         rt_insertion_point = events.end();
110         lookup_cache.left = -1;
111         lookup_cache.range.first = events.end();
112
113         /* now grab the relevant points, and shift them back if necessary */
114
115         AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
116
117         if (!section->empty()) {
118                 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
119                         events.push_back (other.point_factory ((*i)->when, (*i)->value));
120                 }
121         }
122
123         delete section;
124
125         mark_dirty ();
126
127         AutomationListCreated(this);
128 }
129
130 AutomationList::AutomationList (const XMLNode& node)
131 {
132         _frozen = false;
133         changed_when_thawed = false;
134         _touching = false;
135         min_yval = FLT_MIN;
136         max_yval = FLT_MAX;
137         max_xval = 0; // means "no limit" 
138         _dirty = false;
139         _state = Off;
140         _style = Absolute;
141         rt_insertion_point = events.end();
142         lookup_cache.left = -1;
143         lookup_cache.range.first = events.end();
144         
145         set_state (node);
146
147         AutomationListCreated(this);
148 }
149
150 AutomationList::~AutomationList()
151 {
152         GoingAway ();
153         
154         for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
155                 delete (*x);
156         }
157 }
158
159 bool
160 AutomationList::operator== (const AutomationList& other)
161 {
162         return events == other.events;
163 }
164
165 AutomationList&
166 AutomationList::operator= (const AutomationList& other)
167 {
168         if (this != &other) {
169                 
170                 events.clear ();
171                 
172                 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
173                         events.push_back (point_factory (**i));
174                 }
175                 
176                 min_yval = other.min_yval;
177                 max_yval = other.max_yval;
178                 max_xval = other.max_xval;
179                 default_value = other.default_value;
180                 
181                 mark_dirty ();
182                 maybe_signal_changed ();
183         }
184
185         return *this;
186 }
187
188 void
189 AutomationList::maybe_signal_changed ()
190 {
191         mark_dirty ();
192
193         if (_frozen) {
194                 changed_when_thawed = true;
195         } else {
196                 StateChanged ();
197         }
198 }
199
200 void
201 AutomationList::set_automation_state (AutoState s)
202 {
203         if (s != _state) {
204                 _state = s;
205                 automation_state_changed (); /* EMIT SIGNAL */
206         }
207 }
208
209 void
210 AutomationList::set_automation_style (AutoStyle s)
211 {
212         if (s != _style) {
213                 _style = s;
214                 automation_style_changed (); /* EMIT SIGNAL */
215         }
216 }
217
218 void
219 AutomationList::start_touch ()
220 {
221         _touching = true;
222         _new_touch = true;
223 }
224
225 void
226 AutomationList::stop_touch ()
227 {
228         _touching = false;
229         _new_touch = false;
230 }
231
232 void
233 AutomationList::clear ()
234 {
235         {
236                 Glib::Mutex::Lock lm (lock);
237                 events.clear ();
238                 mark_dirty ();
239         }
240
241         maybe_signal_changed ();
242 }
243
244 void
245 AutomationList::x_scale (double factor)
246 {
247         Glib::Mutex::Lock lm (lock);
248         _x_scale (factor);
249 }
250
251 bool
252 AutomationList::extend_to (double when)
253 {
254         Glib::Mutex::Lock lm (lock);
255         if (events.empty() || events.back()->when == when) {
256                 return false;
257         }
258         double factor = when / events.back()->when;
259         _x_scale (factor);
260         return true;
261 }
262
263 void AutomationList::_x_scale (double factor)
264 {
265         for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
266                 (*i)->when = floor ((*i)->when * factor);
267         }
268
269         mark_dirty ();
270 }
271
272 void
273 AutomationList::reposition_for_rt_add (double when)
274 {
275         rt_insertion_point = events.end();
276 }
277
278 #define last_rt_insertion_point rt_insertion_point
279
280 void
281 AutomationList::rt_add (double when, double value)
282 {
283         /* this is for automation recording */
284
285         if ((_state & Touch) && !_touching) {
286                 return;
287         }
288
289         // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
290
291         {
292                 Glib::Mutex::Lock lm (lock);
293
294                 iterator where;
295                 TimeComparator cmp;
296                 ControlEvent cp (when, 0.0);
297                 bool done = false;
298
299                 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
300
301                         /* we have a previous insertion point, so we should delete
302                            everything between it and the position where we are going
303                            to insert this point.
304                         */
305
306                         iterator after = last_rt_insertion_point;
307
308                         if (++after != events.end()) {
309                                 iterator far = after;
310
311                                 while (far != events.end()) {
312                                         if ((*far)->when > when) {
313                                                 break;
314                                         }
315                                         ++far;
316                                 }
317
318                                 if(_new_touch) {
319                                         where = far;
320                                         last_rt_insertion_point = where;
321                                                                                              
322                                         if((*where)->when == when) {
323                                                 (*where)->value = value;
324                                                 done = true;
325                                         }
326                                 } else {
327                                         where = events.erase (after, far);
328                                 }
329
330                         } else {
331
332                                 where = after;
333
334                         }
335                         
336                         iterator previous = last_rt_insertion_point;
337                         --previous;
338                         
339                         if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
340                                 (*last_rt_insertion_point)->when = when;
341                                 done = true;
342                                 
343                         }
344                         
345                 } else {
346
347                         where = lower_bound (events.begin(), events.end(), &cp, cmp);
348
349                         if (where != events.end()) {
350                                 if ((*where)->when == when) {
351                                         (*where)->value = value;
352                                         done = true;
353                                 }
354                         }
355                 }
356                 
357                 if (!done) {
358                         last_rt_insertion_point = events.insert (where, point_factory (when, value));
359                 }
360                 
361                 _new_touch = false;
362                 mark_dirty ();
363         }
364
365         maybe_signal_changed ();
366 }
367
368 void
369 AutomationList::fast_simple_add (double when, double value)
370 {
371         /* to be used only for loading pre-sorted data from saved state */
372         events.insert (events.end(), point_factory (when, value));
373 }
374
375 #undef last_rt_insertion_point
376
377 void
378 AutomationList::add (double when, double value)
379 {
380         /* this is for graphical editing */
381
382         {
383                 Glib::Mutex::Lock lm (lock);
384                 TimeComparator cmp;
385                 ControlEvent cp (when, 0.0f);
386                 bool insert = true;
387                 iterator insertion_point;
388
389                 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
390
391                         /* only one point allowed per time point */
392
393                         if ((*insertion_point)->when == when) {
394                                 (*insertion_point)->value = value;
395                                 insert = false;
396                                 break;
397                         } 
398
399                         if ((*insertion_point)->when >= when) {
400                                 break;
401                         }
402                 }
403
404                 if (insert) {
405
406                         events.insert (insertion_point, point_factory (when, value));
407                         reposition_for_rt_add (0);
408
409                 } 
410
411                 mark_dirty ();
412         }
413
414         maybe_signal_changed ();
415 }
416
417 void
418 AutomationList::erase (AutomationList::iterator i)
419 {
420         {
421                 Glib::Mutex::Lock lm (lock);
422                 events.erase (i);
423                 reposition_for_rt_add (0);
424                 mark_dirty ();
425         }
426         maybe_signal_changed ();
427 }
428
429 void
430 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
431 {
432         {
433                 Glib::Mutex::Lock lm (lock);
434                 events.erase (start, end);
435                 reposition_for_rt_add (0);
436                 mark_dirty ();
437         }
438         maybe_signal_changed ();
439 }       
440
441 void
442 AutomationList::reset_range (double start, double endt)
443 {
444         bool reset = false;
445
446         {
447         Glib::Mutex::Lock lm (lock);
448                 TimeComparator cmp;
449                 ControlEvent cp (start, 0.0f);
450                 iterator s;
451                 iterator e;
452                 
453                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
454
455                         cp.when = endt;
456                         e = upper_bound (events.begin(), events.end(), &cp, cmp);
457
458                         for (iterator i = s; i != e; ++i) {
459                                 (*i)->value = default_value;
460                         }
461                         
462                         reset = true;
463
464                         mark_dirty ();
465                 }
466         }
467
468         if (reset) {
469                 maybe_signal_changed ();
470         }
471 }
472
473 void
474 AutomationList::erase_range (double start, double endt)
475 {
476         bool erased = false;
477
478         {
479                 Glib::Mutex::Lock lm (lock);
480                 TimeComparator cmp;
481                 ControlEvent cp (start, 0.0f);
482                 iterator s;
483                 iterator e;
484
485                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
486                         cp.when = endt;
487                         e = upper_bound (events.begin(), events.end(), &cp, cmp);
488                         events.erase (s, e);
489                         reposition_for_rt_add (0);
490                         erased = true;
491                         mark_dirty ();
492                 }
493                 
494         }
495
496         if (erased) {
497                 maybe_signal_changed ();
498         }
499 }
500
501 void
502 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
503 {
504         /* note: we assume higher level logic is in place to avoid this
505            reordering the time-order of control events in the list. ie. all
506            points after end are later than (end)->when.
507         */
508
509         {
510                 Glib::Mutex::Lock lm (lock);
511
512                 while (start != end) {
513                         (*start)->when += xdelta;
514                         (*start)->value += ydelta;
515                         ++start;
516                 }
517
518                 mark_dirty ();
519         }
520
521         maybe_signal_changed ();
522 }
523
524 void
525 AutomationList::modify (iterator iter, double when, double val)
526 {
527         /* note: we assume higher level logic is in place to avoid this
528            reordering the time-order of control events in the list. ie. all
529            points after *iter are later than when.
530         */
531
532         {
533                 Glib::Mutex::Lock lm (lock);
534                 (*iter)->when = when;
535                 (*iter)->value = val;
536                 mark_dirty ();
537         }
538         
539         maybe_signal_changed ();
540 }
541
542 std::pair<AutomationList::iterator,AutomationList::iterator>
543 AutomationList::control_points_adjacent (double xval)
544 {
545         Glib::Mutex::Lock lm (lock);
546         iterator i;
547         TimeComparator cmp;
548         ControlEvent cp (xval, 0.0f);
549         std::pair<iterator,iterator> ret;
550
551         ret.first = events.end();
552         ret.second = events.end();
553
554         for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
555                 
556                 if (ret.first == events.end()) {
557                         if ((*i)->when >= xval) {
558                                 if (i != events.begin()) {
559                                         ret.first = i;
560                                         --ret.first;
561                                 } else {
562                                         return ret;
563                                 }
564                         }
565                 } 
566                 
567                 if ((*i)->when > xval) {
568                         ret.second = i;
569                         break;
570                 }
571         }
572
573         return ret;
574 }
575
576 void
577 AutomationList::freeze ()
578 {
579         _frozen = true;
580 }
581
582 void
583 AutomationList::thaw ()
584 {
585         _frozen = false;
586         if (changed_when_thawed) {
587                 StateChanged(); /* EMIT SIGNAL */
588         }
589 }
590
591 void
592 AutomationList::set_max_xval (double x)
593 {
594         max_xval = x;
595 }
596
597 void 
598 AutomationList::mark_dirty ()
599 {
600         lookup_cache.left = -1;
601         _dirty = true;
602 }
603
604 void
605 AutomationList::truncate_end (double last_coordinate)
606 {
607         {
608                 Glib::Mutex::Lock lm (lock);
609                 ControlEvent cp (last_coordinate, 0);
610                 list<ControlEvent*>::reverse_iterator i;
611                 double last_val;
612
613                 if (events.empty()) {
614                         return;
615                 }
616
617                 if (last_coordinate == events.back()->when) {
618                         return;
619                 }
620
621                 if (last_coordinate > events.back()->when) {
622                         
623                         /* extending end:
624                         */
625
626                         iterator foo = events.begin();
627                         bool lessthantwo;
628
629                         if (foo == events.end()) {
630                                 lessthantwo = true;
631                         } else if (++foo == events.end()) {
632                                 lessthantwo = true;
633                         } else {
634                                 lessthantwo = false;
635                         }
636
637                         if (lessthantwo) {
638                                 /* less than 2 points: add a new point */
639                                 events.push_back (point_factory (last_coordinate, events.back()->value));
640                         } else {
641
642                                 /* more than 2 points: check to see if the last 2 values
643                                    are equal. if so, just move the position of the
644                                    last point. otherwise, add a new point.
645                                 */
646
647                                 iterator penultimate = events.end();
648                                 --penultimate; /* points at last point */
649                                 --penultimate; /* points at the penultimate point */
650                                 
651                                 if (events.back()->value == (*penultimate)->value) {
652                                         events.back()->when = last_coordinate;
653                                 } else {
654                                         events.push_back (point_factory (last_coordinate, events.back()->value));
655                                 }
656                         }
657
658                 } else {
659
660                         /* shortening end */
661
662                         last_val = unlocked_eval (last_coordinate);
663                         last_val = max ((double) min_yval, last_val);
664                         last_val = min ((double) max_yval, last_val);
665                         
666                         i = events.rbegin();
667                         
668                         /* make i point to the last control point */
669                         
670                         ++i;
671                         
672                         /* now go backwards, removing control points that are
673                            beyond the new last coordinate.
674                         */
675
676                         uint32_t sz = events.size();
677                         
678                         while (i != events.rend() && sz > 2) {
679                                 list<ControlEvent*>::reverse_iterator tmp;
680                                 
681                                 tmp = i;
682                                 ++tmp;
683                                 
684                                 if ((*i)->when < last_coordinate) {
685                                         break;
686                                 }
687                                 
688                                 events.erase (i.base());
689                                 --sz;
690
691                                 i = tmp;
692                         }
693                         
694                         events.back()->when = last_coordinate;
695                         events.back()->value = last_val;
696                 }
697
698                 reposition_for_rt_add (0);
699                 mark_dirty();
700         }
701
702         maybe_signal_changed ();
703 }
704
705 void
706 AutomationList::truncate_start (double overall_length)
707 {
708         {
709                 Glib::Mutex::Lock lm (lock);
710                 AutomationList::iterator i;
711                 double first_legal_value;
712                 double first_legal_coordinate;
713
714                 if (events.empty()) {
715                         fatal << _("programming error:")
716                               << "AutomationList::truncate_start() called on an empty list"
717                               << endmsg;
718                         /*NOTREACHED*/
719                         return;
720                 }
721                 
722                 if (overall_length == events.back()->when) {
723                         /* no change in overall length */
724                         return;
725                 }
726                 
727                 if (overall_length > events.back()->when) {
728                         
729                         /* growing at front: duplicate first point. shift all others */
730
731                         double shift = overall_length - events.back()->when;
732                         uint32_t np;
733
734                         for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
735                                 (*i)->when += shift;
736                         }
737
738                         if (np < 2) {
739
740                                 /* less than 2 points: add a new point */
741                                 events.push_front (point_factory (0, events.front()->value));
742
743                         } else {
744
745                                 /* more than 2 points: check to see if the first 2 values
746                                    are equal. if so, just move the position of the
747                                    first point. otherwise, add a new point.
748                                 */
749
750                                 iterator second = events.begin();
751                                 ++second; /* points at the second point */
752                                 
753                                 if (events.front()->value == (*second)->value) {
754                                         /* first segment is flat, just move start point back to zero */
755                                         events.front()->when = 0;
756                                 } else {
757                                         /* leave non-flat segment in place, add a new leading point. */
758                                         events.push_front (point_factory (0, events.front()->value));
759                                 }
760                         }
761
762                 } else {
763
764                         /* shrinking at front */
765                         
766                         first_legal_coordinate = events.back()->when - overall_length;
767                         first_legal_value = unlocked_eval (first_legal_coordinate);
768                         first_legal_value = max (min_yval, first_legal_value);
769                         first_legal_value = min (max_yval, first_legal_value);
770
771                         /* remove all events earlier than the new "front" */
772
773                         i = events.begin();
774                         
775                         while (i != events.end() && !events.empty()) {
776                                 list<ControlEvent*>::iterator tmp;
777                                 
778                                 tmp = i;
779                                 ++tmp;
780                                 
781                                 if ((*i)->when > first_legal_coordinate) {
782                                         break;
783                                 }
784                                 
785                                 events.erase (i);
786                                 
787                                 i = tmp;
788                         }
789                         
790
791                         /* shift all remaining points left to keep their same
792                            relative position
793                         */
794                         
795                         for (i = events.begin(); i != events.end(); ++i) {
796                                 (*i)->when -= first_legal_coordinate;
797                         }
798
799                         /* add a new point for the interpolated new value */
800                         
801                         events.push_front (point_factory (0, first_legal_value));
802                 }           
803
804                 reposition_for_rt_add (0);
805
806                 mark_dirty();
807         }
808
809         maybe_signal_changed ();
810 }
811
812 double
813 AutomationList::unlocked_eval (double x)
814 {
815         return shared_eval (x);
816 }
817
818 double
819 AutomationList::shared_eval (double x) 
820 {
821         pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
822         int32_t npoints;
823         double lpos, upos;
824         double lval, uval;
825         double fraction;
826
827         npoints = events.size();
828
829         switch (npoints) {
830         case 0:
831                 return default_value;
832
833         case 1:
834                 if (x >= events.front()->when) {
835                         return events.front()->value;
836                 } else {
837                         // return default_value;
838                         return events.front()->value;
839                 } 
840                 
841         case 2:
842                 if (x >= events.back()->when) {
843                         return events.back()->value;
844                 } else if (x == events.front()->when) {
845                         return events.front()->value;
846                 } else if (x < events.front()->when) {
847                         // return default_value;
848                         return events.front()->value;
849                 }
850
851                 lpos = events.front()->when;
852                 lval = events.front()->value;
853                 upos = events.back()->when;
854                 uval = events.back()->value;
855                 
856                 /* linear interpolation betweeen the two points
857                 */
858
859                 fraction = (double) (x - lpos) / (double) (upos - lpos);
860                 return lval + (fraction * (uval - lval));
861
862         default:
863
864                 if (x >= events.back()->when) {
865                         return events.back()->value;
866                 } else if (x == events.front()->when) {
867                         return events.front()->value;
868                 } else if (x < events.front()->when) {
869                         // return default_value;
870                         return events.front()->value;
871                 }
872
873                 return multipoint_eval (x);
874                 break;
875         }
876 }
877
878 double
879 AutomationList::multipoint_eval (double x) 
880 {
881         pair<AutomationList::iterator,AutomationList::iterator> range;
882         double upos, lpos;
883         double uval, lval;
884         double fraction;
885
886         /* only do the range lookup if x is in a different range than last time
887            this was called (or if the lookup cache has been marked "dirty" (left<0)
888         */
889
890         if ((lookup_cache.left < 0) ||
891             ((lookup_cache.left > x) || 
892              (lookup_cache.range.first == events.end()) || 
893              ((*lookup_cache.range.second)->when < x))) {
894
895                 ControlEvent cp (x, 0);
896                 TimeComparator cmp;
897                 
898                 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
899         }
900         
901         range = lookup_cache.range;
902
903         if (range.first == range.second) {
904
905                 /* x does not exist within the list as a control point */
906
907                 lookup_cache.left = x;
908
909                 if (range.first != events.begin()) {
910                         --range.first;
911                         lpos = (*range.first)->when;
912                         lval = (*range.first)->value;
913                 }  else {
914                         /* we're before the first point */
915                         // return default_value;
916                         return events.front()->value;
917                 }
918                 
919                 if (range.second == events.end()) {
920                         /* we're after the last point */
921                         return events.back()->value;
922                 }
923
924                 upos = (*range.second)->when;
925                 uval = (*range.second)->value;
926                 
927                 /* linear interpolation betweeen the two points
928                    on either side of x
929                 */
930
931                 fraction = (double) (x - lpos) / (double) (upos - lpos);
932                 return lval + (fraction * (uval - lval));
933
934         } 
935
936         /* x is a control point in the data */
937         lookup_cache.left = -1;
938         return (*range.first)->value;
939 }
940
941 AutomationList*
942 AutomationList::cut (iterator start, iterator end)
943 {
944         AutomationList* nal = new AutomationList (default_value);
945
946         {
947                 Glib::Mutex::Lock lm (lock);
948
949                 for (iterator x = start; x != end; ) {
950                         iterator tmp;
951                         
952                         tmp = x;
953                         ++tmp;
954                         
955                         nal->events.push_back (point_factory (**x));
956                         events.erase (x);
957                         
958                         reposition_for_rt_add (0);
959
960                         x = tmp;
961                 }
962
963                 mark_dirty ();
964         }
965
966         maybe_signal_changed ();
967
968         return nal;
969 }
970
971 AutomationList*
972 AutomationList::cut_copy_clear (double start, double end, int op)
973 {
974         AutomationList* nal = new AutomationList (default_value);
975         iterator s, e;
976         ControlEvent cp (start, 0.0);
977         TimeComparator cmp;
978         bool changed = false;
979         
980         {
981                 Glib::Mutex::Lock lm (lock);
982
983                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
984                         return nal;
985                 }
986
987                 cp.when = end;
988                 e = upper_bound (events.begin(), events.end(), &cp, cmp);
989
990                 if (op != 2 && (*s)->when != start) {
991                         nal->events.push_back (point_factory (0, unlocked_eval (start)));
992                 }
993
994                 for (iterator x = s; x != e; ) {
995                         iterator tmp;
996                         
997                         tmp = x;
998                         ++tmp;
999
1000                         changed = true;
1001                         
1002                         /* adjust new points to be relative to start, which
1003                            has been set to zero.
1004                         */
1005                         
1006                         if (op != 2) {
1007                                 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1008                         }
1009
1010                         if (op != 1) {
1011                                 events.erase (x);
1012                         }
1013                         
1014                         x = tmp;
1015                 }
1016
1017                 if (op != 2 && nal->events.back()->when != end - start) {
1018                         nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1019                 }
1020
1021                 if (changed) {
1022                         reposition_for_rt_add (0);
1023                 }
1024
1025                 mark_dirty ();
1026         }
1027
1028         maybe_signal_changed ();
1029
1030         return nal;
1031
1032 }
1033
1034 AutomationList*
1035 AutomationList::copy (iterator start, iterator end)
1036 {
1037         AutomationList* nal = new AutomationList (default_value);
1038
1039         {
1040                 Glib::Mutex::Lock lm (lock);
1041                 
1042                 for (iterator x = start; x != end; ) {
1043                         iterator tmp;
1044                         
1045                         tmp = x;
1046                         ++tmp;
1047                         
1048                         nal->events.push_back (point_factory (**x));
1049                         
1050                         x = tmp;
1051                 }
1052         }
1053
1054         return nal;
1055 }
1056
1057 AutomationList*
1058 AutomationList::cut (double start, double end)
1059 {
1060         return cut_copy_clear (start, end, 0);
1061 }
1062
1063 AutomationList*
1064 AutomationList::copy (double start, double end)
1065 {
1066         return cut_copy_clear (start, end, 1);
1067 }
1068
1069 void
1070 AutomationList::clear (double start, double end)
1071 {
1072         (void) cut_copy_clear (start, end, 2);
1073 }
1074
1075 bool
1076 AutomationList::paste (AutomationList& alist, double pos, float times)
1077 {
1078         if (alist.events.empty()) {
1079                 return false;
1080         }
1081
1082         {
1083                 Glib::Mutex::Lock lm (lock);
1084                 iterator where;
1085                 iterator prev;
1086                 double end = 0;
1087                 ControlEvent cp (pos, 0.0);
1088                 TimeComparator cmp;
1089
1090                 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1091
1092                 for (iterator i = alist.begin();i != alist.end(); ++i) {
1093                         events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1094                         end = (*i)->when + pos;
1095                 }
1096         
1097         
1098                 /* move all  points after the insertion along the timeline by 
1099                    the correct amount.
1100                 */
1101
1102                 while (where != events.end()) {
1103                         iterator tmp;
1104                         if ((*where)->when <= end) {
1105                                 tmp = where;
1106                                 ++tmp;
1107                                 events.erase(where);
1108                                 where = tmp;
1109
1110                         } else {
1111                                 break;
1112                         }
1113                 }
1114
1115                 reposition_for_rt_add (0);
1116                 mark_dirty ();
1117         }
1118
1119         maybe_signal_changed ();
1120         return true;
1121 }
1122
1123 ControlEvent*
1124 AutomationList::point_factory (double when, double val) const
1125 {
1126         return new ControlEvent (when, val);
1127 }
1128
1129 ControlEvent*
1130 AutomationList::point_factory (const ControlEvent& other) const
1131 {
1132         return new ControlEvent (other);
1133 }
1134
1135 XMLNode&
1136 AutomationList::get_state ()
1137 {
1138         return state (true);
1139 }
1140
1141 XMLNode&
1142 AutomationList::state (bool full)
1143 {
1144         XMLNode* root = new XMLNode (X_("AutomationList"));
1145         char buf[64];
1146         LocaleGuard lg (X_("POSIX"));
1147
1148         root->add_property ("id", _id.to_s());
1149
1150         snprintf (buf, sizeof (buf), "%.12g", default_value);
1151         root->add_property ("default", buf);
1152         snprintf (buf, sizeof (buf), "%.12g", min_yval);
1153         root->add_property ("min_yval", buf);
1154         snprintf (buf, sizeof (buf), "%.12g", max_yval);
1155         root->add_property ("max_yval", buf);
1156         snprintf (buf, sizeof (buf), "%.12g", max_xval);
1157         root->add_property ("max_xval", buf);
1158
1159         if (full) {
1160                 root->add_property ("state", auto_state_to_string (_state));
1161         } else {
1162                 /* never save anything but Off for automation state to a template */
1163                 root->add_property ("state", auto_state_to_string (Off));
1164         }
1165
1166         root->add_property ("style", auto_style_to_string (_style));
1167
1168         if (!events.empty()) {
1169                 root->add_child_nocopy (serialize_events());
1170         }
1171
1172         return *root;
1173 }
1174
1175 XMLNode&
1176 AutomationList::serialize_events ()
1177 {
1178         XMLNode* node = new XMLNode (X_("events"));
1179         stringstream str;
1180
1181         for (iterator xx = events.begin(); xx != events.end(); ++xx) {
1182                 str << (double) (*xx)->when;
1183                 str << ' ';
1184                 str <<(double) (*xx)->value;
1185                 str << '\n';
1186         }
1187
1188         /* XML is a bit wierd */
1189
1190         XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
1191         content_node->set_content (str.str());
1192
1193         node->add_child_nocopy (*content_node);
1194
1195         return *node;
1196 }
1197
1198 int
1199 AutomationList::deserialize_events (const XMLNode& node)
1200 {
1201         if (node.children().empty()) {
1202                 return -1;
1203         }
1204
1205         XMLNode* content_node = node.children().front();
1206
1207         if (content_node->content().empty()) {
1208                 return -1;
1209         }
1210
1211         freeze ();
1212         clear ();
1213         
1214         stringstream str (content_node->content());
1215         
1216         double x;
1217         double y;
1218         bool ok = true;
1219         
1220         while (str) {
1221                 str >> x;
1222                 if (!str) {
1223                         break;
1224                 }
1225                 str >> y;
1226                 if (!str) {
1227                         ok = false;
1228                         break;
1229                 }
1230                 fast_simple_add (x, y);
1231         }
1232         
1233         if (!ok) {
1234                 clear ();
1235                 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
1236         } else {
1237                 mark_dirty ();
1238                 reposition_for_rt_add (0);
1239                 maybe_signal_changed ();
1240         }
1241
1242         thaw ();
1243         return 0;
1244 }
1245
1246 int
1247 AutomationList::set_state (const XMLNode& node)
1248 {
1249         XMLNodeList nlist = node.children();
1250         XMLNode* nsos;
1251         XMLNodeIterator niter;
1252         const XMLProperty* prop;
1253
1254         if (node.name() == X_("events")) {
1255                 /* partial state setting*/
1256                 return deserialize_events (node);
1257         }
1258         
1259         if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
1260
1261                 if ((nsos = node.child (X_("AutomationList")))) {
1262                         /* new school in old school clothing */
1263                         return set_state (*nsos);
1264                 }
1265
1266                 /* old school */
1267
1268                 const XMLNodeList& elist = node.children();
1269                 XMLNodeConstIterator i;
1270                 XMLProperty* prop;
1271                 jack_nframes_t x;
1272                 double y;
1273                 
1274                 clear ();
1275                 
1276                 for (i = elist.begin(); i != elist.end(); ++i) {
1277                         
1278                         if ((prop = (*i)->property ("x")) == 0) {
1279                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1280                                 continue;
1281                         }
1282                         x = atoi (prop->value().c_str());
1283                         
1284                         if ((prop = (*i)->property ("y")) == 0) {
1285                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1286                                 continue;
1287                         }
1288                         y = atof (prop->value().c_str());
1289                         
1290                         add (x, y);
1291                 }
1292                 
1293                 return 0;
1294         }
1295
1296         if (node.name() != X_("AutomationList") ) {
1297                 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
1298                 return -1;
1299         }
1300         
1301         if ((prop = node.property ("id")) != 0) {
1302                 _id = prop->value ();
1303                 /* update session AL list */
1304                 AutomationListCreated(this);
1305         }
1306         
1307         if ((prop = node.property (X_("default"))) != 0){ 
1308                 default_value = atof (prop->value());
1309         } else {
1310                 default_value = 0.0;
1311         }
1312
1313         if ((prop = node.property (X_("style"))) != 0) {
1314                 _style = string_to_auto_style (prop->value());
1315         } else {
1316                 _style = Absolute;
1317         }
1318
1319         if ((prop = node.property (X_("state"))) != 0) {
1320                 _state = string_to_auto_state (prop->value());
1321         } else {
1322                 _state = Off;
1323         }
1324
1325         if ((prop = node.property (X_("min_yval"))) != 0) {
1326                 min_yval = atof (prop->value ());
1327         } else {
1328                 min_yval = FLT_MIN;
1329         }
1330
1331         if ((prop = node.property (X_("max_yval"))) != 0) {
1332                 max_yval = atof (prop->value ());
1333         } else {
1334                 max_yval = FLT_MAX;
1335         }
1336
1337         if ((prop = node.property (X_("max_xval"))) != 0) {
1338                 max_xval = atof (prop->value ());
1339         } else {
1340                 max_xval = 0; // means "no limit ;
1341         }
1342
1343         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1344                 if ((*niter)->name() == X_("events")) {
1345                         deserialize_events (*(*niter));
1346                 }
1347         }
1348         
1349         return 0;
1350 }
1351