Skip to content

Commit 46dbf54

Browse files
Rafaelle LancelottaRafaelle Lancelotta
authored andcommitted
Add proposal: rekordbox USB Export (PDB/ANLZ) for CDJ/XDJ hardware
Design document for adding a rekordbox USB exporter to Mixxx, following the template. Covers the unencrypted PDB/ANLZ format (widest hardware reach, legally cleanest); OneLibrary is an explicit non-goal. Includes a standalone Qt-free writer library + round-trip verifier, a Mixxx adapter mirroring the Engine Prime exporter, and the rekordcrate-vs-C++ tradeoff as an open question for maintainers. Related: mixxxdj/mixxx#9463, #10321, #12126.
1 parent 1dc9069 commit 46dbf54

1 file changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
# rekordbox USB Export (PDB/ANLZ) for CDJ/XDJ hardware
2+
3+
* **Owners:** @sparkly-quasar (openboxxx)
4+
* **Implementation Status:** Partially implemented. Standalone writer proven and verified against
5+
independent parsers; in-Mixxx exporter working in a fork that compiles + links on macOS
6+
(Intel + Apple Silicon), Windows, and Linux via CI. Pending rekordbox-desktop import
7+
(verification tier 3) and physical CDJ/XDJ playback (tier 4).
8+
* **Related Issues and PRs:**
9+
* mixxxdj/mixxx#9463 — original "export crates/cues/beatgrids to rekordbox USB" request
10+
* mixxxdj/mixxx#10321 — "Rekordbox USB Export (PDB/ANLZ)"
11+
* mixxxdj/mixxx#12126 — "Abstract over library exporters" (the architectural hook)
12+
* mixxxdj/mixxx#15556 — "Add OneLibrary support" (explicitly *out of scope* here; see Non-Goals)
13+
* mixxxdj/mixxx#13293 — updated rekordbox Kaitai definitions (import-side)
14+
* **Other docs or links:**
15+
* Mixxx fork with the working exporter:
16+
[sparkly-quasar/mixxx `feat/rekordbox-usb-export`](https://github.com/sparkly-quasar/mixxx/tree/feat/rekordbox-usb-export)
17+
* Downloadable tri-platform CI builds:
18+
[openboxxx Releases](https://github.com/sparkly-quasar/openboxxx/releases)
19+
* openboxxx design docs + component status table: `libopenboxxx-export/README.md`; plus
20+
architecture, export-design, legal, research-findings (linked inline)
21+
* pyrekordbox (Dylan Jones, MIT) — ANLZ/PDB reference implementation
22+
* Deep Symmetry `crate-digger` / `dysentery` `.ksy` specs (format documentation)
23+
* rekordcrate (Jan Holthuis, MPL-2.0) — Rust PDB parser/serializer
24+
25+
---
26+
27+
> **A note from the author.** I'm a long-time Mixxx user, not a professional C/C++ developer. This
28+
> exporter grew out of experimenting with AI-assisted ("vibecoding") development, and getting a real
29+
> stick to round-trip through the parsers felt like a genuinely worthwhile use of that time — so I'd
30+
> like to offer it back to the community as a gift. I have no attachment to *my* code being the
31+
> answer; if the right outcome is that this informs a rekordcrate-based path (or anything else the
32+
> maintainers prefer), that's a win. I'm mainly hoping to move #9463 forward and to help however is
33+
> most useful, including finding CDJ hardware testers.
34+
35+
## TL;DR
36+
37+
Mixxx can *import* rekordbox libraries but cannot *export* to the USB format that
38+
Pioneer/AlphaTheta CDJ and XDJ hardware reads. This proposal adds a **rekordbox USB
39+
exporter**: a writer for the unencrypted `export.pdb` (DeviceSQL) and per-track `ANLZ`
40+
(`.DAT`/`.EXT`) analysis files, plus the `PIONEER/` USB directory layout and audio-file
41+
copy — the same kind of "export my whole library to foreign DJ hardware" flow Mixxx
42+
already ships for Engine DJ / Engine Prime.
43+
44+
The core byte-writer is built as a **standalone, Qt-free C++ library** (`libopenboxxx-export`)
45+
so the hard, fiddly serialization can be tested in isolation, with Mixxx integration as a thin
46+
adapter modeled on the existing `EnginePrimeExportJob`. A **round-trip verifier** (parse our
47+
own output back with Mixxx's in-tree Kaitai parsers and diff it) is a first-class feature, so a
48+
DJ learns whether a stick is good *before* they leave for the gig.
49+
50+
Scope is deliberately narrowed to the **legally clean, widest-reach** target: the classic
51+
unencrypted PDB/ANLZ format. The newer encrypted OneLibrary / Device Library Plus format
52+
(#15556) is explicitly a non-goal here for legal reasons (see Non-Goals).
53+
54+
**This is not a paper design.** The standalone writer emits a full `export.pdb` (all 20 standard
55+
tables + the 8-colour palette) and per-track ANLZ, every output cross-checked against two
56+
independent real-format parsers and diffed field-for-field against genuine rekordbox files. An
57+
in-Mixxx "Export Library to rekordbox USB" button already exists in a fork that compiles and links
58+
on macOS (Intel + Apple Silicon), Windows, and Linux via CI — downloadable builds are on the
59+
[Releases](https://github.com/sparkly-quasar/openboxxx/releases) page. What remains is the real
60+
proof: importing into rekordbox desktop and playing on physical CDJ/XDJ hardware — testers welcome.
61+
62+
## Why
63+
64+
**Motivation and context.** "Export to rekordbox USB" has been an open, confirmed wishlist item
65+
since 2018 (#9463), re-filed more specifically in 2021 (#10321), with no assignee, no branch, and
66+
no merged PR. It is one of the most-requested missing features because it removes the last reason a
67+
Mixxx user is forced back into rekordbox: preparing a set in open software but being unable to play
68+
it on the club's CDJs. Mixxx already reads rekordbox libraries; the export direction is the missing
69+
half.
70+
71+
Much of the groundwork already exists in-tree, which makes the gap bounded rather than green-field:
72+
73+
* A **working Engine DJ / Engine Prime exporter** (`Library ▸ Export Library to Engine Prime`)
74+
establishes the exact pattern for walking the Mixxx library and writing a foreign DJ database.
75+
* An **exporter-abstraction plan (#12126)** already intends to replace the Engine-specific menu item
76+
with a generic `Library ▸ Export Library…` and a common per-format interface — the natural socket
77+
for a rekordbox exporter.
78+
* **rekordbox format definitions are already in-tree**: the importer uses Kaitai-generated
79+
`rekordbox_pdb` / `rekordbox_anlz` parsers (`lib/rekordbox-metadata/`, kept current by #13293), so
80+
the *read* side of PDB/ANLZ is already described.
81+
* A **mature importer (since v2.3)** already works out the Mixxx↔rekordbox field mapping (hotcues,
82+
memory cues, loops, colors, keys) in reverse; the exporter reuses that mapping the other direction.
83+
84+
**Pitfalls of the current solution.**
85+
86+
* **There is no export path at all** — a Mixxx user with a CDJ has to redo their prep in rekordbox.
87+
* **Kaitai Struct can only READ.** Its C++ target generates parsers, not serializers, so Mixxx can
88+
parse PDB/ANLZ today but has no generated code to *write* them. The single biggest chunk of
89+
net-new code — a hand-written PDB/ANLZ serializer — simply does not exist yet in the project.
90+
* **No abstraction to plug into yet.** The exporter interface envisioned in #12126 hasn't been built,
91+
so today a new exporter would have to bolt onto the Engine-specific plumbing.
92+
93+
## Goals
94+
95+
* Add a **rekordbox USB exporter** that produces a stick a real CDJ/XDJ can browse and play from:
96+
library, playlists, per-track beatgrids, and hot/memory cues.
97+
* Deliver the serializer as a **standalone, dependency-light C++ library** with its own tests, so the
98+
risky byte-layout work is verifiable independently of a Mixxx build and the eventual Mixxx PR is
99+
mostly adapter + dialog + CMake option.
100+
* Ship a **round-trip verifier** as a first-class trust feature: parse our own USB back with the
101+
in-tree Kaitai parsers and report fidelity ("playlists 12/12, hotcues OK, beatgrids OK") before the
102+
DJ relies on it.
103+
* Land as an **upstream contribution**, implementing (and helping shape) the generic exporter
104+
interface of #12126 rather than a one-off menu item — and, ideally, retiring #9463 / #10321.
105+
* Provide an **in-app "report an export bug" flow** that opens a prefilled GitHub issue from a
106+
structural diagnostic manifest (counts, sections written, verifier results, hashes — never the
107+
user's audio or full metadata), so every beta tester becomes a hardware-compatibility data source.
108+
109+
**Audience.** Mixxx users who play on Pioneer/AlphaTheta CDJ/XDJ hardware and currently keep
110+
rekordbox around solely to export USB sticks; Mixxx maintainers who own the library-export subsystem;
111+
and beta testers with access to real CDJ/XDJ units.
112+
113+
## Non-Goals
114+
115+
* **OneLibrary / Device Library Plus (`exportLibrary.db`, #15556) is out of scope — for legal, not
116+
just effort, reasons.** That database is SQLCipher-encrypted (256-bit AES); writing it means
117+
circumventing an access-control measure with an extracted key, which is precisely the DMCA §1201
118+
exposure the project is structured to avoid. It should be pursued **only** via an official
119+
AlphaTheta spec/partnership, never with a recovered key. This proposal writes **only** the
120+
unencrypted classic PDB/ANLZ format. (See openboxxx `docs/legal.md`.)
121+
* **Reading the encrypted rekordbox 6/7 app database (`master.db`).** The sanctioned import path is
122+
the user's own `rekordbox.xml`; we never decrypt their app DB.
123+
* **Full analysis fidelity in the first milestone.** Waveforms (`PWAV`/`PWV3`/`PWV4`/`PWV5`), extended
124+
`.EXT` cues with colors + labels (`PCO2`), `.2EX`, `PVBR`, My-Tags (`exportExt.pdb`), and history
125+
playlists are deferred to a follow-up phase (they are additive ANLZ sections and don't touch the
126+
PDB). The first milestone is a *playable* stick with a blank on-screen waveform, not zero playback.
127+
* **Bundling or shipping any AlphaTheta-extracted key or crate-digger (EPL) code.** Format knowledge
128+
is taken from public specs and permissively-licensed reference implementations only (see How ▸
129+
Licensing).
130+
131+
## How
132+
133+
**Overview.** A standalone C++ library takes a plain intermediate model and emits bytes; Mixxx
134+
integration is a thin adapter that fills that model from the library.
135+
136+
```text
137+
Mixxx library (tracks + cues + beatgrids + crates/playlists [+ waveforms, phase 2])
138+
139+
▼ ExportModel (plain structs — no Qt)
140+
┌──────────────────────────────────────────────────────────────────────┐
141+
│ libopenboxxx-export (standalone, Qt-free) │
142+
│ mapping/ Mixxx-model → rekordbox-model (colors, keys, ms↔frame,…) │
143+
│ pdb/ export.pdb writer — LITTLE-endian DeviceSQL pages │
144+
│ anlz/ ANLZ .DAT/.EXT — BIG-endian tagged sections │
145+
│ usb/ PIONEER/ dir layout, ANLZ path assignment, audio copy │
146+
│ verify/ round-trip: parse our output w/ in-tree Kaitai + diff │
147+
│ diag/ structural diagnostic bundle (feeds in-app bug report) │
148+
└──────────────────────────────────────────────────────────────────────┘
149+
150+
151+
USB stick: /PIONEER/… + copied audio files
152+
```
153+
154+
The Mixxx-side adapter is a `RekordboxExportJob` that mirrors the existing `EnginePrimeExportJob`:
155+
a `QThread` with `loadIds/loadTrack/loadCrate/loadPlaylist` marshalling and
156+
`jobMaximum/jobProgress/completed/failed` signals, driving an export dialog, all behind a
157+
`REKORDBOX_EXPORT` CMake option. This is the concrete implementation of the exporter abstraction
158+
discussed in #12126, so the proposal both uses and helps define that interface. (The fork also adds
159+
a small "Cue Sheet to Tracklist" utility alongside it.) The fork compiles and links on macOS
160+
(Intel + Apple Silicon), Windows, and Linux under CI today.
161+
162+
**Cross-platform by construction.** Byte layout is always explicit, never host-dependent: PDB integers
163+
written explicitly little-endian, ANLZ integers explicitly big-endian (separate helpers, never
164+
shared); strings encoded by hand per `device_sql_string` rules (short-ASCII mangling or `0x90`
165+
UTF-16**LE** for PDB, UTF-16**BE** for ANLZ paths/labels — no `std::wstring`/`wchar_t`);
166+
USB-internal paths always forward-slashed regardless of host OS; no FAT/exFAT-specific code (the DJ
167+
formats the stick). This is why macOS/Linux/Windows are all supported from day one, which also
168+
maximizes the beta-tester (hardware data) pool.
169+
170+
**Testing and verification.** A four-tier ladder, cheapest first:
171+
172+
1. **Round-trip self-check** — write a stick, parse it back with the in-tree Kaitai parsers, confirm
173+
it reproduces the intended model. Free; runs on every build in CI.
174+
2. **Diff vs a real rekordbox stick** — structural field comparison against a genuine
175+
rekordbox-produced stick, to catch layout mistakes the parser tolerates.
176+
3. **Import into rekordbox desktop** — behavioral smoke test. ⚠️ *Not* equivalent to hardware (the
177+
desktop app is more forgiving and may re-analyze). Standardize on **rekordbox 5** for this tier:
178+
a rekordbox 7 stick writes `export.pdb` as an empty compatibility shell and puts the real library
179+
in the encrypted OneLibrary DB, so rb7 is a misleading oracle for classic PDB.
180+
4. **Real CDJ/XDJ hardware** — the only true proof, run at milestones. Launch matrix: CDJ-2000NXS2
181+
(legacy baseline), CDJ-3000/3000X (strict/modern), XDJ-AZ (modern all-in-one).
182+
183+
Tiers 1–2 are exposed as a **library API** (`verify/`) and reused by the in-app verifier so the DJ
184+
gets the same check. **Current status: tiers 1–2 are green** — exports complete without errors and
185+
round-trip through both independent parsers. **Tiers 3–4 (rekordbox-desktop import and physical
186+
CDJ/XDJ playback) are the outstanding proof and have not yet been run**; this is the main thing
187+
hardware testers can help unblock.
188+
189+
**Migration and downtime.** None — this is purely additive. It introduces a new
190+
export path (generalizing the existing Engine Prime menu item) behind a `REKORDBOX_EXPORT` CMake
191+
option, in the same spirit as the existing `ENGINEPRIME`/`__ENGINEPRIME__` gate. No existing data,
192+
schema, or user workflow changes;
193+
nothing is removed. Existing rekordbox *import* is untouched.
194+
195+
**Licensing.** The module is **GPLv2** (Mixxx-compatible). ANLZ layout is *ported* from **pyrekordbox**
196+
(MIT → GPLv2-compatible; attributed). PDB layout is implemented from the **crate-digger `.ksy` spec**
197+
(EPL-1.0) used as a *format description*, not copied code — the same clean-room-ish posture by which
198+
Mixxx's existing rekordbox importer was built (a file format is not itself copyrightable; a specific
199+
code expression is). crate-digger's EPL code is never linked. rekordcrate (MPL-2.0) is
200+
GPL-compatible and is discussed as an alternative below.
201+
202+
**Known unknowns / open questions.**
203+
204+
* `track_row` "always X" magic constants and `bitmask` semantics — written from reference values;
205+
worth confirming behaviorally on hardware.
206+
* All 20 standard PDB tables (including the library-independent `columns` / `unknown_17` /
207+
`unknown_18` / `history` browse/sort menu tables) are now written, and output round-trips through
208+
both parsers. The remaining unknown is purely behavioral: whether CDJ firmware is satisfied with the
209+
exact contents — resolved only at tiers 3–4.
210+
* The **ANLZ-path hash quirk** (flagged by a prior community exporter): confirm whether newer firmware
211+
requires a specific folder-path hash before assuming free choice of `USBANLZ` folder names.
212+
* PDB page conventions and `first_page`/`last_page` semantics — pass the parsers; validate on hardware.
213+
214+
## Alternatives
215+
216+
1. **Use rekordcrate (Rust) for PDB export instead of a C++ writer.** rekordcrate (Jan Holthuis) is
217+
a maintainer-suggested path and already models the PDB format in Rust with serialization in view.
218+
*Objection to this proposal:* "why hand-write a C++ serializer when a Rust one exists?"
219+
*Counter-arguments:* (a) Mixxx is C++/CMake with no Rust in the build today, so adopting
220+
rekordcrate means introducing a Rust toolchain + FFI boundary into the core build — a larger
221+
architectural commitment than the exporter itself; (b) rekordcrate covers PDB but not the full
222+
ANLZ writer path, USB layout, verifier, and diagnostic bundle this proposal needs, so it is a
223+
partial solution regardless; (c) a Qt-free C++ library keeps the serializer inside the language
224+
and build system maintainers already use. **This is genuinely an open decision for maintainers**,
225+
and the standalone-library structure here is deliberately arranged so the writer could be swapped
226+
for a rekordcrate-backed one without disturbing the Mixxx adapter — the adapter/dialog/CMake work
227+
is reusable either way. Maintainer direction on C++-writer vs Rust-via-rekordcrate is explicitly
228+
solicited.
229+
2. **Generate a serializer from Kaitai instead of hand-writing.** Rejected: Kaitai's C++ write
230+
support is experimental/partial and not viable today; the read parsers stay Kaitai-generated, the
231+
writers are hand-written.
232+
3. **Hard-fork Mixxx and add export out-of-tree.** Rejected as the primary path: forking splits the
233+
community and forfeits Mixxx's test base, review, and credibility. Upstreaming into the existing
234+
export subsystem is preferred; a fork is a fallback only if upstream cannot accept the work.
235+
4. **Export `rekordbox.xml` only, and let users import that into rekordbox.** Insufficient: XML round-
236+
trips metadata but does not produce a USB a CDJ can play from standalone — it still requires the
237+
user to open rekordbox and re-export to USB, which is the exact dependency this feature removes.
238+
5. **Target OneLibrary because it's the "newest, sanctioned" format.** Rejected on legal grounds — it
239+
is encrypted; see Non-Goals. Counterintuitively the newest format is the one we must *not*
240+
reverse-engineer key-in-hand.
241+
242+
## Action Plan
243+
244+
* [x] Survey the state of the Mixxx export effort and confirm the in-tree hooks (Engine DJ exporter,
245+
`lib/rekordbox-metadata/`, #12126). — openboxxx `docs/mixxx-export-status.md`,
246+
`research-findings.md`
247+
* [x] **Phase 0 — standalone PDB/ANLZ writer + verifier, proven.** `libopenboxxx-export` emits a full
248+
`export.pdb` (all 20 standard tables + 8-colour palette) and per-track ANLZ (beat grids + hot/
249+
memory cues); every output is cross-checked against two independent parsers (pyrekordbox for
250+
ANLZ, a crate-digger-spec Kaitai parser for PDB) and diffed field-for-field against genuine
251+
rekordbox ANLZ files.
252+
* [x] **Phase 1 reader path.** `openboxxx_from_mixxx` reads a real `mixxxdb.sqlite` into the export
253+
model with no Mixxx build required — validated on a ~2,900-track library.
254+
* [x] **Phase 1 in-Mixxx exporter, working in a fork.** "Export Library to rekordbox USB" button —
255+
`RekordboxExportJob` + dialog mirroring the Engine DJ exporter, behind a `REKORDBOX_EXPORT`
256+
CMake option (plus a "Cue Sheet to Tracklist" tool). Compiles + links on macOS (Intel + Apple
257+
Silicon), Windows, and Linux via CI; downloadable builds on the
258+
[Releases](https://github.com/sparkly-quasar/openboxxx/releases) page. — refs mixxxdj/mixxx#12126
259+
* [ ] **Verification tier 3 — rekordbox desktop import.** Confirm a generated stick imports cleanly
260+
into rekordbox 5 (the classic-PDB target; not rb7, which prefers the encrypted OneLibrary DB).
261+
* [ ] **Verification tier 4 — real CDJ/XDJ hardware.** The true proof, on the launch matrix
262+
(CDJ-2000NXS2, CDJ-3000/3000X, XDJ-AZ). Hardware testers very welcome. — refs mixxxdj/mixxx#9463,
263+
mixxxdj/mixxx#10321
264+
* [ ] Resolve the residual behavioral unknowns at tiers 3–4 (ANLZ-path hash quirk, `track_row` magic
265+
constants, exact firmware expectations for the menu tables).
266+
* [ ] Propose/align the shared exporter interface upstream so this lands as a contribution, not a
267+
fork. — refs mixxxdj/mixxx#12126
268+
* [ ] Add the in-app "report an export bug" flow (structural diagnostic bundle → prefilled GitHub
269+
issue; no telemetry server; user reviews before it leaves the machine).
270+
* [ ] Publish a hardware compatibility matrix with beta-tester results.
271+
* [ ] Phase 2 (follow-up, additive): waveforms and extended `.EXT`/`PCO2` cues with colors + labels,
272+
`.2EX` for CDJ-3000, `PVBR`.

0 commit comments

Comments
 (0)