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"));
68 AutomationListCreated(this);
71 AutomationList::AutomationList (const AutomationList& other)
74 changed_when_thawed = false;
75 _style = other._style;
76 min_yval = other.min_yval;
77 max_yval = other.max_yval;
78 max_xval = other.max_xval;
79 default_value = other.default_value;
80 _state = other._state;
81 _touching = other._touching;
83 rt_insertion_point = events.end();
84 no_state = other.no_state;
85 lookup_cache.left = -1;
86 lookup_cache.range.first = events.end();
88 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
89 /* we have to use other point_factory() because
90 its virtual and we're in a constructor.
92 events.push_back (other.point_factory (**i));
96 AutomationListCreated(this);
99 AutomationList::AutomationList (const AutomationList& other, double start, double end)
102 changed_when_thawed = false;
103 _style = other._style;
104 min_yval = other.min_yval;
105 max_yval = other.max_yval;
106 max_xval = other.max_xval;
107 default_value = other.default_value;
108 _state = other._state;
109 _touching = other._touching;
111 rt_insertion_point = events.end();
112 no_state = other.no_state;
113 lookup_cache.left = -1;
114 lookup_cache.range.first = events.end();
116 /* now grab the relevant points, and shift them back if necessary */
118 AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
120 if (!section->empty()) {
121 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
122 events.push_back (other.point_factory ((*i)->when, (*i)->value));
129 AutomationListCreated(this);
132 AutomationList::~AutomationList()
134 std::set<ControlEvent*> all_events;
135 AutomationList::State* asp;
137 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
138 all_events.insert (*x);
141 for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
143 if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
145 for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
146 all_events.insert (*x);
151 for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
157 AutomationList::operator== (const AutomationList& other)
159 return events == other.events;
163 AutomationList::operator= (const AutomationList& other)
165 if (this != &other) {
169 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
170 events.push_back (point_factory (**i));
173 min_yval = other.min_yval;
174 max_yval = other.max_yval;
175 max_xval = other.max_xval;
176 default_value = other.default_value;
179 maybe_signal_changed ();
186 AutomationList::maybe_signal_changed ()
191 changed_when_thawed = true;
193 StateChanged (Change (0));
198 AutomationList::set_automation_state (AutoState s)
202 automation_state_changed (); /* EMIT SIGNAL */
207 AutomationList::set_automation_style (AutoStyle s)
211 automation_style_changed (); /* EMIT SIGNAL */
216 AutomationList::start_touch ()
223 AutomationList::stop_touch ()
230 AutomationList::clear ()
233 Glib::Mutex::Lock lm (lock);
236 save_state (_("cleared"));
241 maybe_signal_changed ();
245 AutomationList::x_scale (double factor)
247 Glib::Mutex::Lock lm (lock);
252 AutomationList::extend_to (double when)
254 Glib::Mutex::Lock lm (lock);
255 if (events.empty() || events.back()->when == when) {
258 double factor = when / events.back()->when;
263 void AutomationList::_x_scale (double factor)
265 for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
266 (*i)->when = floor ((*i)->when * factor);
269 save_state ("x-scaled");
274 AutomationList::reposition_for_rt_add (double when)
276 rt_insertion_point = events.end();
279 #define last_rt_insertion_point rt_insertion_point
282 AutomationList::rt_add (double when, double value)
284 /* this is for automation recording */
286 if ((_state & Touch) && !_touching) {
290 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
293 Glib::Mutex::Lock lm (lock);
297 ControlEvent cp (when, 0.0);
300 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
302 /* we have a previous insertion point, so we should delete
303 everything between it and the position where we are going
304 to insert this point.
307 iterator after = last_rt_insertion_point;
309 if (++after != events.end()) {
310 iterator far = after;
312 while (far != events.end()) {
313 if ((*far)->when > when) {
321 last_rt_insertion_point = where;
323 if((*where)->when == when) {
324 (*where)->value = value;
328 where = events.erase (after, far);
337 iterator previous = last_rt_insertion_point;
340 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
341 (*last_rt_insertion_point)->when = when;
348 where = lower_bound (events.begin(), events.end(), &cp, cmp);
350 if (where != events.end()) {
351 if ((*where)->when == when) {
352 (*where)->value = value;
359 last_rt_insertion_point = events.insert (where, point_factory (when, value));
366 maybe_signal_changed ();
369 #undef last_rt_insertion_point
372 AutomationList::add (double when, double value, bool for_loading)
374 /* this is for graphical editing and loading data from storage */
377 Glib::Mutex::Lock lm (lock);
379 ControlEvent cp (when, 0.0f);
381 iterator insertion_point;
383 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
385 /* only one point allowed per time point */
387 if ((*insertion_point)->when == when) {
388 (*insertion_point)->value = value;
393 if ((*insertion_point)->when >= when) {
400 events.insert (insertion_point, point_factory (when, value));
401 reposition_for_rt_add (0);
407 if (!no_state && !for_loading) {
408 save_state (_("added event"));
413 maybe_signal_changed ();
418 AutomationList::erase (AutomationList::iterator i)
421 Glib::Mutex::Lock lm (lock);
423 reposition_for_rt_add (0);
425 save_state (_("removed event"));
429 maybe_signal_changed ();
433 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
436 Glib::Mutex::Lock lm (lock);
437 events.erase (start, end);
438 reposition_for_rt_add (0);
440 save_state (_("removed multiple events"));
444 maybe_signal_changed ();
448 AutomationList::reset_range (double start, double endt)
453 Glib::Mutex::Lock lm (lock);
455 ControlEvent cp (start, 0.0f);
459 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
462 e = upper_bound (events.begin(), events.end(), &cp, cmp);
464 for (iterator i = s; i != e; ++i) {
465 (*i)->value = default_value;
471 save_state (_("removed range"));
479 maybe_signal_changed ();
484 AutomationList::erase_range (double start, double endt)
489 Glib::Mutex::Lock lm (lock);
491 ControlEvent cp (start, 0.0f);
495 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
497 e = upper_bound (events.begin(), events.end(), &cp, cmp);
499 reposition_for_rt_add (0);
502 save_state (_("removed range"));
510 maybe_signal_changed ();
515 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
517 /* note: we assume higher level logic is in place to avoid this
518 reordering the time-order of control events in the list. ie. all
519 points after end are later than (end)->when.
523 Glib::Mutex::Lock lm (lock);
525 while (start != end) {
526 (*start)->when += xdelta;
527 (*start)->value += ydelta;
532 save_state (_("event range adjusted"));
538 maybe_signal_changed ();
542 AutomationList::modify (iterator iter, double when, double val)
544 /* note: we assume higher level logic is in place to avoid this
545 reordering the time-order of control events in the list. ie. all
546 points after *iter are later than when.
550 Glib::Mutex::Lock lm (lock);
551 (*iter)->when = when;
552 (*iter)->value = val;
554 save_state (_("event adjusted"));
560 maybe_signal_changed ();
563 std::pair<AutomationList::iterator,AutomationList::iterator>
564 AutomationList::control_points_adjacent (double xval)
566 Glib::Mutex::Lock lm (lock);
569 ControlEvent cp (xval, 0.0f);
570 std::pair<iterator,iterator> ret;
572 ret.first = events.end();
573 ret.second = events.end();
575 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
577 if (ret.first == events.end()) {
578 if ((*i)->when >= xval) {
579 if (i != events.begin()) {
588 if ((*i)->when > xval) {
598 AutomationList::freeze ()
604 AutomationList::thaw ()
607 if (changed_when_thawed) {
608 StateChanged(Change(0)); /* EMIT SIGNAL */
613 AutomationList::state_factory (std::string why) const
615 State* state = new State (why);
617 for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
618 state->events.push_back (point_factory (**x));
625 AutomationList::restore_state (StateManager::State& state)
628 Glib::Mutex::Lock lm (lock);
629 State* lstate = dynamic_cast<State*> (&state);
632 for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
633 events.push_back (point_factory (**x));
641 AutomationList::get_memento () const
643 return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
647 AutomationList::set_max_xval (double x)
653 AutomationList::mark_dirty ()
655 lookup_cache.left = -1;
660 AutomationList::truncate_end (double last_coordinate)
663 Glib::Mutex::Lock lm (lock);
664 ControlEvent cp (last_coordinate, 0);
665 list<ControlEvent*>::reverse_iterator i;
668 if (events.empty()) {
669 fatal << _("programming error:")
670 << "AutomationList::truncate_end() called on an empty list"
676 if (last_coordinate == events.back()->when) {
680 if (last_coordinate > events.back()->when) {
685 iterator foo = events.begin();
688 if (foo == events.end()) {
690 } else if (++foo == events.end()) {
697 /* less than 2 points: add a new point */
698 events.push_back (point_factory (last_coordinate, events.back()->value));
701 /* more than 2 points: check to see if the last 2 values
702 are equal. if so, just move the position of the
703 last point. otherwise, add a new point.
706 iterator penultimate = events.end();
707 --penultimate; /* points at last point */
708 --penultimate; /* points at the penultimate point */
710 if (events.back()->value == (*penultimate)->value) {
711 events.back()->when = last_coordinate;
713 events.push_back (point_factory (last_coordinate, events.back()->value));
721 last_val = unlocked_eval (last_coordinate);
722 last_val = max ((double) min_yval, last_val);
723 last_val = min ((double) max_yval, last_val);
727 /* make i point to the last control point */
731 /* now go backwards, removing control points that are
732 beyond the new last coordinate.
735 uint32_t sz = events.size();
737 while (i != events.rend() && sz > 2) {
738 list<ControlEvent*>::reverse_iterator tmp;
743 if ((*i)->when < last_coordinate) {
747 events.erase (i.base());
753 events.back()->when = last_coordinate;
754 events.back()->value = last_val;
757 reposition_for_rt_add (0);
761 maybe_signal_changed ();
765 AutomationList::truncate_start (double overall_length)
768 Glib::Mutex::Lock lm (lock);
769 AutomationList::iterator i;
770 double first_legal_value;
771 double first_legal_coordinate;
773 if (events.empty()) {
774 fatal << _("programming error:")
775 << "AutomationList::truncate_start() called on an empty list"
781 if (overall_length == events.back()->when) {
782 /* no change in overall length */
786 if (overall_length > events.back()->when) {
788 /* growing at front: duplicate first point. shift all others */
790 double shift = overall_length - events.back()->when;
793 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
799 /* less than 2 points: add a new point */
800 events.push_front (point_factory (0, events.front()->value));
804 /* more than 2 points: check to see if the first 2 values
805 are equal. if so, just move the position of the
806 first point. otherwise, add a new point.
809 iterator second = events.begin();
810 ++second; /* points at the second point */
812 if (events.front()->value == (*second)->value) {
813 /* first segment is flat, just move start point back to zero */
814 events.front()->when = 0;
816 /* leave non-flat segment in place, add a new leading point. */
817 events.push_front (point_factory (0, events.front()->value));
823 /* shrinking at front */
825 first_legal_coordinate = events.back()->when - overall_length;
826 first_legal_value = unlocked_eval (first_legal_coordinate);
827 first_legal_value = max (min_yval, first_legal_value);
828 first_legal_value = min (max_yval, first_legal_value);
830 /* remove all events earlier than the new "front" */
834 while (i != events.end() && !events.empty()) {
835 list<ControlEvent*>::iterator tmp;
840 if ((*i)->when > first_legal_coordinate) {
850 /* shift all remaining points left to keep their same
854 for (i = events.begin(); i != events.end(); ++i) {
855 (*i)->when -= first_legal_coordinate;
858 /* add a new point for the interpolated new value */
860 events.push_front (point_factory (0, first_legal_value));
863 reposition_for_rt_add (0);
868 maybe_signal_changed ();
872 AutomationList::unlocked_eval (double x)
874 return shared_eval (x);
878 AutomationList::shared_eval (double x)
880 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
886 npoints = events.size();
890 return default_value;
893 if (x >= events.front()->when) {
894 return events.front()->value;
896 // return default_value;
897 return events.front()->value;
901 if (x >= events.back()->when) {
902 return events.back()->value;
903 } else if (x == events.front()->when) {
904 return events.front()->value;
905 } else if (x < events.front()->when) {
906 // return default_value;
907 return events.front()->value;
910 lpos = events.front()->when;
911 lval = events.front()->value;
912 upos = events.back()->when;
913 uval = events.back()->value;
915 /* linear interpolation betweeen the two points
918 fraction = (double) (x - lpos) / (double) (upos - lpos);
919 return lval + (fraction * (uval - lval));
923 if (x >= events.back()->when) {
924 return events.back()->value;
925 } else if (x == events.front()->when) {
926 return events.front()->value;
927 } else if (x < events.front()->when) {
928 // return default_value;
929 return events.front()->value;
932 return multipoint_eval (x);
938 AutomationList::multipoint_eval (double x)
940 pair<AutomationList::iterator,AutomationList::iterator> range;
945 /* only do the range lookup if x is in a different range than last time
946 this was called (or if the lookup cache has been marked "dirty" (left<0)
949 if ((lookup_cache.left < 0) ||
950 ((lookup_cache.left > x) ||
951 (lookup_cache.range.first == events.end()) ||
952 ((*lookup_cache.range.second)->when < x))) {
954 ControlEvent cp (x, 0);
957 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
960 range = lookup_cache.range;
962 if (range.first == range.second) {
964 /* x does not exist within the list as a control point */
966 lookup_cache.left = x;
968 if (range.first != events.begin()) {
970 lpos = (*range.first)->when;
971 lval = (*range.first)->value;
973 /* we're before the first point */
974 // return default_value;
975 return events.front()->value;
978 if (range.second == events.end()) {
979 /* we're after the last point */
980 return events.back()->value;
983 upos = (*range.second)->when;
984 uval = (*range.second)->value;
986 /* linear interpolation betweeen the two points
990 fraction = (double) (x - lpos) / (double) (upos - lpos);
991 return lval + (fraction * (uval - lval));
995 /* x is a control point in the data */
996 lookup_cache.left = -1;
997 return (*range.first)->value;
1001 AutomationList::cut (iterator start, iterator end)
1003 AutomationList* nal = new AutomationList (default_value);
1006 Glib::Mutex::Lock lm (lock);
1008 for (iterator x = start; x != end; ) {
1014 nal->events.push_back (point_factory (**x));
1017 reposition_for_rt_add (0);
1025 maybe_signal_changed ();
1031 AutomationList::cut_copy_clear (double start, double end, int op)
1033 AutomationList* nal = new AutomationList (default_value);
1035 ControlEvent cp (start, 0.0);
1037 bool changed = false;
1040 Glib::Mutex::Lock lm (lock);
1042 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
1047 e = upper_bound (events.begin(), events.end(), &cp, cmp);
1049 if (op != 2 && (*s)->when != start) {
1050 nal->events.push_back (point_factory (0, unlocked_eval (start)));
1053 for (iterator x = s; x != e; ) {
1061 /* adjust new points to be relative to start, which
1062 has been set to zero.
1066 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1076 if (op != 2 && nal->events.back()->when != end - start) {
1077 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1081 reposition_for_rt_add (0);
1083 save_state (_("cut/copy/clear"));
1090 maybe_signal_changed ();
1097 AutomationList::copy (iterator start, iterator end)
1099 AutomationList* nal = new AutomationList (default_value);
1102 Glib::Mutex::Lock lm (lock);
1104 for (iterator x = start; x != end; ) {
1110 nal->events.push_back (point_factory (**x));
1116 save_state (_("copy"));
1124 AutomationList::cut (double start, double end)
1126 return cut_copy_clear (start, end, 0);
1130 AutomationList::copy (double start, double end)
1132 return cut_copy_clear (start, end, 1);
1136 AutomationList::clear (double start, double end)
1138 (void) cut_copy_clear (start, end, 2);
1142 AutomationList::paste (AutomationList& alist, double pos, float times)
1144 if (alist.events.empty()) {
1149 Glib::Mutex::Lock lm (lock);
1153 ControlEvent cp (pos, 0.0);
1156 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1158 for (iterator i = alist.begin();i != alist.end(); ++i) {
1159 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1160 end = (*i)->when + pos;
1164 /* move all points after the insertion along the timeline by
1168 while (where != events.end()) {
1170 if ((*where)->when <= end) {
1173 events.erase(where);
1181 reposition_for_rt_add (0);
1184 save_state (_("paste"));
1190 maybe_signal_changed ();
1195 AutomationList::point_factory (double when, double val) const
1197 return new ControlEvent (when, val);
1201 AutomationList::point_factory (const ControlEvent& other) const
1203 return new ControlEvent (other);
1207 AutomationList::store_state (XMLNode& node) const
1209 LocaleGuard lg (X_("POSIX"));
1211 for (const_iterator i = const_begin(); i != const_end(); ++i) {
1214 XMLNode *pointnode = new XMLNode ("point");
1216 snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*i)->when));
1217 pointnode->add_property ("x", buf);
1218 snprintf (buf, sizeof (buf), "%.12g", (*i)->value);
1219 pointnode->add_property ("y", buf);
1221 node.add_child_nocopy (*pointnode);
1226 AutomationList::load_state (const XMLNode& node)
1228 const XMLNodeList& elist = node.children();
1229 XMLNodeConstIterator i;
1236 for (i = elist.begin(); i != elist.end(); ++i) {
1238 if ((prop = (*i)->property ("x")) == 0) {
1239 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1242 x = atoi (prop->value().c_str());
1244 if ((prop = (*i)->property ("y")) == 0) {
1245 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1248 y = atof (prop->value().c_str());
1254 XMLNode &AutomationList::get_state ()
1256 XMLNode *node = new XMLNode("AutomationList");
1261 int AutomationList::set_state(const XMLNode &s)