Fixes various issues when changing AutomationState while rolling.
XMLNode& get_state ();
int set_state (const XMLNode &, int version);
- XMLNode& state (bool full);
- XMLNode& serialize_events ();
Command* memento_command (XMLNode* before, XMLNode* after);
XMLNode* before () { XMLNode* rv = _before; _before = 0; return rv; }
void clear_history ();
+ void snapshot_history (bool need_lock);
ControlList::InterpolationStyle default_interpolation () const;
void create_curve_if_necessary ();
int deserialize_events (const XMLNode&);
+ XMLNode& state (bool full, bool need_lock);
+ XMLNode& serialize_events (bool need_lock);
+
void maybe_signal_changed ();
AutoState _state;
gint _touching;
+ PBD::ScopedConnection _writepass_connection;
+
bool operator== (const AutomationList&) const { /* not called */ abort(); return false; }
XMLNode* _before; //used for undo of touch start/stop pairs.
default:
break;
}
+
+ WritePassStarted.connect_same_thread (_writepass_connection, boost::bind (&AutomationList::snapshot_history, this, false));
}
AutomationList&
void
AutomationList::set_automation_state (AutoState s)
{
- if (s != _state) {
- _state = s;
- delete _before;
- if (s == Write && _desc.toggled) {
- _before = &get_state ();
- } else {
- _before = 0;
- }
- automation_state_changed (s); /* EMIT SIGNAL */
+ if (s == _state) {
+ return;
+ }
+ _state = s;
+ if (s == Write && _desc.toggled) {
+ snapshot_history (true);
}
+ automation_state_changed (s); /* EMIT SIGNAL */
}
Evoral::ControlList::InterpolationStyle
void
AutomationList::start_write_pass (double when)
{
- delete _before;
- if (in_new_write_pass ()) {
- _before = &get_state ();
- } else {
- _before = 0;
- }
+ snapshot_history (true);
ControlList::start_write_pass (when);
}
void
AutomationList::start_touch (double when)
{
- if (_state == Touch) {
+ if (_state == Touch) {
start_write_pass (when);
- }
+ }
g_atomic_int_set (&_touching, 1);
}
_before = 0;
}
+void
+AutomationList::snapshot_history (bool need_lock)
+{
+ if (!in_new_write_pass ()) {
+ return;
+ }
+ delete _before;
+ _before = &state (true, need_lock);
+}
+
+
void
AutomationList::thaw ()
{
XMLNode&
AutomationList::get_state ()
{
- return state (true);
+ return state (true, true);
}
XMLNode&
-AutomationList::state (bool full)
+AutomationList::state (bool full, bool need_lock)
{
XMLNode* root = new XMLNode (X_("AutomationList"));
}
if (!_events.empty()) {
- root->add_child_nocopy (serialize_events());
+ root->add_child_nocopy (serialize_events (need_lock));
}
return *root;
}
XMLNode&
-AutomationList::serialize_events ()
+AutomationList::serialize_events (bool need_lock)
{
XMLNode* node = new XMLNode (X_("events"));
stringstream str;
+ Glib::Threads::RWLock::ReaderLock lm (Evoral::ControlList::_lock, Glib::Threads::NOT_LOCK);
+ if (need_lock) {
+ lm.acquire ();
+ }
for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
str << PBD::to_string ((*xx)->when);
str << ' ';
bool in_write_pass () const;
bool in_new_write_pass () { return new_write_pass; }
+ PBD::Signal0<void> WritePassStarted;
/** Emitted when mark_dirty() is called on this object */
mutable PBD::Signal0<void> Dirty;
/** Emitted when our interpolation style changes */
void unlocked_remove_duplicates ();
void unlocked_invalidate_insert_iterator ();
void add_guard_point (double when, double offset);
+
+ bool is_sorted () const;
};
} // namespace Evoral
copy_events (*(section.get()));
}
- new_write_pass = false;
+ new_write_pass = true;
_in_write_pass = false;
did_write_during_pass = false;
insert_position = -1;
return;
}
+ assert (is_sorted ());
+
bool changed = false;
{
DEBUG_TRACE (DEBUG::ControlList, string_compose ("%1: setup write pass @ %2\n", this, when));
- new_write_pass = true;
- did_write_during_pass = false;
insert_position = when;
/* leave the insert iterator invalid, so that we will do the lookup
}
}
+ /* don't do this again till the next write pass,
+ * unless we're not in a write-pass (transport stopped)
+ */
+ if (_in_write_pass && new_write_pass) {
+ WritePassStarted (); /* EMIT SIGNAL w/WriteLock */
+ new_write_pass = false;
+ }
+
when += offset;
ControlEvent cp (when, 0.0);
most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
- double eval_value = unlocked_eval (insert_position);
+ double eval_value = unlocked_eval (when);
if (most_recent_insert_iterator == _events.end()) {
++most_recent_insert_iterator;
}
-
- /* don't do this again till the next write pass,
- * unless we're not in a write-pass (transport stopped)
- */
- if (_in_write_pass) {
- new_write_pass = false;
- }
}
bool
const ControlEvent cp (when, 0.0);
most_recent_insert_iterator = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
}
+ WritePassStarted (); /* EMIT SIGNAL w/WriteLock */
new_write_pass = false;
} else if (_in_write_pass &&
);
}
+bool
+ControlList::is_sorted () const
+{
+ Glib::Threads::RWLock::ReaderLock lm (_lock);
+ if (_events.size () == 0) {
+ return true;
+ }
+ const_iterator i = _events.begin();
+ const_iterator n = i;
+ while (++n != _events.end ()) {
+ if (event_time_less_than(*n,*i)) {
+ return false;
+ }
+ ++i;
+ }
+ return true;
+}
+
void
ControlList::dump (ostream& o)
{