# Ryder's MPC Tools Build MPC60 hard-disk images from folders of WAVs, and take apart the ones you already have. For the Akai MPC60 / MPC60-II running from a ZuluSCSI (or any SCSI emulator that serves a disk image). Tested on hardware: a 26-partition, 753 MB image built by these tools loads on an MPC60 and plays. Requires Python 3 and numpy. Nothing else. ```bash pip install numpy ``` --- ## Quick start Lay your samples out one folder per partition: ``` STAGING/ Roland R-70/ -> becomes drive A AcidLab Miami/ -> drive B LSDJ/ -> drive C ``` Subfolders inside a kit are just grouping — every WAV underneath gets used. ```bash python3 tools/mpc60build.py STAGING -o HD0_512.hda \ --boot reference/boot.bin \ --pgm-donor reference/donor.PGM \ --snd-header reference/header.SND \ --memory-kb 1400 ``` Check it before you copy it: ```bash python3 tools/mpc60drive.py list HD0_512.hda ``` Then copy `HD0_512.hda` to the root of the SD card. **The filename matters** — ZuluSCSI parses the SCSI ID and block size out of it. `HD0_512.hda` through `HD6_512.hda`; never `HD7`, that is the MPC's own controller. --- ## The tools ### `mpc60build.py` — folders of WAVs to a finished drive Converts every WAV to 40 kHz / 12-bit `.SND`, builds `.PGM` programs automatically, lays out FAT12 partitions, and writes the whole drive image. ``` --memory-kb 700 stock MPC60 (750 KB). Use 1400 if yours is expanded. --reserve 2 leave the first N partitions empty for your own work --gain kit one gain per folder, set by its loudest sample (default) --gain sample peak every sound individually — flattens a kit's dynamics --gain none leave source levels alone --dither TPDF before the 12-bit round; helps decay tails --max-sounds 190 per partition; 224 root entries minus room for programs --partitions 26 how many the drive holds ``` Programs are split automatically whenever a kit exceeds 16 sounds or the memory budget, and each one is packed round-robin across drum roles — kick, snare, hat, tom, cymbal, perc — so every program is playable on its own instead of being fifteen consecutive kicks. ### `mpc60drive.py` — inspect and edit partitions ```bash python3 tools/mpc60drive.py list drive.hda python3 tools/mpc60drive.py extract drive.hda C -o C.img open C.img # macOS mounts it; drag files in Finder python3 tools/mpc60drive.py replace drive.hda C C.img python3 tools/mpc60drive.py init new.hda --boot reference/boot.bin python3 tools/mpc60drive.py format drive.hda H ``` `list` reports the number that actually matters — **worst program KB** — since a 29 MB partition holds far more than the machine can load at once. `replace` refuses a wrong-sized or non-MPC60 image rather than corrupting the partition next door. ### `mpc60decompile.py` — take programs apart ```bash python3 tools/mpc60decompile.py map drive.hda python3 tools/mpc60decompile.py ls drive.hda C python3 tools/mpc60decompile.py pgm drive.hda C BD # or a loose .PGM python3 tools/mpc60decompile.py snd drive.hda C KICK1 python3 tools/mpc60decompile.py export drive.hda C -o out python3 tools/mpc60decompile.py diff a.PGM b.PGM ``` Every field nobody has decoded is labelled **UNKNOWN** in the output. `diff` is the interesting one: save a program from your MPC, diff it against a generated one, and each difference is a field worth working out. ### `mpc60conv.py` — samples on their own ```bash python3 tools/mpc60conv.py convert ./wavs -o ./out python3 tools/mpc60conv.py decode KICK.SND -o kick.wav python3 tools/mpc60conv.py inspect KICK.SND python3 tools/mpc60conv.py verify KICK.SND ``` `verify` proves the codec round-trips a file byte-for-byte. --- ## The limits that actually bite Budget against **memory**, not disk. This is the thing that wastes people's time on this machine. | limit | value | |---|---| | Sound memory | **750 KB stock, 1500 KB expanded** — 12.8 / 25.6 seconds | | Partition load | the **whole partition** must fit in RAM, not just a program | | Partition size | ~29 MB — FAT12 caps at 4084 clusters at 8 KB each | | Root directory | **224 entries** per partition, `.SND` and `.PGM` both count | | Partitions | **26**, lettered A–Z. Not SCSI IDs — SCSI only has 8 | | Sounds per program | **16** | | Filenames | DOS 8.3, uppercase | --- ## Formats Recovered from a working CompactFlash card and a real `PROGRAM_.pgm`. Fields marked UNKNOWN are undecoded and copied verbatim from the donor. ### `.SND` 39-byte header, then **12-bit linear audio at 40 kHz mono**, packed two samples per three bytes: ``` A = (b0 << 4) | (b1 & 0x0F) B = (b1 >> 4) | (b2 << 4) ``` ``` 0x00 u16 magic 0x0101 0x02 16s name, ASCII, space padded 0x13 u24 sample count 0x16 u24 UNKNOWN (1280) 0x19 u24 UNKNOWN (3199) 0x1C u24 UNKNOWN (5120) 0x1F u8 UNKNOWN (100) 0x26 u8 UNKNOWN (100) ``` There is no sample-rate field — the MPC60 is fixed at 40 kHz. Sample counts are kept even. ### `.PGM` — 4608 bytes ``` 0x0000 u16 UNKNOWN (7) 0x0002 name table: 16-byte names, stride 17, 19 slots 0x0882 u32 sample lengths, one per slot, positionally locked to the names 0x0A82 program name and parameters — PARTLY UNKNOWN 0x0AC0 64 pad records, stride 24. Byte 0 = slot index, 0xFF = unassigned. Bytes 1-23 UNKNOWN 0x11C0 64-byte note table, a permutation of MIDI notes 35-98 ``` Three things that are easy to get wrong: - **The name table starts at 0x02, not 0x24.** What looks like the program's name is slot 0 — a sample like any other. 16 of the 19 slots are usable. - **The length table is not optional.** Writing names without the matching lengths at `0x0882 + 4i` leaves the donor's sizes and the MPC loads samples of the wrong length. - **Unused slots must be cleared and their pads set to `0xFF`.** A pad still pointing at an emptied slot references a nameless, zero-length sample. ### Partition and drive FAT12. Boot sector OEM `MPC-60 `, 512-byte sectors, 16 per cluster, 1 reserved, 2 FATs of 11 sectors, 224 root entries, 59304 sectors. Directory entries use attr `0x00` and a zero date, not the `0x20` a normal FAT writer produces. A drive is N partitions laid **end to end with no partition table**. Partition *i* starts at `i × 59304 × 512`. --- ## Reference files `reference/` holds what the tools need to match the machine: - **`boot.bin`** — a real MPC60 FAT12 boot sector. Partition geometry comes from here. - **`donor.PGM`** — a real MPC60 program, used as the template. It carries pad parameters and the note table, which are not fully decoded, so programs inherit the donor's defaults. - **`header.SND`** — a synthetic reference sample (440 Hz tone) generated from the documented layout, so no third-party audio ships with these tools. If you have your own MPC60 disk, using **your** `boot.bin` and a program from your own machine is better — the geometry will match exactly. --- ## Still unknown - The three u24 and two u8 fields in the SND header. - The `u16` at PGM offset 0, and the block at `0x0A82`. - **Bytes 1–23 of each pad record** — level, tuning, decay, mute group. Pads currently inherit the donor's settings and cannot be set per sample. This is the most useful thing left to decode. - Whether the MPC loads a whole partition or only a program's samples. Evidence favours the whole partition; not proven. To work on any of these: save a program from your MPC and `diff` it against a generated one. A **small** program (4–6 sounds) tells you most, because a full 16-slot donor never shows what the machine does with fewer. --- ## Notes on prior art There is very little. [mpc2emu](https://github.com/lentferj/mpc2emu) has an MPC60 12-bit unpack, but it targets **SET** files and its algorithm produces noise on a standalone `.SND`. [ConvertWithMoss](https://github.com/git-moss/ConvertWithMoss) reads MPC60 SET monoliths and MPC2000/3000 programs, all read-only. Sources describing MPC60 audio as "12-bit non-linear" are describing the machine's recording path — the stored data is linear. --- ## License MIT. The reference boot sector and program are functional data structures from Akai hardware, included so the tools can match the machine.