Keyboard-driven DJ tools for the Linux terminal:
- odj-player, a DJ player inspired by the Pioneer CDJ-800.
- odj-sampler, which plays the cues stored by odj-player from 16 pads.
cargo run --release -p odj-player -- ~/Music # browse a folder
cargo run --release -p odj-player -- track.mp3 # load a track straight away
cargo run --release -p odj-sampler # 16 pads of stored cues
Needs librubberband (for Master Tempo), ALSA and PipeWire; building also
needs libclang. Plays MP3, FLAC, WAV, OGG/Vorbis, AAC/M4A and AIFF.
If there is more than one stereo output (say, a USB audio interface next to the built-in card), odj-player and odj-sampler ask at start which one to play on: a stereo output, or one side of it in mono. HDMI outputs are not listed. Audio goes through PipeWire, so you can run one odj-player per output, e.g. two decks on the two line outs of an interface. Without PipeWire, odj-player opens the card directly and holds it while it runs.
Use a terminal with the kitty keyboard protocol (Ghostty, kitty, foot, Alacritty, WezTerm) and run it outside tmux. Hold-to-preview cue, jog nudges and search need key-release events; elsewhere those holds fall back to timers.
odj-player finds where each track's beats and bars fall, not just its tempo,
and shows them as lines on the waveform with a bar.beat counter. When the
grid is off, fix it: D makes the playhead beat 1 of a bar, A taps the
tempo (while playing the beats also move onto your taps), and Y enters grid
adjust, where , / . shift the beats by 1 ms and ↑ / ↓ change the BPM by
0.01. Shift+A goes back to the detected grid.
With quantize on (Q), cues, hot cues, loop in and out and auto loops go on
the nearest beat, the paused beat jog steps along the grid, and a hot cue or
reloop pressed while playing waits for the next beat, so the music stays in
phase. Quantize is set per track. It starts on for tracks whose beats keep
to the grid, such as most electronic music, and off for loosely played ones.
On the grid you can also beatjump (- / =, Shift to change the size from 1
to 64 beats; while looping, the loop moves along), make beat loops of 1/32 to
64 beats (L, with [ / ] setting the size), and hold ; for a loop roll
of that size; tapping [ / ] while holding it halves or doubles the roll
for a stutter build-up. A roll stops when you let go, and the track goes
on from where it would have been had you never rolled. Slip mode (Z) does the same
for loops, reverse, held hot cues and pause: the track keeps going
underneath, shown as a purple line on the waveform, and comes back in time.
Run one odj-player per deck and they find each other: each takes a deck
number (DECK 1 to 4, in the header) and shows the others' tempo. F syncs a
deck to the master: it takes the master's tempo, or half or double it,
whichever is nearer, and keeps its beats on the master's, correcting drift by
up to 0.3% of speed and jumping back in if it's far out. Play on a synced deck
waits for the master's next beat, and quantize stays on while synced. Decks
whose beats don't keep to the grid sync their tempo only (SYNC BPM).
The first synced deck to play becomes the master; when it stops, a synced deck
that's playing takes over, so loading your next track never moves the tempo
of what's playing. Shift+F makes a deck the master. The phase meter shows
how far a deck's beats are from the master's, or another playing deck's,
in ms, so it helps when beatmatching by ear too. Sync allows for each sound
card's latency and Master Tempo's, so decks on different cards meet at the
mixer.
Press ? in the app for all keys. Memory points, hot cues, loops, beat grid
corrections and quantize are remembered per track, one JSON file each under
~/.local/share/odj/tracks/. Files are named by a fingerprint of the audio, so
cues survive renaming, moving and retagging. Settings such as Auto Cue live in
~/.config/odj/settings.json.
Sixteen pads on the keys 1234 / QWER / ASDF / ZXCV. Press Enter to put one
of your stored cues (hot cues, memory points, last loops) on the selected pad.
A loop goes on as it is. A cue without an end opens the sample editor, which
starts at 4 bars and lets you move the start and the end along the beat grid
by beats or bars, or by milliseconds, and preview it; T brings a pad back into it. Each pad can be one-shot, gate (plays while held) or toggle, loop
or not, and has its own gain.
A pad holds its own copy of the audio, cut out and converted to the output
rate, so the track file isn't read again while you play. The header and the
memory panel show how much memory the pads take. The kit (which cue is on which
pad, the trimmed end and the pad settings) is saved as you go to
~/.local/share/odj/kit.json, and the pads are cut from the files again at the
next start. A pad whose file has moved or changed shows so.
A Cargo workspace: crates/odj-core holds what both apps share (decoding and
fingerprinting, the cue library, audio output, the file browser, the link
between decks);
crates/odj-player and crates/odj-sampler are the two binaries.
git clone https://github.com/ibz/odj && cd odj/packaging/arch
makepkg -si
This builds the latest master, pulls in rubberband and alsa-lib, and
installs odj-player and odj-sampler plus a launcher entry (Super+Space →
"odj player"), which opens in your default terminal. Run makepkg -si again
to update; sudo pacman -R odj-git removes it. Once it's on the AUR: yay -S odj-git.
MIT, see LICENSE.