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.
27 #include <sigc++/bind.h>
28 #include <ardour/automation_event.h>
29 #include <pbd/convert.h>
34 using namespace ARDOUR;
38 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
41 static void dumpit (const AutomationList& al, string prefix = "")
43 cerr << prefix << &al << endl;
44 for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
45 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
51 AutomationList::AutomationList (double defval)
54 changed_when_thawed = false;
60 max_xval = 0; // means "no limit"
61 default_value = defval;
63 rt_insertion_point = events.end();
64 lookup_cache.left = -1;
65 lookup_cache.range.first = events.end();
67 AutomationListCreated(this);
70 AutomationList::AutomationList (const AutomationList& other)
73 changed_when_thawed = false;
74 _style = other._style;
75 min_yval = other.min_yval;
76 max_yval = other.max_yval;
77 max_xval = other.max_xval;
78 default_value = other.default_value;
79 _state = other._state;
80 _touching = other._touching;
82 rt_insertion_point = events.end();
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));
94 AutomationListCreated(this);
97 AutomationList::AutomationList (const AutomationList& other, double start, double end)
100 changed_when_thawed = false;
101 _style = other._style;
102 min_yval = other.min_yval;
103 max_yval = other.max_yval;
104 max_xval = other.max_xval;
105 default_value = other.default_value;
106 _state = other._state;
107 _touching = other._touching;
109 rt_insertion_point = events.end();
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));
127 AutomationListCreated(this);
130 AutomationList::AutomationList (const XMLNode& node)
133 changed_when_thawed = false;
137 max_xval = 0; // means "no limit"
141 rt_insertion_point = events.end();
142 lookup_cache.left = -1;
143 lookup_cache.range.first = events.end();
147 AutomationListCreated(this);
150 AutomationList::~AutomationList()
154 for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
160 AutomationList::operator== (const AutomationList& other)
162 return events == other.events;
166 AutomationList::operator= (const AutomationList& other)
168 if (this != &other) {
172 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
173 events.push_back (point_factory (**i));
176 min_yval = other.min_yval;
177 max_yval = other.max_yval;
178 max_xval = other.max_xval;
179 default_value = other.default_value;
182 maybe_signal_changed ();
189 AutomationList::maybe_signal_changed ()
194 changed_when_thawed = true;
201 AutomationList::set_automation_state (AutoState s)
205 automation_state_changed (); /* EMIT SIGNAL */
210 AutomationList::set_automation_style (AutoStyle s)
214 automation_style_changed (); /* EMIT SIGNAL */
219 AutomationList::start_touch ()
226 AutomationList::stop_touch ()
233 AutomationList::clear ()
236 Glib::Mutex::Lock lm (lock);
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);
273 AutomationList::reposition_for_rt_add (double when)
275 rt_insertion_point = events.end();
278 #define last_rt_insertion_point rt_insertion_point
281 AutomationList::rt_add (double when, double value)
283 /* this is for automation recording */
285 if ((_state & Touch) && !_touching) {
289 // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
292 Glib::Mutex::Lock lm (lock);
296 ControlEvent cp (when, 0.0);
299 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
301 /* we have a previous insertion point, so we should delete
302 everything between it and the position where we are going
303 to insert this point.
306 iterator after = last_rt_insertion_point;
308 if (++after != events.end()) {
309 iterator far = after;
311 while (far != events.end()) {
312 if ((*far)->when > when) {
320 last_rt_insertion_point = where;
322 if((*where)->when == when) {
323 (*where)->value = value;
327 where = events.erase (after, far);
336 iterator previous = last_rt_insertion_point;
339 if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
340 (*last_rt_insertion_point)->when = when;
347 where = lower_bound (events.begin(), events.end(), &cp, cmp);
349 if (where != events.end()) {
350 if ((*where)->when == when) {
351 (*where)->value = value;
358 last_rt_insertion_point = events.insert (where, point_factory (when, value));
365 maybe_signal_changed ();
369 AutomationList::fast_simple_add (double when, double value)
371 /* to be used only for loading pre-sorted data from saved state */
372 events.insert (events.end(), point_factory (when, value));
375 #undef last_rt_insertion_point
378 AutomationList::add (double when, double value)
380 /* this is for graphical editing */
383 Glib::Mutex::Lock lm (lock);
385 ControlEvent cp (when, 0.0f);
387 iterator insertion_point;
389 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
391 /* only one point allowed per time point */
393 if ((*insertion_point)->when == when) {
394 (*insertion_point)->value = value;
399 if ((*insertion_point)->when >= when) {
406 events.insert (insertion_point, point_factory (when, value));
407 reposition_for_rt_add (0);
414 maybe_signal_changed ();
418 AutomationList::erase (AutomationList::iterator i)
421 Glib::Mutex::Lock lm (lock);
423 reposition_for_rt_add (0);
426 maybe_signal_changed ();
430 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
433 Glib::Mutex::Lock lm (lock);
434 events.erase (start, end);
435 reposition_for_rt_add (0);
438 maybe_signal_changed ();
442 AutomationList::reset_range (double start, double endt)
447 Glib::Mutex::Lock lm (lock);
449 ControlEvent cp (start, 0.0f);
453 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
456 e = upper_bound (events.begin(), events.end(), &cp, cmp);
458 for (iterator i = s; i != e; ++i) {
459 (*i)->value = default_value;
469 maybe_signal_changed ();
474 AutomationList::erase_range (double start, double endt)
479 Glib::Mutex::Lock lm (lock);
481 ControlEvent cp (start, 0.0f);
485 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
487 e = upper_bound (events.begin(), events.end(), &cp, cmp);
489 reposition_for_rt_add (0);
497 maybe_signal_changed ();
502 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
504 /* note: we assume higher level logic is in place to avoid this
505 reordering the time-order of control events in the list. ie. all
506 points after end are later than (end)->when.
510 Glib::Mutex::Lock lm (lock);
512 while (start != end) {
513 (*start)->when += xdelta;
514 (*start)->value += ydelta;
521 maybe_signal_changed ();
525 AutomationList::modify (iterator iter, double when, double val)
527 /* note: we assume higher level logic is in place to avoid this
528 reordering the time-order of control events in the list. ie. all
529 points after *iter are later than when.
533 Glib::Mutex::Lock lm (lock);
534 (*iter)->when = when;
535 (*iter)->value = val;
539 maybe_signal_changed ();
542 std::pair<AutomationList::iterator,AutomationList::iterator>
543 AutomationList::control_points_adjacent (double xval)
545 Glib::Mutex::Lock lm (lock);
548 ControlEvent cp (xval, 0.0f);
549 std::pair<iterator,iterator> ret;
551 ret.first = events.end();
552 ret.second = events.end();
554 for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
556 if (ret.first == events.end()) {
557 if ((*i)->when >= xval) {
558 if (i != events.begin()) {
567 if ((*i)->when > xval) {
577 AutomationList::freeze ()
583 AutomationList::thaw ()
586 if (changed_when_thawed) {
587 StateChanged(); /* EMIT SIGNAL */
592 AutomationList::set_max_xval (double x)
598 AutomationList::mark_dirty ()
600 lookup_cache.left = -1;
605 AutomationList::truncate_end (double last_coordinate)
608 Glib::Mutex::Lock lm (lock);
609 ControlEvent cp (last_coordinate, 0);
610 list<ControlEvent*>::reverse_iterator i;
613 if (events.empty()) {
617 if (last_coordinate == events.back()->when) {
621 if (last_coordinate > events.back()->when) {
626 iterator foo = events.begin();
629 if (foo == events.end()) {
631 } else if (++foo == events.end()) {
638 /* less than 2 points: add a new point */
639 events.push_back (point_factory (last_coordinate, events.back()->value));
642 /* more than 2 points: check to see if the last 2 values
643 are equal. if so, just move the position of the
644 last point. otherwise, add a new point.
647 iterator penultimate = events.end();
648 --penultimate; /* points at last point */
649 --penultimate; /* points at the penultimate point */
651 if (events.back()->value == (*penultimate)->value) {
652 events.back()->when = last_coordinate;
654 events.push_back (point_factory (last_coordinate, events.back()->value));
662 last_val = unlocked_eval (last_coordinate);
663 last_val = max ((double) min_yval, last_val);
664 last_val = min ((double) max_yval, last_val);
668 /* make i point to the last control point */
672 /* now go backwards, removing control points that are
673 beyond the new last coordinate.
676 uint32_t sz = events.size();
678 while (i != events.rend() && sz > 2) {
679 list<ControlEvent*>::reverse_iterator tmp;
684 if ((*i)->when < last_coordinate) {
688 events.erase (i.base());
694 events.back()->when = last_coordinate;
695 events.back()->value = last_val;
698 reposition_for_rt_add (0);
702 maybe_signal_changed ();
706 AutomationList::truncate_start (double overall_length)
709 Glib::Mutex::Lock lm (lock);
710 AutomationList::iterator i;
711 double first_legal_value;
712 double first_legal_coordinate;
714 if (events.empty()) {
715 fatal << _("programming error:")
716 << "AutomationList::truncate_start() called on an empty list"
722 if (overall_length == events.back()->when) {
723 /* no change in overall length */
727 if (overall_length > events.back()->when) {
729 /* growing at front: duplicate first point. shift all others */
731 double shift = overall_length - events.back()->when;
734 for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
740 /* less than 2 points: add a new point */
741 events.push_front (point_factory (0, events.front()->value));
745 /* more than 2 points: check to see if the first 2 values
746 are equal. if so, just move the position of the
747 first point. otherwise, add a new point.
750 iterator second = events.begin();
751 ++second; /* points at the second point */
753 if (events.front()->value == (*second)->value) {
754 /* first segment is flat, just move start point back to zero */
755 events.front()->when = 0;
757 /* leave non-flat segment in place, add a new leading point. */
758 events.push_front (point_factory (0, events.front()->value));
764 /* shrinking at front */
766 first_legal_coordinate = events.back()->when - overall_length;
767 first_legal_value = unlocked_eval (first_legal_coordinate);
768 first_legal_value = max (min_yval, first_legal_value);
769 first_legal_value = min (max_yval, first_legal_value);
771 /* remove all events earlier than the new "front" */
775 while (i != events.end() && !events.empty()) {
776 list<ControlEvent*>::iterator tmp;
781 if ((*i)->when > first_legal_coordinate) {
791 /* shift all remaining points left to keep their same
795 for (i = events.begin(); i != events.end(); ++i) {
796 (*i)->when -= first_legal_coordinate;
799 /* add a new point for the interpolated new value */
801 events.push_front (point_factory (0, first_legal_value));
804 reposition_for_rt_add (0);
809 maybe_signal_changed ();
813 AutomationList::unlocked_eval (double x)
815 return shared_eval (x);
819 AutomationList::shared_eval (double x)
821 pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
827 npoints = events.size();
831 return default_value;
834 if (x >= events.front()->when) {
835 return events.front()->value;
837 // return default_value;
838 return events.front()->value;
842 if (x >= events.back()->when) {
843 return events.back()->value;
844 } else if (x == events.front()->when) {
845 return events.front()->value;
846 } else if (x < events.front()->when) {
847 // return default_value;
848 return events.front()->value;
851 lpos = events.front()->when;
852 lval = events.front()->value;
853 upos = events.back()->when;
854 uval = events.back()->value;
856 /* linear interpolation betweeen the two points
859 fraction = (double) (x - lpos) / (double) (upos - lpos);
860 return lval + (fraction * (uval - lval));
864 if (x >= events.back()->when) {
865 return events.back()->value;
866 } else if (x == events.front()->when) {
867 return events.front()->value;
868 } else if (x < events.front()->when) {
869 // return default_value;
870 return events.front()->value;
873 return multipoint_eval (x);
879 AutomationList::multipoint_eval (double x)
881 pair<AutomationList::iterator,AutomationList::iterator> range;
886 /* only do the range lookup if x is in a different range than last time
887 this was called (or if the lookup cache has been marked "dirty" (left<0)
890 if ((lookup_cache.left < 0) ||
891 ((lookup_cache.left > x) ||
892 (lookup_cache.range.first == events.end()) ||
893 ((*lookup_cache.range.second)->when < x))) {
895 ControlEvent cp (x, 0);
898 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
901 range = lookup_cache.range;
903 if (range.first == range.second) {
905 /* x does not exist within the list as a control point */
907 lookup_cache.left = x;
909 if (range.first != events.begin()) {
911 lpos = (*range.first)->when;
912 lval = (*range.first)->value;
914 /* we're before the first point */
915 // return default_value;
916 return events.front()->value;
919 if (range.second == events.end()) {
920 /* we're after the last point */
921 return events.back()->value;
924 upos = (*range.second)->when;
925 uval = (*range.second)->value;
927 /* linear interpolation betweeen the two points
931 fraction = (double) (x - lpos) / (double) (upos - lpos);
932 return lval + (fraction * (uval - lval));
936 /* x is a control point in the data */
937 lookup_cache.left = -1;
938 return (*range.first)->value;
942 AutomationList::cut (iterator start, iterator end)
944 AutomationList* nal = new AutomationList (default_value);
947 Glib::Mutex::Lock lm (lock);
949 for (iterator x = start; x != end; ) {
955 nal->events.push_back (point_factory (**x));
958 reposition_for_rt_add (0);
966 maybe_signal_changed ();
972 AutomationList::cut_copy_clear (double start, double end, int op)
974 AutomationList* nal = new AutomationList (default_value);
976 ControlEvent cp (start, 0.0);
978 bool changed = false;
981 Glib::Mutex::Lock lm (lock);
983 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
988 e = upper_bound (events.begin(), events.end(), &cp, cmp);
990 if (op != 2 && (*s)->when != start) {
991 nal->events.push_back (point_factory (0, unlocked_eval (start)));
994 for (iterator x = s; x != e; ) {
1002 /* adjust new points to be relative to start, which
1003 has been set to zero.
1007 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
1017 if (op != 2 && nal->events.back()->when != end - start) {
1018 nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1022 reposition_for_rt_add (0);
1028 maybe_signal_changed ();
1035 AutomationList::copy (iterator start, iterator end)
1037 AutomationList* nal = new AutomationList (default_value);
1040 Glib::Mutex::Lock lm (lock);
1042 for (iterator x = start; x != end; ) {
1048 nal->events.push_back (point_factory (**x));
1058 AutomationList::cut (double start, double end)
1060 return cut_copy_clear (start, end, 0);
1064 AutomationList::copy (double start, double end)
1066 return cut_copy_clear (start, end, 1);
1070 AutomationList::clear (double start, double end)
1072 (void) cut_copy_clear (start, end, 2);
1076 AutomationList::paste (AutomationList& alist, double pos, float times)
1078 if (alist.events.empty()) {
1083 Glib::Mutex::Lock lm (lock);
1087 ControlEvent cp (pos, 0.0);
1090 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1092 for (iterator i = alist.begin();i != alist.end(); ++i) {
1093 events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1094 end = (*i)->when + pos;
1098 /* move all points after the insertion along the timeline by
1102 while (where != events.end()) {
1104 if ((*where)->when <= end) {
1107 events.erase(where);
1115 reposition_for_rt_add (0);
1119 maybe_signal_changed ();
1124 AutomationList::point_factory (double when, double val) const
1126 return new ControlEvent (when, val);
1130 AutomationList::point_factory (const ControlEvent& other) const
1132 return new ControlEvent (other);
1136 AutomationList::get_state ()
1138 return state (true);
1142 AutomationList::state (bool full)
1144 XMLNode* root = new XMLNode (X_("AutomationList"));
1146 LocaleGuard lg (X_("POSIX"));
1148 root->add_property ("id", _id.to_s());
1150 snprintf (buf, sizeof (buf), "%.12g", default_value);
1151 root->add_property ("default", buf);
1152 snprintf (buf, sizeof (buf), "%.12g", min_yval);
1153 root->add_property ("min_yval", buf);
1154 snprintf (buf, sizeof (buf), "%.12g", max_yval);
1155 root->add_property ("max_yval", buf);
1156 snprintf (buf, sizeof (buf), "%.12g", max_xval);
1157 root->add_property ("max_xval", buf);
1160 root->add_property ("state", auto_state_to_string (_state));
1162 /* never save anything but Off for automation state to a template */
1163 root->add_property ("state", auto_state_to_string (Off));
1166 root->add_property ("style", auto_style_to_string (_style));
1168 if (!events.empty()) {
1169 root->add_child_nocopy (serialize_events());
1176 AutomationList::serialize_events ()
1178 XMLNode* node = new XMLNode (X_("events"));
1181 for (iterator xx = events.begin(); xx != events.end(); ++xx) {
1182 str << (double) (*xx)->when;
1184 str <<(double) (*xx)->value;
1188 /* XML is a bit wierd */
1190 XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
1191 content_node->set_content (str.str());
1193 node->add_child_nocopy (*content_node);
1199 AutomationList::deserialize_events (const XMLNode& node)
1201 if (node.children().empty()) {
1205 XMLNode* content_node = node.children().front();
1207 if (content_node->content().empty()) {
1214 stringstream str (content_node->content());
1230 fast_simple_add (x, y);
1235 error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
1238 reposition_for_rt_add (0);
1239 maybe_signal_changed ();
1247 AutomationList::set_state (const XMLNode& node)
1249 XMLNodeList nlist = node.children();
1251 XMLNodeIterator niter;
1252 const XMLProperty* prop;
1254 if (node.name() == X_("events")) {
1255 /* partial state setting*/
1256 return deserialize_events (node);
1259 if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
1261 if ((nsos = node.child (X_("AutomationList")))) {
1262 /* new school in old school clothing */
1263 return set_state (*nsos);
1268 const XMLNodeList& elist = node.children();
1269 XMLNodeConstIterator i;
1276 for (i = elist.begin(); i != elist.end(); ++i) {
1278 if ((prop = (*i)->property ("x")) == 0) {
1279 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1282 x = atoi (prop->value().c_str());
1284 if ((prop = (*i)->property ("y")) == 0) {
1285 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1288 y = atof (prop->value().c_str());
1296 if (node.name() != X_("AutomationList") ) {
1297 error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
1301 if ((prop = node.property ("id")) != 0) {
1302 _id = prop->value ();
1303 /* update session AL list */
1304 AutomationListCreated(this);
1307 if ((prop = node.property (X_("default"))) != 0){
1308 default_value = atof (prop->value());
1310 default_value = 0.0;
1313 if ((prop = node.property (X_("style"))) != 0) {
1314 _style = string_to_auto_style (prop->value());
1319 if ((prop = node.property (X_("state"))) != 0) {
1320 _state = string_to_auto_state (prop->value());
1325 if ((prop = node.property (X_("min_yval"))) != 0) {
1326 min_yval = atof (prop->value ());
1331 if ((prop = node.property (X_("max_yval"))) != 0) {
1332 max_yval = atof (prop->value ());
1337 if ((prop = node.property (X_("max_xval"))) != 0) {
1338 max_xval = atof (prop->value ());
1340 max_xval = 0; // means "no limit ;
1343 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1344 if ((*niter)->name() == X_("events")) {
1345 deserialize_events (*(*niter));