Do not yield stale frames from blocks() when out= is longer than the file - #495
Do not yield stale frames from blocks() when out= is longer than the file#495dylanpulver wants to merge 1 commit into
Conversation
… file The last block was sliced as out[:frames + overlap], where frames is the number of frames still unread. That is only the valid length when the iteration already carries overlap frames at the front of the buffer. On the first pass output_offset is 0, so the slice reaches overlap frames past the data actually read, and with out= those frames are whatever the caller left in the array. The valid length is output_offset + toread on every pass. Slicing to it also removes the need for the blocksize > frames + overlap guard, which did not fire when blocksize == frames + overlap and yielded the whole buffer instead. bastibe#446 fixed the same arithmetic for the out is None path by allocating min(blocksize, frames); a caller-supplied array cannot be shrunk, so that path kept the defect.
|
I must confess, I do not understand the problem, nor the solution (and don't have the time to look into it). Could you explain the problem with a few more examples of how it works currently, what is wrong with it, and how this PR fixes it? |
|
Sorry — the description was far too long to be useful. Plainly:
Two examples, both on master today. The A 1-frame stereo file, into a 3x2 buffer, A 2-frame mono file, into a 4-frame buffer, The first file holds one frame, so one frame is all that can come back. The other two rows are the caller's own data being presented as audio. Why: the yielded slice is It only appears when Happy to close this instead if #213 (dropping |
blocks()should only yield frames that came out of the file. When the caller suppliesout=and that array is longer than the file, it also yields part of the caller's own array.A 1-frame stereo file, read into a 3x2 buffer with
overlap=2:Cause.
soundfile.py:1208slices the block asout[:frames + overlap], whereframesis the number of frames still unread. That is only the right length once the buffer already carriesoverlapframes from the previous block; on the first passoutput_offsetis 0, so the slice runsoverlapframes past the real data. This usesoutput_offset + toread, correct on every pass.#446 fixed the same arithmetic for
out is Noneby allocatingmin(blocksize, frames). A caller-supplied array cannot be shrunk, so that path kept the defect. The existingout=tests use a file longer thanout, so the intersection was untested.Verification (libsndfile 1.2.2, same venv both legs). An oracle written from the docstring, not from the source, over a 196-cell grid: 70 failures before, 0 after on the
out=path, and 0 in both legs on theblocksize=path — that second number validates the oracle rather than the fix.pytest331 -> 339. Reverting the slice fails the 8 new tests and no others; the naiveout[:frames]breaks theblocksize=path, so dropping the overlap term is not the fix.Not covered: WAV/DOUBLE and mono int16 only, no non-seekable file.
overlap_memory = np.copy(out[-overlap:])reads the same stale region but is unreachable today, so it is untouched.If #213 (removing
out=) is still the direction, close this instead.Written with AI assistance (Claude).