Skip to main content

pyflx4

tests PyPI Python versions License

Python control for the Pioneer DDJ-FLX4, with a MIDI map that states where every value came from.

pip install pyflx4

On Linux with a recent Python, python-rtmidi may have no prebuilt wheel and will build from source, which needs the ALSA headers:

sudo apt-get install libasound2-dev

macOS and Windows install from wheels with nothing extra.

from pyflx4 import DDJFLX4, DDJFLX4Tools

dj = DDJFLX4Tools(DDJFLX4())

dj.play(deck=1)
dj.set_eq(deck=1, low=-1.0)        # kill the bass on deck 1
dj.set_crossfader(0.5)
dj.trigger_hot_cue(deck=2, pad=1)

Why the map is the point

The FLX4's four deck knobs sit at CC 0x04, 0x07, 0x0B, 0x0F — adjacent values in one run. A map shifted by a single position still looks entirely plausible and still works, in the sense that turning things produces MIDI. It just moves the wrong control: trim adjusts the highs, "EQ low" sweeps the filter. Nothing errors. You find out by ear, mid-set.

Every value in pyflx4.mapping was cross-checked against the Pioneer-DDJ-FLX4 mapping shipped by Mixxx, the most widely exercised FLX4 map in existence, and the ones that matter are asserted in the test suite rather than merely documented.

Where a value could not be cross-checked, the library says so:

>>> from pyflx4 import provenance, unverified
>>> provenance("CC", "EQ_LOW")
<Source.MIXXX: 'cross-checked against the Mixxx DDJ-FLX4 mapping'>
>>> unverified()
('CC.HEADPHONES_VOL', 'CC.MASTER_VOLUME')

Two values are unverified, and they are named. That is the whole list.

The things that cost people an afternoon

  • Deck 2's pads are on channel 9, not 8. Deck 1 is 7. The obvious guess is wrong.
  • Pads send a different note range per mode, not a mode flag: hot cue 0x00, pad FX 0x10, beat jump 0x20, sampler 0x30. Listening only at 0x00 means your hot cues stop working the moment the user switches pad mode.
  • The controller never sends note-off. Release is note-on with velocity 0 — there is not one 0x8n message in its vocabulary. Decoders waiting for note-off latch a held button forever.
  • Every analog control is a 14-bit pair, LSB exactly 0x20 above MSB. Reading the MSB alone gives you a control that works but jumps in 128 steps.
  • Shift is not a modifier. The firmware sends a different note when shift is held, so there is no shift state to track.
  • The tempo fader is on the deck channel, not the mixer channel where the rest of the faders live.

Two layers

DDJFLX4 is raw MIDI: connect, send notes and CCs, receive input, drive LEDs. DDJFLX4Tools sits on top and speaks in DJ terms — play, set_eq, set_loop, trigger_hot_cue — returning a plain dict from each call, so it drops into a CLI, an HTTP handler, or a tool-calling agent without the library caring which.

Both MIDI ports are injectable, so the whole control surface can be driven with no hardware attached:

controller = DDJFLX4(midi_out=fake_out, midi_in=fake_in)

That is how this library's own tests assert the exact bytes each call emits.

Reading the map without the hardware bindings

from pyflx4 import CC, NOTE works without python-rtmidi installed — the controller classes are imported lazily, and even touching DDJFLX4 does not load it until you actually open a port. The map is useful on its own, for generating a mapping for other software or for checking your own values against these — and it stays usable on a machine where the MIDI bindings will not build.

Related

flx4py is another Python FLX4 library, and covers LED animation and an event system more thoroughly than this one does. Check its EQ and trim CCs against your hardware before relying on them; at the time of writing they sit one control away from the values in the Mixxx mapping.

Mixxx itself is the reference if you want the complete picture, including every shifted note. It is GPL-2.0 and written as a JavaScript mapping, so it is a source to read rather than one to depend on from Python.

Status

0.1.0. 40 tests, no hardware required to run them. Extracted from a working system, so the paths exercised in anger — transport, EQ, crossfader, loops, hot cues, pad modes — are the solid ones. The API is not settled; feedback before 1.0 is welcome.

See CONTRIBUTING.md to build on it and SECURITY.md to report a vulnerability.

License

MIT

Release files for pyflx4 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyflx4 0.1.0
File Size Uploaded
pyflx4-0.1.0.tar.gz 37.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyflx4 0.1.0
File Interpreter ABI Platform
pyflx4-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 56.3 kB

Release files / pyflx4-0.1.0.tar.gz

Download URL pyflx4-0.1.0.tar.gz
Size 37.7 kB
Tags Source
SHA-256 checksum
How to use checksums
dc5b101aa55863ee954c8084b4ffbda3b02d4a21528ebaefe53a3faadb8b1a26
BLAKE2b-256 checksum
How to use checksums
325353a691053d7b408ab57bfc670cc2aa7c13db29871cb3b6bac6638cc055c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.

Transparency log

Release files / pyflx4-0.1.0-py3-none-any.whl

Download URL pyflx4-0.1.0-py3-none-any.whl
Size 18.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b2e55d1ac5024a0a5d945b2b8a713048a7653adb1abb76c4cec502614d1dd8e2
BLAKE2b-256 checksum
How to use checksums
14c101acdf331a16367572035d044831fb2caec7fce72ae1fe0db24f3e4f8584
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page