Which of your tracks actually mixes with this one?
DJ library intelligence for Rekordbox, Traktor and Serato. Track2Mix reads your collection (or scans a folder of music), decodes every track on your own machine, scores it for energy, and then — given any anchor track — hands you the harmonically and rhythmically compatible shortlist, ranked. Export it as an M3U8 for Rekordbox, an NML playlist for Traktor, or a Serato crate.
Runs entirely offline. No account, no upload, no server.
- 🌐 Site:
site/— static landing page, see site/README.md - 📈 Go-to-market: GO-TO-MARKET.md
- 🚀 Deploy the site: docs/DEPLOY.md
- 💾 Install: docs/INSTALL.md
- 🔑 Signing a release: docs/CODE-SIGNING.md
- CLI (
track2mix) — import a library or scan a folder, query the resulting DB, export Rekordbox / Traktor / Serato playlists from the shell. - GUI (
track2mix-tauri) — Import / scan a Rekordbox XML, Traktor collection, Serato library or crate, or any folder; search and filter the analysed library, click a track to get harmonically / BPM-compatible suggestions, tick the ones you want, and Export playlist for Rekordbox (M3U8), Traktor (NML) or Serato (crate). Imports go into the open database.
Run the GUI in development:
bun install # first time
bun tauri dev
The GUI auto-opens ./library.db from the project root if it exists; otherwise click Open DB in the title bar.
A release build installs the CLI as track2mix; the examples below use cargo run so they work from a checkout.
- Rekordbox XML is parseable and round-trippable. If this chokes on your real library, the whole product is in trouble.
- Pure-Rust audio decoding handles your formats. MP3, FLAC, WAV, AIFF, M4A via Symphonia.
- Feature extraction is fast enough. Target: < 2 seconds per track on an M-series Mac, parallelized.
- The computed energy values correlate with your ear. This is the real test. If a liquid roller and a neurofunk banger score the same, the features are too coarse.
# Rekordbox: File → Export Collection in xml format
cargo run --release -- analyze --xml ~/rekordbox.xml --limit 500
# Real-world Rekordbox libraries contain a lot of streaming / stale /
# moved entries — do a fast coverage check before committing to a
# long full-library run:
cargo run --release -- analyze --xml ~/rekordbox.xml --dry-run
# If a directory moved (files still there, just at a new path), remap it:
cargo run --release -- analyze --xml ~/rekordbox.xml \
--path-map '/Volumes/OldName/music=/Volumes/NewName/music' \
--skip-analyzedanalyze takes exactly one source:
| Flag | Source |
|---|---|
--xml rekordbox.xml |
Rekordbox: File → Export Collection in xml format |
--nml collection.nml |
Traktor: the collection file (~/Documents/Native Instruments/Traktor x.y/collection.nml) or an exported .nml playlist |
--serato ~/Music/_Serato_ |
Serato: the _Serato_ folder, its database V2 file, or a single .crate |
--dir ~/Music |
Any folder, scanned recursively. Title, artist, genre, BPM and key come from the files' tags, falling back to Artist - Title.ext file names |
cargo run --release -- analyze --dir ~/Music/DnB --skip-analyzed
cargo run --release -- analyze --nml ~/Documents/Native\ Instruments/Traktor\ 3.11.1/collection.nml
cargo run --release -- analyze --serato ~/Music/_Serato_Sources can be mixed in one database. Tracks are matched by file path, so a file imported from Rekordbox and again from a folder scan stays one row (later imports refresh its metadata and features).
Notes:
--skip-analyzedmakes re-runs cheap — only newly-resolved tracks get decoded.- Tracks with paths like
/v4/catalog/tracks/...are Beatport streaming entries, not local files; they are dropped and reported in the coverage summary.
Then explore the library without touching SQL:
# Top / bottom N by any extracted feature, optionally filtered by genre.
cargo run --release -- top --by energy --limit 20
cargo run --release -- top --by energy --genre liquid --asc
# Harmonically + BPM-compatible neighbours around a reference track.
# Matches Camelot / classical / open-key notation in the Rekordbox "Tonality" field.
cargo run --release -- compat "circles around" --bpm-tol 3
cargo run --release -- compat "my favourite track" --any-key # BPM-only
# Snapshot of what's in the database.
cargo run --release -- statsEvery subcommand accepts --db /path/to/library.db if you keep multiple libraries.
top --export, compat --export and the export subcommand pick the format
from the file extension:
cargo run --release -- compat "circles around" --export ~/Desktop/circles.nml
cargo run --release -- export ~/Music/_Serato_/Subcrates/Liquid.crate --sub-genre liquid
cargo run --release -- export ~/Desktop/everything.m3u8Serato crates can only hold files on the same drive as their _Serato_ folder;
tracks on other drives are skipped and listed. Traktor addresses boot-disk files
by volume name, detected automatically on macOS — pass --traktor-volume if
you export elsewhere for a Mac whose disk isn't called "Macintosh HD".
Direct SQL still works if you want ad-hoc queries:
sqlite3 library.db "SELECT name, artist, bpm, tonality, energy FROM tracks ORDER BY energy DESC LIMIT 20"Run against 500 of your own tracks, then:
- Speed. Total time under 10 minutes? Per-track under 2 seconds on release build?
- Coverage. How many tracks failed? If > 5%, investigate why (moved files, exotic formats, encrypted M4A from iTunes).
- Energy sanity. Sort by energy descending. Top 20 should be your hardest tracks. Bottom 20 should be your intros/ambient/deep tracks. If it's random noise, the weights in
features.rsneed retuning. - Genre coherence. Group by genre, plot energy distribution. Liquid DnB should cluster lower than neurofunk. Tech house should cluster mid. If genres overlap completely, features aren't discriminating.
- Harmonic mixing sanity check. Pick a track you know mixes well with X. Query for tracks in compatible keys (same Camelot number, ±1, relative major/minor) within ±3 BPM. Do the suggestions actually mix?
- Linear resampling. Fine for MIR, not for audio playback. Swap
rubatoif you ever play back. - Onset detection is a simple flux-spike heuristic. For real use, port a proper algorithm (complex domain or superflux). Aubio has a well-tested one.
- No key detection. We trust the key from your DJ app (or the file's tags for folder scans). If you want to verify or re-detect, that's a chromagram + key profile correlation — another 100 lines.
- No embedding generation yet. Once features look good, the next step is composing a text profile per track (metadata + feature descriptors like "dense, bright, dynamic") and embedding it.
- Energy weights are hand-tuned. For a real product, collect labels (users rating sets they built) and learn the weights.
- Write a Rekordbox XML playlist (with cue points) in addition to M3U8, proving the round-trip.
- Add text embedding generation via the OpenAI or Voyage API, store vectors in a sibling
vectorstable usingsqlite-vec. - Build a tiny TUI that takes a natural-language prompt and returns a candidate playlist.
- Port to Tauri v2 as a sidecar binary or compile the analyzer as a library and call it from the Rust backend directly.
