summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2016-09-13 14:10:04 -0500
committerPaul Davis <paul@linuxaudiosystems.com>2016-09-13 14:11:29 -0500
commitf41bc70ee900257d2d9008f3a749981b10de16bc (patch)
tree37f3f5950af81cd5f958da0a37a381dbf256c74d /libs/ardour
parent182e35235c41abb6ec7ab92897be0a1a228dd004 (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')
-rw-r--r--libs/ardour/ardour/midi_playlist.h2
-rw-r--r--libs/ardour/ardour/midi_playlist_source.h1
-rw-r--r--libs/ardour/ardour/midi_region.h4
-rw-r--r--libs/ardour/ardour/midi_source.h5
-rw-r--r--libs/ardour/ardour/smf_source.h1
-rw-r--r--libs/ardour/midi_diskstream.cc114
-rw-r--r--libs/ardour/midi_playlist.cc9
-rw-r--r--libs/ardour/midi_playlist_source.cc6
-rw-r--r--libs/ardour/midi_region.cc57
-rw-r--r--libs/ardour/midi_source.cc188
-rw-r--r--libs/ardour/plugin.cc2
-rw-r--r--libs/ardour/smf_source.cc7
12 files changed, 236 insertions, 160 deletions
diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h
index a84c464801..ad0a812cba 100644
--- a/libs/ardour/ardour/midi_playlist.h
+++ b/libs/ardour/ardour/midi_playlist.h
@@ -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);
diff --git a/libs/ardour/ardour/midi_playlist_source.h b/libs/ardour/ardour/midi_playlist_source.h
index 12c184d7b5..7e54cde788 100644
--- a/libs/ardour/ardour/midi_playlist_source.h
+++ b/libs/ardour/ardour/midi_playlist_source.h
@@ -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;
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
index 03166bb0c3..b19751aaa5 100644
--- a/libs/ardour/ardour/midi_region.h
+++ b/libs/ardour/ardour/midi_region.h
@@ -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,
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
index c2d029c63b..5e3484bb2f 100644
--- a/libs/ardour/ardour/midi_source.h
+++ b/libs/ardour/ardour/midi_source.h
@@ -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;
diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h
index 8f58fda2fe..979c20dd21 100644
--- a/libs/ardour/ardour/smf_source.h
+++ b/libs/ardour/ardour/smf_source.h
@@ -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;
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index 13f1d7e549..708884900c 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -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) {
-
- loc = loop_location;
- get_location_times(loc, &loop_start, &loop_end, &loop_length);
+ frameoffset_t loop_offset = 0;
- /* 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
diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc
index e3274c77b1..b845758f47 100644
--- a/libs/ardour/midi_playlist.cc
+++ b/libs/ardour/midi_playlist.cc
@@ -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);
}
diff --git a/libs/ardour/midi_playlist_source.cc b/libs/ardour/midi_playlist_source.cc
index 65c2794947..8b7e87889e 100644
--- a/libs/ardour/midi_playlist_source.cc
+++ b/libs/ardour/midi_playlist_source.cc
@@ -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
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
index 67bd7a1b3d..24e4b21a4a 100644
--- a/libs/ardour/midi_region.cc
+++ b/libs/ardour/midi_region.cc
@@ -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" */
}
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
diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc
index a0ac97b440..7d04c3f5d7 100644
--- a/libs/ardour/plugin.cc
+++ b/libs/ardour/plugin.cc
@@ -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;
}
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
index dac78cd020..22245abe52 100644
--- a/libs/ardour/smf_source.cc
+++ b/libs/ardour/smf_source.cc
@@ -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));
}
-
-