revert VST debug hacks
[ardour.git] / libs / ardour / automation_event.cc
index 88932009bf85c8abdc623848450d361558ada485..af6fffdeb95e026ba4d8c06af734bfbb58eab8e3 100644 (file)
 #include <climits>
 #include <float.h>
 #include <cmath>
+#include <sstream>
 #include <algorithm>
 #include <sigc++/bind.h>
 #include <ardour/automation_event.h>
+#include <pbd/stacktrace.h>
 
 #include "i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
 using namespace sigc;
+using namespace PBD;
+
+sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
+
+static bool sort_events_by_time (ControlEvent* a, ControlEvent* b)
+{
+       return a->when < b->when;
+}
 
 #if 0
 static void dumpit (const AutomationList& al, string prefix = "")
@@ -43,14 +53,13 @@ static void dumpit (const AutomationList& al, string prefix = "")
 }
 #endif
 
-AutomationList::AutomationList (double defval, bool with_state)
+AutomationList::AutomationList (double defval)
 {
-       _frozen = false;
+       _frozen = 0;
        changed_when_thawed = false;
        _state = Off;
        _style = Absolute;
        _touching = false;
-       no_state = with_state;
        min_yval = FLT_MIN;
        max_yval = FLT_MAX;
        max_xval = 0; // means "no limit" 
@@ -59,15 +68,14 @@ AutomationList::AutomationList (double defval, bool with_state)
        rt_insertion_point = events.end();
        lookup_cache.left = -1;
        lookup_cache.range.first = events.end();
+       sort_pending = false;
 
-       if (!no_state) {
-               save_state (_("initial"));
-       }
+        AutomationListCreated(this);
 }
 
 AutomationList::AutomationList (const AutomationList& other)
 {
-       _frozen = false;
+       _frozen = 0;
        changed_when_thawed = false;
        _style = other._style;
        min_yval = other.min_yval;
@@ -78,9 +86,9 @@ AutomationList::AutomationList (const AutomationList& other)
        _touching = other._touching;
        _dirty = false;
        rt_insertion_point = events.end();
-       no_state = other.no_state;
        lookup_cache.left = -1;
        lookup_cache.range.first = events.end();
+       sort_pending = false;
 
        for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
                /* we have to use other point_factory() because
@@ -90,11 +98,12 @@ AutomationList::AutomationList (const AutomationList& other)
        }
 
        mark_dirty ();
+        AutomationListCreated(this);
 }
 
 AutomationList::AutomationList (const AutomationList& other, double start, double end)
 {
-       _frozen = false;
+       _frozen = 0;
        changed_when_thawed = false;
        _style = other._style;
        min_yval = other.min_yval;
@@ -105,9 +114,9 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
        _touching = other._touching;
        _dirty = false;
        rt_insertion_point = events.end();
-       no_state = other.no_state;
        lookup_cache.left = -1;
        lookup_cache.range.first = events.end();
+       sort_pending = false;
 
        /* now grab the relevant points, and shift them back if necessary */
 
@@ -122,29 +131,37 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
        delete section;
 
        mark_dirty ();
+
+        AutomationListCreated(this);
 }
 
-AutomationList::~AutomationList()
+AutomationList::AutomationList (const XMLNode& node)
 {
-       std::set<ControlEvent*> all_events;
-       AutomationList::State* asp;
-
-       for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
-               all_events.insert (*x);
-       }
-
-       for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
+       _frozen = 0;
+       changed_when_thawed = false;
+       _touching = false;
+       min_yval = FLT_MIN;
+       max_yval = FLT_MAX;
+       max_xval = 0; // means "no limit" 
+       _dirty = false;
+       _state = Off;
+       _style = Absolute;
+       rt_insertion_point = events.end();
+       lookup_cache.left = -1;
+       lookup_cache.range.first = events.end();
+       sort_pending = false;
+       
+       set_state (node);
 
-               if ((asp = dynamic_cast<AutomationList::State*> (*i)) != 0) {
-                       
-                       for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) {
-                               all_events.insert (*x);
-                       }
-               }
-       }
+        AutomationListCreated(this);
+}
 
