Skip to content

About

Pioneer CDJ & XDJ Emulator

Topics

Resources

Stars

68 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

119 Commits

Folders and files

Repository files navigation

Pioneer CDJ & XDJ Emulator

Original firmware. Emulated hardware.

Pioneer DJ players on your computer, from the CDJ-2000 to the NXS2 and XDJ family.

License: GPL-2.0-or-later Platform: Windows | Linux | macOS QEMU 9.1 Firmware not included

Supported players Β· Quick start Β· Keyboard Β· Virtual deck Β· Mods Β· How it works

Real emulator screens for six players: supported CDJ-2000NXS2, and experimental XDJ-700, XDJ-1000, CDJ-900NXS, CDJ-2000NXS and CDJ-2000. Except the CDJ-900, the older decks play in real time with the DSP module setup builds.

Actual emulator captures from development builds. Select the image for a closer look.

The CDJ-2000NXS2 boots, mounts a virtual USB stick, and loads and plays tracks from it. The waveform scrolls, the time counts down, and the sound you hear is computed by the player's own DSP program on an emulated DSP. Start two and they find each other on an emulated Pro DJ Link network, where MASTER and SYNC work between them. Plug in a MIDI controller and it plays them, or use the virtual deck.

Seven older players have experimental emulation, with further models in development. The status table separates usable features from ongoing work.

Features

These are the CDJ-2000NXS2's; the older players are covered under Supported players.

πŸŽ›οΈ The real firmware All three processors of the player run its own code: MAIN, the display board and the audio DSP.
πŸ”Š The DSP's own sound A C66x emulator with a JIT runs the DSP program MAIN uploads at boot; the audio you hear is its output.
πŸ“ˆ Load and play A track from the stick loads and plays; the waveform and playhead move, the time counts down.
πŸ”— Pro DJ Link Two decks take their own addresses from a small DHCP server, announce themselves, and MASTER and SYNC work between them.
🎚️ Any MIDI controller A profile of what the controller sends plus a mapping to CDJ keys. A Roland DJ-202 mapping is included, with its LEDs mirroring the player's lamps.
πŸŒ€ Jog and touch The jog bends the DSP's playback speed through the firmware's own jog engine; a click in the window is a touch on the screen.
πŸ§ͺ Built for modding Trace any firmware address, watch memory, change it while it runs, call firmware functions, script every input.

Screenshots

The CDJ-2000NXS2's own screen, emulated: the colour waveform scrolling and the time counting down while a track plays

Two emulated CDJ-2000NXS2s: deck 1 in SYNC follows master deck 2 to 193.7 BPM

More Pro DJ Link screens
Deck 2 showing player 1's beat
Deck 2 tracks the master's beat over Pro DJ Link
MASTER TEMPO and SYNC on
MASTER TEMPO and SYNC, colour waveform
Browsing player 1's USB from deck 2
Browsing the other player's USB over the link
A track loaded from player 1
...and playing a track loaded from it

Supported players

The CDJ-2000NXS2 is the fully supported player; the older models are experimental. Sound is on by default (NOSOUND=1 turns it off). Screenshots show the firmware interface, not a claim of feature parity.

player status what works
CDJ-2000NXS2 βœ… Fully supported Plays tracks from USB in real time with sound. Pro DJ Link, MIDI controllers and mods.
XDJ-1000MK2 πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds. PLAY, CUE and touch work.
XDJ-700 πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds. Touch works.
XDJ-1000 πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds. PLAY, CUE, tempo, jog and touch work.
CDJ-900NXS πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds.
CDJ-2000NXS πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds. PLAY, CUE, tempo and jog work.
CDJ-900 πŸ§ͺ Experimental Loads and plays from USB; its display shows the overview, playhead and time.
CDJ-2000 πŸ§ͺ Experimental Loads and plays from USB in real time with the DSP module setup builds. PLAY, CUE, tempo and jog work.
Feature matrix per player

βœ… works, measured Β  ⚠️ partly, see the note Β  ❌ does not work Β  βž– the player does not have it Β  Β· not tested yet

CDJ-2000NXS2 XDJ-1000MK2 XDJ-700 XDJ-1000 CDJ-900NXS CDJ-2000NXS CDJ-900 CDJ-2000
Media
USB βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ…
Playback
Sound βœ… βœ… βœ… βœ… βœ… βœ… ❌ βœ…
Real time with the setup DSP module βœ… βœ… βœ… βœ… βœ… βœ… Β· βœ…
Display
Waveform / overview βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ…
Tempo and BPM readout βœ… βœ… βœ… βœ… βœ… βœ… βœ… βœ…
Transport
PLAY / pause βœ… βœ… ⚠️1 βœ… ⚠️1 βœ… βœ… βœ…
CUE βœ… βœ… Β· βœ… Β· βœ… Β· βœ…
Tempo
Tempo fader βœ… Β· Β· βœ… Β· βœ… Β· βœ…
MASTER TEMPO ⚠️2 · · · · · · ·
Jog
Pitch bend βœ… Β· Β· βœ… Β· βœ… Β· βœ…
Vinyl / scratch ⚠️3 · · · · · · ·
Needle search βœ… Β· Β· Β· βž– Β· βž– Β·
Performance
Hot cues βœ… Β· Β· Β· βž– Β· βž– Β·
Loops ⚠️4 · · · · · · ·
Beat jump Β· Β· Β· Β· βž– βž– βž– βž–
Slip ⚠️5 Β· Β· Β· Β· Β· Β· βž–
Interface
Touch screen βœ… βœ… βœ… βœ… βž– βž– βž– βž–
Pro DJ Link
Link with other players βœ… ❌6 ❌6 ❌6 ❌6 ⚠️7 ❌6 ❌6

