change all MIDI read-from-source to map all events into the loop-range for seamless looping (if using)

This commit is contained in:
Paul Davis 2016-09-13 14:10:04 -05:00
parent 182e35235c
commit f41bc70ee9
12 changed files with 245 additions and 169 deletions

View File

@ -70,6 +70,7 @@ public:
* @param buf Destination for events.
* @param start First frame of read range.
* @param cnt Number of frames in read range.
* @param loop_range If non-null, all event times will be mapped into this loop range.
* @param chan_n Must be 0 (this is the audio-style "channel", where each
* channel is backed by a separate region, not MIDI channels, which all
* exist in the same region and are not handled here).
@ -78,6 +79,7 @@ public:
framecnt_t read (Evoral::EventSink<framepos_t>& buf,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n = 0,
MidiChannelFilter* filter = NULL);

View File

@ -65,6 +65,7 @@ protected:
framepos_t position,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const;

View File

@ -24,6 +24,7 @@
#include <vector>
#include "evoral/Beats.hpp"
#include "evoral/Range.hpp"
#include "ardour/ardour.h"
#include "ardour/region.h"
@ -75,6 +76,7 @@ class LIBARDOUR_API MidiRegion : public Region
framecnt_t read_at (Evoral::EventSink<framepos_t>& dst,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n = 0,
NoteMode mode = Sustained,
MidiStateTracker* tracker = 0,
@ -83,6 +85,7 @@ class LIBARDOUR_API MidiRegion : public Region
framecnt_t master_read_at (MidiRingBuffer<framepos_t>& dst,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n = 0,
NoteMode mode = Sustained) const;
@ -130,6 +133,7 @@ class LIBARDOUR_API MidiRegion : public Region
framecnt_t _read_at (const SourceList&, Evoral::EventSink<framepos_t>& dst,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n = 0,
NoteMode mode = Sustained,
MidiStateTracker* tracker = 0,

View File

@ -27,6 +27,7 @@
#include "pbd/stateful.h"
#include "pbd/xml++.h"
#include "evoral/Sequence.hpp"
#include "evoral/Range.hpp"
#include "ardour/ardour.h"
#include "ardour/buffer.h"
#include "ardour/source.h"
@ -82,14 +83,17 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
* \param source_start Start position of the SOURCE in this read context.
* \param start Start of range to be read.
* \param cnt Length of range to be read (in audio frames).
* \param loop_range If non-null, all event times will be mapped into this loop range.
* \param tracker an optional pointer to MidiStateTracker object, for note on/off tracking.
* \param filtered Parameters whose MIDI messages will not be returned.
*/
virtual framecnt_t midi_read (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
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,
@ -210,6 +214,7 @@ class LIBARDOUR_API MidiSource : virtual public Source, public boost::enable_sha
framepos_t position,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const = 0;

View File

@ -94,6 +94,7 @@ public:
framepos_t position,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const;

View File

@ -360,7 +360,8 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
framepos_t loop_start = 0;
framepos_t loop_end = 0;
framepos_t loop_length = 0;
get_location_times(loop_loc, &loop_start, &loop_end, &loop_length);
get_location_times (loop_loc, &loop_start, &loop_end, &loop_length);
adjust_capture_position = 0;
@ -506,7 +507,7 @@ MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t
playback_distance = nframes;
}
if (need_disk_signal) {
if (need_disk_signal && !_session.declick_out_pending()) {
/* copy the diskstream data to all output buffers */
MidiBuffer& mbuf (bufs.get_midi (0));
@ -703,42 +704,48 @@ int
MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
{
framecnt_t this_read = 0;
bool reloop = false;
framepos_t loop_end = 0;
framepos_t loop_start = 0;
framecnt_t loop_length = 0;
Location* loc = 0;
Location* loc = loop_location;
framepos_t effective_start = start;
Evoral::Range<framepos_t>* loop_range (0);
MidiTrack* mt = dynamic_cast<MidiTrack*>(_track);
MidiChannelFilter* filter = mt ? &mt->playback_filter() : NULL;
if (!reversed) {
frameoffset_t loop_offset = 0;
loc = loop_location;
get_location_times(loc, &loop_start, &loop_end, &loop_length);
/* if we are looping, ensure that the first frame we read is at the correct
position within the loop.
*/
if (loc && (start >= loop_end)) {
//cerr << "start adjusted from " << start;
start = loop_start + ((start - loop_start) % loop_length);
//cerr << "to " << start << endl;
}
// cerr << "start is " << start << " end " << start+dur << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
if (!reversed && loc) {
get_location_times (loc, &loop_start, &loop_end, &loop_length);
}
while (dur) {
/* take any loop into account. we can't read past the end of the loop. */
if (loc && (loop_end - start <= dur)) {
this_read = loop_end - start;
// cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl;
reloop = true;
if (loc && !reversed) {
if (!loop_range) {
loop_range = new Evoral::Range<framepos_t> (loop_start, loop_end-1); // inclusive semantics require -1
}
/* if we are (seamlessly) looping, ensure that the first frame we read is at the correct
position within the loop.
*/
effective_start = loop_range->squish (effective_start);
if ((loop_end - effective_start) <= dur) {
/* too close to end of loop to read "dur", so
shorten it.
*/
this_read = loop_end - effective_start;
} else {
this_read = dur;
}
} else {
reloop = false;
this_read = dur;
}
@ -746,9 +753,11 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
break;
}
this_read = min(dur,this_read);
this_read = min (dur,this_read);
if (midi_playlist()->read (*_playback_buf, start, this_read, 0, filter) != this_read) {
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS ::read at %1 for %2 loffset %3\n", effective_start, this_read, loop_offset));
if (midi_playlist()->read (*_playback_buf, effective_start, this_read, loop_range, 0, filter) != this_read) {
error << string_compose(
_("MidiDiskstream %1: cannot read %2 from playlist at frame %3"),
id(), this_read, start) << endmsg;
@ -765,14 +774,16 @@ MidiDiskstream::read (framepos_t& start, framecnt_t dur, bool reversed)
} else {
/* if we read to the end of the loop, go back to the beginning */
if (reloop) {
// Synthesize LoopEvent here, because the next events
// written will have non-monotonic timestamps.
start = loop_start;
} else {
start += this_read;
}
/* adjust passed-by-reference argument (note: this is
monotonic and does not reflect looping.
*/
start += this_read;
/* similarly adjust effective_start, but this may be
readjusted for seamless looping as we continue around
the loop.
*/
effective_start += this_read;
}
dur -= this_read;
@ -795,6 +806,10 @@ MidiDiskstream::do_refill ()
size_t write_space = _playback_buf->write_space();
bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MDS refill, write space = %1 file frame = %2\n",
write_space, file_frame));
/* no space to write */
if (write_space == 0) {
return 0;
}
@ -808,22 +823,15 @@ MidiDiskstream::do_refill ()
return 0;
}
/* no space to write */
if (_playback_buf->write_space() == 0) {
return 0;
}
uint32_t frames_read = g_atomic_int_get(&_frames_read_from_ringbuffer);
uint32_t frames_written = g_atomic_int_get(&_frames_written_to_ringbuffer);
if ((frames_read < frames_written) && (frames_written - frames_read) >= midi_readahead) {
return 0;
}
framecnt_t to_read = midi_readahead - ((framecnt_t)frames_written - (framecnt_t)frames_read);
//cout << "MDS read for midi_readahead " << to_read << " rb_contains: "
// << frames_written - frames_read << endl;
to_read = min (to_read, (framecnt_t) (max_framepos - file_frame));
to_read = min (to_read, (framecnt_t) write_space);
@ -1077,7 +1085,7 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
_write_source->drop_references ();
_write_source.reset();
}
}
}
}
@ -1428,25 +1436,21 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes)
Location* loc = loop_location;
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose (
"%1 MDS pre-read read %8 @ %4..%5 from %2 write to %3, LOOPED ? %6-%7\n", _name,
"%1 MDS pre-read read %8 offset = %9 @ %4..%5 from %2 write to %3, LOOPED ? %6 .. %7\n", _name,
_playback_buf->get_read_ptr(), _playback_buf->get_write_ptr(), playback_sample, playback_sample + nframes,
(loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes));
(loc ? loc->start() : -1), (loc ? loc->end() : -1), nframes, Port::port_offset()));
// cerr << "================\n";
// _playback_buf->dump (cerr);
// cerr << "----------------\n";
//cerr << "======== PRE ========\n";
//_playback_buf->dump (cerr);
//cerr << "----------------\n";
size_t events_read = 0;
const size_t split_cycle_offset = Port::port_offset ();
if (loc) {
framepos_t effective_start;
if (playback_sample >= loc->end()) {
effective_start = loc->start() + ((playback_sample - loc->end()) % loc->length());
} else {
effective_start = playback_sample;
}
Evoral::Range<framepos_t> loop_range (loc->start(), loc->end() - 1);
effective_start = loop_range.squish (playback_sample);
DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("looped, effective start adjusted to %1\n", effective_start));
@ -1505,6 +1509,10 @@ MidiDiskstream::get_playback (MidiBuffer& dst, framecnt_t nframes)
_playback_buf->get_read_ptr(), _playback_buf->get_write_ptr()));
g_atomic_int_add (&_frames_read_from_ringbuffer, nframes);
//cerr << "======== POST ========\n";
//_playback_buf->dump (cerr);
//cerr << "----------------\n";
}
bool

