Skip to content

Update script for stand-alone ppak generation#2

Open
mogenson wants to merge 1 commit into
DannyDesert:mainfrom
mogenson:stand-alone
Open

Update script for stand-alone ppak generation#2
mogenson wants to merge 1 commit into
DannyDesert:mainfrom
mogenson:stand-alone

Conversation

@mogenson

Copy link
Copy Markdown

Continue to reverse engineer the ppak format on a 128 mb EP-133 K.O. II with OS 2.0.5. The goal is to generate a stand-alone ppak file without relying on copying settings from a backup.

EP-133 Format Findings

Error Clock 43 — BPM Must Be Non-Zero

The settings file (222 bytes) stores BPM as a float32 LE at bytes 4–7. A zero BPM causes "Error Clock 43" on load. Always write
a valid BPM there, and also at bytes 12–15 of every pad file.

Pads Silent After Loading

Four separate root causes, discovered in order:

  1. Per-pad volume (byte 16) defaults to 0 Pad file byte 16 is volume (0–100). An uninitialized bytearray(27) leaves it at 0 = silent. Fix: set data[16] = 100.

  2. Group project volume defaults to 0 The settings file has four group volume floats at offsets 24, 72, 120, 168. An uninitialized settings buffer leaves them at 0.0 = silent. Fix: write float32(1.0) at those offsets.

  3. Settings sentinel value is -1.0, not 0.0 All other float slots in the settings group blocks (48 bytes × 4 groups) must be filled with -1.0 (0x000080bf), which the device treats as "use default." Writing 0.0 is interpreted as zero and silences those parameters.

  4. Sample ROM ID (bytes 8–9) — the critical one Bytes 8–9 of each pad file are a uint16 LE that the device uses to locate the sample's audio data in ROM. Without the correct value, the pad shows the right sample name in the UI but produces no sound. Zero is not a valid stand-in.

These are not derivable from the sample number — they are firmware ROM addresses discovered empirically:

┌────────┬───────────────┬────────┐
│ Sample │ Name │ ROM ID │
├────────┼───────────────┼────────┤
│ 1 │ MICRO KICK │ 22412 │
├────────┼───────────────┼────────┤
│ 31 │ BOOMER KICK │ 30133 │
├────────┼───────────────┼────────┤
│ 126 │ SNARE OPEN │ 18697 │
├────────┼───────────────┼────────┤
│ 203 │ CLOSED HAT LO │ 7558 │
├────────┼───────────────┼────────┤
│ 221 │ OPEN HAT REAL │ 14798 │
└────────┴───────────────┴────────┘

Discovery method: assign the sample on-device (or reassign away and back), take a project backup, extract bytes 8–9 from the pad
file in the backup tar.

ROM ID Structure

Internal storage order is hats → snares → kicks — opposite of the user-facing 200s/100s/1s numbering. Within each category,
higher sample number = higher ROM offset. The slope differs by category (~402 ROM units/sample for hats, ~257 for kicks),
reflecting different average sample lengths.

Labeled Pad Numbers ≠ File Numbers

The numbers printed on the device do not match the p01–p12 file numbers in the tar. The bottom two rows are swapped:

Row 3 (labeled 1-3) → files p07-p09
Row 1 (labeled 7-9) → files p01-p03
Rows 2, 4 (labeled 4-6, 10-12) → files p04-p06, p10-p12 (direct)

Pattern events reference pad file numbers via row_byte = (file_num - 1) * 8. Using labeled numbers directly sends events to the
wrong pads.

Two Separate Volume Controls

  • Byte 16 of the pad file: per-pad volume (0–100). Fixed at write time; the sound-edit volume knob on the device does not update this.
  • Group project volume float in the settings file: what the volume knob actually controls (0.0–1.0). Both must be non-zero for sound to play.

Continue to reverse engineer the ppak format on a 128 mb EP-133 K.O. II
with OS 2.0.5. The goal is to generate a stand-alone ppak file without
relying on copying settings from a backup.

