D23: refuse the E-mu whole-extent "no loop" at both ends, not only at frame 0 - #46
Merged
Conversation
… frame 0 The E-mu format's "no loop" is the sample's own bounds written into the loop pointers, inset by a small fixed amount at *both* ends (ditto-drums (12,12), EIIIX (4,4), esi32-gm/protozoa (12,10)). The guard refused it only where the start was exactly 0, so ten discs shipped a smpl chunk looping the whole file -- 934 of ditto-drums's 948 records. Measured across all ten reference discs, the inset whole-extent population is a filled-in no-loop: 70-100% ends in silence (real loops 13-33%), the bounds are the record's own extent inset by a fixed constant (real loops sit at arbitrary positions), and it carries a uniquely-splicing loop point on 0-11% of records (real loops 33-56%). The eiv-studio/eiv-vitous loops ADR-0025 validated are the kept population; only the whole-extent no-loops are refused. Widen the guard to a <= FULL_EXTENT_SLACK. No audio moves -- read_file and the offset arithmetic are untouched, every payload digest is unchanged; only loop emission moves, on every disc. See ADR-0030 and docs/formats/emu3.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #41.
What this does
sample/emu3.pyrefuses a loop spanning a sample's whole declared extent, because that span is the E-mu format's "no loop": the sampler fills the four loop pointers with the sample's own bounds when nothing set them, and emitting it writes asmplchunk telling a DAW to loop the entire file. The guard wasa == 0 and b >= extent − FULL_EXTENT_SLACK— slack at the end, an exact zero required at the start.The discs do not write those bounds at frame 0. They write them inset by a small fixed amount at both ends —
loop_start = start + C1,loop_end = end − C2for a per-disc constant of a handful of bytes:ditto-drums(12, 12), the EIIIX discs(4, 4),esi32-gm/protozoa/eiv-vitous/eiv-analogia(12, 10). So the frame-0 guard never fired, and ten discs shipped a loop over the whole file — 934 ofditto-drums's 948 records most visibly.This widens the guard to refuse a loop within
FULL_EXTENT_SLACKframes of both ends:What the discs showed
Widening was not obviously right — a sustained organ or string can legitimately loop over nearly its whole length, and a rule that deletes those to catch drum hits would destroy real loop points. It was measured against all ten reference discs before the guard was written.
The shape/join correlation is the wrong instrument here, exactly as the issue warned: a whole-extent loop starts within a few frames of 0 and ends within a few of the last, so there is almost no audio before its start for the windowed correlation to score. Two measurements that do apply separate the populations on every disc:
esi32-gmprotozoaeiiix-1eiiix-2emu-classicsvintageditto-drumseiv-analogiaeiv-studioeiv-vitousThe right-hand column is that same uniqueness measured on the disc's real loops — the calibration — and the inset population sits far below it every time. Structure agrees with content: the inset bounds are the record's own extent inset by a fixed constant (an auto-filled field), where a hand-set loop start is at an arbitrary musical position (44, 136, 11 204, 52 536, never a fixed few).
eiv-vitousandeiv-studioare the check that matters most: ADR-0025 validated their loops by the shape test at +0.68 and +0.86, and those validated loops are the real-loop column — they are kept. What is refused there is a separate whole-extent population that ends in silence.eiv-analogia's loops never had independent evidence (ADR-0025: 34 scored, showed nothing), so refusing 443 of its 449 takes nothing that was ever established.Effect
Loop counts move on every disc; no audio moves.
read_fileand the offset arithmetic are untouched, so every per-disc payload digest, and the sample and stereo counts, are unchanged — only the loop-count column moved, and the disc-backed suite pins the new numbers.esi32-gmditto-drumsprotozoaeiv-analogiaeiiix-1eiv-studioeiiix-2eiv-vitousemu-classicsvintageEnd-to-end:
ditto-drumsnow writes 948 WAVs of which 14 carry a loop, down from 948.Anyone who extracted an E-mu disc before this got a
smplchunk looping the whole file on a large fraction of its samples — harmless where a DAW ignoressmpl, wrong where it does not. The audio was always right.Alternatives rejected
a == 0— ships the bug on ten discs.(C1, C2)inset — needs a per-disc constant in the parser and buys nothing the simple both-ends slack does not.What it deliberately does not claim
It catches the whole-extent-with-inset form the ten discs write, not every "no loop" a future generation might encode. And on
esi32-gmandprotozoaabout 10 % of the refused records splice uniquely and could conceivably be a near-whole-extent loop the disc intended; they are refused with the rest because they are structurally the record's own bounds inset by the same fixed constant, and separating them would need the content test this project keeps out of the parser. That cost is recorded rather than hidden (ADR-0030).🤖 Generated with Claude Code