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>
32 using namespace ARDOUR;
36 static void dumpit (const AutomationList& al, string prefix = "")
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;
46 AutomationList::AutomationList (double defval, bool with_state)
49 changed_when_thawed = false;
53 no_state = with_state;
56 max_xval = 0; // means "no limit"
57 default_value = defval;
59 rt_insertion_point = events.end();
60 lookup_cache.left = -1;
61 lookup_cache.range.first = events.end();
64 save_state (_("initial"));
68 AutomationList::AutomationList (const AutomationList& other)
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;
80 rt_insertion_point = events.end();
81 no_state = other.no_state;
82 lookup_cache.left = -1;
83 lookup_cache.range.first = events.end();
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.
89 events.push_back (other.point_factory (**i));
95 AutomationList::AutomationList (const AutomationList& other, double start, double end)
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;
107 rt_insertion_point = events.end();
108 no_state = other.no_state;
109 lookup_cache.left = -1;
110 lookup_cache.range.first = events.end();
112 /* now grab the relevant points, and shift them back if necessary */
114 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
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));
127 AutomationList::~AutomationList()
129 std::set<ControlEvent*> all_events;
130 AutomationList::State* asp;
132 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
133 all_events.insert (*x);
136 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
138 if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
140 for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
141 all_events.insert (*x);
146 for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
152 AutomationList::operator== (const AutomationList& other)
154 return events == other.events;
158 AutomationList::operator= (const AutomationList& other)
160 if (this != &other) {
164 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
165 events.push_back (point_factory (**i));
168 min_yval = other.min_yval;
169 max_yval = other.max_yval;
170 max_xval = other.max_xval;
171 default_value = other.default_value;
174 maybe_signal_changed ();
181 AutomationList::maybe_signal_changed ()
186 changed_when_thawed = true;
188 StateChanged (Change (0));
193 AutomationList::set_automation_state (AutoState s)
197 automation_state_changed (); /* EMIT SIGNAL */
202 AutomationList::set_automation_style (AutoStyle s)
206 automation_style_changed (); /* EMIT SIGNAL */
211 AutomationList::start_touch ()
218 AutomationList::stop_touch ()
225 AutomationList::clear ()
228 LockMonitor lm (lock, __LINE__, __FILE__);
231 save_state (_("cleared"));
236 maybe_signal_changed ();
240 AutomationList::x_scale (double factor)
242 LockMonitor lm (lock, __LINE__, __FILE__);
247 AutomationList::extend_to (double when)
249 LockMonitor lm (lock, __LINE__, __FILE__);
250 if (events.empty() || events.back()->when == when) {
253 double factor = when / events.back()->when;
258 void AutomationList::_x_scale (double factor)
260 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
261 (*i)->when = floor ((*i)->when * factor);
264 save_state ("x-scaled");
269 AutomationList::reposition_for_rt_add (double when)
271 rt_insertion_point = events.end();
274 #define last_rt_insertion_point rt_insertion_point
277 AutomationList::rt_add (double when, double value)
279 /* this is for automation recording */
281 if ((_state & Touch) && !_touching) {
285 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
288 LockMonitor lm (lock, __LINE__, __FILE__);
292 ControlEvent cp (when, 0.0);
295 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
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.
302 iterator after = last_rt_insertion_point;
304 if (++after != events.end()) {
305 iterator far = after;
307 while (far != events.end()) {
308 if ((*far)->when > when) {
316 last_rt_insertion_point = where;
318 if((*where)->when == when) {
319 (*where)->value = value;
323 where = events.erase (after, far);
332 iterator previous = last_rt_insertion_point;
335 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
336 (*last_rt_insertion_point)->when = when;
343 where = lower_bound (events.begin(), events.end(), &cp, cmp);
345 if (where != events.end()) {
346 if ((*where)->when == when) {
347 (*where)->value = value;
354 last_rt_insertion_point = events.insert (where, point_factory (when, value));
361 maybe_signal_changed ();
364 #undef last_rt_insertion_point
367 AutomationList::add (double when, double value, bool for_loading)
369 /* this is for graphical editing and loading data from storage */
372 LockMonitor lm (lock, __LINE__, __FILE__);
374 ControlEvent cp (when, 0.0f);
376 iterator insertion_point;
378 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
380 /* only one point allowed per time point */
382 if ((*insertion_point)->when == when) {
383 (*insertion_point)->value = value;
388 if ((*insertion_point)->when >= when) {
395 events.insert (insertion_point, point_factory (when, value));
396 reposition_for_rt_add (0);
402 if (!no_state && !for_loading) {
403 save_state (_("added event"));
408 maybe_signal_changed ();
413 AutomationList::erase (AutomationList::iterator i)
416 LockMonitor lm (lock, __LINE__, __FILE__);
418 reposition_for_rt_add (0);
420 save_state (_("removed event"));
424 maybe_signal_changed ();
428 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
431 LockMonitor lm (lock, __LINE__, __FILE__);
432 events.erase (start, end);
433 reposition_for_rt_add (0);
435 save_state (_("removed multiple events"));
439 maybe_signal_changed ();
443 AutomationList::reset_range (double start, double endt)
448 LockMonitor lm (lock, __LINE__, __FILE__);
450 ControlEvent cp (start, 0.0f);
454 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
457 e = upper_bound (events.begin(), events.end(), &cp, cmp);
459 for (iterator i = s; i != e; ++i) {
460 (*i)->value = default_value;
466 save_state (_("removed range"));
474 maybe_signal_changed ();
479 AutomationList::erase_range (double start, double endt)
484 LockMonitor lm (lock, __LINE__, __FILE__);
486 ControlEvent cp (start, 0.0f);
490 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
492 e = upper_bound (events.begin(), events.end(), &cp, cmp);
494 reposition_for_rt_add (0);
497 save_state (_("removed range"));
505 maybe_signal_changed ();
510 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
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.
518 LockMonitor lm (lock, __LINE__, __FILE__);
520 while (start != end) {
521 (*start)->when += xdelta;
522 (*start)->value += ydelta;
527 save_state (_("event range adjusted"));
533 maybe_signal_changed ();
537 AutomationList::modify (iterator iter, double when, double val)
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.
545 LockMonitor lm (lock, __LINE__, __FILE__);
546 (*iter)->when = when;
547 (*iter)->value = val;
549 save_state (_("event adjusted"));
555 maybe_signal_changed ();
558 std::pair<AutomationList::iterator,AutomationList::iterator>
559 AutomationList::control_points_adjacent (double xval)
561 LockMonitor lm (lock, __LINE__, __FILE__);
564 ControlEvent cp (xval, 0.0f);
565 std::pair<iterator,iterator> ret;
567 ret.first = events.end();
568 ret.second = events.end();
570 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
572 if (ret.first == events.end()) {
573 if ((*i)->when >= xval) {
574 if (i != events.begin()) {
583 if ((*i)->when > xval) {
593 AutomationList::freeze ()
599 AutomationList::thaw ()
602 if (changed_when_thawed) {
603 StateChanged(Change(0)); /* EMIT SIGNAL */
608 AutomationList::state_factory (std::string why) const
610 State* state = new State (why);
612 for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
613 state->events.push_back (point_factory (**x));
620 AutomationList::restore_state (StateManager::State& state)
623 LockMonitor lm (lock, __LINE__, __FILE__);
624 State* lstate = dynamic_cast<State*> (&state);
627 for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
628 events.push_back (point_factory (**x));
636 AutomationList::get_memento () const
638 return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
642 AutomationList::set_max_xval (double x)
648 AutomationList::mark_dirty ()
650 lookup_cache.left = -1;
655 AutomationList::truncate_end (double last_coordinate)
658 LockMonitor lm (lock, __LINE__, __FILE__);
659 ControlEvent cp (last_coordinate, 0);
660 list<ControlEvent*>::reverse_iterator i;
663 if (events.empty()) {
664 fatal << _("programming error:")
665 << "AutomationList::truncate_end() called on an empty list"
671 if (last_coordinate == events.back()->when) {
675 if (last_coordinate > events.back()->when) {
680 iterator foo = events.begin();
683 if (foo == events.end()) {
685 } else if (++foo == events.end()) {
692 /* less than 2 points: add a new point */
693 events.push_back (point_factory (last_coordinate, events.back()->value));
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.
701 iterator penultimate = events.end();
702 --penultimate; /* points at last point */
703 --penultimate; /* points at the penultimate point */
705 if (events.back()->value == (*penultimate)->value) {
706 events.back()->when = last_coordinate;
708 events.push_back (point_factory (last_coordinate, events.back()->value));
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);
722 /* make i point to the last control point */
726 /* now go backwards, removing control points that are
727 beyond the new last coordinate.
730 uint32_t sz = events.size();
732 while (i != events.rend() && sz > 2) {
733 list<ControlEvent*>::reverse_iterator tmp;
738 if ((*i)->when < last_coordinate) {
742 events.erase (i.base());
748 events.back()->when = last_coordinate;
749 events.back()->value = last_val;
752 reposition_for_rt_add (0);
756 maybe_signal_changed ();
760 AutomationList::truncate_start (double overall_length)
763 LockMonitor lm (lock, __LINE__, __FILE__);
764 AutomationList::iterator i;
765 double first_legal_value;
766 double first_legal_coordinate;
768 if (events.empty()) {
769 fatal << _("programming error:")
770 << "AutomationList::truncate_start() called on an empty list"
776 if (overall_length == events.back()->when) {
777 /* no change in overall length */
781 if (overall_length > events.back()->when) {
783 /* growing at front: duplicate first point. shift all others */
785 double shift = overall_length - events.back()->when;
788 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
794 /* less than 2 points: add a new point */
795 events.push_front (point_factory (0, events.front()->value));
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.
804 iterator second = events.begin();
805 ++second; /* points at the second point */
807 if (events.front()->value == (*second)->value) {
808 /* first segment is flat, just move start point back to zero */
809 events.front()->when = 0;
811 /* leave non-flat segment in place, add a new leading point. */
812 events.push_front (point_factory (0, events.front()->value));
818 /* shrinking at front */
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);
825 /* remove all events earlier than the new "front" */
829 while (i != events.end() && !events.empty()) {
830 list<ControlEvent*>::iterator tmp;
835 if ((*i)->when > first_legal_coordinate) {
845 /* shift all remaining points left to keep their same
849 for (i = events.begin(); i != events.end(); ++i) {
850 (*i)->when -= first_legal_coordinate;
853 /* add a new point for the interpolated new value */
855 events.push_front (point_factory (0, first_legal_value));
858 reposition_for_rt_add (0);
863 maybe_signal_changed ();
867 AutomationList::unlocked_eval (double x)
869 return shared_eval (x);
873 AutomationList::shared_eval (double x)
875 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
881 npoints = events.size();
885 return default_value;
888 if (x >= events.front()->when) {
889 return events.front()->value;
891 // return default_value;
892 return events.front()->value;
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;
905 lpos = events.front()->when;
906 lval = events.front()->value;
907 upos = events.back()->when;
908 uval = events.back()->value;
910 /* linear interpolation betweeen the two points
913 fraction = (double) (x - lpos) / (double) (upos - lpos);
914 return lval + (fraction * (uval - lval));
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;
927 return multipoint_eval (x);
933 AutomationList::multipoint_eval (double x)
935 pair<AutomationList::iterator,AutomationList::iterator> range;
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)
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))) {
949 ControlEvent cp (x, 0);
952 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
955 range = lookup_cache.range;
957 if (range.first == range.second) {
959 /* x does not exist within the list as a control point */
961 lookup_cache.left = x;
963 if (range.first != events.begin()) {
965 lpos = (*range.first)->when;
966 lval = (*range.first)->value;
968 /* we're before the first point */
969 // return default_value;
970 return events.front()->value;
973 if (range.second == events.end()) {
974 /* we're after the last point */
975 return events.back()->value;
978 upos = (*range.second)->when;
979 uval = (*range.second)->value;
981 /* linear interpolation betweeen the two points
985 fraction = (double) (x - lpos) / (double) (upos - lpos);
986 return lval + (fraction * (uval - lval));
990 /* x is a control point in the data */
991 lookup_cache.left = -1;
992 return (*range.first)->value;
996 AutomationList::cut (iterator start, iterator end)
998 AutomationList* nal = new AutomationList (default_value);
1001 LockMonitor lm (lock, __LINE__, __FILE__);
1003 for (iterator x = start; x != end; ) {
1009 nal->events.push_back (point_factory (**x));
1012 reposition_for_rt_add (0);
1020 maybe_signal_changed ();
1026 AutomationList::cut_copy_clear (double start, double end, int op)
1028 AutomationList* nal = new AutomationList (default_value);
1030 ControlEvent cp (start, 0.0);
1032 bool changed = false;
1035 LockMonitor lm (lock, __LINE__, __FILE__);
1037 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1042 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1044 if (op != 2 && (*s)->when != start) {
1045 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1048 for (iterator x = s; x != e; ) {
1056 /* adjust new points to be relative to start, which
1057 has been set to zero.
1061 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1071 if (op != 2 && nal->events.back()->when != end - start) {
1072 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1076 reposition_for_rt_add (0);
1078 save_state (_("cut/copy/clear"));
1085 maybe_signal_changed ();
1092 AutomationList::copy (iterator start, iterator end)
1094 AutomationList* nal = new AutomationList (default_value);
1097 LockMonitor lm (lock, __LINE__, __FILE__);
1099 for (iterator x = start; x != end; ) {
1105 nal->events.push_back (point_factory (**x));
1111 save_state (_("copy"));
1119 AutomationList::cut (double start, double end)
1121 return cut_copy_clear (start, end, 0);
1125 AutomationList::copy (double start, double end)
1127 return cut_copy_clear (start, end, 1);
1131 AutomationList::clear (double start, double end)
1133 (void) cut_copy_clear (start, end, 2);
1137 AutomationList::paste (AutomationList& alist, double pos, float times)
1139 if (alist.events.empty()) {
1144 LockMonitor lm (lock, __LINE__, __FILE__);
1148 ControlEvent cp (pos, 0.0);
1151 where = upper_bound (events.begin(), events.end(), &cp, cmp);
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;
1159 /* move all points after the insertion along the timeline by
1163 while (where != events.end()) {
1165 if ((*where)->when <= end) {
1168 events.erase(where);
1176 reposition_for_rt_add (0);
1179 save_state (_("paste"));
1185 maybe_signal_changed ();
1190 AutomationList::point_factory (double when, double val) const
1192 return new ControlEvent (when, val);
1196 AutomationList::point_factory (const ControlEvent& other) const
1198 return new ControlEvent (other);
1202 AutomationList::store_state (XMLNode& node) const
1204 LocaleGuard lg (X_("POSIX"));
1206 for (const_iterator i = const_begin(); i != const_end(); ++i) {
1209 XMLNode *pointnode = new XMLNode ("point");
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);
1216 node.add_child_nocopy (*pointnode);
1221 AutomationList::load_state (const XMLNode& node)
1223 const XMLNodeList& elist = node.children();
1224 XMLNodeConstIterator i;
1231 for (i = elist.begin(); i != elist.end(); ++i) {
1233 if ((prop = (*i)->property ("x")) == 0) {
1234 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1237 x = atoi (prop->value().c_str());
1239 if ((prop = (*i)->property ("y")) == 0) {
1240 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1243 y = atof (prop->value().c_str());