Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ Notable changes to `samplerdisc`. Format-level findings live in [docs/formats/](

### Added

- **E-mu stereo samples come out as stereo.** The sample record's pointer block declares a channel count, and where it declares two the payload is a **block** split — all of the left channel, then all of the right. Read as one mono stream that is a file twice as long as the sound, with the right channel playing after the left instead of alongside it, and that is what this project wrote for **2 656 of the 14 738** E-mu samples: 28, 601, 592, 8, 279, 320 and 828 across the seven reference discs. `Miroslav Vitous String Ensembles` is all of it — 828 of 828. They are now interleaved into one stereo WAV each. ([docs/formats/emu3.md](docs/formats/emu3.md), [ADR-0026](docs/adr/0026-the-record-declares-the-channel-count.md), [#32](https://github.com/bmxcode/samplerdisc/issues/32))

**A channel count is not enough on its own.** 2 721 records declare `start_R` half a payload on, and **65 of them close `end_L` somewhere else** — 19 on `protozoa`, 40 on `eiiix-1`, 6 on `eiiix-2`. Those 65 are not stereo: their halves correlate at 0.01 on fine structure and 0.01 at best lag, which is the score of two unrelated records, against 0.40 and 0.53 for the known-true stereo pairs on the same shelf. On `protozoa` six of them can be identified exactly: the first half of each is, byte for byte, the whole of a one-channel record of the same name in another bank, nothing matches the second half, and `end_L` closes 8 bytes past the halfway point rather than on it. Requiring the left block to close exactly where the right one opens is what separates them, and those 65 stay mono.

**The obvious measurement is confounded, which is why it took two more instruments.** A single decaying note's two halves both decay and correlate at 0.94 by RMS envelope without being two channels of anything — every disc has a tail of those, named `Piano Db3` and `Glockenspiel D5`. Dividing the envelope by its own trend, and correlating the waveform at a lag, separates them; the twelve name-paired `-L`/`-R` records on `eiv-analogia` served as a positive control that the pointer block knows nothing about.

**Nothing else moved, and that is asserted.** The SHA-256 of every sample payload per disc is unchanged on all seven, the sample counts are unchanged — one record is one sample, only its shape changed — and all seven loop counts are unchanged: 107, 1 157, 1 260, 1 689, 449, 2 551, 826. The suite now also de-interleaves each stereo sample and requires it to reproduce the disc's two blocks byte for byte, because audio that moved has to be shown to be the same audio.

**The mono halves are not written alongside**, unlike the `-L`/`-R` pairs `stereo.py` joins. That rule exists because name pairing is a heuristic and the halves are how a user notices a bad guess; here the disc states the channel count and two further fields of the same record confirm it, so there is nothing to hedge — and the half would be one leg of a concatenation, not a sound anyone recorded.

- **E-mu samples carry their loop points.** The 92-byte sample record's undecoded middle is an eight-pointer block — a start, an end, a loop start and a loop end, per channel, as byte offsets from the record's own start — and **8 039 of the 14 738** E-mu samples now come out with those loop points in the WAV's `smpl` chunk: 107, 1 157, 1 260, 1 689, 449, 2 551 and 826 across the seven reference discs. Every other backend has carried this since D7; the E-mu path carried none. ([docs/formats/emu3.md](docs/formats/emu3.md), [ADR-0025](docs/adr/0025-the-loop-is-decoded-the-root-key-is-not.md))

**The loop was established from the audio, not from the structure fitting.** Correlating the waveform at the loop start against the waveform at the loop end gives **+0.64 to +0.86 on six of the seven discs**, against a control — the same loop end with the start put somewhere else — of about zero on all of them. Three publishers and both E-mu generations agree. `eiv-analogia` is the exception and is reported as one: only 34 of its records carry audio loud enough at both ends to score, and those 34 show nothing, so its loops rest on the rule the other six establish.
Expand Down Expand Up @@ -84,7 +94,7 @@ Notable changes to `samplerdisc`. Format-level findings live in [docs/formats/](

### Known limits

- **E-mu stereo samples are written as double-length mono.** Roughly **2 700 of the 14 738** E-mu samples declare two channels in their record, stored as a block of left followed by a block of right. Reading the payload as one mono stream concatenates them. The earlier finding that "everything is mono" tested *interleaved* stereo — a sound refutation of the wrong hypothesis, since de-interleaving cannot detect a block split. Measured directly, the two halves of a two-channel record are the same performance, correlating by RMS envelope at 0.99, 0.96 and 0.95 on three discs against 0.13–0.26 for one-channel records. Not fixed here: correcting it moves audio, and this release was scoped to add metadata without touching a byte of payload. ([docs/formats/emu3.md](docs/formats/emu3.md), [ADR-0025](docs/adr/0025-the-loop-is-decoded-the-root-key-is-not.md))
- **65 E-mu records declare two channels and are not stereo, and only `protozoa` says what they are.** There the second half of a rejected record is another bank's, at the constant shift [ADR-0021](docs/adr/0021-a-bank-owns-the-run-its-header-declares.md) describes. On `eiiix-1`'s 40 and `eiiix-2`'s 6 the halves are measurably not one performance and nothing accounts for the second one. They come out mono, which is the safe answer rather than an explained one. ([docs/formats/emu3.md](docs/formats/emu3.md), [ADR-0026](docs/adr/0026-the-record-declares-the-channel-count.md))

- **The ISO 9660 directory hierarchy is not preserved.** A disc's audio is written flat into one directory per volume, so `PS-34 AIFF …/056_Ballad de Boo/34a-bas-56Dmin.aif` becomes `34a-bas-56Dmin.wav` and the folder that grouped it by tempo is gone. Nothing is lost on these discs — Best Service named every file uniquely, and all thirteen list zero collisions after flattening — but a disc that reused a basename would rely on `unique_path` suffixing it.

Expand All @@ -94,8 +104,6 @@ Notable changes to `samplerdisc`. Format-level findings live in [docs/formats/](
- **A declared partition the image has no header at is skipped, never searched for.** Nine of the 44 AKAI discs are short of the disc they were made from, and on those the missing header sits *earlier* — displaced by a whole number of the container's own 32 KB blocks, accumulating down the disc, from 4 blocks on `Best Service - Alpha Dance I` to 7 288 on `AKAI.S3000.Sound.Library.7`. Roughly fifteen thousand readable files stay unread on those images, which is deliberate rather than a gap in the walk: a search would find their partitions, and the audio inside a short image is displaced by the same missing blocks that moved the header — the nine wrong files above are the standing evidence of what displaced audio extracts as. `list` prints how many partitions the disc declares against how many the image holds, so the shortfall is a stated fact and not an absence. ([docs/formats/akai-fs.md](docs/formats/akai-fs.md), [ADR-0023](docs/adr/0023-partitions-come-from-the-table-the-disc-declares.md), [#25](https://github.com/bmxcode/samplerdisc/issues/25))
- **100 of `Studio Essentials`'s 230 banks list without extracting.** They have no confirmed sample directory, and carry a note saying so rather than being guessed at. That disc holds 901 `E4P1` presets and preset-only banks are the likely explanation — likely is not established, so it is not claimed.
- **An E-mu bank whose `0x34` is damaged will list empty and say the header declares no sample area.** The field bounds the walk now, so the note that follows an empty bank restates that bound rather than corroborating it independently — the note is true about the header and would be wrong about the bank. The alternative was measured and is worse: `protozoa`'s index bank would be credited with 63 of the Phatt banks' records. ([ADR-0021](docs/adr/0021-a-bank-owns-the-run-its-header-declares.md))
- **Loop points and root key are still absent from E-mu WAVs.** Eight fields in the 92-byte sample header are undecoded and some are very likely those. Decoding them changes the *shared* record parser and would alter every E-mu sample already extracted, so it is its own piece of work rather than a rider on this one.
- **The E-mu sample record has no channel count.** The paired length fields at `+26`/`+30` and `+34`/`+50` look exactly like one — `+34 == 2 × (+30) − 90` on both EIII and E-IV — and measurement says otherwise: de-interleaving any of these payloads as stereo roughly doubles its sample-to-sample delta, the known-good `Piano E0` included. Everything is mono, and stereo pairs are joined by name as on every other format.

## 0.3.0 — 2026-08-20

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ out/AKAI.S3000.Sound.Library.1/

Extraction writes one WAV per sample, grouped by volume. Where a disc stores stereo as split mono files — the AKAI `-L` / `-R` convention — you also get a joined stereo WAV, and the mono originals are kept alongside it rather than replaced.

Some samples are stereo on the disc itself rather than paired by name: an E-mu record declares its own channel count, and 2 656 of the 14 738 E-mu samples here declare two. Those come out as one stereo WAV under the sample's own name — not in `stereo/`, which means "rebuilt from two files", and with no mono halves to keep, because nothing was guessed at ([ADR-0026](docs/adr/0026-the-record-declares-the-channel-count.md)).

The audio is a byte-for-byte copy: AKAI stores signed 16-bit little-endian PCM and so does WAV, so there is no resampling, no bit-depth change and no dithering anywhere in the process. Loop points, root key and tuning from the disc are written into the WAV's standard `smpl` chunk, so a DAW that understands them picks them up and one that doesn't sees an ordinary WAV.

`--keep-originals` additionally writes each sample and program out byte-for-byte as the sampler stored it, into an `original/` folder beside the WAVs. Two reasons to want it: programs hold the key ranges and envelopes, which a WAV cannot carry and which are otherwise left on the disc; and the files are named by the generation that wrote them — `.s3p`/`.s3s` for an S3000 disc, `.s1p`/`.s1s` for an S1000 — which is the shape [ConvertWithMoss](https://github.com/git-moss/ConvertWithMoss) wants for turning programs into a playable instrument.
Expand Down Expand Up @@ -92,7 +94,9 @@ By filesystem:
| ISO 9660 | 15 | 11 601 | — | 0 |
| Roland `S770 MR25A` | 5 | 6 392 | 1 341 | 0 |

Every WAV was checked against the disc it came from — **70 of 70 discs match exactly**, comparing multisets of SHA-256 over the PCM per disc rather than going via filenames, so duplicate names cannot mask a mismatch and no path is guessed. 89 125 payloads, zero mismatches. The two audio CDs are not in that count: their tracks are cut from a stream by a cue, so there is no run of bytes on the disc to compare a track against.
"Stereo pairs" counts files joined from an `-L`/`-R` pair by name. E-mu's six are the only ones on those discs, and they are a different and much rarer thing than the 2 656 samples whose record declares two channels.

Every WAV was checked against the disc it came from — **70 of 70 discs match exactly**, comparing multisets of SHA-256 over the PCM per disc rather than going via filenames, so duplicate names cannot mask a mismatch and no path is guessed. 89 125 payloads, zero mismatches. The E-mu stereo samples are compared with their channels put back the way the disc stored them, since their WAV holds the same bytes interleaved; `tests/test_discs.py` asserts that de-interleaving reproduces the disc's two blocks exactly, per sample, on all seven discs. The two audio CDs are not in that count: their tracks are cut from a stream by a cue, so there is no run of bytes on the disc to compare a track against.

105 082 WAV files were written in all — the samples, the stereo joins and the audio CD tracks. None is unreadable and none is zero-length. **275 are silent for their whole length, and every one of them matches the disc exactly**: 267 are the blank `15G-KIT…Z` slots on `ProSamples vol.15`, six are on a Proteus library that ships `Dead Air` as a sample, and two are on a Roland disc. That is what the discs hold, not something the decoder did.

Expand Down
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@ All of that vanishes into a working parser. Six months on, the code says *what*
| D15 | Every partition of an AKAI disc, from the table it declares ([ADR-0023](adr/0023-partitions-come-from-the-table-the-disc-declares.md)) | done |
| D16 | AIFF payloads converted, deduplicated against their WAV twin, and EXS24/HALion instruments kept ([ADR-0024](adr/0024-the-aiff-twin-is-converted-and-deduplicated.md)) | done |
| D17 | E-mu loop points in the WAV `smpl` chunk ([ADR-0025](adr/0025-the-loop-is-decoded-the-root-key-is-not.md)) | done |
| D18 | E-mu stereo samples decoded from the record's channel count ([ADR-0026](adr/0026-the-record-declares-the-channel-count.md)) | done |

Across the local collection, by listing: 71 of 79 images claimed, 2 578 volumes, 110 989 files, 77 620 of them samples. The AKAI discs are 44 of those images and 68 997 of those files, read across 275 partitions — before D15 they were 14 670 files, because only the partition at the origin was read.

## What is not done

- **Roland S-550.** `Roland LCD1.iso`/`.nrg` opens `* ROLAND S-550 *` and is a different format from the S-7xx entirely ([ADR-0014](adr/0014-one-backend-per-on-disc-format.md)). Neither archive holds a second specimen, so it stays deferred rather than being reverse-engineered from one disc.
- **Ensoniq and Kurzweil backends.** The archives are full of these discs and the containers already open them; each needs a module in `fs/` and nothing else ([ADR-0003](adr/0003-brand-neutral-pluggable-backends.md)).
- **E-mu stereo samples come out as double-length mono.** The sample record's pointer block declares a channel count, and roughly **2 700 of the 14 738** E-mu samples declare two — stored as a block of left followed by a block of right, not interleaved. Reading the payload as one mono stream concatenates the channels. D17 established this and deliberately did not fix it: correcting it moves audio, and that deliverable was scoped to add metadata without touching a byte of payload. It is the largest known defect in the E-mu path ([formats/emu3.md](formats/emu3.md), [ADR-0025](adr/0025-the-loop-is-decoded-the-root-key-is-not.md)).
- **65 records declare two channels and are not stereo.** They put `start_R` half a payload on and then close `end_L` somewhere else, and their halves measure as unrelated audio. Six of `protozoa`'s are explained — their first half is byte for byte a whole one-channel record of the same name in another bank, so the payload is twice the sound — and the rest are not. They come out mono, which is right, but *what they hold* is unestablished on `eiiix-1`'s 40, `eiiix-2`'s 6 and thirteen of `protozoa`'s ([formats/emu3.md](formats/emu3.md), [ADR-0026](adr/0026-the-record-declares-the-channel-count.md)).
- **`esi32-gm` and `protozoa` declare a longer extent than their record length gives.** On about 2 200 records of one and 3 800 of the other, `end_L` runs some 45 frames past the payload `+34` produces. Either the reader is 90 bytes short on those samples or the extent field means something else; the splice test does not settle it — a scan for the true loop end peaks within ±2 frames of the declared one on 10% and 20% of records, against about 3% for chance. Those records are refused a loop rather than given a clamped one, and nothing is changed on the strength of an unsettled measurement.
- **No root key for E-mu.** It is not in the sample record: no byte of the 92 tracks the note in the sample's own name above chance, over 1 741 named records of `esi32-gm` and 917 of `eiiix-1`. The E3 keeps it in the preset, and presets are not read — see the `E4P1` entry below. E-mu WAVs carry their loop with the RIFF neutral root key of 60 ([ADR-0025](adr/0025-the-loop-is-decoded-the-root-key-is-not.md)).
- **`E4P1` presets are not read.** The three E-IV discs carry 916, 901 and 284 of them. On `eiv-studio` 100 of 230 banks have no sample directory and are listed with a note; preset-only banks are the likely explanation, and it is not established.
Expand Down
2 changes: 2 additions & 0 deletions docs/adr/0025-the-loop-is-decoded-the-root-key-is-not.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,5 @@ Measured directly, on the records whose pointers declare two channels, the two h
Those samples are stereo, and this project currently writes each as a double-length mono WAV. `eiiix-2`'s one-channel figure of 0.59 is the weakest separation and is left standing rather than explained away; `protozoa` has only 18 two-channel records with enough audio to score.

Nothing in D17 changes that — fixing it moves audio, which is the one thing this deliverable must not do. It is written up in [formats/emu3.md](../formats/emu3.md) and left open for a deliverable of its own. The loop frames decoded here survive it unchanged: `(pointer − start) / 2` is a per-channel frame index either way.

> **D18 acted on this in [ADR-0026](0026-the-record-declares-the-channel-count.md), and every loop count above survived it.** One correction to the paragraph above: the channel count alone selects 2 721 records and 65 of those are not stereo — their own `end_L` contradicts the split — so 2 656 are written as stereo. `protozoa`'s weak 0.55 is those false positives, 19 of its 27. The two records anywhere whose loop end lies past its own channel are both among the 65, which is why 107, 1 157, 1 260, 1 689, 449, 2 551 and 826 are unchanged rather than nearly unchanged.
Loading
Loading