Fix Temporal::Range::squish reduced to samples

Loop Location start="a1665678660" end="b145920"
Loop-end (at 122BPM) is a2109859636
at 48kHz this is sample 1794098.32

Now play the loop and play sample 1794098 = a2109859248
Range::squish start: a1665678660 end: a2109859636 squish: a2109859248

squish() does nothing, since there are still 388 superclock-ticks
until the end of the loop.

However, DiskReader::get_midi_playback convertes the value back
to samples(), this leads to effective_start == loop_end;
resulting in an endless loop.

Thanks to MikeLupe to provide a session to reproduce this issue.
This commit is contained in:
Robin Gareus 2022-07-15 19:54:21 +02:00
parent 419f934ecd
commit 2de84c97d0
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
2 changed files with 13 additions and 2 deletions

View File

@ -1466,8 +1466,13 @@ DiskReader::get_midi_playback (MidiBuffer& dst, samplepos_t start_sample, sample
Location* loc = _loop_location;
if (loc) {
/* Evoral::Range has inclusive range semantics. Ugh. Hence the -1 */
const Temporal::Range loop_range (loc->start (), loc->end ());
/* squish() operates in the location's time-domain. When the location was created
* using music-time, and later converted to audio-time, it can happen that the
* corresponding super-clock is "between samples". e.g loop-end is at sample 1000.12.
* if start_sample = 1000; squish() does nothing because 1000 < 1000.12.
* This is solved by creating the range using (rounded) sample-times.
*/
const Temporal::Range loop_range (loc->start ().samples(), loc->end ().samples());
samplepos_t effective_start = start_sample;
samplecnt_t cnt = nframes;
sampleoffset_t offset = 0;

View File

@ -150,6 +150,7 @@ class LIBTEMPORAL_API Range {
*/
Range (timepos_t const & s, timepos_t const & e) : _start (s), _end (e) {}
Range (samplepos_t const s, samplepos_t const e) : _start (s), _end (e) {}
bool empty() const { return _start == _end; }
timecnt_t length() const { return _start.distance (_end); }
@ -165,6 +166,11 @@ class LIBTEMPORAL_API Range {
return other._start == _start && other._end == _end;
}
void force_to_samples () {
_start = timepos_t((samplepos_t)_start.samples ());
_end = timepos_t((samplepos_t)_end.samples ());
}
/** for a T, return a mapping of it into the range (used for
* looping). If the argument is earlier than or equal to the end of
* this range, do nothing.