Professional DJ app for macOS. Built for Detroit techno, house, trance and dance music. Pioneer DDJ-200 controller. AI stem separation. Studio-grade beat sync with transient quantization.
- Connect DDJ-200 via USB before launching
- Open
U-DJ.appfrom Applications - Click + Add Folder → select your music folder
- Tracks analyse automatically in the background
- Drag a track onto each deck or double-click to load
- Press PLAY
| Control | Action |
|---|---|
| PLAY/PAUSE | Play / pause |
| CUE | Set cue / return to cue / stutter play (hold) |
| Shift + CUE | Jump to cue and stop |
| SYNC | This deck becomes follower, other deck becomes master — snaps bar phase and locks tempo |
| Shift + SYNC | Set loop IN point |
| SYNC (2nd press while follower) | Re-snap to master's current bar phase |
Adjusts playback rate ±16%. Moving the master slider adjusts both decks together when sync is active. Moving the follower slider redirects to the master — sync stays on, tempo changes together.
| Action | Result |
|---|---|
| Touch surface | Scratch — suspends sync momentarily |
| Rim only | Pitch bend (accumulates, decays 230ms) |
| Shift + Rim | Key shift ±1 semitone per 5 ticks |
| Knob | Action |
|---|---|
| HI | EQ High — kill to +10 dB (soft-pickup) |
| MID | EQ Mid — kill to +10 dB |
| LOW | EQ Low — kill to +10 dB |
| CFX Super Knob | Color FX depth — centre = bypass |
- Channel fader — per-deck volume (p² taper)
- Crossfader — constant-power cosine-law A→B blend
- PFL button — headphone cue for that deck
- Shift + PFL — Key Sync: auto-match key to master via Camelot wheel (≤6 semitones)
Top row: [ VOC ] [ BASS ] [ DRM ] [ SYNTH ] ← mode function (see below)
Bottom row: [ VOC ] [ BASS ] [ DRM ] [ ●8● ]
ECH ECH ECH (mode cycle)
Pad 8 (bottom-right, hidden on screen) cycles pad modes:
STEMS → HOT CUE → BEAT LOOP → BEAT JUMP → STEMS
Top row — stem mute toggles:
| Pad | Stem | LED dark | LED lit |
|---|---|---|---|
| 1 | VOC (Vocals) | Playing | Muted |
| 2 | BASS | Playing | Muted |
| 3 | DRM (Drums) | Playing | Muted |
| 4 | SYNTH (Other) | Playing | Muted |
Press to mute → pad lights up. Press again → stem returns, pad goes dark. Smooth 10ms crossfade on every cut — no clicks.
Bottom row — echo effects:
| Pad | Effect |
|---|---|
| 5 | VOC ECHO — apply echo to vocal stem |
| 6 | BSS ECHO — apply echo to bass |
| 7 | DRM ECHO — apply echo to drums |
- Press to toggle echo ON/OFF (lit = active, dark = off)
- Shift + echo pad — cycle FX type:
ECHO → DELAY → REVERB → FILTER - Only one echo active at a time
Stems must show ● STEMS READY in the mixer before pads are active. Shows ◌ STEMS LOADING… while separating (~90s first time, instant from cache).
| Action | Result |
|---|---|
| Pad (empty) | Set cue at current position (beat-snapped, 40% window) |
| Pad (set, lit) | Jump to cue with 30ms pre-roll crossfade |
| Right-click pad | Delete cue |
Up to 8 auto cue points (INTRO / DROP / BUILD / BREAK / OUTRO) loaded automatically.
Pads left→right: 1/4 1/2 1 2 4 8 16 bars
| Action | Result |
|---|---|
| Pad | Activate loop at that size (lit = active) |
| Same pad | Exit loop |
Loop IN is snapped to the nearest beat/phrase boundary. Loop OUT is snapped to the stored beat grid (sample-accurate end alignment).
Manual loop: Shift + SYNC → set loop IN, SYNC → set loop OUT.
| Pads 1–4 | Pads 5–7 |
|---|---|
| −1 bar | +1 bar |
| −2 bars | +2 bars |
| −4 bars | +4 bars |
U-DJ uses a two-model pipeline tuned specifically for Detroit techno / electronic music:
1. MelBand Roformer (InstVoc Duality V2) → clean vocal stem + vocal-free instrumental
2. demucs htdemucs_ft (Apple Silicon MPS) → drums, bass, other — run on the vocal-free instrumental so no vocal energy bleeds into those stems
Running demucs on the instrumental rather than the full mix gives cleaner drums and bass because the model never has to fight vocal energy.
| Stem | Source model | Post-processing |
|---|---|---|
| VOC | MelBand Roformer | HP 180Hz, adaptive gate at 30th-percentile RMS, presence +1.5dB 1.5–4kHz |
| BASS | demucs (on instrumental) | LP 900Hz (keeps 303 acid squeal 400–900Hz), sub +3dB, mono <120Hz |
| DRM | demucs (on instrumental) | HP 30Hz (preserves kick body), transient boost, sub-kick +4dB 55–105Hz, hat air +3dB >10kHz |
| SYNTH | demucs (on instrumental) | 10th-order HP 200Hz (kills bass bleed), transient suppressor, presence +2dB 2–6kHz, air +1.5dB >12kHz |
Post-separation: Wiener soft-mask refinement (STFT 4096/hop 1024) sharpens TF-bin separation across all four stems. Drum bleed removed from vocals via spectral subtraction above 2kHz. Level calibrated so stem sum matches original RMS.
If audio-separator is not installed, falls back to demucs full 4-stem separation on the original mix.
First separation: ~2–4 minutes on Apple Silicon M-series (Roformer + demucs)
Subsequent loads: instant from cache (~/.udj/stems/)
U-DJ uses a 40% quantize window (rekordbox/Serato standard). When you trigger a cue, hot cue, or loop:
- The hit is snapped to the nearest beat only if it lands within ±40% of a beat interval. Hits in the 40–50% zone (near the middle of two beats) stay where they are — forced snapping that far would sound unnatural.
- Playback extends 30ms before the target point. The outgoing audio fades out over the same 30ms window while the incoming audio fades in — a smooth overlap instead of a hard cut.
- If an active loop's out-point falls inside that 30ms pre-roll window, it is trimmed back to leave at most 1ms of overlap.
- Beat loop out-points are snapped to the stored beat grid (not nominal BPM math), giving sample-accurate loop lengths regardless of BPM drift.
- Pressing PLAY on a paused deck snaps position to the nearest beat (same 40% window).
Press SYNC on the incoming deck → it locks to the playing deck (follower) and snaps bar phase immediately.
Studio-grade phase engine:
- Phase is measured against the stored beat grid (actual inter-beat intervals), not uniform BPM arithmetic — eliminates phantom drift on tracks with natural BPM variation.
- Dead zone: ±0.46ms at 130 BPM. Errors inside this window are ignored to avoid micro-jitter.
- Convergence: phase errors correct in ~50ms (P-gain 0.8, cap 4% per beat).
- Rate lock is hard nominal (zero tempo wobble); only position is nudged for phase correction.
The beat grid strip at top shows both decks — when bar markers align, you're locked.
CFX Super Knob controls depth. Select type from mixer panel dropdown:
FILTER · ECHO · ROLL · DELAY · REVERB · FLANGER · PHASER · DRIVE
Centre = bypass on all effects.
- UI ▲/▼ buttons or Shift + jog rim → shift pitch ±1 semitone
- UI 0 button → reset
- Shift + PFL → Key Sync via Camelot wheel
- Key display turns amber when shifted
NORM button per deck applies EBU R128 normalisation. Toggle per deck independently.
| Dot | Colour | Meaning |
|---|---|---|
| ● | Green | Fully analysed — BPM, key, beats, cues, stems cached |
| ● | Blue | BPM/key done, stems not yet cached |
| ● | Amber | Beat grid done, some metadata missing |
| ● | Orange | BPM only |
| ○ | Grey | Not yet analysed |
+ Add Folder — replaces library with new folder scan
⟳ Analyse All — queues all tracks for analysis and stem pre-computation
When no tracks are loaded on either deck, U-DJ runs an attract-mode animation across every LED and UI element:
Hardware (DDJ-200):
- Rainbow comet sweeps across all 7 pads on both decks — all three LED layers (hot cue, beat loop, sampler) fire simultaneously
- PLAY + CUE flash as the comet enters each deck
- SYNC cross-fades: deck 1 lit during the first half of the sweep, deck 2 during the second
- PFL alternates between decks on a slow ~1s throb
- Master row ripples: TRANS FX → PHRASE SYNC → MASTER HP → pause → repeat
UI:
- Pad widgets chase the same comet with a rainbow colour trail
- All EQ and FX knobs spin randomly and cycle through the palette
- Mode tabs (STEMS / HOT CUE / BEAT LOOP / BEAT JUMP) flash a random colour per deck
- Crossfader sweeps a slow sine wave A→B→A
- Channel badges, NO TRACK labels, and FILTER selectors pulse through the palette
- TRANS FX button pulses in sync with the hardware
- Library toolbar buttons light up sequentially
The show stops immediately when a track is loaded. All UI state (knob positions, mode selections, crossfader) is restored exactly as it was.
Select tracks in the library, then click 🔬 Inspect Stems in the toolbar. No deck load required — stems are separated and cached automatically on first inspect.
The inspector runs fully local DSP analysis: band energy ratios, cross-contamination correlation, adaptive auto-fix (HP filters, shelf cuts, Wiener soft-mask refinement), and logs results to ~/.udj/library.db. High drums→synths and drums→vocals correlation on dance music is identified as rhythmic co-occurrence (~ RHYTHMIC) rather than acoustic bleed, so no unnecessary DSP is applied.
U-DJ uses a studio-grade sync engine modelled on rekordbox/Mixxx behaviour:
- Hard rate lock — follower tempo matches master exactly every audio callback, zero wobble
- Bar-phase snap — on SYNC press or master play-start, follower snaps to the master's current bar phase with a 12ms fade-in (imperceptible in a live mix)
- Continuous phase P-controller — sub-millisecond position nudges keep both decks within ±1ms of each other once locked
- Tempo slider while synced — follower slider redirects to master; neither deck breaks sync
- No backward playback — nudge is capped to never exceed the buffer advance, eliminating the screech that position-jump sync engines produce
- Catmull-Rom (Hermite) interpolation — same quality as Mixxx cubic at any playback rate
- Equal-power crossfades — sin/cos curves on cue triggers, loop boundaries, and stem toggles
- Soft limiting — smooth knee at 0 dBFS on master bus and stem mix
- TPDF dither — 24-bit triangular dither on final output
- DC blocking — single-pole high-pass at ~5 Hz eliminates DC offset buildup
- Loudness normalisation — optional per-deck EBU R128 gain (NORM button)
- Buffer size — 512 samples at 44100 Hz (~11.6ms latency)
| Path | Contents |
|---|---|
~/.udj/library.db |
Track database |
~/.udj/events.db |
Sync/quantise event log |
~/.udj/stems/<hash>.npz |
Stem cache |
~/.cache/torch/hub/checkpoints/ |
Demucs model weights |
| Minimum | Recommended | |
|---|---|---|
| OS | macOS 12 Monterey | macOS 13+ |
| Chip | Intel x86_64 | Apple Silicon (M1/M2/M3/M4) |
| Python | 3.9 | 3.9 (tested) |
| Disk | 2 GB | 5 GB |
| RAM | 8 GB | 16 GB |
brew install python@3.9git clone https://github.com/ProperProperProper/U-DDJ200.git
cd U-DDJ200Core:
pip3 install -r requirements.txt
pip3 install pyloudnormML stack — Apple Silicon:
pip3 install torch==2.8.0 torchaudio==2.8.0
pip3 install demucs==4.0.1
pip3 install "audio-separator[cpu]"ML stack — Intel Mac:
pip3 install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
pip3 install demucs pyloudnorm "audio-separator[cpu]"Build tool:
pip3 install pyinstaller==6.20.0Verify:
python3 -c "import torch, torchaudio, demucs, sounddevice, PyQt6; print('All good')"python3 main.pypython3 -m PyInstaller udj.spec --clean -y
rm -rf /Applications/U-DJ.app
cp -R dist/U-DJ.app /Applications/U-DJ.app
open /Applications/U-DJ.appAlways use
--clean -y. Never incremental builds.
Alwaysrm -rfthe old bundle beforecp -R— plain copy merges rather than replaces, leaving stale files from the previous build.
- Approve Microphone access when prompted
- DDJ-200 detected automatically — no driver needed
- Click + Add Folder → select your music
- First stem separation triggers a one-time ~50 MB model download
Stem inspection (local DSP analysis, no API key required):
python3 dev_inspect_stems.py track1.wav track2.wav