Firmware: CDJ-2000NXS2 1.87, XDJ-1000MK2 1.45, XDJ-700 1.15, XDJ-1000 1.13, CDJ-900NXS 1.31, CDJ-2000NXS 1.44, CDJ-900 4.32, CDJ-2000 4.33.

  1. Only one run so far.
  2. Sound can drop out after switching it on.
  3. VINYL scratching not tested yet.
  4. Loops engage in only some runs.
  5. The slipped playback is not checked yet.
  6. Only one deck can be started so far.
  7. Announces itself; browsing not checked yet.

Everything below describes the CDJ-2000NXS2 unless it says otherwise.

Nothing from Pioneer DJ is included

This repository distributes no firmware, no ROM or flash images, no fonts, no artwork files and no manuals or schematics. Everything here was written for this project. To run it you supply your own copy of the public firmware update file for the player, which the setup unpacks on your machine, into a folder that is never committed.

The screenshots in this README show the emulated firmware's own interface, running in this emulator, for illustration.

This project is not affiliated with, endorsed by or supported by Pioneer DJ or AlphaTheta. Pioneer DJ, CDJ, rekordbox and PRO DJ LINK are trademarks of their respective owners.

Quick start

You will need a recent multi-core CPU (see Limits), about 3 GB of disk for the build trees and the DSP module, Python 3.11 or newer, the update file for your player, and your own music. For the CDJ-2000NXS2 that is C2KNXS2.UPD version 1.87; every address in the model is for that version.

1. Get a shell for your system (only your own line applies):

system what to do first
Windows Install MSYS2 and open the MSYS2 MINGW64 shell. setup.sh tells you which packages to add.
Linux (native or WSL) Nothing. Any shell will do; setup.sh prints the apt command for anything missing.
macOS (Apple silicon or Intel) Install the Xcode Command Line Tools and Homebrew, then the build tools and a newer bash (macOS's own is 3.2; the scripts find Homebrew's by themselves): xcode-select --install then brew install bash ninja meson pkgconf glib pixman python

2. Clone and run setup (the same on every system):

git clone https://github.com/Stijn-Jacobs/cdj-nxs2-qemu.git cdj-nxs2
cd cdj-nxs2
./setup.sh

setup.sh walks you through seven steps. Every one is safe to re-run and is skipped when it is already done:

  1. Prerequisites: checks compilers, libraries and Python packages, and prints the exact pacman, apt or brew command for whatever is missing.
  2. Build: fetches QEMU 9.1.0, applies this project's patches and builds the MAIN emulator, the display-board emulator and the DSP library, with a progress line per phase and full logs in logs/ (10–40 minutes).
  3. Firmware: asks for your update file (C2KNXS2.UPD for the CDJ-2000NXS2) and turns it into the images the emulator boots, checking each against a known SHA-256.
  4. USB stick: either a folder of your own music exported by rekordbox (the folder that holds PIONEER/), or a plain folder of music files (MP3, FLAC, AAC/M4A, WAV, AIFF, ALAC), which baken analyses instead, so no rekordbox is needed. Either way, a disk image is built from the result.
  5. DSP module: asks whether to build the DSP module now (about 20-40 minutes, about 2 GB of free disk while it runs; the default is yes). --skip-dsp, or answering no, leaves it for later.
  6. Your setup: one deck or two, Pro DJ Link, sound, a MIDI controller; saved to cdj.conf.
  7. Mods: small on/off tweaks to how the deck behaves. Take the defaults in one answer or choose each (see Mods); saved alongside your setup.

3. Play:

./start.sh          # Ctrl-C stops everything

The deck window opens and the CDJ-2000NXS2 boots to its screen. Click the window, press U for the USB stick, ↓ to a track, Enter to load it and Space to play (every key: Keyboard). Click the screen to touch it.

Updating: git pull, then ./start.sh as usual. The emulator is compiled, so when a pull changed its code start.sh notices and offers to rebuild (./build.sh main display does the same by hand). A pull that changed the DSP module's generator also needs the module built again (see DSP module).

