summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/midi_source.h11
-rw-r--r--libs/ardour/midi_model.cc5
-rw-r--r--libs/ardour/midi_region.cc7
-rw-r--r--libs/ardour/midi_source.cc33
-rw-r--r--libs/ardour/smf_source.cc3
-rw-r--r--libs/evoral/src/Sequence.cpp13
6 files changed, 59 insertions, 13 deletions
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index 364b220ebb..65e382c4b7 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -140,6 +140,11 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
virtual void load_model(bool lock=true, bool force_reload=false) = 0;
virtual void destroy_model() = 0;
+ /** This must be called with the source lock held whenever the
+ * source/model contents have been changed (reset iterators/cache/etc).
+ */
+ void invalidate();
+
void set_note_mode(NoteMode mode);
boost::shared_ptr<MidiModel> model() { return _model; }
@@ -186,7 +191,11 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
boost::shared_ptr<MidiModel> _model;
bool _writing;
- mutable double _length_beats;
+ mutable Evoral::Sequence<Evoral::MusicalTime>::const_iterator _model_iter;
+ mutable bool _model_iter_valid;
+
+ mutable double _length_beats;
+ mutable framepos_t _last_read_end;
/** The total duration of the current capture. */
framepos_t _capture_length;
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
index 8e17e1d3ec..259a04bc0f 100644
--- a/libs/ardour/midi_model.cc
+++ b/libs/ardour/midi_model.cc
@@ -1630,6 +1630,7 @@ MidiModel::edit_lock()
assert (ms);
Glib::Threads::Mutex::Lock* source_lock = new Glib::Threads::Mutex::Lock (ms->mutex());
+ ms->invalidate(); // Release cached iterator's read lock on model
return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
}
@@ -1853,6 +1854,10 @@ MidiModel::set_midi_source (boost::shared_ptr<MidiSource> s)
{
boost::shared_ptr<MidiSource> old = _midi_source.lock ();
+ if (old) {
+ old->invalidate ();
+ }
+
_midi_source_connections.drop_connections ();
_midi_source = s;
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
index cb0d103b1e..71fd796b81 100644
--- a/libs/ardour/midi_region.cc
+++ b/libs/ardour/midi_region.cc
@@ -421,6 +421,13 @@ MidiRegion::model_automation_state_changed (Evoral::Parameter const & p)
} else {
_filtered_parameters.insert (p);
}
+
+ /* the source will have an iterator into the model, and that iterator will have been set up
+ for a given set of filtered_parameters, so now that we've changed that list we must invalidate
+ the iterator.
+ */
+ Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex());
+ midi_source(0)->invalidate ();
}
/** This is called when a trim drag has resulted in a -ve _start time for this region.
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
index 494bb513fe..823ca9dc5c 100644
--- a/libs/ardour/midi_source.cc
+++ b/libs/ardour/midi_source.cc
@@ -56,7 +56,9 @@ PBD::Signal1<void,MidiSource*> MidiSource::MidiSourceCreated;
MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
: Source(s, DataType::MIDI, name, flags)
, _writing(false)
+ , _model_iter_valid(false)
, _length_beats(0.0)
+ , _last_read_end(0)
, _capture_length(0)
, _capture_loop_length(0)
{
@@ -65,7 +67,9 @@ MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
MidiSource::MidiSource (Session& s, const XMLNode& node)
: Source(s, node)
, _writing(false)
+ , _model_iter_valid(false)
, _length_beats(0.0)
+ , _last_read_end(0)
, _capture_length(0)
, _capture_loop_length(0)
{
@@ -169,6 +173,13 @@ MidiSource::update_length (framecnt_t)
// You're not the boss of me!
}
+void
+MidiSource::invalidate ()
+{
+ _model_iter_valid = false;
+ _model_iter.invalidate();
+}
+
framecnt_t
MidiSource::midi_read (Evoral::EventSink<framepos_t>& dst,
framepos_t source_start,
@@ -186,11 +197,18 @@ MidiSource::midi_read (Evoral::EventSink<framepos_t>& dst,
source_start, start, cnt, tracker, name()));
if (_model) {
- // Read events up to end
- const double start_beats = converter.from(start);
- for (Evoral::Sequence<double>::const_iterator i = _model->begin(start_beats, false, filtered);
- i != _model->end();
- ++i) {
+ // Find appropriate model iterator
+ Evoral::Sequence<double>::const_iterator& i = _model_iter;
+ if (_last_read_end == 0 || start != _last_read_end || !_model_iter_valid) {
+ // Cached iterator is invalid, search for the first event past start
+ i = _model->begin(converter.from(start), false, filtered);
+ _model_iter_valid = true;
+ }
+
+ _last_read_end = start + cnt;
+
+ // Copy events in [start, start + cnt) into dst
+ for (; i != _model->end(); ++i) {
const framecnt_t time_frames = converter.to(i->time());
if (time_frames < start + cnt) {
// Offset by source start to convert event time to session time
@@ -225,7 +243,10 @@ MidiSource::midi_write (MidiRingBuffer<framepos_t>& source,
const framecnt_t ret = write_unlocked (source, source_start, cnt);
- if (cnt != max_framecnt) {
+ if (cnt == max_framecnt) {
+ _last_read_end = 0;
+ invalidate();
+ } else {
_capture_length += cnt;
}
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index 3f22028e62..8a956495a5 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -701,6 +701,7 @@ SMFSource::load_model (bool lock, bool force_reload)
_model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
_model->set_edited (false);
+ invalidate();
free(buf);
}
@@ -724,6 +725,8 @@ SMFSource::flush_midi ()
Evoral::SMF::end_write ();
/* data in the file means its no longer removable */
mark_nonremovable ();
+
+ invalidate();
}
void
diff --git a/libs/evoral/src/Sequence.cpp b/libs/evoral/src/Sequence.cpp
index 1cc8ff6e0f..082f8f24ea 100644
--- a/libs/evoral/src/Sequence.cpp
+++ b/libs/evoral/src/Sequence.cpp
@@ -61,10 +61,13 @@ namespace Evoral {
template<typename Time>
Sequence<Time>::const_iterator::const_iterator()
: _seq(NULL)
+ , _event(boost::shared_ptr< Event<Time> >(new Event<Time>()))
+ , _active_patch_change_message (0)
+ , _type(NIL)
, _is_end(true)
, _control_iter(_control_iters.end())
+ , _force_discrete(false)
{
- _event = boost::shared_ptr< Event<Time> >(new Event<Time>());
}
/** @param force_discrete true to force ControlLists to use discrete evaluation, otherwise false to get them to use their configured mode */
@@ -125,7 +128,7 @@ Sequence<Time>::const_iterator::const_iterator(const Sequence<Time>& seq, Time t
DEBUG_TRACE (DEBUG::Sequence, string_compose ("Iterator: control: %1\n", seq._type_map.to_symbol(i->first)));
double x, y;
bool ret;
- if (_force_discrete) {
+ if (_force_discrete || i->second->list()->interpolation() == ControlList::Discrete) {
ret = i->second->list()->rt_safe_earliest_event_discrete_unlocked (t, x, y, true);
} else {
ret = i->second->list()->rt_safe_earliest_event_unlocked(t, x, y, true);
@@ -290,12 +293,10 @@ Sequence<Time>::const_iterator::operator++()
// Increment current controller iterator
if (_force_discrete || _control_iter->list->interpolation() == ControlList::Discrete) {
ret = _control_iter->list->rt_safe_earliest_event_discrete_unlocked (
- _control_iter->x, x, y, false
- );
+ _control_iter->x, x, y, false);
} else {
ret = _control_iter->list->rt_safe_earliest_event_linear_unlocked (
- _control_iter->x + time_between_interpolated_controller_outputs, x, y, false
- );
+ _control_iter->x + time_between_interpolated_controller_outputs, x, y, false);
}
assert(!ret || x > _control_iter->x);
if (ret) {