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