-       for (std::set<ControlEvent*>::iterator i = all_events.begin(); i != all_events.end(); ++i) {
-               delete (*i);
+AutomationList::~AutomationList()
+{
+       GoingAway ();
+       
+       for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
+               delete (*x);
        }
 }
 
@@ -185,7 +202,7 @@ AutomationList::maybe_signal_changed ()
        if (_frozen) {
                changed_when_thawed = true;
        } else {
-               StateChanged (Change (0));
+               StateChanged ();
        }
 }
 
@@ -225,11 +242,8 @@ void
 AutomationList::clear ()
 {
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                events.clear ();
-               if (!no_state) {
-                       save_state (_("cleared"));
-               }
                mark_dirty ();
        }
 
@@ -239,14 +253,14 @@ AutomationList::clear ()
 void
 AutomationList::x_scale (double factor)
 {
-       LockMonitor lm (lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock lm (lock);
        _x_scale (factor);
 }
 
 bool
 AutomationList::extend_to (double when)
 {
-       LockMonitor lm (lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock lm (lock);
        if (events.empty() || events.back()->when == when) {
                return false;
        }
@@ -261,7 +275,6 @@ void AutomationList::_x_scale (double factor)
                (*i)->when = floor ((*i)->when * factor);
        }
 
-       save_state ("x-scaled");
        mark_dirty ();
 }
 
@@ -285,7 +298,7 @@ AutomationList::rt_add (double when, double value)
        // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
 
                iterator where;
                TimeComparator cmp;
@@ -361,15 +374,22 @@ AutomationList::rt_add (double when, double value)
        maybe_signal_changed ();
 }
 
+void
+AutomationList::fast_simple_add (double when, double value)
+{
+       /* to be used only for loading pre-sorted data from saved state */
+       events.insert (events.end(), point_factory (when, value));
+}
+
 #undef last_rt_insertion_point
 
 void
-AutomationList::add (double when, double value, bool for_loading)
+AutomationList::add (double when, double value)
 {
-       /* this is for graphical editing and loading data from storage */
+       /* this is for graphical editing */
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                TimeComparator cmp;
                ControlEvent cp (when, 0.0f);
                bool insert = true;
@@ -391,34 +411,25 @@ AutomationList::add (double when, double value, bool for_loading)
                }
 
                if (insert) {
-
+                       
                        events.insert (insertion_point, point_factory (when, value));
                        reposition_for_rt_add (0);
 
                } 
 
                mark_dirty ();
-
-               if (!no_state && !for_loading) {
-                       save_state (_("added event"));
-               }
        }
 
-       if (!for_loading) {
-               maybe_signal_changed ();
-       }
+       maybe_signal_changed ();
 }
 
 void
 AutomationList::erase (AutomationList::iterator i)
 {
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                events.erase (i);
                reposition_for_rt_add (0);
-               if (!no_state) {
-                       save_state (_("removed event"));
-               }
                mark_dirty ();
        }
        maybe_signal_changed ();
@@ -428,12 +439,9 @@ void
 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
 {
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                events.erase (start, end);
                reposition_for_rt_add (0);
-               if (!no_state) {
-                       save_state (_("removed multiple events"));
-               }
                mark_dirty ();
        }
        maybe_signal_changed ();
@@ -445,7 +453,7 @@ AutomationList::reset_range (double start, double endt)
        bool reset = false;
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+        Glib::Mutex::Lock lm (lock);
                TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
@@ -462,10 +470,6 @@ AutomationList::reset_range (double start, double endt)
                        
                        reset = true;
 
-                       if (!no_state) {
-                               save_state (_("removed range"));
-                       }
-
                        mark_dirty ();
                }
        }
@@ -481,7 +485,7 @@ AutomationList::erase_range (double start, double endt)
        bool erased = false;
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                TimeComparator cmp;
                ControlEvent cp (start, 0.0f);
                iterator s;
@@ -493,9 +497,6 @@ AutomationList::erase_range (double start, double endt)
                        events.erase (s, e);
                        reposition_for_rt_add (0);
                        erased = true;
-                       if (!no_state) {
-                               save_state (_("removed range"));
-                       }
                        mark_dirty ();
                }
                