View File

@ -110,6 +110,7 @@ framecnt_t
MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst,
framepos_t start,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
unsigned chan_n,
MidiChannelFilter* filter)
{
@ -190,7 +191,11 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst,
}
/* Read from region into target. */
mr->read_at (tgt, start, dur, chan_n, _note_mode, &tracker->tracker, filter);
DEBUG_TRACE (DEBUG::MidiPlaylistIO, string_compose ("read from %1 at %2 for %3 LR %4 .. %5\n",
mr->name(), start, dur,
(loop_range ? loop_range->from : -1),
(loop_range ? loop_range->to : -1)));
mr->read_at (tgt, start, dur, loop_range, chan_n, _note_mode, &tracker->tracker, filter);
DEBUG_TRACE (DEBUG::MidiPlaylistIO,
string_compose ("\tPost-read: %1 active notes\n", tracker->tracker.on()));
@ -202,7 +207,7 @@ MidiPlaylist::read (Evoral::EventSink<framepos_t>& dst,
string_compose ("\t%1 ended, resolve notes and delete (%2) tracker\n",
mr->name(), ((new_tracker) ? "new" : "old")));
tracker->tracker.resolve_notes (tgt, (*i)->last_frame());
tracker->tracker.resolve_notes (tgt, loop_range ? loop_range->squish ((*i)->last_frame()) : (*i)->last_frame());
if (!new_tracker) {
_note_trackers.erase (t);
}

