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;
37 static void dumpit (const AutomationList& al, string prefix = "")
39 cerr << prefix << &al << endl;
40 for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
41 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
47 AutomationList::AutomationList (double defval, bool with_state)
50 changed_when_thawed = false;
54 no_state = with_state;
57 max_xval = 0; // means "no limit"
58 default_value = defval;
60 rt_insertion_point = events.end();
61 lookup_cache.left = -1;
62 lookup_cache.range.first = events.end();
65 save_state (_("initial"));
69 AutomationList::AutomationList (const AutomationList& other)
72 changed_when_thawed = false;
73 _style = other._style;
74 min_yval = other.min_yval;
75 max_yval = other.max_yval;
76 max_xval = other.max_xval;
77 default_value = other.default_value;
78 _state = other._state;
79 _touching = other._touching;
81 rt_insertion_point = events.end();
82 no_state = other.no_state;
83 lookup_cache.left = -1;
84 lookup_cache.range.first = events.end();
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.
90 events.push_back (other.point_factory (**i));
96 AutomationList::AutomationList (const AutomationList& other, double start, double end)
99 changed_when_thawed = false;
100 _style = other._style;
101 min_yval = other.min_yval;
102 max_yval = other.max_yval;
103 max_xval = other.max_xval;
104 default_value = other.default_value;
105 _state = other._state;
106 _touching = other._touching;
108 rt_insertion_point = events.end();
109 no_state = other.no_state;
110 lookup_cache.left = -1;
111 lookup_cache.range.first = events.end();
113 /* now grab the relevant points, and shift them back if necessary */
115 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
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));
128 AutomationList::~AutomationList()
130 std::set<ControlEvent*> all_events;
131 AutomationList::State* asp;
133 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
134 all_events.insert (*x);
137 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
139 if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
141 for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
142 all_events.insert (*x);
147 for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
153 AutomationList::operator== (const AutomationList& other)
155 return events == other.events;
159 AutomationList::operator= (const AutomationList& other)
161 if (this != &other) {
165 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
166 events.push_back (point_factory (**i));
169 min_yval = other.min_yval;
170 max_yval = other.max_yval;
171 max_xval = other.max_xval;
172 default_value = other.default_value;
175 maybe_signal_changed ();
182 AutomationList::maybe_signal_changed ()
187 changed_when_thawed = true;
189 StateChanged (Change (0));
194 AutomationList::set_automation_state (AutoState s)
198 automation_state_changed (); /* EMIT SIGNAL */
203 AutomationList::set_automation_style (AutoStyle s)
207 automation_style_changed (); /* EMIT SIGNAL */
212 AutomationList::start_touch ()
219 AutomationList::stop_touch ()
226 AutomationList::clear ()
229 Glib::Mutex::Lock lm (lock);
232 save_state (_("cleared"));
237 maybe_signal_changed ();
241 AutomationList::x_scale (double factor)
243 Glib::Mutex::Lock lm (lock);
248 AutomationList::extend_to (double when)
250 Glib::Mutex::Lock lm (lock);
251 if (events.empty() || events.back()->when == when) {
254 double factor = when / events.back()->when;
259 void AutomationList::_x_scale (double factor)
261 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
262 (*i)->when = floor ((*i)->when * factor);
265 save_state ("x-scaled");
270 AutomationList::reposition_for_rt_add (double when)
272 rt_insertion_point = events.end();
275 #define last_rt_insertion_point rt_insertion_point
278 AutomationList::rt_add (double when, double value)
280 /* this is for automation recording */
282 if ((_state & Touch) && !_touching) {
286 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
289 Glib::Mutex::Lock lm (lock);
293 ControlEvent cp (when, 0.0);
296 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
298 /* we have a previous insertion point, so we should delete
299 everything between it and the position where we are going
300 to insert this point.
303 iterator after = last_rt_insertion_point;
305 if (++after != events.end()) {
306 iterator far = after;
308 while (far != events.end()) {
309 if ((*far)->when > when) {
317 last_rt_insertion_point = where;
319 if((*where)->when == when) {
320 (*where)->value = value;
324 where = events.erase (after, far);
333 iterator previous = last_rt_insertion_point;
336 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
337 (*last_rt_insertion_point)->when = when;
344 where = lower_bound (events.begin(), events.end(), &cp, cmp);
346 if (where != events.end()) {
347 if ((*where)->when == when) {
348 (*where)->value = value;
355 last_rt_insertion_point = events.insert (where, point_factory (when, value));
362 maybe_signal_changed ();
365 #undef last_rt_insertion_point
368 AutomationList::add (double when, double value, bool for_loading)
370 /* this is for graphical editing and loading data from storage */
373 Glib::Mutex::Lock lm (lock);
375 ControlEvent cp (when, 0.0f);
377 iterator insertion_point;
379 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
381 /* only one point allowed per time point */
383 if ((*insertion_point)->when == when) {
384 (*insertion_point)->value = value;
389 if ((*insertion_point)->when >= when) {
396 events.insert (insertion_point, point_factory (when, value));
397 reposition_for_rt_add (0);
403 if (!no_state && !for_loading) {
404 save_state (_("added event"));
409 maybe_signal_changed ();
414 AutomationList::erase (AutomationList::iterator i)
417 Glib::Mutex::Lock lm (lock);
419 reposition_for_rt_add (0);
421 save_state (_("removed event"));
425 maybe_signal_changed ();
429 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
432 Glib::Mutex::Lock lm (lock);
433 events.erase (start, end);
434 reposition_for_rt_add (0);
436 save_state (_("removed multiple events"));
440 maybe_signal_changed ();
444 AutomationList::reset_range (double start, double endt)
449 Glib::Mutex::Lock lm (lock);
451 ControlEvent cp (start, 0.0f);
455 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
458 e = upper_bound (events.begin(), events.end(), &cp, cmp);
460 for (iterator i = s; i != e; ++i) {
461 (*i)->value = default_value;
467 save_state (_("removed range"));
475 maybe_signal_changed ();
480 AutomationList::erase_range (double start, double endt)
485 Glib::Mutex::Lock lm (lock);
487 ControlEvent cp (start, 0.0f);
491 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
493 e = upper_bound (events.begin(), events.end(), &cp, cmp);
495 reposition_for_rt_add (0);
498 save_state (_("removed range"));
506 maybe_signal_changed ();
511 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
513 /* note: we assume higher level logic is in place to avoid this
514 reordering the time-order of control events in the list. ie. all
515 points after end are later than (end)->when.
519 Glib::Mutex::Lock lm (lock);
521 while (start != end) {
522 (*start)->when += xdelta;
523 (*start)->value += ydelta;
528 save_state (_("event range adjusted"));
534 maybe_signal_changed ();
538 AutomationList::modify (iterator iter, double when, double val)
540 /* note: we assume higher level logic is in place to avoid this
541 reordering the time-order of control events in the list. ie. all
542 points after *iter are later than when.
546 Glib::Mutex::Lock lm (lock);
547 (*iter)->when = when;
548 (*iter)->value = val;
550 save_state (_("event adjusted"));
556 maybe_signal_changed ();
559 std::pair<AutomationList::iterator,AutomationList::iterator>
560 AutomationList::control_points_adjacent (double xval)
562 Glib::Mutex::Lock lm (lock);
565 ControlEvent cp (xval, 0.0f);
566 std::pair<iterator,iterator> ret;
568 ret.first = events.end();
569 ret.second = events.end();
571 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
573 if (ret.first == events.end()) {
574 if ((*i)->when >= xval) {
575 if (i != events.begin()) {
584 if ((*i)->when > xval) {
594 AutomationList::freeze ()
600 AutomationList::thaw ()
603 if (changed_when_thawed) {
604 StateChanged(Change(0)); /* EMIT SIGNAL */
609 AutomationList::state_factory (std::string why) const
611 State* state = new State (why);
613 for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
614 state->events.push_back (point_factory (**x));
621 AutomationList::restore_state (StateManager::State& state)
624 Glib::Mutex::Lock lm (lock);
625 State* lstate = dynamic_cast<State*> (&state);
628 for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
629 events.push_back (point_factory (**x));
637 AutomationList::get_memento () const
639 return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
643 AutomationList::set_max_xval (double x)
649 AutomationList::mark_dirty ()
651 lookup_cache.left = -1;
656 AutomationList::truncate_end (double last_coordinate)
659 Glib::Mutex::Lock lm (lock);
660 ControlEvent cp (last_coordinate, 0);
661 list<ControlEvent*>::reverse_iterator i;
664 if (events.empty()) {
665 fatal << _("programming error:")
666 << "AutomationList::truncate_end() called on an empty list"
672 if (last_coordinate == events.back()->when) {
676 if (last_coordinate > events.back()->when) {
681 iterator foo = events.begin();
684 if (foo == events.end()) {
686 } else if (++foo == events.end()) {
693 /* less than 2 points: add a new point */
694 events.push_back (point_factory (last_coordinate, events.back()->value));
697 /* more than 2 points: check to see if the last 2 values
698 are equal. if so, just move the position of the
699 last point. otherwise, add a new point.
702 iterator penultimate = events.end();
703 --penultimate; /* points at last point */
704 --penultimate; /* points at the penultimate point */
706 if (events.back()->value == (*penultimate)->value) {
707 events.back()->when = last_coordinate;
709 events.push_back (point_factory (last_coordinate, events.back()->value));
717 last_val = unlocked_eval (last_coordinate);
718 last_val = max ((double) min_yval, last_val);
719 last_val = min ((double) max_yval, last_val);
723 /* make i point to the last control point */
727 /* now go backwards, removing control points that are
728 beyond the new last coordinate.
731 uint32_t sz = events.size();
733 while (i != events.rend() && sz > 2) {
734 list<ControlEvent*>::reverse_iterator tmp;
739 if ((*i)->when < last_coordinate) {
743 events.erase (i.base());
749 events.back()->when = last_coordinate;
750 events.back()->value = last_val;
753 reposition_for_rt_add (0);
757 maybe_signal_changed ();
761 AutomationList::truncate_start (double overall_length)
764 Glib::Mutex::Lock lm (lock);
765 AutomationList::iterator i;
766 double first_legal_value;
767 double first_legal_coordinate;
769 if (events.empty()) {
770 fatal << _("programming error:")
771 << "AutomationList::truncate_start() called on an empty list"
777 if (overall_length == events.back()->when) {
778 /* no change in overall length */
782 if (overall_length > events.back()->when) {
784 /* growing at front: duplicate first point. shift all others */
786 double shift = overall_length - events.back()->when;
789 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
795 /* less than 2 points: add a new point */
796 events.push_front (point_factory (0, events.front()->value));
800 /* more than 2 points: check to see if the first 2 values
801 are equal. if so, just move the position of the
802 first point. otherwise, add a new point.
805 iterator second = events.begin();
806 ++second; /* points at the second point */
808 if (events.front()->value == (*second)->value) {
809 /* first segment is flat, just move start point back to zero */
810 events.front()->when = 0;
812 /* leave non-flat segment in place, add a new leading point. */
813 events.push_front (point_factory (0, events.front()->value));
819 /* shrinking at front */
821 first_legal_coordinate = events.back()->when - overall_length;
822 first_legal_value = unlocked_eval (first_legal_coordinate);
823 first_legal_value = max (min_yval, first_legal_value);
824 first_legal_value = min (max_yval, first_legal_value);
826 /* remove all events earlier than the new "front" */
830 while (i != events.end() && !events.empty()) {
831 list<ControlEvent*>::iterator tmp;
836 if ((*i)->when > first_legal_coordinate) {
846 /* shift all remaining points left to keep their same
850 for (i = events.begin(); i != events.end(); ++i) {
851 (*i)->when -= first_legal_coordinate;
854 /* add a new point for the interpolated new value */
856 events.push_front (point_factory (0, first_legal_value));
859 reposition_for_rt_add (0);
864 maybe_signal_changed ();
868 AutomationList::unlocked_eval (double x)
870 return shared_eval (x);
874 AutomationList::shared_eval (double x)
876 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
882 npoints = events.size();
886 return default_value;
889 if (x >= events.front()->when) {
890 return events.front()->value;
892 // return default_value;
893 return events.front()->value;
897 if (x >= events.back()->when) {
898 return events.back()->value;
899 } else if (x == events.front()->when) {
900 return events.front()->value;
901 } else if (x < events.front()->when) {
902 // return default_value;
903 return events.front()->value;
906 lpos = events.front()->when;
907 lval = events.front()->value;
908 upos = events.back()->when;
909 uval = events.back()->value;
911 /* linear interpolation betweeen the two points
914 fraction = (double) (x - lpos) / (double) (upos - lpos);
915 return lval + (fraction * (uval - lval));
919 if (x >= events.back()->when) {
920 return events.back()->value;
921 } else if (x == events.front()->when) {
922 return events.front()->value;
923 } else if (x < events.front()->when) {
924 // return default_value;
925 return events.front()->value;
928 return multipoint_eval (x);
934 AutomationList::multipoint_eval (double x)
936 pair<AutomationList::iterator,AutomationList::iterator> range;
941 /* only do the range lookup if x is in a different range than last time
942 this was called (or if the lookup cache has been marked "dirty" (left<0)
945 if ((lookup_cache.left < 0) ||
946 ((lookup_cache.left > x) ||
947 (lookup_cache.range.first == events.end()) ||
948 ((*lookup_cache.range.second)->when < x))) {
950 ControlEvent cp (x, 0);
953 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
956 range = lookup_cache.range;
958 if (range.first == range.second) {
960 /* x does not exist within the list as a control point */
962 lookup_cache.left = x;
964 if (range.first != events.begin()) {
966 lpos = (*range.first)->when;
967 lval = (*range.first)->value;
969 /* we're before the first point */
970 // return default_value;
971 return events.front()->value;
974 if (range.second == events.end()) {
975 /* we're after the last point */
976 return events.back()->value;
979 upos = (*range.second)->when;
980 uval = (*range.second)->value;
982 /* linear interpolation betweeen the two points
986 fraction = (double) (x - lpos) / (double) (upos - lpos);
987 return lval + (fraction * (uval - lval));
991 /* x is a control point in the data */
992 lookup_cache.left = -1;
993 return (*range.first)->value;
997 AutomationList::cut (iterator start, iterator end)
999 AutomationList* nal = new AutomationList (default_value);
1002 Glib::Mutex::Lock lm (lock);
1004 for (iterator x = start; x != end; ) {
1010 nal->events.push_back (point_factory (**x));
1013 reposition_for_rt_add (0);
1021 maybe_signal_changed ();
1027 AutomationList::cut_copy_clear (double start, double end, int op)
1029 AutomationList* nal = new AutomationList (default_value);
1031 ControlEvent cp (start, 0.0);
1033 bool changed = false;
1036 Glib::Mutex::Lock lm (lock);
1038 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1043 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1045 if (op != 2 && (*s)->when != start) {
1046 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1049 for (iterator x = s; x != e; ) {
1057 /* adjust new points to be relative to start, which
1058 has been set to zero.
1062 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1072 if (op != 2 && nal->events.back()->when != end - start) {
1073 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1077 reposition_for_rt_add (0);
1079 save_state (_("cut/copy/clear"));
1086 maybe_signal_changed ();
1093 AutomationList::copy (iterator start, iterator end)
1095 AutomationList* nal = new AutomationList (default_value);
1098 Glib::Mutex::Lock lm (lock);
1100 for (iterator x = start; x != end; ) {
1106 nal->events.push_back (point_factory (**x));
1112 save_state (_("copy"));
1120 AutomationList::cut (double start, double end)
1122 return cut_copy_clear (start, end, 0);
1126 AutomationList::copy (double start, double end)
1128 return cut_copy_clear (start, end, 1);
1132 AutomationList::clear (double start, double end)
1134 (void) cut_copy_clear (start, end, 2);
1138 AutomationList::paste (AutomationList& alist, double pos, float times)
1140 if (alist.events.empty()) {
1145 Glib::Mutex::Lock lm (lock);
1149 ControlEvent cp (pos, 0.0);
1152 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1154 for (iterator i = alist.begin();i != alist.end(); ++i) {
1155 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1156 end = (*i)->when + pos;
1160 /* move all points after the insertion along the timeline by
1164 while (where != events.end()) {
1166 if ((*where)->when <= end) {
1169 events.erase(where);
1177 reposition_for_rt_add (0);
1180 save_state (_("paste"));
1186 maybe_signal_changed ();
1191 AutomationList::point_factory (double when, double val) const
1193 return new ControlEvent (when, val);
1197 AutomationList::point_factory (const ControlEvent& other) const
1199 return new ControlEvent (other);
1203 AutomationList::store_state (XMLNode& node) const
1205 LocaleGuard lg (X_("POSIX"));
1207 for (const_iterator i = const_begin(); i != const_end(); ++i) {
1210 XMLNode *pointnode = new XMLNode ("point");
1212 snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*i)->when));
1213 pointnode->add_property ("x", buf);
1214 snprintf (buf, sizeof (buf), "%.12g", (*i)->value);
1215 pointnode->add_property ("y", buf);
1217 node.add_child_nocopy (*pointnode);
1222 AutomationList::load_state (const XMLNode& node)
1224 const XMLNodeList& elist = node.children();
1225 XMLNodeConstIterator i;
1232 for (i = elist.begin(); i != elist.end(); ++i) {
1234 if ((prop = (*i)->property ("x")) == 0) {
1235 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1238 x = atoi (prop->value().c_str());
1240 if ((prop = (*i)->property ("y")) == 0) {
1241 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1244 y = atof (prop->value().c_str());