@@ -515,16 +516,21 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double
        */
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
 
                while (start != end) {
                        (*start)->when += xdelta;
                        (*start)->value += ydelta;
+                       if (isnan ((*start)->value)) {
+                               abort ();
+                       }
                        ++start;
                }
 
-               if (!no_state) {
-                       save_state (_("event range adjusted"));
+               if (!_frozen) {
+                       events.sort (sort_events_by_time);
+               } else {
+                       sort_pending = true;
                }
 
                mark_dirty ();
@@ -533,6 +539,25 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double
        maybe_signal_changed ();
 }
 
+void
+AutomationList::slide (iterator before, double distance)
+{
+       {
+               Glib::Mutex::Lock lm (lock);
+
+               if (before == events.end()) {
+                       return;
+               }
+               
+               while (before != events.end()) {
+                       (*before)->when += distance;
+                       ++before;
+               }
+       }
+
+       maybe_signal_changed ();
+}
+
 void
 AutomationList::modify (iterator iter, double when, double val)
 {
@@ -542,23 +567,31 @@ AutomationList::modify (iterator iter, double when, double val)
        */
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
+
                (*iter)->when = when;
                (*iter)->value = val;
-               if (!no_state) {
-                       save_state (_("event adjusted"));
+
+               if (isnan (val)) {
+                       abort ();
+               }
+
+               if (!_frozen) {
+                       events.sort (sort_events_by_time);
+               } else {
+                       sort_pending = true;
                }
 
                mark_dirty ();
        }
-       
+
        maybe_signal_changed ();
 }
 
 std::pair<AutomationList::iterator,AutomationList::iterator>
 AutomationList::control_points_adjacent (double xval)
 {
-       LockMonitor lm (lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock lm (lock);
        iterator i;
        TimeComparator cmp;
        ControlEvent cp (xval, 0.0f);
@@ -592,50 +625,34 @@ AutomationList::control_points_adjacent (double xval)
 void
 AutomationList::freeze ()
 {
-       _frozen = true;
+       _frozen++;
 }
 
 void
 AutomationList::thaw ()
 {
-       _frozen = false;
-       if (changed_when_thawed) {
-                StateChanged(Change(0)); /* EMIT SIGNAL */
+       if (_frozen == 0) {
+               PBD::stacktrace (cerr);
+               fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg;
+               /*NOTREACHED*/
        }
-}
 
-StateManager::State*
-AutomationList::state_factory (std::string why) const
-{
-       State* state = new State (why);
-
-       for (AutomationEventList::const_iterator x = events.begin(); x != events.end(); ++x) {
-               state->events.push_back (point_factory (**x));
+       if (--_frozen > 0) {
+               return;
        }
 
-       return state;
-}
-
-Change
-AutomationList::restore_state (StateManager::State& state) 
-{
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
-               State* lstate = dynamic_cast<State*> (&state);
+               Glib::Mutex::Lock lm (lock);
 
-               events.clear ();
-               for (AutomationEventList::const_iterator x = lstate->events.begin(); x != lstate->events.end(); ++x) {
-                       events.push_back (point_factory (**x));
+               if (sort_pending) {
+                       events.sort (sort_events_by_time);
+                       sort_pending = false;
                }
        }
 
-       return Change (0);
-}
-
-UndoAction
-AutomationList::get_memento () const
-{
-  return sigc::bind (mem_fun (*(const_cast<AutomationList*> (this)), &StateManager::use_state), _current_state_id);
+       if (changed_when_thawed) {
+               StateChanged(); /* EMIT SIGNAL */
+       }
 }
 
 void
@@ -655,16 +672,12 @@ void
 AutomationList::truncate_end (double last_coordinate)
 {
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                ControlEvent cp (last_coordinate, 0);
                list<ControlEvent*>::reverse_iterator i;
                double last_val;
 
                if (events.empty()) {
-                       fatal << _("programming error:")
-                             << "AutomationList::truncate_end() called on an empty list"
-                             << endmsg;
-                       /*NOTREACHED*/
                        return;
                }
 
@@ -760,7 +773,7 @@ void
 AutomationList::truncate_start (double overall_length)
 {
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                AutomationList::iterator i;
                double first_legal_value;
                double first_legal_coordinate;
@@ -998,7 +1011,7 @@ AutomationList::cut (iterator start, iterator end)
        AutomationList* nal = new AutomationList (default_value);
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
 
                for (iterator x = start; x != end; ) {
                        iterator tmp;
@@ -1032,7 +1045,7 @@ AutomationList::cut_copy_clear (double start, double end, int op)
        bool changed = false;
        
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
 
                if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
                        return nal;
@@ -1074,9 +1087,6 @@ AutomationList::cut_copy_clear (double start, double end, int op)
 
                if (changed) {
                        reposition_for_rt_add (0);
-                       if (!no_state) {
-                               save_state (_("cut/copy/clear"));
-                       }
                }
 
                mark_dirty ();
@@ -1094,7 +1104,7 @@ AutomationList::copy (iterator start, iterator end)
        AutomationList* nal = new AutomationList (default_value);
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                
                for (iterator x = start; x != end; ) {
                        iterator tmp;
@@ -1106,10 +1116,6 @@ AutomationList::copy (iterator start, iterator end)
                        
                        x = tmp;
                }
-
-               if (!no_state) {
-                       save_state (_("copy"));
-               }
        }
 
        return nal;
@@ -1141,7 +1147,7 @@ AutomationList::paste (AutomationList& alist, double pos, float times)
        }
 
        {
-               LockMonitor lm (lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (lock);
                iterator where;
                iterator prev;
                double end = 0;
@@ -1174,11 +1180,6 @@ AutomationList::paste (AutomationList& alist, double pos, float times)
                }
 
                reposition_for_rt_add (0);
-
-               if (!no_state) {
-                       save_state (_("paste"));
-               }
-
                mark_dirty ();
        }
 
@@ -1198,50 +1199,224 @@ AutomationList::point_factory (const ControlEvent& other) const
        return new ControlEvent (other);
 }
 
-void
-AutomationList::store_state (XMLNode& node) const
+XMLNode&
+AutomationList::get_state ()
+{
+       return state (true);
+}
+
+XMLNode&
+AutomationList::state (bool full)
 {
+       XMLNode* root = new XMLNode (X_("AutomationList"));
+       char buf[64];
        LocaleGuard lg (X_("POSIX"));
 
-       for (const_iterator i = const_begin(); i != const_end(); ++i) {
-               char buf[64];
-               
-               XMLNode *pointnode = new XMLNode ("point");
-               
-               snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*i)->when));
-               pointnode->add_property ("x", buf);
-               snprintf (buf, sizeof (buf), "%f", (*i)->value);
-               pointnode->add_property ("y", buf);
+       root->add_property ("id", _id.to_s());
+
+       snprintf (buf, sizeof (buf), "%.12g", default_value);
+       root->add_property ("default", buf);
+       snprintf (buf, sizeof (buf), "%.12g", min_yval);
+       root->add_property ("min_yval", buf);
+       snprintf (buf, sizeof (buf), "%.12g", max_yval);
+       root->add_property ("max_yval", buf);
+       snprintf (buf, sizeof (buf), "%.12g", max_xval);
+       root->add_property ("max_xval", buf);
 
-               node.add_child_nocopy (*pointnode);
+       if (full) {
+               root->add_property ("state", auto_state_to_string (_state));
+       } else {
+               /* never save anything but Off for automation state to a template */
+               root->add_property ("state", auto_state_to_string (Off));
        }
+
+       root->add_property ("style", auto_style_to_string (_style));
+
+       if (!events.empty()) {
+               root->add_child_nocopy (serialize_events());
+       }
+
+       return *root;
 }
 
-void
-AutomationList::load_state (const XMLNode& node)
+XMLNode&
+AutomationList::serialize_events ()
 {
-       const XMLNodeList& elist = node.children();
-       XMLNodeConstIterator i;
-       XMLProperty* prop;
-       jack_nframes_t x;
-       double y;
+       XMLNode* node = new XMLNode (X_("events"));
+       stringstream str;
+
+       for (iterator xx = events.begin(); xx != events.end(); ++xx) {
+               str << (double) (*xx)->when;
+               str << ' ';
+               str <<(double) (*xx)->value;
+               str << '\n';
+       }
+
+       /* XML is a bit wierd */
+
+       XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
+       content_node->set_content (str.str());
 
+       node->add_child_nocopy (*content_node);
+
+       return *node;
+}
+
+int
+AutomationList::deserialize_events (const XMLNode& node)
+{
+       if (node.children().empty()) {
+               return -1;
+       }
+
+       XMLNode* content_node = node.children().front();
+
+       if (content_node->content().empty()) {
+               return -1;
+       }
+
+       freeze ();
        clear ();
        
-       for (i = elist.begin(); i != elist.end(); ++i) {
-               
-               if ((prop = (*i)->property ("x")) == 0) {
-                       error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
-                       continue;
+       stringstream str (content_node->content());
+       
+       double x;
+       double y;
+       bool ok = true;
+       
+       while (str) {
+               str >> x;
+               if (!str) {
+                       break;
+               }
+               str >> y;
+               if (!str) {
+                       ok = false;
+                       break;
                }
-               x = atoi (prop->value().c_str());
+               fast_simple_add (x, y);
+       }
+       
+       if (!ok) {
+               clear ();
+               error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
+       } else {
+               mark_dirty ();
+               reposition_for_rt_add (0);
+               maybe_signal_changed ();
+       }
+
+       thaw ();
+
+       return 0;
+}
+
+int
+AutomationList::set_state (const XMLNode& node)
+{
+       XMLNodeList nlist = node.children();
+       XMLNode* nsos;
+       XMLNodeIterator niter;
+       const XMLProperty* prop;
+
+       if (node.name() == X_("events")) {
+               /* partial state setting*/
+               return deserialize_events (node);
+       }
+       
+       if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
+
+               if ((nsos = node.child (X_("AutomationList")))) {
+                       /* new school in old school clothing */
+                       return set_state (*nsos);
+               }
+
+               /* old school */
+
+               const XMLNodeList& elist = node.children();
+               XMLNodeConstIterator i;
+               XMLProperty* prop;
+               jack_nframes_t x;
+               double y;
+               
+               freeze ();
+               clear ();
                
-               if ((prop = (*i)->property ("y")) == 0) {
-                       error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
-                       continue;
+               for (i = elist.begin(); i != elist.end(); ++i) {
+                       
+                       if ((prop = (*i)->property ("x")) == 0) {
+                               error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
+                               continue;
+                       }
+                       x = atoi (prop->value().c_str());
+                       
+                       if ((prop = (*i)->property ("y")) == 0) {
+                               error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
+                               continue;
+                       }
+                       y = atof (prop->value().c_str());
+                       
+                       fast_simple_add (x, y);
                }
-               y = atof (prop->value().c_str());
                
-               add (x, y);
+               thaw ();
+
+               return 0;
+       }
+
+       if (node.name() != X_("AutomationList") ) {
+               error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
+               return -1;
+       }
+       
+       if ((prop = node.property ("id")) != 0) {
+               _id = prop->value ();
+               /* update session AL list */
+               AutomationListCreated(this);
+       }
+       
+       if ((prop = node.property (X_("default"))) != 0){ 
+               default_value = atof (prop->value());
+       } else {
+               default_value = 0.0;
+       }
+
+       if ((prop = node.property (X_("style"))) != 0) {
+               _style = string_to_auto_style (prop->value());
+       } else {
+               _style = Absolute;
+       }
+
+       if ((prop = node.property (X_("state"))) != 0) {
+               _state = string_to_auto_state (prop->value());
+       } else {
+               _state = Off;
+       }
+
+       if ((prop = node.property (X_("min_yval"))) != 0) {
+               min_yval = atof (prop->value ());
+       } else {
+               min_yval = FLT_MIN;
        }
+
+       if ((prop = node.property (X_("max_yval"))) != 0) {
+               max_yval = atof (prop->value ());
+       } else {
+               max_yval = FLT_MAX;
+       }
+
+       if ((prop = node.property (X_("max_xval"))) != 0) {
+               max_xval = atof (prop->value ());
+       } else {
+               max_xval = 0; // means "no limit ;
+       }
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+               if ((*niter)->name() == X_("events")) {
+                       deserialize_events (*(*niter));
+               }
+       }
+
+       return 0;
 }
+