README from GitHub, MIT
ep133-ppak
_ __________ _
___ _ __ / |___ /___ / _ __ _ __ __ _| | __
/ _ \ '_ \| | |_ \ |_ \ ____| '_ \| '_ \ / _` | |/ /
| __/ |_) | |___) |__) |____| |_) | |_) | (_| | <
\___| .__/|_|____/____/ | .__/| .__/ \__,_|_|\_\
|_| |_| |_|
Reverse-engineered SysEx + .ppak protocol library for the
Teenage Engineering EP-133 K.O. II.
Write valid .ppak archives from Python — both sample-mode pads and
full song-mode projects with multiple patterns, scenes, and a
song-position playlist. Built on a chain of community RE — see
building on prior work below.
What you can build with it
Sample-mode — load samples onto pads
A BatchManifest describes which WAVs go on which pads with which settings —
per-sample BPM, time-stretch mode, playmode, and routing hints. The package
handles the byte-level .ppak format so your pipeline stays in plain Python
or a one-line CLI invocation. Drives ppak-load-one
and ppak-load-manifest.
Song-mode — bake an arrangement into a multi-scene project
An arrangement.json + manifest.json pair describes a timeline of clips on
tracks A/B/C/D plus the source stems those clips reference. The package
turns it into a song-mode .ppak with multiple patterns, multiple scenes,
and a song-position playlist that plays without touching the front panel.
Drives ppak-export-song.
Install
pip install -e .
# or with MIDI backends for live device interaction:
pip install -e ".[midi]"
Requires Python 3.11+. Live USB-MIDI requires mido + python-rtmidi.
Quick start
Three flows, in order of complexity.
a. Single sample on a single pad
One WAV, one pad. Requires the [midi] extra (live device I/O). See
tools/load_one.py.
# Drop kick.wav onto Project 1, Group A, pad "7" (top-left), slot 100
ppak-load-one windowlicker_kick.wav --project 1 --group A --pad 7 --slot 100
# Tag a loop with its true BPM so time.mode=bpm stretches it cleanly
# at any project tempo, and target pad "4" (middle-left)
ppak-load-one windowlicker_bass_lead.wav \
--project 9 --group B --pad 4 --slot 220 \
--bpm 134.0
--bpm writes sound.bpm and sets time.mode=bpm on the slot. Pad
labels follow the physical keypad: 7 8 9 / 4 5 6 / 1 2 3 / . 0 ENTER.
b. Bulk-load samples from a manifest
Many WAVs in one pass, BPM and group routing baked in. See
docs/MANIFEST.md for the full schema.
// manifest.json
{
"version": 1,
"track": "windowlicker",
"bpm": 134.0,
"samples": [
{"file": "windowlicker_kick.wav", "stem": "drums", "suggested_group": "A", "suggested_pad": "7"},
{"file": "windowlicker_snare.wav", "stem": "drums", "suggested_group": "A", "suggested_pad": "8"},
{"file": "windowlicker_perc_glitch.wav", "stem": "drums", "suggested_group": "A", "suggested_pad": "9", "time_mode": "bpm"},
{"file": "windowlicker_bass_sub.wav", "stem": "bass", "suggested_group": "B", "suggested_pad": "7", "playmode": "key"},
{"file": "windowlicker_bass_lead.wav", "stem": "bass", "suggested_group": "B", "suggested_pad": "4", "playmode": "legato", "time_mode": "bpm"},
{"file": "windowlicker_pad_chord.wav", "stem": "other", "suggested_group": "C", "suggested_pad": "7", "playmode": "key", "time_mode": "bpm"},
{"file": "windowlicker_vox_lick.wav", "stem": "vocals", "suggested_group": "D", "suggested_pad": "7", "bpm": 67.0}
]
}
ppak-load-manifest manifest.json --project 9
The track-level bpm is the default for every sample missing its own;
per-sample bpm overrides. suggested_group / suggested_pad claim
specific placements; samples without them fill remaining pads in order.
c. Build a full song-mode .ppak from an arrangement
Inputs: an arrangement.json (locators + clip placements) and a
manifest.json (WAVs + slot metadata). Output: a song-mode .ppak
with multiple patterns, scenes, and a song-position playlist. See
docs/MANIFEST.md for both schemas, and
docs/diagrams/04_song_pipeline.md
for the pipeline narrative.
# Synthesize a minimal device-default template (no capture needed)
ppak-export-song \
--arrangement snapshot.json \
--manifest stems.json \
--out song.ppak
# Use a captured reference .ppak for byte-accurate per-pad metadata
ppak-export-song \
--arrangement snapshot.json \
--manifest stems.json \
--reference-template tests/fixtures/reference.ppak \
--project 3 \
--out out/song.ppak
Pull a reference template off your own device with
tools/ep133_capture_reference.py.
Driving the device live (no Chrome)
Want to drive the device live without going through the WebMIDI Sample
Tool? See docs/LOADING_SAMPLES.md for the
EP133Client Python API.
What this gives you
Sample-mode (load samples + pads)
- A
.ppakwriter that loads cleanly into Sample Tool. Patches a real Sample Tool backup as a base and modifies only the bytes that need to change — guarantees format conformance. Seeep133/ppak/writer.py. - Sample Tool emit format documented end-to-end —
meta.jsonschema, ZIP entry conventions, the absent-on-purposesettingsfile (a footgun: adding one triggers ERROR CLOCK 43 and a flash-format recovery), and WAV requirements (44.1 kHz stereo for.ppak; the device transcodes to 46875 mono internally). - The diff method — a reproducible procedure for verifying pad-record byte offsets via two Sample Tool backups, before and after one UI change. Anyone with the device can confirm or extend the byte layout in this repo. See docs/verifying-byte-offsets.md.
- Diff-verified pad-record byte layout — the 26-byte record (factory native; PROTOCOL.md §7.0 covers the erratum vs older published tables) gets a field-by-field verification status. Offsets that came out a bit different from earlier tables are flagged so future work can reconcile them. See PROTOCOL.md §7.
- Two pad-numbering conventions, called out — the TAR's
pNNcounts bottom-up; the SysExpad_numcounts top-down. Same physical pad, different numbers. Easy to conflate; harder once you've seen the diagram. - Time-stretch math for
time.mode=bpmwith the practical implication: set each loop'ssound.bpmto its true recorded tempo, and the device's bar inference works cleanly at any project tempo.
Song-mode (full projects from arrangements)
ppak-export-song— turns anarrangement.jsonplus amanifest.jsoninto a song-mode.ppakthe device boots straight into: multiple patterns, multiple scenes, and a song-position playlist that plays without touching the front panel. See docs/diagrams/04_song_pipeline.md.- A three-stage pipeline under
ep133/song/.resolve_scenes()walks the arrangement's locators and picks which clip is active on each track;synthesize()dedupes patterns by(group, pad, bars)and encodes EP-133 limits (99 scenes, 99 patterns/group, 12 pads/ group) into aPpakSpec;build_ppak()authors fresh patterns / scenes / sounds while patching a reference template'smeta.jsonand per-pad byte templates.build_synthetic_template_ppak()covers the no-capture case. - Stem-export pipeline integration. The arrangement / manifest
shapes are the integration surface for tools like
StemForge, whose
Ableton Live arrangement-export flow is the original consumer of
ppak-export-song.
Repository layout
ep133/ The Python library
__init__.py Lazy-loaded EP133Client
client.py High-level SysEx client
transport.py MIDI port discovery + I/O
sysex.py Frame build/parse, request IDs
packing.py 7-bit packing
commands.py Command bytes, sub-cmd bytes, fileId formula
payloads.py PadParams, SampleParams, payload builders
audio.py WAV → 46875 Hz mono PCM transcode
transfer.py Upload message-sequence generator
project_reader.py Live read project TAR via SysEx
pad_record.py Decode 26-byte pad records
manifest.py SampleMeta + BatchManifest schemas
ppak/
writer.py .ppak archive writer (patch-from-base)
song_writer.py Build-from-spec writer (song-mode)
song/
format.py Byte builders: patterns, scenes, pads, settings
resolver.py arrangement → scene snapshots
synthesizer.py snapshots → PpakSpec
wav.py EP-133-native WAV conversion
tools/ CLI utilities (5 entry points)
ppak_writer.py ppak-writer — patch a base .ppak with a preset
load_one.py ppak-load-one — single sample on a single pad
load_from_manifest.py ppak-load-manifest — bulk-load from JSON
bpm_matrix.py ppak-bpm-matrix — 12-pad BPM matrix via SysEx
export_song.py ppak-export-song — arrangement → song-mode .ppak
ep133_capture_reference.py Pull a project TAR off live hardware
tests/ pytest suite (319+ tests, all passing)
docs/
diagrams/ Illustrated docs (10 SVGs + companion .md)
MANIFEST.md Both manifest shapes, annotated
LOADING_SAMPLES.md Sample-mode walkthrough (CLI + Python API)
validation-guide.md What to expect when validating a generated .ppak
verifying-byte-offsets.md The diff method for verifying pad-record bytes
PORT_PLAN.md Architectural notes for the song-mode port
PROTOCOL.md Complete protocol + format specification
LICENSE MIT
Building on prior work
This project stands on a chain of community reverse-engineering:
- phones24 — the original
.ppakarchive parser. Their work is the reason any of this was tractable; their RE bootstrapped the broad protocol shape, including the pad-record fields. - ep133-krate — extensive
live SysEx capture-based protocol RE + a polished sample-manager TUI.
Complementary surface area to this project: krate covers the live
SysEx path with capture-backed depth; this repo covers the on-disk
.ppakarchive format and the in-TAR binary pad record. - garrettjwilke/ep_133_sysex_thingy, benjaminr/mcp-koii, and abrilstudios/rcy — earlier or adjacent community efforts cited by the projects above.
ACKNOWLEDGMENTS.md has the full breakdown of what each project contributed and where to use their work today.
See also
- PROTOCOL.md — full SysEx +
.ppakformat reference - docs/diagrams/00_overview.md — illustrated tour of the whole pipeline
- docs/diagrams/04_song_pipeline.md — song-mode pipeline narrative
- ACKNOWLEDGMENTS.md — what each upstream project contributed and where their work shines today
Status
This is community RE; expect errors. Verify against real device output before using for anything load-bearing. PRs and corrections welcome.
Tested against firmware OS 2.0.5 as of 2026-04. Sample-mode flows are hardware-validated. Song-mode export is new: the source code is hardware-validated through StemForge (where it originated), but end-to-end validation of the packaged version in this repo is still pending — see CHANGELOG.md for the open items.
License
MIT — see LICENSE.