View File

@ -125,7 +125,9 @@ framecnt_t
MidiPlaylistSource::read_unlocked (const Lock& lock,
Evoral::EventSink<framepos_t>& dst,
framepos_t /*position*/,
framepos_t start, framecnt_t cnt,
framepos_t start,
framecnt_t cnt,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker*,
MidiChannelFilter*) const
{
@ -135,7 +137,7 @@ MidiPlaylistSource::read_unlocked (const Lock& lock,
return 0;
}
return mp->read (dst, start, cnt);
return mp->read (dst, start, cnt, loop_range);
}
framecnt_t

View File

@ -334,18 +334,24 @@ framecnt_t
MidiRegion::read_at (Evoral::EventSink<framepos_t>& out,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n,
NoteMode mode,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const
{
return _read_at (_sources, out, position, dur, chan_n, mode, tracker, filter);
return _read_at (_sources, out, position, dur, loop_range, chan_n, mode, tracker, filter);
}
framecnt_t
MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out, framepos_t position, framecnt_t dur, uint32_t chan_n, NoteMode mode) const
MidiRegion::master_read_at (MidiRingBuffer<framepos_t>& out,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n,
NoteMode mode) const
{
return _read_at (_master_sources, out, position, dur, chan_n, mode); /* no tracker */
return _read_at (_master_sources, out, position, dur, loop_range, chan_n, mode); /* no tracker */
}
framecnt_t
@ -353,6 +359,7 @@ MidiRegion::_read_at (const SourceList& /*srcs*/,
Evoral::EventSink<framepos_t>& dst,
framepos_t position,
framecnt_t dur,
Evoral::Range<framepos_t>* loop_range,
uint32_t chan_n,
NoteMode mode,
MidiStateTracker* tracker,
@ -392,30 +399,34 @@ MidiRegion::_read_at (const SourceList& /*srcs*/,
src->set_note_mode(lm, mode);
/*
cerr << "MR " << name () << " read @ " << position << " * " << to_read
<< " _position = " << _position
<< " _start = " << _start
<< " intoffset = " << internal_offset
<< " pulse = " << pulse()
<< " start_pulse = " << start_pulse()
<< " start_beat = " << _start_beats
<< endl;
*/
#if 0
cerr << "MR " << name () << " read @ " << position << " + " << to_read
<< " dur was " << dur
<< " len " << _length
<< " l-io " << (_length - internal_offset)
<< " _position = " << _position
<< " _start = " << _start
<< " intoffset = " << internal_offset
<< " pulse = " << pulse()
<< " start_pulse = " << start_pulse()
<< " start_beat = " << _start_beats
<< endl;
#endif
/* This call reads events from a source and writes them to `dst' timed in session frames */
if (src->midi_read (
lm, // source lock
dst, // destination buffer
_position - _start, // start position of the source in session frames
_start + internal_offset, // where to start reading in the source
to_read, // read duration in frames
tracker,
filter,
_filtered_parameters,
pulse(),
start_pulse()
dst, // destination buffer
_position - _start, // start position of the source in session frames
_start + internal_offset, // where to start reading in the source
to_read, // read duration in frames
loop_range,
tracker,
filter,
_filtered_parameters,
pulse(),
start_pulse()
) != to_read) {
return 0; /* "read nothing" */
}

View File

@ -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();
}
#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();
}
#endif
// 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();
}
_last_read_end = start + cnt;
// 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());
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("%1: add event @ %2 type %3 size %4\n",
_name, time_frames, i->event_type(), i->size()));
if (tracker) {
tracker->track (*i);
}
} else {
DEBUG_TRACE (DEBUG::MidiSourceIO,
string_compose ("%1: reached end with event @ %2 vs. %3\n",
_name, time_frames, start+cnt));
#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;
}
}
return cnt;
} else {
return read_unlocked (lm, dst, source_start, start, cnt, tracker, filter);
_model_iter_valid = true;
if (!linear_read) {
_model->active_notes().clear();
}
#endif
}
_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
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: filter event @ %2 type %3 size %4\n",
_name, time_frames, i->event_type(), i->size()));
continue;
}
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,' ');
}
DEBUG_STR_APPEND(a,'\n');
DEBUG_TRACE (DEBUG::MidiSourceIO, DEBUG_STR(a).str());
}
#endif
if (tracker) {
tracker->track (*i);
}
}
}
return cnt;
}
framecnt_t

View File

@ -377,7 +377,7 @@ Plugin::resolve_midi ()
*/
_pending_stop_events.get_midi(0).clear ();
_tracker.resolve_notes (_pending_stop_events.get_midi (0), /* split cycle offset*/ Port::port_offset());
_tracker.resolve_notes (_pending_stop_events.get_midi (0), 0);
_have_pending_stop_events = true;
}

View File

@ -215,6 +215,7 @@ SMFSource::read_unlocked (const Lock& lock,
framepos_t const source_start,
framepos_t start,
framecnt_t duration,
Evoral::Range<framepos_t>* loop_range,
MidiStateTracker* tracker,
MidiChannelFilter* filter) const
{
@ -288,6 +289,10 @@ SMFSource::read_unlocked (const Lock& lock,
*/
const framepos_t ev_frame_time = converter.to(Evoral::Beats::ticks_at_rate(time, ppqn())) + source_start;
if (loop_range) {
loop_range->squish (ev_frame_time);
}
if (ev_frame_time < start + duration) {
if (!filter || !filter->filter(ev_buffer, ev_size)) {
destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
@ -793,5 +798,3 @@ SMFSource::prevent_deletion ()
_flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
}