v2.0.0
headroom 2.0.0 — rbsort ships
This is the first major-version bump since the project began. headroom is no longer just a loudness analyzer: it now has a CLI subcommand surface, and the inaugural subcommand rbsort ships in this release.
All v1.x loudness behaviour is 100% backward-compatible — headroom <paths> and bare-invocation interactive mode are unchanged.
Headliner: rbsort — Rekordbox playlist sorter for harmonic mixing
A pure XML-in / XML-out workflow. Takes a Rekordbox collection.xml export and produces a key/tempo-sorted copy ready to re-import. No audio is touched, no database is written, ffmpeg is not required.
What it does
Sorts a target playlist — or every TrackID-referenced playlist in the XML — by:
- Camelot Key ascending (
1A→1B→2A→ … →12B) - BPM ascending within each key group
Sorted copies are written into a brand-new Sorted (Key+BPM)/ folder appended to the ROOT node, each playlist keeping its source name. Mirrors the analyzer's existing backup/ directory pattern.
Tracks with no Camelot key sort after all known keys; within a key group, tracks with no/0 BPM sort last.
Usage
# Sort *every* TrackID-referenced playlist in one pass (recommended)
headroom rbsort --xml /path/to/collection.xml
# Sort a single playlist with a custom name
headroom rbsort \
--xml /path/to/collection.xml \
--playlist "Folder/MyPlaylist" \
--output sorted.xml \
--name "MyPlaylist (Camelot+BPM)"Defaults:
| Flag | Behaviour when omitted |
|---|---|
--output |
<input-stem>-out.<ext> next to the input (e.g. coll.xml → coll-out.xml) |
--playlist |
Sort every TrackID-referenced (KeyType="0") playlist in the XML |
Rekordbox workflow
- Preferences > View > Key display format → Alphanumeric so
Tonalityexports as Camelot (1A..12B). Non-Camelot tonalities silently sort last. - File > Export Collection in xml format →
collection.xml. - Run
headroom rbsort --xml collection.xml. - Preferences > Advanced > Database > rekordbox xml → Imported Library points at the output XML; restart Rekordbox.
- Open the rekordbox xml tree in the left sidebar; drag the sorted playlist into your real Playlists tree, or right-click → Export Playlist for CDJ EXPORT mode.
Implementation highlights
- New module
src/rbsort/(mod,camelot,xml) on top ofquick-xml0.40. - Two-pass design: scan
COLLECTION+ target playlists, then stream-rewrite with the sorted folder injected inside<PLAYLISTS>before the ROOT</NODE>closes. ROOTCountis bumped by 1. - Robust against real Rekordbox exports —
<TRACK>elements with nested<TEMPO>/<POSITION_MARK>children are handled correctly (the originalEvent::Empty-only path produced an empty collection map and a no-op sort). - Subcommand dispatch short-circuits before the audio pipeline, the banner, ffmpeg check, and the update check —
rbsortruns in milliseconds and has zero audio-stack dependencies. - Verified end-to-end on a real 5,255-track / 24-playlist Rekordbox export — sorted in under a second with zero ordering violations.
Why a major version?
Until now, headroom only did one thing: audio loudness analysis and gain adjustment. v2.0.0 introduces a subcommand surface to the CLI, and rbsort is the first to land there. Future non-audio DJ-prep workflows (set planning, transition analysis, etc.) will be added as subcommands too.
Existing flows are untouched:
headroom→ interactive loudness analyzer (unchanged)headroom <paths> [flags]→ scriptable loudness analyzer (unchanged)headroom rbsort ...→ new Rekordbox playlist sorter
Internal
- XML scanner/rewriter switched to byte-level element-name comparison and single-pass attribute extraction — fewer allocations on large exports.
Option<T>"None-last" ordering extracted into a reusable helper.processor.rsinternal helpers tightened frompubto private; deadAnalysisSummary::total()removed.default_output_pathsimplified to usePath::with_file_name.- 19 unit tests, including full XML round-trip with multi-playlist nested folders.
Migration
None. The audio side is wire-compatible with v1.x. rbsort is purely additive.