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 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
39 static void dumpit (const AutomationList& al, string prefix = "")
41 cerr << prefix << &al << endl;
42 for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
43 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
49 AutomationList::AutomationList (double defval, bool with_state)
52 changed_when_thawed = false;
56 no_state = with_state;
59 max_xval = 0; // means "no limit"
60 default_value = defval;
62 rt_insertion_point = events.end();
63 lookup_cache.left = -1;
64 lookup_cache.range.first = events.end();
68 save_state (_("initial"));
72 AutomationListCreated(this);
75 AutomationList::AutomationList (const AutomationList& other)
78 changed_when_thawed = false;
79 _style = other._style;
80 min_yval = other.min_yval;
81 max_yval = other.max_yval;
82 max_xval = other.max_xval;
83 default_value = other.default_value;
84 _state = other._state;
85 _touching = other._touching;
87 rt_insertion_point = events.end();
88 no_state = other.no_state;
89 lookup_cache.left = -1;
90 lookup_cache.range.first = events.end();
92 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
93 /* we have to use other point_factory() because
94 its virtual and we're in a constructor.
96 events.push_back (other.point_factory (**i));
100 AutomationListCreated(this);
103 AutomationList::AutomationList (const AutomationList& other, double start, double end)
106 changed_when_thawed = false;
107 _style = other._style;
108 min_yval = other.min_yval;
109 max_yval = other.max_yval;
110 max_xval = other.max_xval;
111 default_value = other.default_value;
112 _state = other._state;
113 _touching = other._touching;
115 rt_insertion_point = events.end();
116 no_state = other.no_state;
117 lookup_cache.left = -1;
118 lookup_cache.range.first = events.end();
120 /* now grab the relevant points, and shift them back if necessary */
122 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
124 if (!section->empty()) {
125 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
126 events.push_back (other.point_factory ((*i)->when, (*i)->value));
133 AutomationListCreated(this);
136 AutomationList::~AutomationList()
140 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
145 std::set<ControlEvent*> all_events;
146 AutomationList::State* asp;
148 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
150 if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
152 for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
153 all_events.insert (*x);
158 for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
165 AutomationList::operator== (const AutomationList& other)
167 return events == other.events;
171 AutomationList::operator= (const AutomationList& other)
173 if (this != &other) {
177 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
178 events.push_back (point_factory (**i));
181 min_yval = other.min_yval;
182 max_yval = other.max_yval;
183 max_xval = other.max_xval;
184 default_value = other.default_value;
187 maybe_signal_changed ();
194 AutomationList::maybe_signal_changed ()
199 changed_when_thawed = true;
201 StateChanged (Change (0));
206 AutomationList::set_automation_state (AutoState s)
210 automation_state_changed (); /* EMIT SIGNAL */
215 AutomationList::set_automation_style (AutoStyle s)
219 automation_style_changed (); /* EMIT SIGNAL */
224 AutomationList::start_touch ()
231 AutomationList::stop_touch ()
238 AutomationList::clear ()
241 Glib::Mutex::Lock lm (lock);
245 save_state (_("cleared"));
251 maybe_signal_changed ();
255 AutomationList::x_scale (double factor)
257 Glib::Mutex::Lock lm (lock);
262 AutomationList::extend_to (double when)
264 Glib::Mutex::Lock lm (lock);
265 if (events.empty() || events.back()->when == when) {
268 double factor = when / events.back()->when;
273 void AutomationList::_x_scale (double factor)
275 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
276 (*i)->when = floor ((*i)->when * factor);
280 save_state ("x-scaled");
286 AutomationList::reposition_for_rt_add (double when)
288 rt_insertion_point = events.end();
291 #define last_rt_insertion_point rt_insertion_point
294 AutomationList::rt_add (double when, double value)
296 /* this is for automation recording */
298 if ((_state & Touch) && !_touching) {
302 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
305 Glib::Mutex::Lock lm (lock);
309 ControlEvent cp (when, 0.0);
312 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
314 /* we have a previous insertion point, so we should delete
315 everything between it and the position where we are going
316 to insert this point.
319 iterator after = last_rt_insertion_point;
321 if (++after != events.end()) {
322 iterator far = after;
324 while (far != events.end()) {
325 if ((*far)->when > when) {
333 last_rt_insertion_point = where;
335 if((*where)->when == when) {
336 (*where)->value = value;
340 where = events.erase (after, far);
349 iterator previous = last_rt_insertion_point;
352 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
353 (*last_rt_insertion_point)->when = when;
360 where = lower_bound (events.begin(), events.end(), &cp, cmp);
362 if (where != events.end()) {
363 if ((*where)->when == when) {
364 (*where)->value = value;
371 last_rt_insertion_point = events.insert (where, point_factory (when, value));
378 maybe_signal_changed ();
381 #undef last_rt_insertion_point
384 AutomationList::add (double when, double value, bool for_loading)
386 /* this is for graphical editing and loading data from storage */
389 Glib::Mutex::Lock lm (lock);
391 ControlEvent cp (when, 0.0f);
393 iterator insertion_point;
395 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
397 /* only one point allowed per time point */
399 if ((*insertion_point)->when == when) {
400 (*insertion_point)->value = value;
405 if ((*insertion_point)->when >= when) {
412 events.insert (insertion_point, point_factory (when, value));
413 reposition_for_rt_add (0);
419 if (!no_state && !for_loading) {
421 save_state (_("added event"));
427 maybe_signal_changed ();
432 AutomationList::erase (AutomationList::iterator i)
435 Glib::Mutex::Lock lm (lock);
437 reposition_for_rt_add (0);
440 save_state (_("removed event"));
445 maybe_signal_changed ();
449 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
452 Glib::Mutex::Lock lm (lock);
453 events.erase (start, end);
454 reposition_for_rt_add (0);
457 save_state (_("removed multiple events"));
462 maybe_signal_changed ();
466 AutomationList::reset_range (double start, double endt)
471 Glib::Mutex::Lock lm (lock);
473 ControlEvent cp (start, 0.0f);
477 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
480 e = upper_bound (events.begin(), events.end(), &cp, cmp);
482 for (iterator i = s; i != e; ++i) {
483 (*i)->value = default_value;
490 save_state (_("removed range"));
499 maybe_signal_changed ();
504 AutomationList::erase_range (double start, double endt)
509 Glib::Mutex::Lock lm (lock);
511 ControlEvent cp (start, 0.0f);
515 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
517 e = upper_bound (events.begin(), events.end(), &cp, cmp);
519 reposition_for_rt_add (0);
523 save_state (_("removed range"));
532 maybe_signal_changed ();
537 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
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 end are later than (end)->when.
545 Glib::Mutex::Lock lm (lock);
547 while (start != end) {
548 (*start)->when += xdelta;
549 (*start)->value += ydelta;
555 save_state (_("event range adjusted"));
562 maybe_signal_changed ();
566 AutomationList::modify (iterator iter, double when, double val)
568 /* note: we assume higher level logic is in place to avoid this
569 reordering the time-order of control events in the list. ie. all
570 points after *iter are later than when.
574 Glib::Mutex::Lock lm (lock);
575 (*iter)->when = when;
576 (*iter)->value = val;
579 save_state (_("event adjusted"));
586 maybe_signal_changed ();
589 std::pair<AutomationList::iterator,AutomationList::iterator>
590 AutomationList::control_points_adjacent (double xval)
592 Glib::Mutex::Lock lm (lock);
595 ControlEvent cp (xval, 0.0f);
596 std::pair<iterator,iterator> ret;
598 ret.first = events.end();
599 ret.second = events.end();
601 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
603 if (ret.first == events.end()) {
604 if ((*i)->when >= xval) {
605 if (i != events.begin()) {
614 if ((*i)->when > xval) {
624 AutomationList::freeze ()
630 AutomationList::thaw ()
633 if (changed_when_thawed) {
634 StateChanged(Change(0)); /* EMIT SIGNAL */
640 AutomationList::state_factory (std::string why) const
642 State* state = new State (why);
644 for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
645 state->events.push_back (point_factory (**x));
652 AutomationList::restore_state (StateManager::State& state)
655 Glib::Mutex::Lock lm (lock);
656 State* lstate = dynamic_cast<State*> (&state);
659 for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
660 events.push_back (point_factory (**x));
668 AutomationList::get_memento () const
670 return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
675 AutomationList::set_max_xval (double x)
681 AutomationList::mark_dirty ()
683 lookup_cache.left = -1;
688 AutomationList::truncate_end (double last_coordinate)
691 Glib::Mutex::Lock lm (lock);
692 ControlEvent cp (last_coordinate, 0);
693 list<ControlEvent*>::reverse_iterator i;
696 if (events.empty()) {
697 fatal << _("programming error:")
698 << "AutomationList::truncate_end() called on an empty list"
704 if (last_coordinate == events.back()->when) {
708 if (last_coordinate > events.back()->when) {
713 iterator foo = events.begin();
716 if (foo == events.end()) {
718 } else if (++foo == events.end()) {
725 /* less than 2 points: add a new point */
726 events.push_back (point_factory (last_coordinate, events.back()->value));
729 /* more than 2 points: check to see if the last 2 values
730 are equal. if so, just move the position of the
731 last point. otherwise, add a new point.
734 iterator penultimate = events.end();
735 --penultimate; /* points at last point */
736 --penultimate; /* points at the penultimate point */
738 if (events.back()->value == (*penultimate)->value) {
739 events.back()->when = last_coordinate;
741 events.push_back (point_factory (last_coordinate, events.back()->value));
749 last_val = unlocked_eval (last_coordinate);
750 last_val = max ((double) min_yval, last_val);
751 last_val = min ((double) max_yval, last_val);
755 /* make i point to the last control point */
759 /* now go backwards, removing control points that are
760 beyond the new last coordinate.
763 uint32_t sz = events.size();
765 while (i != events.rend() && sz > 2) {
766 list<ControlEvent*>::reverse_iterator tmp;
771 if ((*i)->when < last_coordinate) {
775 events.erase (i.base());
781 events.back()->when = last_coordinate;
782 events.back()->value = last_val;
785 reposition_for_rt_add (0);
789 maybe_signal_changed ();
793 AutomationList::truncate_start (double overall_length)
796 Glib::Mutex::Lock lm (lock);
797 AutomationList::iterator i;
798 double first_legal_value;
799 double first_legal_coordinate;
801 if (events.empty()) {
802 fatal << _("programming error:")
803 << "AutomationList::truncate_start() called on an empty list"
809 if (overall_length == events.back()->when) {
810 /* no change in overall length */
814 if (overall_length > events.back()->when) {
816 /* growing at front: duplicate first point. shift all others */
818 double shift = overall_length - events.back()->when;
821 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
827 /* less than 2 points: add a new point */
828 events.push_front (point_factory (0, events.front()->value));
832 /* more than 2 points: check to see if the first 2 values
833 are equal. if so, just move the position of the
834 first point. otherwise, add a new point.
837 iterator second = events.begin();
838 ++second; /* points at the second point */
840 if (events.front()->value == (*second)->value) {
841 /* first segment is flat, just move start point back to zero */
842 events.front()->when = 0;
844 /* leave non-flat segment in place, add a new leading point. */
845 events.push_front (point_factory (0, events.front()->value));
851 /* shrinking at front */
853 first_legal_coordinate = events.back()->when - overall_length;
854 first_legal_value = unlocked_eval (first_legal_coordinate);
855 first_legal_value = max (min_yval, first_legal_value);
856 first_legal_value = min (max_yval, first_legal_value);
858 /* remove all events earlier than the new "front" */
862 while (i != events.end() && !events.empty()) {
863 list<ControlEvent*>::iterator tmp;
868 if ((*i)->when > first_legal_coordinate) {
878 /* shift all remaining points left to keep their same
882 for (i = events.begin(); i != events.end(); ++i) {
883 (*i)->when -= first_legal_coordinate;
886 /* add a new point for the interpolated new value */
888 events.push_front (point_factory (0, first_legal_value));
891 reposition_for_rt_add (0);
896 maybe_signal_changed ();
900 AutomationList::unlocked_eval (double x)
902 return shared_eval (x);
906 AutomationList::shared_eval (double x)
908 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
914 npoints = events.size();
918 return default_value;
921 if (x >= events.front()->when) {
922 return events.front()->value;
924 // return default_value;
925 return events.front()->value;
929 if (x >= events.back()->when) {
930 return events.back()->value;
931 } else if (x == events.front()->when) {
932 return events.front()->value;
933 } else if (x < events.front()->when) {
934 // return default_value;
935 return events.front()->value;
938 lpos = events.front()->when;
939 lval = events.front()->value;
940 upos = events.back()->when;
941 uval = events.back()->value;
943 /* linear interpolation betweeen the two points
946 fraction = (double) (x - lpos) / (double) (upos - lpos);
947 return lval + (fraction * (uval - lval));
951 if (x >= events.back()->when) {
952 return events.back()->value;
953 } else if (x == events.front()->when) {
954 return events.front()->value;
955 } else if (x < events.front()->when) {
956 // return default_value;
957 return events.front()->value;
960 return multipoint_eval (x);
966 AutomationList::multipoint_eval (double x)
968 pair<AutomationList::iterator,AutomationList::iterator> range;
973 /* only do the range lookup if x is in a different range than last time
974 this was called (or if the lookup cache has been marked "dirty" (left<0)
977 if ((lookup_cache.left < 0) ||
978 ((lookup_cache.left > x) ||
979 (lookup_cache.range.first == events.end()) ||
980 ((*lookup_cache.range.second)->when < x))) {
982 ControlEvent cp (x, 0);
985 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
988 range = lookup_cache.range;
990 if (range.first == range.second) {
992 /* x does not exist within the list as a control point */
994 lookup_cache.left = x;
996 if (range.first != events.begin()) {
998 lpos = (*range.first)->when;
999 lval = (*range.first)->value;
1001 /* we're before the first point */
1002 // return default_value;
1003 return events.front()->value;
1006 if (range.second == events.end()) {
1007 /* we're after the last point */
1008 return events.back()->value;
1011 upos = (*range.second)->when;
1012 uval = (*range.second)->value;
1014 /* linear interpolation betweeen the two points
1018 fraction = (double) (x - lpos) / (double) (upos - lpos);
1019 return lval + (fraction * (uval - lval));
1023 /* x is a control point in the data */
1024 lookup_cache.left = -1;
1025 return (*range.first)->value;
1029 AutomationList::cut (iterator start, iterator end)
1031 AutomationList* nal = new AutomationList (default_value);
1034 Glib::Mutex::Lock lm (lock);
1036 for (iterator x = start; x != end; ) {
1042 nal->events.push_back (point_factory (**x));
1045 reposition_for_rt_add (0);
1053 maybe_signal_changed ();
1059 AutomationList::cut_copy_clear (double start, double end, int op)
1061 AutomationList* nal = new AutomationList (default_value);
1063 ControlEvent cp (start, 0.0);
1065 bool changed = false;
1068 Glib::Mutex::Lock lm (lock);
1070 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1075 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1077 if (op != 2 && (*s)->when != start) {
1078 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1081 for (iterator x = s; x != e; ) {
1089 /* adjust new points to be relative to start, which
1090 has been set to zero.
1094 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1104 if (op != 2 && nal->events.back()->when != end - start) {
1105 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1109 reposition_for_rt_add (0);
1111 #ifdef STATE_MANAGER
1112 save_state (_("cut/copy/clear"));
1120 maybe_signal_changed ();
1127 AutomationList::copy (iterator start, iterator end)
1129 AutomationList* nal = new AutomationList (default_value);
1132 Glib::Mutex::Lock lm (lock);
1134 for (iterator x = start; x != end; ) {
1140 nal->events.push_back (point_factory (**x));
1146 #ifdef STATE_MANAGER
1147 save_state (_("copy"));
1156 AutomationList::cut (double start, double end)
1158 return cut_copy_clear (start, end, 0);
1162 AutomationList::copy (double start, double end)
1164 return cut_copy_clear (start, end, 1);
1168 AutomationList::clear (double start, double end)
1170 (void) cut_copy_clear (start, end, 2);
1174 AutomationList::paste (AutomationList& alist, double pos, float times)
1176 if (alist.events.empty()) {
1181 Glib::Mutex::Lock lm (lock);
1185 ControlEvent cp (pos, 0.0);
1188 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1190 for (iterator i = alist.begin();i != alist.end(); ++i) {
1191 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1192 end = (*i)->when + pos;
1196 /* move all points after the insertion along the timeline by
1200 while (where != events.end()) {
1202 if ((*where)->when <= end) {
1205 events.erase(where);
1213 reposition_for_rt_add (0);
1216 #ifdef STATE_MANAGER
1217 save_state (_("paste"));
1224 maybe_signal_changed ();
1229 AutomationList::point_factory (double when, double val) const
1231 return new ControlEvent (when, val);
1235 AutomationList::point_factory (const ControlEvent& other) const
1237 return new ControlEvent (other);
1241 AutomationList::store_state (XMLNode& node) const
1243 LocaleGuard lg (X_("POSIX"));
1245 for (const_iterator i = const_begin(); i != const_end(); ++i) {
1248 XMLNode *pointnode = new XMLNode ("point");
1250 snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*i)->when));
1251 pointnode->add_property ("x", buf);
1252 snprintf (buf, sizeof (buf), "%.12g", (*i)->value);
1253 pointnode->add_property ("y", buf);
1255 node.add_child_nocopy (*pointnode);
1260 AutomationList::load_state (const XMLNode& node)
1262 const XMLNodeList& elist = node.children();
1263 XMLNodeConstIterator i;
1272 for (i = elist.begin(); i != elist.end(); ++i) {
1274 if ((prop = (*i)->property ("x")) == 0) {
1275 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1278 x = atoi (prop->value().c_str());
1280 if ((prop = (*i)->property ("y")) == 0) {
1281 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1284 y = atof (prop->value().c_str());
1292 XMLNode &AutomationList::get_state ()
1294 XMLNode *node = new XMLNode("AutomationList");
1299 int AutomationList::set_state(const XMLNode &s)