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

@ -257,7 +257,7 @@ MidiRegion::update_after_tempo_map_change (bool /* send */)
/*
set _start to new position in tempo map.
The user probably expects the region contents to maintain audio position as the
The user probably expects the region contents to maintain audio position as the
tempo changes, but AFAICT this requires modifying the src file to use
SMPTE timestamps with the current disk read model (?).
@ -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));
}