Universal Controller MIDI

Getting started

First launch + onboarding

What happens the first time you open Universal Controller MIDI. Permissions, the onboarding wizard, default mapping, and the first MIDI port.

Updated

The first launch of Universal Controller MIDI runs a 90-second, four-step wizard — permissions, controller pick, virtual port, starter template. End state: a paired controller, a virtual MIDI port live, and audio already playable in your DAW before you've touched the mapping editor.

Step 1 — Permissions

The wizard surfaces only the OS prompts you actually need. On macOS that's Input Monitoring and (for wireless) Bluetooth. On Windows, nothing — HID is open by default. On Linux it confirms the udev rule is loaded.

If a permission was denied, the wizard shows a deep link directly to the right OS pane instead of making you hunt.

The four onboarding steps at a glance

Here's what runs, in what order, and roughly how long each step blocks for. Total: ~90 s.

Step Action Typical duration Skippable?
1OS permission grants5–20 sNo — controller can't read otherwise
2Pick a controller10–40 sYes — defer with "I'll connect later"
3Create virtual MIDI port2–5 sNo — required for DAW output
4Choose starter template5–15 sYes — pick "Empty" to skip

Step 2 — Pick a controller

Plug in a wired controller or pair one over Bluetooth. The wizard scans every ~200 ms and lights up matching devices the moment they appear. Supported families: DualSense, DualShock 4, Xbox One/Series, Switch Pro, Stadia, 8BitDo, generic XInput, generic HID gamepads.

Step 3 — Create a virtual MIDI port

The wizard prompts you to name the virtual port. Default name: Universal Controller. This is the name your DAW will see in its MIDI input list.

On macOS you'll be sent to the IAC setup page. On Windows the port is created automatically via the built-in teVirtualMIDI driver bundled with the app. On Linux it's an ALSA seq client — no extra step.

Step 4 — Pick a starter template

The wizard offers four starters: Drum Pads, Synth XY, DJ Decks, and Empty. Drum Pads is the default — face buttons map to a GM kick/snare/hat layout, triggers map to velocity. Pick one and you're done.

You can swap templates any time from the templates panel.

Where the onboarding state is stored

The wizard writes a single JSON blob to the app's config directory the moment it finishes. Re-running it is as simple as deleting that file. Locations:

# macOS
~/Library/Application Support/UniversalControllerMIDI/onboarding.json

# Windows
%APPDATA%\UniversalControllerMIDI\onboarding.json

# Linux
$XDG_CONFIG_HOME/universal-controller-midi/onboarding.json
# (defaults to ~/.config/universal-controller-midi/)

The file shape is tiny — useful to know if you're scripting first-run setup across multiple machines:

{
  "completedAt": "2026-05-29T09:14:22Z",
  "version": 4,
  "permissions": { "inputMonitoring": true, "bluetooth": true },
  "controller": { "vendor": "054c", "product": "0ce6", "name": "DualSense" },
  "midiPort": { "name": "Universal Controller", "channel": 1 },
  "template": "drum-pads"
}

What if it doesn't work?

If onboarding stalls or restarts, one of these is almost always the cause:

  • Wizard re-opens every launch — the config dir isn't writable. Check perms on the path listed above. On Linux it's commonly ~/.config being owned by root after an sudo launch.
  • Controller never appears in step 2 — wired? Try a different USB port. Wireless? Walk through connect Bluetooth first, then resume the wizard.
  • Virtual port creation fails on Windows — the teVirtualMIDI driver didn't install. Re-run the installer elevated and reboot. See virtual-port-windows.
  • Template list is empty — the template bundle didn't ship with the build. Re-download from store.aidxn.com — the SHA on the changelog page is the source of truth.
  • "Permission denied" toast on macOS step 1 — TCC has stale entries from an old build with the same bundle id. Run tccutil reset All com.aidxn.universalcontrollermidi in a terminal, then re-launch.

What's next

Wire your DAW to the new virtual port, then jump to verify everything works for a loopback test.

Edit this page on GitHub Updated
ESC

Type to search.