EP-133 Format Findings

  Error Clock 43 — BPM Must Be Non-Zero

  The settings file (222 bytes) stores BPM as a float32 LE at bytes 4–7. A zero BPM causes "Error Clock 43" on load. Always write
  a valid BPM there, and also at bytes 12–15 of every pad file.

  Pads Silent After Loading

  Four separate root causes, discovered in order:

  1. Per-pad volume (byte 16) defaults to 0
  Pad file byte 16 is volume (0–100). An uninitialized bytearray(27) leaves it at 0 = silent. Fix: set data[16] = 100.

  2. Group project volume defaults to 0
  The settings file has four group volume floats at offsets 24, 72, 120, 168. An uninitialized settings buffer leaves them at 0.0
  = silent. Fix: write float32(1.0) at those offsets.

  3. Settings sentinel value is -1.0, not 0.0
  All other float slots in the settings group blocks (48 bytes × 4 groups) must be filled with -1.0 (0x000080bf), which the device
   treats as "use default." Writing 0.0 is interpreted as zero and silences those parameters.

  4. Sample ROM ID (bytes 8–9) — the critical one
  Bytes 8–9 of each pad file are a uint16 LE that the device uses to locate the sample's audio data in ROM. Without the correct
  value, the pad shows the right sample name in the UI but produces no sound. Zero is not a valid stand-in.

  These are not derivable from the sample number — they are firmware ROM addresses discovered empirically:

  ┌────────┬───────────────┬────────┐
  │ Sample │     Name      │ ROM ID │
  ├────────┼───────────────┼────────┤
  │ 1      │ MICRO KICK    │ 22412  │
  ├────────┼───────────────┼────────┤
  │ 31     │ BOOMER KICK   │ 30133  │
  ├────────┼───────────────┼────────┤
  │ 126    │ SNARE OPEN    │ 18697  │
  ├────────┼───────────────┼────────┤
  │ 203    │ CLOSED HAT LO │ 7558   │
  ├────────┼───────────────┼────────┤
  │ 221    │ OPEN HAT REAL │ 14798  │
  └────────┴───────────────┴────────┘

  Discovery method: assign the sample on-device (or reassign away and back), take a project backup, extract bytes 8–9 from the pad
   file in the backup tar.

  ROM ID Structure

  Internal storage order is hats → snares → kicks — opposite of the user-facing 200s/100s/1s numbering. Within each category,
  higher sample number = higher ROM offset. The slope differs by category (~402 ROM units/sample for hats, ~257 for kicks),
  reflecting different average sample lengths.

  Labeled Pad Numbers ≠ File Numbers

  The numbers printed on the device do not match the p01–p12 file numbers in the tar. The bottom two rows are swapped:

  Row 3 (labeled 1-3) → files p07-p09
  Row 1 (labeled 7-9) → files p01-p03
  Rows 2, 4 (labeled 4-6, 10-12) → files p04-p06, p10-p12 (direct)

  Pattern events reference pad file numbers via row_byte = (file_num - 1) * 8. Using labeled numbers directly sends events to the
  wrong pads.

  Two Separate Volume Controls

  - Byte 16 of the pad file: per-pad volume (0–100). Fixed at write time; the sound-edit volume knob on the device does not update
   this.
  - Group project volume float in the settings file: what the volume knob actually controls (0.0–1.0). Both must be non-zero for
  sound to play.
@mogenson

Copy link
Copy Markdown
Author

Hey this is a pretty big diff so don't feel pressured to take this PR. I just wanted to post an update somewhere about some additional reverse engineering discoveries. Notably: setting non-zero volumes and BPM, the pad labels being flipped from their internal IDs, and the necessary ROM offset values for samples.

This last one is kind of a blocker. There's no real way to pre-calculate the ROM offset.

@joseph-holland

Copy link
Copy Markdown

Hey, dropping in from EP-PatchStudio (https://ep-patch.studio). Spotted this PR while looking at the same pad-byte questions and wanted to share some data in case it helps.

I ran the analysis across a few real backups from my EP-133 K.O. II and EP-40 Riddim and the bytes 8-11 value is not a ROM offset. It is the assigned WAV's frame count.

Numbers checked exactly:

  • EP-133: 38/38 pads matched (bytes 8-11 == WAV frame count)
  • EP-40: 52/54 pads matched

The two EP-40 outliers were both the same pad with a user-trimmed sample (the value sat at 67% of the WAV's full length, exactly what you would expect if someone trimmed the sample end on the device).

Your own table corroborates it once you divide by 44100:

  • MICRO KICK 22412 frames = 0.508s
  • BOOMER KICK 30133 = 0.683s
  • SNARE OPEN 18697 = 0.424s
  • CLOSED HAT LO 7558 = 0.171s
  • OPEN HAT REAL 14798 = 0.336s

Every one of those is a plausible drum-hit duration for the named sample.

So the interpretation that fits the data:

  • bytes 1-2 = soundId (sample reference, what you already had)
  • bytes 4-7 = trim_left (frames in to start playback, 0 = play from start)
  • bytes 8-11 = trim_right (frames in to stop playback, default = full sample length)

Bytes 4-7 and 8-11 together describe a playback window into the sample. Your "assign on device, take backup, extract bytes 8-9" discovery method worked because the device needs the playback end frame to be correct. When it is zero the device plays zero frames and you get silence. When you copy from a real backup you are copying the device's already-correct end frame.

For the standalone .ppak use case this is much better news. To reference factory sample N from scratch you only need:

  • bytes 1-2 = N
  • bytes 4-7 = 0
  • bytes 8-11 = frame count from the sample's WAV header (or the device default sample rate, depending on which side resolves it)
  • everything else as you already have it

No lookup table required, no firmware-version dependency, and the backup format stays self-describing which is the nice architectural property the .pak format seems to lean into anyway.

Two more bits in case useful:

  • We see the same pad-record layout in both 26 and 27 byte forms within a single project (33×26 + 5×27 in one EP-133 capture, all in the same backup). Worth flagging because ep133-ppak's erratum warns about the 27-byte form, but my data has both happily coexisting. Probably worth a separate hardware test before assuming 27-byte is always rejected.
  • Pad byte 20 looks like envelope.release with default 0xff = 255 (matches ep133-ppak's diff), and byte 21 is the time.mode enum (0=off, 1=bpm). I had previously labelled those filterFx and loopEnabled which were both wrong.

Happy to share the analysis script and the raw extract if it would help. I have several more backups across EP-133, EP-40 and EP-1320 if there are other byte interpretations you would like to settle.

Joe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants