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();
67 save_state (_("initial"));
70 AutomationListCreated(this);
73 AutomationList::AutomationList (const AutomationList& other)
76 changed_when_thawed = false;
77 _style = other._style;
78 min_yval = other.min_yval;
79 max_yval = other.max_yval;
80 max_xval = other.max_xval;
81 default_value = other.default_value;
82 _state = other._state;
83 _touching = other._touching;
85 rt_insertion_point = events.end();
86 no_state = other.no_state;
87 lookup_cache.left = -1;
88 lookup_cache.range.first = events.end();
90 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
91 /* we have to use other point_factory() because
92 its virtual and we're in a constructor.
94 events.push_back (other.point_factory (**i));
98 AutomationListCreated(this);
101 AutomationList::AutomationList (const AutomationList& other, double start, double end)
104 changed_when_thawed = false;
105 _style = other._style;
106 min_yval = other.min_yval;
107 max_yval = other.max_yval;
108 max_xval = other.max_xval;
109 default_value = other.default_value;
110 _state = other._state;
111 _touching = other._touching;
113 rt_insertion_point = events.end();
114 no_state = other.no_state;
115 lookup_cache.left = -1;
116 lookup_cache.range.first = events.end();
118 /* now grab the relevant points, and shift them back if necessary */
120 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
122 if (!section->empty()) {
123 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
124 events.push_back (other.point_factory ((*i)->when, (*i)->value));
131 AutomationListCreated(this);
134 AutomationList::~AutomationList()
136 std::set<ControlEvent*> all_events;
137 AutomationList::State* asp;
139 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
140 all_events.insert (*x);
143 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
145 if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
147 for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
148 all_events.insert (*x);
153 for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
159 AutomationList::operator== (const AutomationList& other)
161 return events == other.events;
165 AutomationList::operator= (const AutomationList& other)
167 if (this != &other) {
171 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
172 events.push_back (point_factory (**i));
175 min_yval = other.min_yval;
176 max_yval = other.max_yval;
177 max_xval = other.max_xval;
178 default_value = other.default_value;
181 maybe_signal_changed ();
188 AutomationList::maybe_signal_changed ()
193 changed_when_thawed = true;
195 StateChanged (Change (0));
200 AutomationList::set_automation_state (AutoState s)
204 automation_state_changed (); /* EMIT SIGNAL */
209 AutomationList::set_automation_style (AutoStyle s)
213 automation_style_changed (); /* EMIT SIGNAL */
218 AutomationList::start_touch ()
225 AutomationList::stop_touch ()
232 AutomationList::clear ()
235 Glib::Mutex::Lock lm (lock);
238 save_state (_("cleared"));
243 maybe_signal_changed ();
247 AutomationList::x_scale (double factor)
249 Glib::Mutex::Lock lm (lock);
254 AutomationList::extend_to (double when)
256 Glib::Mutex::Lock lm (lock);
257 if (events.empty() || events.back()->when == when) {
260 double factor = when / events.back()->when;
265 void AutomationList::_x_scale (double factor)
267 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
268 (*i)->when = floor ((*i)->when * factor);
271 save_state ("x-scaled");
276 AutomationList::reposition_for_rt_add (double when)
278 rt_insertion_point = events.end();
281 #define last_rt_insertion_point rt_insertion_point
284 AutomationList::rt_add (double when, double value)
286 /* this is for automation recording */
288 if ((_state & Touch) && !_touching) {
292 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
295 Glib::Mutex::Lock lm (lock);
299 ControlEvent cp (when, 0.0);
302 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
304 /* we have a previous insertion point, so we should delete
305 everything between it and the position where we are going
306 to insert this point.
309 iterator after = last_rt_insertion_point;
311 if (++after != events.end()) {
312 iterator far = after;
314 while (far != events.end()) {
315 if ((*far)->when > when) {
323 last_rt_insertion_point = where;
325 if((*where)->when == when) {
326 (*where)->value = value;
330 where = events.erase (after, far);
339 iterator previous = last_rt_insertion_point;
342 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
343 (*last_rt_insertion_point)->when = when;
350 where = lower_bound (events.begin(), events.end(), &cp, cmp);
352 if (where != events.end()) {
353 if ((*where)->when == when) {
354 (*where)->value = value;
361 last_rt_insertion_point = events.insert (where, point_factory (when, value));
368 maybe_signal_changed ();
371 #undef last_rt_insertion_point
374 AutomationList::add (double when, double value, bool for_loading)
376 /* this is for graphical editing and loading data from storage */
379 Glib::Mutex::Lock lm (lock);
381 ControlEvent cp (when, 0.0f);
383 iterator insertion_point;
385 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
387 /* only one point allowed per time point */
389 if ((*insertion_point)->when == when) {
390 (*insertion_point)->value = value;
395 if ((*insertion_point)->when >= when) {
402 events.insert (insertion_point, point_factory (when, value));
403 reposition_for_rt_add (0);
409 if (!no_state && !for_loading) {
410 save_state (_("added event"));
415 maybe_signal_changed ();
420 AutomationList::erase (AutomationList::iterator i)
423 Glib::Mutex::Lock lm (lock);
425 reposition_for_rt_add (0);
427 save_state (_("removed event"));
431 maybe_signal_changed ();
435 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
438 Glib::Mutex::Lock lm (lock);
439 events.erase (start, end);
440 reposition_for_rt_add (0);
442 save_state (_("removed multiple events"));
446 maybe_signal_changed ();
450 AutomationList::reset_range (double start, double endt)
455 Glib::Mutex::Lock lm (lock);
457 ControlEvent cp (start, 0.0f);
461 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
464 e = upper_bound (events.begin(), events.end(), &cp, cmp);
466 for (iterator i = s; i != e; ++i) {
467 (*i)->value = default_value;
473 save_state (_("removed range"));
481 maybe_signal_changed ();
486 AutomationList::erase_range (double start, double endt)
491 Glib::Mutex::Lock lm (lock);
493 ControlEvent cp (start, 0.0f);
497 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
499 e = upper_bound (events.begin(), events.end(), &cp, cmp);
501 reposition_for_rt_add (0);
504 save_state (_("removed range"));
512 maybe_signal_changed ();
517 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
519 /* note: we assume higher level logic is in place to avoid this
520 reordering the time-order of control events in the list. ie. all
521 points after end are later than (end)->when.
525 Glib::Mutex::Lock lm (lock);
527 while (start != end) {
528 (*start)->when += xdelta;
529 (*start)->value += ydelta;
534 save_state (_("event range adjusted"));
540 maybe_signal_changed ();
544 AutomationList::modify (iterator iter, double when, double val)
546 /* note: we assume higher level logic is in place to avoid this
547 reordering the time-order of control events in the list. ie. all
548 points after *iter are later than when.
552 Glib::Mutex::Lock lm (lock);
553 (*iter)->when = when;
554 (*iter)->value = val;
556 save_state (_("event adjusted"));
562 maybe_signal_changed ();
565 std::pair<AutomationList::iterator,AutomationList::iterator>
566 AutomationList::control_points_adjacent (double xval)
568 Glib::Mutex::Lock lm (lock);
571 ControlEvent cp (xval, 0.0f);
572 std::pair<iterator,iterator> ret;
574 ret.first = events.end();
575 ret.second = events.end();
577 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
579 if (ret.first == events.end()) {
580 if ((*i)->when >= xval) {
581 if (i != events.begin()) {
590 if ((*i)->when > xval) {
600 AutomationList::freeze ()
606 AutomationList::thaw ()
609 if (changed_when_thawed) {
610 StateChanged(Change(0)); /* EMIT SIGNAL */
615 AutomationList::state_factory (std::string why) const
617 State* state = new State (why);
619 for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
620 state->events.push_back (point_factory (**x));
627 AutomationList::restore_state (StateManager::State& state)
630 Glib::Mutex::Lock lm (lock);
631 State* lstate = dynamic_cast<State*> (&state);
634 for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
635 events.push_back (point_factory (**x));
643 AutomationList::get_memento () const
645 return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
649 AutomationList::set_max_xval (double x)
655 AutomationList::mark_dirty ()
657 lookup_cache.left = -1;
662 AutomationList::truncate_end (double last_coordinate)
665 Glib::Mutex::Lock lm (lock);
666 ControlEvent cp (last_coordinate, 0);
667 list<ControlEvent*>::reverse_iterator i;
670 if (events.empty()) {
671 fatal << _("programming error:")
672 << "AutomationList::truncate_end() called on an empty list"
678 if (last_coordinate == events.back()->when) {
682 if (last_coordinate > events.back()->when) {
687 iterator foo = events.begin();
690 if (foo == events.end()) {
692 } else if (++foo == events.end()) {
699 /* less than 2 points: add a new point */
700 events.push_back (point_factory (last_coordinate, events.back()->value));
703 /* more than 2 points: check to see if the last 2 values
704 are equal. if so, just move the position of the
705 last point. otherwise, add a new point.
708 iterator penultimate = events.end();
709 --penultimate; /* points at last point */
710 --penultimate; /* points at the penultimate point */
712 if (events.back()->value == (*penultimate)->value) {
713 events.back()->when = last_coordinate;
715 events.push_back (point_factory (last_coordinate, events.back()->value));
723 last_val = unlocked_eval (last_coordinate);
724 last_val = max ((double) min_yval, last_val);
725 last_val = min ((double) max_yval, last_val);
729 /* make i point to the last control point */
733 /* now go backwards, removing control points that are
734 beyond the new last coordinate.
737 uint32_t sz = events.size();
739 while (i != events.rend() && sz > 2) {
740 list<ControlEvent*>::reverse_iterator tmp;
745 if ((*i)->when < last_coordinate) {
749 events.erase (i.base());
755 events.back()->when = last_coordinate;
756 events.back()->value = last_val;
759 reposition_for_rt_add (0);
763 maybe_signal_changed ();
767 AutomationList::truncate_start (double overall_length)
770 Glib::Mutex::Lock lm (lock);
771 AutomationList::iterator i;
772 double first_legal_value;
773 double first_legal_coordinate;
775 if (events.empty()) {
776 fatal << _("programming error:")
777 << "AutomationList::truncate_start() called on an empty list"
783 if (overall_length == events.back()->when) {
784 /* no change in overall length */
788 if (overall_length > events.back()->when) {
790 /* growing at front: duplicate first point. shift all others */
792 double shift = overall_length - events.back()->when;
795 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
801 /* less than 2 points: add a new point */
802 events.push_front (point_factory (0, events.front()->value));
806 /* more than 2 points: check to see if the first 2 values
807 are equal. if so, just move the position of the
808 first point. otherwise, add a new point.
811 iterator second = events.begin();
812 ++second; /* points at the second point */
814 if (events.front()->value == (*second)->value) {
815 /* first segment is flat, just move start point back to zero */
816 events.front()->when = 0;
818 /* leave non-flat segment in place, add a new leading point. */
819 events.push_front (point_factory (0, events.front()->value));
825 /* shrinking at front */
827 first_legal_coordinate = events.back()->when - overall_length;
828 first_legal_value = unlocked_eval (first_legal_coordinate);
829 first_legal_value = max (min_yval, first_legal_value);
830 first_legal_value = min (max_yval, first_legal_value);
832 /* remove all events earlier than the new "front" */
836 while (i != events.end() && !events.empty()) {
837 list<ControlEvent*>::iterator tmp;
842 if ((*i)->when > first_legal_coordinate) {
852 /* shift all remaining points left to keep their same
856 for (i = events.begin(); i != events.end(); ++i) {
857 (*i)->when -= first_legal_coordinate;
860 /* add a new point for the interpolated new value */
862 events.push_front (point_factory (0, first_legal_value));
865 reposition_for_rt_add (0);
870 maybe_signal_changed ();
874 AutomationList::unlocked_eval (double x)
876 return shared_eval (x);
880 AutomationList::shared_eval (double x)
882 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
888 npoints = events.size();
892 return default_value;
895 if (x >= events.front()->when) {
896 return events.front()->value;
898 // return default_value;
899 return events.front()->value;
903 if (x >= events.back()->when) {
904 return events.back()->value;
905 } else if (x == events.front()->when) {
906 return events.front()->value;
907 } else if (x < events.front()->when) {
908 // return default_value;
909 return events.front()->value;
912 lpos = events.front()->when;
913 lval = events.front()->value;
914 upos = events.back()->when;
915 uval = events.back()->value;
917 /* linear interpolation betweeen the two points
920 fraction = (double) (x - lpos) / (double) (upos - lpos);
921 return lval + (fraction * (uval - lval));
925 if (x >= events.back()->when) {
926 return events.back()->value;
927 } else if (x == events.front()->when) {
928 return events.front()->value;
929 } else if (x < events.front()->when) {
930 // return default_value;
931 return events.front()->value;
934 return multipoint_eval (x);
940 AutomationList::multipoint_eval (double x)
942 pair<AutomationList::iterator,AutomationList::iterator> range;
947 /* only do the range lookup if x is in a different range than last time
948 this was called (or if the lookup cache has been marked "dirty" (left<0)
951 if ((lookup_cache.left < 0) ||
952 ((lookup_cache.left > x) ||
953 (lookup_cache.range.first == events.end()) ||
954 ((*lookup_cache.range.second)->when < x))) {
956 ControlEvent cp (x, 0);
959 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
962 range = lookup_cache.range;
964 if (range.first == range.second) {
966 /* x does not exist within the list as a control point */
968 lookup_cache.left = x;
970 if (range.first != events.begin()) {
972 lpos = (*range.first)->when;
973 lval = (*range.first)->value;
975 /* we're before the first point */
976 // return default_value;
977 return events.front()->value;
980 if (range.second == events.end()) {
981 /* we're after the last point */
982 return events.back()->value;
985 upos = (*range.second)->when;
986 uval = (*range.second)->value;
988 /* linear interpolation betweeen the two points
992 fraction = (double) (x - lpos) / (double) (upos - lpos);
993 return lval + (fraction * (uval - lval));
997 /* x is a control point in the data */
998 lookup_cache.left = -1;
999 return (*range.first)->value;
1003 AutomationList::cut (iterator start, iterator end)
1005 AutomationList* nal = new AutomationList (default_value);
1008 Glib::Mutex::Lock lm (lock);
1010 for (iterator x = start; x != end; ) {
1016 nal->events.push_back (point_factory (**x));
1019 reposition_for_rt_add (0);
1027 maybe_signal_changed ();
1033 AutomationList::cut_copy_clear (double start, double end, int op)
1035 AutomationList* nal = new AutomationList (default_value);
1037 ControlEvent cp (start, 0.0);
1039 bool changed = false;
1042 Glib::Mutex::Lock lm (lock);
1044 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1049 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1051 if (op != 2 && (*s)->when != start) {
1052 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1055 for (iterator x = s; x != e; ) {
1063 /* adjust new points to be relative to start, which
1064 has been set to zero.
1068 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1078 if (op != 2 && nal->events.back()->when != end - start) {
1079 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1083 reposition_for_rt_add (0);
1085 save_state (_("cut/copy/clear"));
1092 maybe_signal_changed ();
1099 AutomationList::copy (iterator start, iterator end)
1101 AutomationList* nal = new AutomationList (default_value);
1104 Glib::Mutex::Lock lm (lock);
1106 for (iterator x = start; x != end; ) {
1112 nal->events.push_back (point_factory (**x));
1118 save_state (_("copy"));
1126 AutomationList::cut (double start, double end)
1128 return cut_copy_clear (start, end, 0);
1132 AutomationList::copy (double start, double end)
1134 return cut_copy_clear (start, end, 1);
1138 AutomationList::clear (double start, double end)
1140 (void) cut_copy_clear (start, end, 2);
1144 AutomationList::paste (AutomationList& alist, double pos, float times)
1146 if (alist.events.empty()) {
1151 Glib::Mutex::Lock lm (lock);
1155 ControlEvent cp (pos, 0.0);
1158 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1160 for (iterator i = alist.begin();i != alist.end(); ++i) {
1161 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1162 end = (*i)->when + pos;
1166 /* move all points after the insertion along the timeline by
1170 while (where != events.end()) {
1172 if ((*where)->when <= end) {
1175 events.erase(where);
1183 reposition_for_rt_add (0);
1186 save_state (_("paste"));
1192 maybe_signal_changed ();
1197 AutomationList::point_factory (double when, double val) const
1199 return new ControlEvent (when, val);
1203 AutomationList::point_factory (const ControlEvent& other) const
1205 return new ControlEvent (other);
1209 AutomationList::store_state (XMLNode& node) const
1211 LocaleGuard lg (X_("POSIX"));
1213 for (const_iterator i = const_begin(); i != const_end(); ++i) {
1216 XMLNode *pointnode = new XMLNode ("point");
1218 snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*i)->when));
1219 pointnode->add_property ("x", buf);
1220 snprintf (buf, sizeof (buf), "%.12g", (*i)->value);
1221 pointnode->add_property ("y", buf);
1223 node.add_child_nocopy (*pointnode);
1228 AutomationList::load_state (const XMLNode& node)
1230 const XMLNodeList& elist = node.children();
1231 XMLNodeConstIterator i;
1238 for (i = elist.begin(); i != elist.end(); ++i) {
1240 if ((prop = (*i)->property ("x")) == 0) {
1241 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1244 x = atoi (prop->value().c_str());
1246 if ((prop = (*i)->property ("y")) == 0) {
1247 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1250 y = atof (prop->value().c_str());
1256 XMLNode &AutomationList::get_state ()
1258 XMLNode *node = new XMLNode("AutomationList");
1263 int AutomationList::set_state(const XMLNode &s)