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; processBlock only touches lock-free atomics and a SpinLock-protected snapshot

Building

Requirements

DependencyLocationNotes
JUCE../../JUCE (two levels up from the project root)Not a submodule
SDL3libs/SDLGit submodule — initialise before configuring
CMake ≥ 3.22system
Xcode / clangmacOSAU 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

FieldValue
CompanySonoTron
Manufacturer codeSNTN
Plugin codeGPMN
FormatsAU, VST3, Standalone
MIDI outputYes
MIDI inputNo

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