diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2016-09-13 14:10:04 -0500 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2016-09-13 14:11:29 -0500 |
commit | f41bc70ee900257d2d9008f3a749981b10de16bc (patch) | |
tree | 37f3f5950af81cd5f958da0a37a381dbf256c74d /libs/ardour/midi_source.cc | |
parent | 182e35235c41abb6ec7ab92897be0a1a228dd004 (diff) |
change all MIDI read-from-source to map all events into the loop-range for seamless looping (if using)
Diffstat (limited to 'libs/ardour/midi_source.cc')
-rw-r--r-- | libs/ardour/midi_source.cc | 188 |
1 files changed, 111 insertions, 77 deletions
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc index 30318b361f..0982f3dda8 100644 --- a/libs/ardour/midi_source.cc +++ b/libs/ardour/midi_source.cc @@ -193,6 +193,7 @@ MidiSource::midi_read (const Lock& lm, framepos_t source_start, framepos_t start, framecnt_t cnt, + Evoral::Range<framepos_t>* loop_range, MidiStateTracker* tracker, MidiChannelFilter* filter, const std::set<Evoral::Parameter>& filtered, @@ -203,102 +204,135 @@ MidiSource::midi_read (const Lock& lm, const int32_t tpb = Timecode::BBT_Time::ticks_per_beat; const double pulse_tick_res = floor ((pulse * 4.0 * tpb) + 0.5) / tpb; const double start_qn = (pulse - start_pulse) * 4.0; + 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) { - // Find appropriate model iterator - Evoral::Sequence<Evoral::Beats>::const_iterator& i = _model_iter; - const bool linear_read = _last_read_end != 0 && start == _last_read_end; - if (!linear_read || !_model_iter_valid) { + 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 = _model_iter; + const bool linear_read = _last_read_end != 0 && start == _last_read_end; + if (!linear_read || !_model_iter_valid) { #if 0 - // Cached iterator is invalid, search for the first event past start - i = _model->begin(converter.from(start), false, filtered, - linear_read ? &_model->active_notes() : NULL); - _model_iter_valid = true; - if (!linear_read) { - _model->active_notes().clear(); - } + // Cached iterator is invalid, search for the first event past start + i = _model->begin(converter.from(start), false, filtered, + linear_read ? &_model->active_notes() : NULL); + _model_iter_valid = true; + if (!linear_read) { + _model->active_notes().clear(); + } #else - /* hot-fix http://tracker.ardour.org/view.php?id=6541 - * "parallel playback of linked midi regions -> no note-offs" - * - * A midi source can be used by multiple tracks simultaneously, - * in which case midi_read() may be called from different tracks for - * overlapping time-ranges. - * - * However there is only a single iterator for a given midi-source. - * This results in every midi_read() performing a seek. - * - * If seeking is performed with - * _model->begin(converter.from(start),...) - * the model is used for seeking. That method seeks to the first - * *note-on* event after 'start'. - * - * _model->begin(converter.from( ) ,..) eventually calls - * Sequence<Time>::const_iterator() in libs/evoral/src/Sequence.cpp - * which looks up the note-event via seq.note_lower_bound(t); - * but the sequence 'seq' only contains note-on events(!). - * note-off events are implicit in Sequence<Time>::operator++() - * via _active_notes.pop(); and not part of seq. - * - * see also http://tracker.ardour.org/view.php?id=6287#c16671 - * - * The linear search below assures that reading starts at the first - * event for the given time, regardless of its event-type. - * - * The performance of this approach is O(N), while the previous - * implementation is O(log(N)). This needs to be optimized: - * The model-iterator or event-sequence needs to be re-designed in - * some way (maybe keep an iterator per playlist). - */ - for (i = _model->begin(); i != _model->end(); ++i) { - if (floor (((i->time().to_double() + start_qn) * tpb) + 0.5) / tpb >= pulse_tick_res) { - break; - } - } - _model_iter_valid = true; - if (!linear_read) { - _model->active_notes().clear(); + /* hot-fix http://tracker.ardour.org/view.php?id=6541 + * "parallel playback of linked midi regions -> no note-offs" + * + * A midi source can be used by multiple tracks simultaneously, + * in which case midi_read() may be called from different tracks for + * overlapping time-ranges. + * + * However there is only a single iterator for a given midi-source. + * This results in every midi_read() performing a seek. + * + * If seeking is performed with + * _model->begin(converter.from(start),...) + * the model is used for seeking. That method seeks to the first + * *note-on* event after 'start'. + * + * _model->begin(converter.from( ) ,..) eventually calls + * Sequence<Time>::const_iterator() in libs/evoral/src/Sequence.cpp + * which looks up the note-event via seq.note_lower_bound(t); + * but the sequence 'seq' only contains note-on events(!). + * note-off events are implicit in Sequence<Time>::operator++() + * via _active_notes.pop(); and not part of seq. + * + * see also http://tracker.ardour.org/view.php?id=6287#c16671 + * + * The linear search below assures that reading starts at the first + * event for the given time, regardless of its event-type. + * + * The performance of this approach is O(N), while the previous + * implementation is O(log(N)). This needs to be optimized: + * The model-iterator or event-sequence needs to be re-designed in + * some way (maybe keep an iterator per playlist). + */ + for (i = _model->begin(); i != _model->end(); ++i) { + if (floor (((i->time().to_double() + start_qn) * tpb) + 0.5) / tpb >= pulse_tick_res) { + break; } -#endif } + _model_iter_valid = true; + if (!linear_read) { + _model->active_notes().clear(); + } +#endif + } - _last_read_end = start + cnt; + _last_read_end = start + cnt; - // Copy events in [start, start + cnt) into dst - for (; i != _model->end(); ++i) { + // Copy events in [start, start + cnt) into dst + for (; i != _model->end(); ++i) { - const framecnt_t time_frames = _session.tempo_map().frame_at_quarter_note (i->time().to_double() + start_qn); - if (time_frames < start + cnt + source_start) { - if (filter && filter->filter(i->buffer(), i->size())) { - DEBUG_TRACE (DEBUG::MidiSourceIO, - string_compose ("%1: filter event @ %2 type %3 size %4\n", - _name, time_frames, i->event_type(), i->size())); - continue; - } - // Offset by source start to convert event time to session time - dst.write (time_frames, i->event_type(), i->size(), i->buffer()); + // Offset by source start to convert event time to session time + + framecnt_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; + + } else { + + /* in range */ + + if (filter && filter->filter(i->buffer(), i->size())) { DEBUG_TRACE (DEBUG::MidiSourceIO, - string_compose ("%1: add event @ %2 type %3 size %4\n", + string_compose ("%1: filter event @ %2 type %3 size %4\n", _name, time_frames, i->event_type(), i->size())); + continue; + } - if (tracker) { - tracker->track (*i); + if (loop_range) { + time_frames = loop_range->squish (time_frames); + } + + dst.write (time_frames, i->event_type(), i->size(), i->buffer()); + +#ifndef NDEBUG + if (DEBUG_ENABLED(DEBUG::MidiSourceIO)) { + DEBUG_STR_DECL(a); + DEBUG_STR_APPEND(a, string_compose ("%1 added event @ %2 sz %3 within %4 .. %5\n", + _name, time_frames, i->size(), + start + source_start, start + cnt + source_start)); + for (size_t n=0; n < i->size(); ++n) { + DEBUG_STR_APPEND(a,hex); + DEBUG_STR_APPEND(a,"0x"); + DEBUG_STR_APPEND(a,(int)i->buffer()[n]); + DEBUG_STR_APPEND(a,' '); } - } else { - DEBUG_TRACE (DEBUG::MidiSourceIO, - string_compose ("%1: reached end with event @ %2 vs. %3\n", - _name, time_frames, start+cnt)); - break; + DEBUG_STR_APPEND(a,'\n'); + DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str()); + } +#endif + + if (tracker) { + tracker->track (*i); } } - return cnt; - } else { - return read_unlocked (lm, dst, source_start, start, cnt, tracker, filter); } + + return cnt; } framecnt_t |