GamepadMidi
March 15, 2026 · View on GitHub
A JUCE audio plugin (AU / VST3 / Standalone) that reads gamepad and joystick input via SDL3 and outputs MIDI CC messages. Each button, axis, and hat on the connected controller gets its own row in the UI where you can configure the target CC number and the output value range (min/max within 0–127).
Mappings are saved per-device inside the plugin state, so they survive project reloads and reconnects.
Features
- Detects any SDL3-compatible joystick/gamepad connected to the system
- Maps buttons, axes, and hat switches to individual MIDI CC messages
- Per-input configurable CC number (0–127) and output range (min / max)
- Live readout of the normalised raw value [0.00–1.00] and the mapped integer
- Per-device mapping state persisted in the DAW project (XML inside the plugin state blob)
- Auto-reconnects to the last-used device when a project is reloaded
- Thread-safe: SDL runs on its own background thread;
processBlockonly touches lock-free atomics and aSpinLock-protected snapshot
Building
Requirements
| Dependency | Location | Notes |
|---|---|---|
| JUCE | ../../JUCE (two levels up from the project root) | Not a submodule |
| SDL3 | libs/SDL | Git submodule — initialise before configuring |
| CMake ≥ 3.22 | system | |
| Xcode / clang | macOS | AU support requires macOS |
First-time clone
git submodule update --init --recursive # pulls libs/SDL
Configure & build
# Configure (a build/ directory is created for you)
cmake -B build -DCMAKE_BUILD_TYPE=Debug
# Build individual formats
cmake --build build --target GamepadMidi_VST3 -- -j$(sysctl -n hw.logicalcpu)
cmake --build build --target GamepadMidi_AU -- -j$(sysctl -n hw.logicalcpu)
COPY_PLUGIN_AFTER_BUILD TRUE is set in CMakeLists.txt, so the built plugins
are automatically copied to the system plugin directories on macOS.
Project layout
GamepadMidi/
├── CMakeLists.txt
├── libs/
│ └── SDL/ SDL3 git submodule (static, joystick subsystem only)
└── src/
├── GamepadManager.h SDL background thread; device list; input callback
├── MappingPanel.h Scrollable mapping UI; MappingEntry struct
├── PluginProcessor.h AudioProcessor; owns GamepadManager and atomics
├── PluginProcessor.cpp processBlock MIDI output; state save/restore
├── PluginEditor.h Editor declaration; device ComboBox + MappingPanel
└── PluginEditor.cpp Timer logic; MappingPanel::updateValues() definition
/Users/christiantronhjem/dev/JUCE/ Shared JUCE install (not a submodule)
Plugin metadata
| Field | Value |
|---|---|
| Company | SonoTron |
| Manufacturer code | SNTN |
| Plugin code | GPMN |
| Formats | AU, VST3, Standalone |
| MIDI output | Yes |
| MIDI input | No |
Known gaps / future work
- MIDI channel is hardcoded to channel 1 — a channel selector UI is planned
- No UI resizing / minimum size constraint
- Hat switches map their raw SDL hat value (bitmask) directly, not split into individual directions