# rbsort Sort Behavior — Single-column vs Multi-column This document walks through what `rbsort` does using a minimal 6-track example, comparing single-column sort (Key only or BPM only — what Rekordbox and the CDJ-3000 / XDJ-1000MK2 expose in their UIs) to the multi-column sort `rbsort` produces. ## The gap rbsort fills Rekordbox's desktop app, the CDJ-3000, and the XDJ-1000MK2 all let you sort a list by **Key** *or* **BPM** as a single column. None of them expose a **compound** sort — Key as the primary key, BPM ascending as the tiebreaker within each Key group. That gap is what `rbsort` fills. The example below shows what each of the three sort modes actually produces from the same input. ## Input Consider a playlist holding the following 6 tracks in registration order (i.e. the order they were dragged into the playlist, which is also what the playlist's *stored* order is): | # | Track | Key | BPM | |---|---|---|---| | 1 | A | 1A | 130 | | 2 | B | 2A | 124 | | 3 | C | 1A | 124 | | 4 | D | 2B | 130 | | 5 | E | 2A | 128 | | 6 | F | 2B | 124 | ## Sort by Key (single column) Clicking the *Key* column header in Rekordbox, or picking *Sort by Key* in the CDJ's browse menu, groups tracks by Key. BPM inside each group falls back to whatever the playlist had: | # | Track | Key | BPM | |---|---|---|---| | 1 | A | 1A | **130** | | 2 | C | 1A | **124** | | 3 | B | 2A | 124 | | 4 | E | 2A | 128 | | 5 | D | 2B | **130** | | 6 | F | 2B | **124** | In `1A` and `2B` the BPM runs 130 → 124 — the opposite direction of a harmonic warm-up. ## Sort by BPM (single column) Sorting by *BPM* aligns BPM but scatters the Key grouping: | # | Track | Key | BPM | |---|---|---|---| | 1 | C | **1A** | 124 | | 2 | B | **2A** | 124 | | 3 | F | **2B** | 124 | | 4 | E | **2A** | 128 | | 5 | A | **1A** | 130 | | 6 | D | **2B** | 130 | Key walks `1A → 2A → 2B → 2A → 1A → 2B` — a tour of the Camelot wheel that doesn't follow harmonic adjacency. ## rbsort (multi-column: Key → BPM) `rbsort` produces a compound sort: Camelot Key ascending as the primary key, BPM ascending as the tiebreaker within each Key group: | # | Track | Key | BPM | |---|---|---|---| | 1 | C | 1A | 124 | | 2 | A | 1A | 130 | | 3 | B | 2A | 124 | | 4 | E | 2A | 128 | | 5 | F | 2B | 124 | | 6 | D | 2B | 130 | Key groups walk `1A → 2A → 2B` (Camelot ascending) and BPM rises monotonically inside each group. This is the ordering a harmonic warm-up set typically wants. ## Summary | Sort | Key order | BPM within Key group | |----------------------------|-------------|--------------------------------| | Sort by Key (single) | aligned | registration order (no guarantee) | | Sort by BPM (single) | scattered | aligned (Key grouping gone) | | `rbsort` (multi-column) | aligned | ascending | Toggling between *Sort by Key* and *Sort by BPM* in the UI does not combine them — that is the gap `rbsort` fills. ## Why this has to be baked into the playlist's stored order Even when single-column sort is enough for browsing, CDJs play tracks in the playlist's **stored** order — they ignore any on-screen sort that was active in Rekordbox or on the deck's browser. Toggling *Sort by Key* on the desktop and then exporting to USB does not change the order the CDJ plays the tracks in. `rbsort` rewrites the playlist's stored order inside the exported `collection.xml`, so the compound sort survives the USB export and reaches the CDJ. The same reason the loudness side of headroom writes gain into the audio files rather than relying on Rekordbox's Auto Gain tags: anything that lives only in the laptop app does not follow the music onto the deck. ## Related - [README — Rekordbox Playlist Sorter (rbsort)](../README.md#rekordbox-playlist-sorter-rbsort) - [docs/true-peak-ceiling.md](true-peak-ceiling.md) — the same kind of design note for the loudness side