On macOS
  • The deck window is QEMU's native Cocoa window and the sound goes to the default output through Core Audio; neither needs anything from Homebrew. GUI_DISPLAY=sdl and AUDIODEV=... choose others, as elsewhere.
  • Pro DJ Link runs over multicast on your network interface. The first time, macOS may ask whether your terminal may find devices on the local network: allow it, or the two decks will not see each other.
  • The DSP module compiles with Apple's clang (gcc on macOS is clang), and its profile-guided build uses clang's own with Xcode's llvm-profdata.
  • Python packages go into .venv/ in this folder, since Homebrew's Python refuses pip install outside a virtual environment; setup offers the exact command. QEMU 9.1's configure needs distlib, which current pip no longer carries, so the build makes itself a small venv with it in the build tree.
  • Over ssh, with no desktop session, the decks run without a window.
Setup and start options
./setup.sh --dry-run            show every step and command, change nothing
./setup.sh --reconfigure        ask the "your setup" and "mods" questions again
./setup.sh --yes                never ask; take the defaults and the options below
./setup.sh --skip-build         leave the build out (a build tree you made yourself)
./setup.sh --rebuild            build even when the emulators are already built
./setup.sh --skip-dsp           leave the DSP module out (./start.sh says how to build it later)
  --model <id>                  the player (see Other players); asked when not given
  --firmware <file>             your update file for that player (re-installs the images)
  --music <folder>              the rekordbox USB export to image
  --tracks <folder>             a plain folder of music to image instead, analysed by baken
  --decks 1|2   --name <deck name>   --djlink on|off   --audio on|off
  --controller none|<profile>|learn  --relay-port <port>
  --build-dir <dir>             where the two QEMU build trees go

./start.sh                      start what cdj.conf describes
./start.sh --model <id>         start that player
./start.sh --app | --no-app     the virtual deck app, or the plain window
./start.sh --service | --no-service   boot into SERVICE MODE or not
./start.sh stop                 stop a running rig from another shell
./start.sh --dry-run            show what would be started
./build.sh [source|patches|main|display|dsp]   one build phase at a time

Other players

The XDJ-1000MK2, XDJ-700, XDJ-1000, CDJ-900NXS, CDJ-2000NXS, CDJ-900 and CDJ-2000 start from the same two commands with --model; plain ./setup.sh also asks which player you want (Enter keeps the CDJ-2000NXS2). Pass the update file as --firmware:

--model update file firmware
xdj1000mk2 XDJ1KMK2.UPD 1.45
xdj700 XDJ700.UPD 1.15
xdj1000 XDJ1000.UPD 1.13
cdj900nxs C900NXS.UPD 1.31
cdj2000nxs C2KNXS.UPD 1.44
cdj900 C900GUI.UPD, C900DRIV.UPD, C900MAIN.UPD, C900PANL.UPD 4.32
cdj2000 C2KGUI.UPD, C2KDRIV.UPD, C2KMAIN.UPD, C2KPANL.UPD 4.33
./setup.sh --model cdj2000 --firmware path/to/C2KGUI.UPD   # the other three files beside it
./start.sh --model cdj2000

Setup stores the player in cdj.conf, so ./start.sh alone starts it from then on. An older player gets the build, firmware and USB steps and opens one window with your stick; a second deck, Pro DJ Link, MIDI controllers, mods and the virtual deck app are CDJ-2000NXS2 features and are skipped. The CDJ-2000, CDJ-2000NXS, XDJ-1000, XDJ-1000MK2, XDJ-700 and CDJ-900NXS also get the DSP module step. Which players exist is whatever has a profile in models/.

