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 model() { return _model; } @@ -186,7 +191,11 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha boost::shared_ptr _model; bool _writing; - mutable double _length_beats; + mutable Evoral::Sequence::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 s) { boost::shared_ptr 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 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& dst, framepos_t source_start, @@ -186,11 +197,18 @@ MidiSource::midi_read (Evoral::EventSink& dst, source_start, start, cnt, tracker, name())); if (_model) { - // Read events up to end - const double start_beats = converter.from(start); - for (Evoral::Sequence::const_iterator i = _model->begin(start_beats, false, filtered); - i != _model->end(); - ++i) { + // Find appropriate model iterator + Evoral::Sequence::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& 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::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 Sequence