X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fautomation_event.cc;h=af6fffdeb95e026ba4d8c06af734bfbb58eab8e3;hb=f21094937a2ae7dae7929de3ed883d93ed12b66b;hp=afdeecbbfe3e3222b164fb1030cf5b0dcc76db03;hpb=ce234f363e95c38fc92728e520bf5ba240a89aa7;p=ardour.git diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index afdeecbbfe..af6fffdeb9 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -22,9 +22,11 @@ #include #include #include +#include #include #include #include +#include #include "i18n.h" @@ -35,6 +37,11 @@ using namespace PBD; sigc::signal 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 = "") { @@ -46,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" @@ -62,17 +68,14 @@ AutomationList::AutomationList (double defval, bool with_state) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); - - if (!no_state) { - save_state (_("initial")); - } + sort_pending = false; AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _style = other._style; min_yval = other.min_yval; @@ -83,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 @@ -100,7 +103,7 @@ AutomationList::AutomationList (const AutomationList& other) 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; @@ -111,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 */ @@ -128,32 +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 all_events; - AutomationList::State* asp; + _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); - GoingAway (); + AutomationListCreated(this); +} +AutomationList::~AutomationList() +{ + GoingAway (); + for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) { - all_events.insert (*x); - } - - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - - if ((asp = dynamic_cast (*i)) != 0) { - - for (AutomationEventList::iterator x = asp->events.begin(); x != asp->events.end(); ++x) { - all_events.insert (*x); - } - } - } - - for (std::set::iterator i = all_events.begin(); i != all_events.end(); ++i) { - delete (*i); + delete (*x); } } @@ -194,7 +202,7 @@ AutomationList::maybe_signal_changed () if (_frozen) { changed_when_thawed = true; } else { - StateChanged (Change (0)); + StateChanged (); } } @@ -236,9 +244,6 @@ AutomationList::clear () { Glib::Mutex::Lock lm (lock); events.clear (); - if (!no_state) { - save_state (_("cleared")); - } mark_dirty (); } @@ -270,7 +275,6 @@ void AutomationList::_x_scale (double factor) (*i)->when = floor ((*i)->when * factor); } - save_state ("x-scaled"); mark_dirty (); } @@ -370,12 +374,19 @@ 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 */ { Glib::Mutex::Lock lm (lock); @@ -400,22 +411,16 @@ 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 @@ -425,9 +430,6 @@ AutomationList::erase (AutomationList::iterator i) 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 (); @@ -440,9 +442,6 @@ AutomationList::erase (AutomationList::iterator start, AutomationList::iterator 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 (); @@ -471,10 +470,6 @@ AutomationList::reset_range (double start, double endt) reset = true; - if (!no_state) { - save_state (_("removed range")); - } - mark_dirty (); } } @@ -502,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 (); } @@ -529,11 +521,16 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double 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 (); @@ -542,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) { @@ -552,15 +568,23 @@ AutomationList::modify (iterator iter, double when, double val) { 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 (); } @@ -601,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) -{ { Glib::Mutex::Lock lm (lock); - State* lstate = dynamic_cast (&state); - 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 (this)), &StateManager::use_state), _current_state_id); + if (changed_when_thawed) { + StateChanged(); /* EMIT SIGNAL */ + } } void @@ -670,10 +678,6 @@ AutomationList::truncate_end (double last_coordinate) double last_val; if (events.empty()) { - fatal << _("programming error:") - << "AutomationList::truncate_end() called on an empty list" - << endmsg; - /*NOTREACHED*/ return; } @@ -1083,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 (); @@ -1115,10 +1116,6 @@ AutomationList::copy (iterator start, iterator end) x = tmp; } - - if (!no_state) { - save_state (_("copy")); - } } return nal; @@ -1183,11 +1180,6 @@ AutomationList::paste (AutomationList& alist, double pos, float times) } reposition_for_rt_add (0); - - if (!no_state) { - save_state (_("paste")); - } - mark_dirty (); } @@ -1207,64 +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), "%.12g", (*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; } - 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; + str >> y; + if (!str) { + ok = false; + break; } - y = atof (prop->value().c_str()); - - add (x, y); + 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 (); } -} -XMLNode &AutomationList::get_state () -{ - XMLNode *node = new XMLNode("AutomationList"); - store_state(*node); - return *node; + thaw (); + + return 0; } -int AutomationList::set_state(const XMLNode &s) +int +AutomationList::set_state (const XMLNode& node) { - load_state(s); - return 0; + 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 (); + + 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); + } + + 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; }