Not every key is mapped on the older players yet. On the XDJ-700, XDJ-1000 and CDJ-900NXS only U (USB), Enter (the select knob's push) and Space (PLAY) are: U and seven Enters load the first track. The CDJ-900 also has ↑ / ↓, , (TRACK back), [ / ] (SEARCH), B, T, I, M, L and D; U and four Enters load the first track. C (CUE) and Esc (BACK) are mapped, but the emulated CDJ-900 does not react to them yet.

Keyboard

No controller needed: click a deck's window and play it from the keyboard. Each window drives its own deck. The mouse is the touch screen.

key does key does
Space PLAY/PAUSE C CUE (held, as on the deck)
↑ / ↓ turn the browse knob PgUp / PgDn ten rows at a time
Enter / β†’ push the knob: open a folder, load a track Esc / ← / Backspace BACK
, / . TRACK previous / next [ / ] SEARCH back / forward (held)
- / = nudge slower / faster while held (Shift for more) Q / W / E LOOP IN / OUT / RELOOP
B BROWSE M MENU (UTILITY)
U / L / R / D USB / LINK / REKORDBOX / DISC source T / I TAG LIST / INFO
S SYNC A MASTER
K MASTER TEMPO P TEMPO RANGE
V SLIP Z REVERSE
J JOG MODE

The virtual deck app

The virtual deck app: a drawn NXS2-style player, a track loaded and playing, PLAY and CUE lit, a loop active

A full player drawn around the emulated screen instead of a bare window: source and browse keys, the rotary selector, hot cue pads, the loop section, CUE and PLAY, a jog with its centre display, the tempo fader. Everything on it is drawn by the app itself; no photo, logo or artwork of the real unit is used.

./start.sh --app          # or CDJ_APP=1 in cdj.conf; --no-app for the plain window

With two decks both stand side by side in one window. Closing the window, or Ctrl-C, stops the decks.

Mouse and keyboard
you do the deck gets
click a key the key, held for as long as the mouse button is
drag the jog round the platter turning: the rim bends the track, the top plate is touch-sensitive
mouse wheel over the jog a nudge
wheel over (or drag) the rotary selector, click its centre turn, push
drag the tempo fader the tempo slider
click the screen a touch
type the keyboard map, to the deck under the mouse
F2 the screen beside the face at full size, on/off
F3, or right-click the screen the screen in a window of its own (resizable, F11 full screen)
Lamps

The lamps are the deck's own: PLAY, CUE, SLIP, MASTER TEMPO and the jog ring light and blink from MAIN's panel-lamp frame, and the jog's centre display turns with the firmware's own pointer. Keys whose report bit is decoded from the firmware but not yet tried on a running deck carry a small amber ring; keys with no known report bit (hot cues, BANK, QUANTIZE, TRACK FILTER, SHORT CUT, the vinyl speed knobs, the needle strip) are drawn but do nothing. Hovering a control says which is which in the status line.

A bigger screen

The NXS2's 7-inch screen is a small part of a tall deck, so a face that fits a monitor shows it at about half size. F3 opens a deck's screen in a window of its own at any size (F11 there for full screen); F2 docks it beside the face at its own 800 x 480 instead. The default is the decks alone (--screen face|dock|window|auto, or CDJ_APP_SCREEN).

Stuck on "Waiting for the screen"

The screen comes from each deck's display board, a QEMU of its own. After 45 seconds without it the status strip says why, and the terminal prints the end of that board's log (bridge-gui-show1.log in /tmp, or the system's temp folder on Windows). logs/app.log records every attempt the app made to reach each screen. Those two files are what to send with a bug report. The emulator itself prints little; CDJ_REPORT=1 brings back its exit reports for diagnostics.

How it is built

Pillow draws the face and pygame-ce (SDL) puts it on screen; both are in requirements.txt, which ./setup.sh installs into .venv/. The window is laid out in points and drawn in the display's own pixels, so on a Retina Mac or a scaled Windows display the face is drawn at 2x (or whatever the display's density is) rather than stretched. The face is drawn once per window size with anti-aliasing and every lamp is a small pre-drawn image, so a lamp or the jog costs almost nothing per frame and the time goes to the screen: about 4 ms for an 800 x 480 frame at 2x on an Apple-silicon Mac, with the window shown at the display's refresh rate (120 Hz there). The app talks to the deck three ways:

  • the screen comes from a frame file the display board writes whenever its picture changes (CDJ_GUI_FRAME_FILE, checked up to 120 times a second), so every frame the firmware draws is shown: with the high_fps mod about 70 a second while zoomed in on a playing track. QEMU's VNC server, the portable alternative, stops at 33 updates a second and is kept only as the fallback for an older build;
  • the touch screen and the keyboard go over that VNC server (loopback only, one port per deck from 5921, or the next free one: the launcher tells the app which), straight into the display board's own touch and key handlers, exactly as with the plain window;
  • the drawn controls and the lamps go through the controller relay, the same path and the same key table (midi/cdj_actions.py) as a MIDI controller, which can stay connected alongside.

MIDI controllers

Using one: plug the controller in before ./setup.sh. Setup recognises a controller it has a profile for (or offers to learn a new one) and saves your choice, and from then on ./start.sh starts the bridge together with the decks. With two decks the controller's left side plays deck 1 and its right side deck 2. The bridge's own output is in logs/bridge.log. On Windows it runs on a normal Windows Python (python.org or the Microsoft Store) with mido and python-rtmidi installed, because MSYS2's Python cannot open MIDI devices; setup finds it and prints the one pip command it needs if the packages are missing. On macOS the packages go into this folder's .venv/ (setup prints that command too), and the bridge reaches the controller through Core MIDI.

Any controller works as a profile (what the hardware sends, midi/controllers/<name>.json) plus a mapping (which CDJ key each control presses, midi/mappings/<name>.json). The Roland DJ-202 ships with both. For another controller:

python midi/learn.py new                          # name it, move each control, name each one
python midi/learn.py map --controller <name>      # bind controls to CDJ actions
python midi/bridge.py --controller <name> --dry-run

Every key of the player's front panel can be mapped, including ones nothing binds yet. ./setup.sh --reconfigure offers the same walk-through. Details, the mapping format and the full action catalogue: midi/README.md.

Pro DJ Link

Setup asks for one deck or two (--decks 1|2, or CDJ_DECKS=2 in cdj.conf). Two decks find each other over an emulated Pro DJ Link network, and MASTER and SYNC light up between them, same as the real players.

Connecting rekordbox and Ableton Live on this PC

Setup, start command and troubleshooting

The decks' usual Pro DJ Link network is QEMU's multicast segment, which no program on your PC can join. On Windows the deck can instead sit on a virtual network adapter of your PC, and then:

  • rekordbox on the PC is a Pro DJ Link peer of the deck. In Export mode they see each other, the deck browses the rekordbox library (the deck's REKORDBOX / LINK source) and loads and plays tracks from it.
  • Ableton Live (12 tested) shows 1 Link at the deck's BPM, and follows the deck's tempo, with the ableton_link mod on. rekordbox in Performance mode also lists the deck as a Link peer and syncs to it.

One-time setup. The adapter is the TAP-Windows6 driver that ships with OpenVPN (the "TAP Virtual Ethernet Adapter" component is enough). Install OpenVPN, then from an elevated PowerShell:

powershell -ExecutionPolicy Bypass -File scripts\net\tap_setup.ps1

It creates an adapter named CDJ-Link with its tapctl.exe (expected in C:\Program Files\OpenVPN\bin; pass -TapCtl <path> if OpenVPN lives elsewhere) and gives it the address 192.168.50.1/24. It also caps the adapter's MTU at 1500, adds a fixed ARP entry for the deck (192.168.50.10), and opens one firewall rule on that adapter for UDP 67 (DHCP), 20808 (Ableton Link) and 50000-50002 (Pro DJ Link). It is safe to run again, and starting a deck afterwards needs no admin rights.

Start the deck on it (one deck; the rig's own DHCP server gives it 192.168.50.10):

DJLINK=tap:CDJ-Link bash scripts/run/rig.sh show
# with the ableton_link mod:
DJLINK=tap:CDJ-Link CDJ_MAIN_ABLETONLINK=1 CDJ_MAIN_ABLETONLINKPONG=1 bash scripts/run/rig.sh show

In rekordbox, switch to Export mode (the deck does not show up in Performance mode for browsing). Once the deck has booted it appears in the player list. On the deck press L (LINK) or R (REKORDBOX) for the source, browse the library and load a track as from a USB stick.

In Ableton Live, turn on Preferences > Link/Tempo/MIDI > Show Link Toggle, then press LINK in the transport. With the mod on, the toggle shows 1 Link and Live's tempo follows the deck's.

If it does not work:

  • E-8309 on the deck when you load from rekordbox: the adapter's MTU is not capped at 1500. TAP-Windows reports 65500, so Windows sends rekordbox's NFS replies as single oversized frames, which the deck drops. Run tap_setup.ps1 again, or netsh interface ipv4 set subinterface "CDJ-Link" mtu=1500 store=persistent.
  • rekordbox does not see the deck: rekordbox is in Performance mode, or it is sending on another adapter. Switch to Export mode; if the deck still does not appear, check that CDJ-Link is the adapter rekordbox's network settings use.
  • The launcher says there is no adapter of that name, or that it has no address: the setup has not run, or the adapter was renamed.
  • One deck only, and Windows only. A TAP adapter carries a single deck (NDECKS=1), and DJLINK=tap: is refused on other systems.

Mods

Small on/off tweaks to how the deck behaves, each with its own default, listed in mods/mods.conf:

mod what it does default
high_fps Draw the zoomed-in waveform about twice as often (~70 fps instead of ~33). on
live_clock Repaint the REMAIN clock every frame instead of about three times a second. on
three_band Draw the centre waveform and the overview as three bands (low blue, mid amber, high white), CDJ-3000 style, from the track's own 3-band data (.2EX). off
phrase Draw a coloured phrase band along the bottom of the overview strip, CDJ-3000 style, from the track's own phrase analysis in its .EXT file; tracks without it get no band. off
osc Send the deck's beats, state and load/play/stop/cue/loop events as OSC messages on UDP broadcast port 50010, for lighting desks and scripts. off
usb_midi Send MIDI Start, Stop, Continue and Timing Clock over the rear USB-B port, so a DAW or drum machine follows the deck. off
ableton_link Join Ableton Link as a peer: Live or any Link app on the deck's network follows its tempo and beat. off
tcnet Act as a TCNet Master node, broadcasting the deck's play position, track length, beat in the bar and play state on UDP 60000/60001 and answering Metrics (BPM, speed) and MetaData requests on UDP 65023, so ShowKontrol, Resolume or other TCNet receivers can follow it. off

Each mod has a page of its own: its knobs, what it patches, and what has and has not been verified. osc needs a relay on the emulator (python3 scripts/net/osc_relay.py, see its page); ableton_link is reached through a TAP adapter.

./setup.sh shows the defaults (step 7) and either takes them or asks about each one, and saves your answers to cdj.conf; ./setup.sh --reconfigure asks again. You can also edit cdj.conf directly, or override any of them for one run with the environment, e.g. CDJ_GUI_FRAME_MS=0 ./start.sh (a caller's own environment always wins over cdj.conf).

SERVICE MODE is a separate boot option, not a mod: it changes what the deck boots into, not how it behaves once it is up.

Patching a real firmware update

three_band, phrase, osc, usb_midi and ableton_link are firmware code patches, not emulator knobs: the launcher applies the display ones to a copy of the display image at boot (mods/patch_gui.py) and the MAIN ones to a copy of the MAIN image (mods/patch_main.py), the same code a real update would carry. mods/patch_update.py makes the same patches to a real Pioneer .UPD file, so they end up on an actual player instead of a copy this emulator throws away on exit; each mod's page gives its command, for example:

python mods/patch_update.py C2KNXS2.UPD C2KNXS2-link.UPD ableton_link

MAIN has no scatter-load table of its own: it runs in place, so a mod's routine goes straight into a run of erased flash inside its own address space rather than being copied out to RAM the way a display patch is.

Only a mod that is an actual firmware code patch is offered this way. An emulator-only knob such as high_fps or live_clock has nothing to write into a real update, and does not appear. python mods/patch_update.py --list shows what is available and which firmware version it was verified against; a mismatched version is refused unless you pass --force-version.

Combining mods. Name as many as you like in one command, e.g. python mods/patch_update.py C2KNXS2.UPD out.UPD three_band osc ableton_link. Each name brings every patch its row in mods/mods.conf needs. All routines of one image are placed in a single pass into one free area, so they cannot overlap, and mods that hook the same spot in the firmware share it: the spot is replaced once by a stub that runs the original code and then each mod in turn. The launcher combines the mods you switched on the same way.

This is untested on real hardware. The container repacking and the LZSS re-encoding have been checked against the real update file byte for byte, and the patched image has been checked against patch_gui.py's/patch_main.py's own output (see tests/), but nobody has flashed a patched update into an actual player. Flashing a modified firmware update is entirely at your own risk. Keep the original file, and expect that a mistake here could mean a trip through service mode's recovery path, or worse.

Service mode

./start.sh --service      # or CDJ_SERVICE=1 in cdj.conf; --no-service overrides it

This is the service manual's own diagnostic screen: on real hardware you get it by holding TEMPO RANGE and MEMORY while powering the unit on, until the Pioneer logo clears, and this flag does exactly that at boot. The screen lists BUTTON, JOG, ENCODER, NEEDLE, SLIDER VOLUME, JOG TOUCH VOLUME and JOG RELEASE VOLUME; press or move a control and its own row lights up, so it is a live test of every input rather than the player screen.

Both keys release once the logo clears, so nothing stays held down and the rest of the deck is otherwise normal.

By default every run boots from a shared, throwaway flash image, so nothing you change in SERVICE MODE (or anywhere else) survives past Ctrl-C. With PERSIST=1 a deck keeps its own flash image between runs (extract/flash-<tag>.bin), the same as a real unit's memory: a setting changed in SERVICE MODE on a persistent deck stays changed.

The DSP module

A deck keeps up with real time only with a native module of the DSP program's hot code. None is shipped, because it would be derived from Pioneer's code; setup builds it from your own firmware (step 5) and installs it into ~/c14gen/. A headless deck plays a track for 30 seconds, the DSP's hot code is compiled to a native module from what it ran, and a second 30-second play with that module collects the profile-guided counts for the final build. It uses 6 compile jobs (less on an idle machine). With the module, playback holds real time from the first seconds after a load.

The module is installed with a stamp naming what it was built from: the generator version, the DSP core's interface, the firmware and the player. ./start.sh compares the stamp with the current code. After a git pull that changed the generator or the core's interface it does not load the module and shows a banner that it needs building again (./setup.sh rebuilds it); the deck runs far below real time until then. If you skipped step 5, the banner says how to build the module.

Mods do not affect it: they patch the MAIN and display firmware, never the DSP program the module is built from, so turning a mod on or off later needs no rebuild. To compare without the module, start with MODULE=none ./start.sh (the DSP's hot code is then compiled as it plays).

Limits

  • Speed depends on your CPU. One deck runs in real time on a fast desktop. Two decks need roughly twice that, and on a busy or modest machine they fall behind real time (the audio then has gaps).
  • MASTER TEMPO is heavy. It makes the DSP program do far more work per sample, and it is currently the most demanding thing you can ask of the emulator.
  • Only firmware v1.87 is supported for the CDJ-2000NXS2.
  • Work in progress. The older players are experimental (see Supported players). On the NXS2 the USB stick is the only medium so far. Some panel keys are decoded by the firmware but have not been tried here; the controller tools say so when you bind one.

How it works

A CDJ-2000NXS2 is three computers in one box, and all three run here (the older players are built the same way, from their own parts):

chip job in the player here
Renesas SH7724 (SH-4A) MAIN: transport, file system, rekordbox database, Pro DJ Link, the front panel QEMU's SH-4 with a board model written for this project (hw/cdj/)
Renesas SH7269 (SH-2A) the display processor: draws the 7-inch screen from what MAIN sends it a second, big-endian QEMU with its own board model (hw/cdj/boards/nxs2/sh7269gui.c) and a window on your desktop
TI TMS320C6655 (C66x) the DSP: decodes the track, time-stretches it, produces the audio a C66x core written for this project (hw/cdj/c6x/), running the program MAIN uploads to it at boot
flowchart LR
    subgraph deck1["Deck 1"]
        direction LR
        MAIN1["MAIN<br/>SH7724 (SH-4A)"] <-- "SPI link" --> GUI1["Display<br/>SH7269 (SH-2A)"]
        MAIN1 <-- "host port / McBSP" --> DSP1["DSP<br/>C66x"]
        DSP1 --> AUDIO1(["πŸ”Š audio"])
        GUI1 --> WIN1(["πŸ–₯️ window"])
    end
    subgraph deck2["Deck 2"]
        direction LR
        MAIN2["MAIN<br/>SH7724 (SH-4A)"] <-- "SPI link" --> GUI2["Display<br/>SH7269 (SH-2A)"]
        MAIN2 <-- "host port / McBSP" --> DSP2["DSP<br/>C66x"]
    end
    MAIN1 <-- "Pro DJ Link (emulated Ethernet)" --> MAIN2
    CTRL(["πŸŽ›οΈ MIDI controller"]) --> BRIDGE["midi/bridge.py"] -- TCP --> RELAY["midi_relay.py"] -- "panel report" --> MAIN1
    RELAY -- "panel report" --> MAIN2
Loading

The CDJ-2000 and CDJ-2000NXS have a board model of their own (hw/cdj/boards/cdj2000/): an SH7763 MAIN, a Blackfin BF531 display processor, and a DSP behind a host port (a C6727 on the CDJ-2000, a C6747 on the CDJ-2000NXS). The XDJ-700, XDJ-1000 and CDJ-900NXS share the NXS2's SH7724 MAIN, which draws their screen itself, with a C6747 DSP (hw/cdj/boards/xdj1000/). The launcher starts each older player in one window (see Other players).

None of the three knows it is emulated. MAIN talks to the display processor over the same SPI link, to the DSP over the same host port and McBSP audio bus, and reads its front panel through the same report from the panel microcontroller as on the real board. The models were written device by device, from what the firmware actually touches.

How it was done. By reverse engineering, under QEMU, one blocked boot at a time. The firmware update was unpacked (its container, the LZSS packing of the MAIN and display images, the resource archives), and the boards were modelled wherever the firmware stopped: interrupt controllers, DMA, IΒ²C, the USB host controller, the Ethernet MAC, the SPI link between the two processors, the DSP's boot path and command protocol. The firmware turned out to be its own best documentation: it logs to an internal ring, it names its tasks, its keys and its errors, and a trace hook at any firmware address plus a memory watch said the rest.

The NXS2's DSP needed the most work. There is no usable C66x emulator, so this project has its own: a VLIW core that issues up to eight instructions a cycle, with the SoC peripherals the program uses. Interpreted, it is far too slow for real-time audio, so a JIT compiles the hot parts of the DSP program to native code (hw/cdj/c6x/tools/); the DSP module is that code, built ahead of time. The instruction decode tables come from GNU binutils.

A platform for modding the firmware

Running the firmware is the first step; changing what a player does is the next, and this is built for it. The emulator makes the whole machine observable and every part of it adjustable, without touching a real deck:

πŸ” Watch it think CDJ_FWLOG prints the firmware's own internal log; CDJ_FWTRACE=<address>,... prints the registers each time execution passes an address; CDJ_MWATCH=<address> reports which instruction changed a memory word; QEMU's monitor dumps memory and the screen while it runs.
✏️ Change it while it runs CDJ_MPOKE holds memory at a value, CDJ_PPOKE writes one when a chosen instruction executes, and CDJ_PCALL2 calls any firmware function from inside a running task.
πŸ•ΉοΈ Drive it from outside Every front-panel key, the jog, the pots and the touch screen are one datagram away (scripts/run/panel_key.py, midi/bridge.py), so input can be scripted, remapped or generated.
πŸ” Replay the DSP The DSP core can record its execution and replay it deterministically, so a change to the audio path can be tested against a recording.

From here, new behaviour is a question of finding the right code and writing the right change: remapping controls, adding features to the deck, studying the Pro DJ Link protocol from a player that speaks it natively, or experimenting with the audio chain. Keep what you derive from the firmware on your own machine: the update file, and anything built from it, is Pioneer's.

Booting from the flash

MAIN_BOOT=flash ./start.sh

By default MAIN starts with its firmware already unpacked into memory (extract/main_unpacked.bin), past the point where the player's own bootloader hands over. With MAIN_BOOT=flash it starts the way the real unit does: from the reset vector, in Pioneer's bootloader. The firmware update's MAIN section carries that bootloader, an emergency updater and the packed MAIN image, at the flash addresses the player's own updater writes them to, and ./setup.sh --firmware lays them into extract/flash.bin the same way. The bootloader sets up the clocks and memory, checks the image's checksum, unpacks it and starts it; a damaged image starts the emergency updater instead, as on the real player. It adds about two seconds to MAIN's start.

A flash.bin made by an older setup holds no bootloader; MAIN then refuses to start and says so. Run ./setup.sh --firmware <C2KNXS2.UPD> again to rebuild it.

Starting from a saved deck

Booting a deck and loading a track takes minutes on the older players and longer on the NXS2; a test or a change to the firmware's later stages should not pay that every time. A deck can be saved while it runs and started from that moment:

SNAPSHOT=loaded ./start.sh --model xdj1000      # or idle, the settled screen

The first start boots as usual, drives the deck to the point (the model profile's MODEL_IDLE_S and MODEL_LOAD_STEPS) and saves it; every later start restores it at once. Saves live in /tmp/cdj-snap/, one folder per combination of QEMU build, firmware, USB image and MAIN_ARGS, so a rebuild or a new stick starts a fresh save by itself; delete the folder to forget them. A save only comes back into the machine that wrote it, so a device that gains state needs that state in its VMStateDescription before saves of it restore correctly. The USB image is not part of a save: the stick is attached as a read-only-in-practice snapshot and comes back as the original file.

scripts/test/quick.sh snap is the same thing as a test: with the deck table of deck_smoke.py it restores each deck's loaded point and checks that the playhead moves (deck_smoke.py --snapshot). The first run saves the points, so it takes as long as decks2; after that a deck takes a minute and a half.

πŸ“ Repository layout
path what it is
setup.sh, start.sh the guided setup and the everyday start
launcher/ what setup.sh, start.sh and the scripts in scripts/run/ do (python -m launcher), and what the portable program runs
packaging/ the portable program: package.py builds it for this OS, release.sh builds everything and then packages; a GitHub release runs that for Windows, macOS and Linux
build.sh the build step underneath setup (./build.sh main, display, …)
hw/cdj/common/ what every board shares: the boot (DRAM, NOR flash, image, reset vector), the SH-4 core blocks and the board descriptor (cdj_common.h)
hw/cdj/boards/nxs2/ the CDJ-2000NXS2: its MAIN board, one file per device, and the display board (sh7269gui.c); diag/ holds the diagnostic hooks, standin/ historical models that are off by default
hw/cdj/boards/cdj2000/ the CDJ-2000 and CDJ-2000NXS: MAIN board (Renesas SH7763), Blackfin display and C6727 / C6747 DSP models, experimental
hw/cdj/boards/cdj900/ and hw/cdj/m16c/ the CDJ-900: the CDJ-2000's MAIN board with its M16C display micro (core, SoC and the display model), experimental
hw/cdj/boards/xdj1000/ the XDJ-700, XDJ-1000 and CDJ-900NXS: their SH7724 MAIN board with its LCD controller and graphics blocks, experimental
mods/ the mods registry and the display- and MAIN-firmware patches behind them
models/ one profile per player, read by the firmware and launch scripts (see models/README.md)
hw/cdj/c6x/ the C66x DSP core, its SoC peripherals, the JIT generator (tools/) and unit tests
patches/ the changes to QEMU 9.1.0 itself
scripts/build/ the QEMU builds (Linux, macOS and MSYS2), the board installer and the DSP module build
scripts/firmware/ unpacking and verifying the update file
scripts/media/ the USB stick image builder; collection_xml.py and bpm_estimate.py turn a plain folder of music into the XML baken reads for the other USB step
scripts/run/ the run chain behind the launchers, the panel and monitor sockets, the controller relay and the run reports
scripts/net/ the Pro DJ Link segment: DHCP server, capture, capture scorer, and tap_setup.ps1 for the TAP adapter
midi/ the MIDI controller bridge, controller profiles, mappings and the learn tool
app/ the virtual deck app: the drawn player around the emulated screen
tests/, scripts/test/ the offline unit tests (python -m pytest tests, no firmware needed) and the quick check: scripts/test/quick.sh runs them beside a replay of DSP recordings and a headless boot of each deck to its first stable screen (deck_smoke.py), in a couple of minutes
docs/img/ the screenshots on this page

The board sources are copied into the QEMU tree on every build; edit them here, never in qemu-src/.

Licence

This project's own files are licensed under the GNU General Public License, version 2 or (at your option) any later version (GPL-2.0-or-later); the full text is in LICENSE, and each file carries an SPDX-License-Identifier line.

Third-party files and binaries
  • The C66x instruction decode tables in hw/cdj/c6x/binutils/ are copied from GNU binutils and licensed GPL-3.0-or-later. They are compiled into the MAIN emulator, so a binary built from this tree is distributed under GPL-3.0-or-later terms.
  • hw/cdj/boards/nxs2/standin/minimp3.h is minimp3, released into the public domain (CC0).

QEMU itself is GPL-2.0-or-later overall, with a few GPL-2.0-only files; if you distribute a binary, check how those parts combine with GPL-3.0. This note describes the files' licences; it is not legal advice.

Credits

QEMU, which this machine plugs into; GNU binutils, for the TI C6x opcode tables; minimp3; baken (MIT), which writes the USB export for the folder-of-music setup step.

About

Pioneer CDJ & XDJ Emulator

Topics

Resources

Stars

68 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages