+ const double start_qn = pos_beats - start_beats;
+
+ DEBUG_TRACE (DEBUG::MidiSourceIO,
+ string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
+ source_start, start, cnt, tracker, name()));
+
+ if (!_model) {
+ return read_unlocked (lm, dst, source_start, start, cnt, loop_range, tracker, filter);
+ }
+
+ // Find appropriate model iterator
+ Evoral::Sequence<Evoral::Beats>::const_iterator& i = cursor.iter;
+ const bool linear_read = cursor.last_read_end != 0 && start == cursor.last_read_end;
+ if (!linear_read || !i.valid()) {
+ /* Cached iterator is invalid, search for the first event past start.
+ Note that multiple tracks can use a MidiSource simultaneously, so
+ all playback state must be in parameters (the cursor) and must not
+ be cached in the source of model itself.
+ See http://tracker.ardour.org/view.php?id=6541
+ */
+ cursor.connect(Invalidated);
+ cursor.iter = _model->begin(converter.from(start), false, filtered, &cursor.active_notes);
+ cursor.active_notes.clear();
+ }
+
+ cursor.last_read_end = start + cnt;
+
+ // Copy events in [start, start + cnt) into dst
+ for (; i != _model->end(); ++i) {
+
+ // Offset by source start to convert event time to session time
+
+ framepos_t time_frames = _session.tempo_map().frame_at_quarter_note (i->time().to_double() + start_qn);
+
+ if (time_frames < start + source_start) {
+ /* event too early */
+
+ continue;
+
+ } else if (time_frames >= start + cnt + source_start) {
+
+ DEBUG_TRACE (DEBUG::MidiSourceIO,
+ string_compose ("%1: reached end with event @ %2 vs. %3\n",
+ _name, time_frames, start+cnt));
+ break;