Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Multi-Device Setup

Conductor supports connecting and mapping multiple MIDI devices simultaneously (v4.19.0+, ADR-009). Each device can have independent mappings, and events are routed to the correct device-specific rules automatically.

Defining Devices

Use [[devices]] sections in your config.toml to define device identities:

[[devices]]
alias = "pads"
matchers = [{ type = "NameContains", value = "Mikro" }]

[[devices]]
alias = "keys"
matchers = [{ type = "NameContains", value = "Launchpad" }]

Each device needs:

  • alias: A short name used in trigger mappings (e.g., "pads", "keys")
  • matchers: One or more rules for matching physical MIDI ports

Matcher Types

MatcherDescriptionExample
ExactNameExact port name match{ type = "ExactName", value = "Maschine Mikro MK3" }
NameContainsSubstring match{ type = "NameContains", value = "Mikro" }
NameRegexRegex pattern match{ type = "NameRegex", value = "Mikro.*MK[23]" }
UsbIdentifierUSB vendor/product ID{ type = "UsbIdentifier", vendor_id = 0x17CC, product_id = 0x1600 }
CoreMidiUniqueIdmacOS CoreMIDI unique ID{ type = "CoreMidiUniqueId", value = 12345 }

Matchers have a specificity ordering for priority: CoreMidiUniqueId > UsbIdentifier > UsbTopology > ExactName > PlatformId > NameContains > NameRegex. When multiple identities could match a port, the most specific matcher wins.

Device-Specific Mappings

Add a device field to any trigger to restrict it to a specific device:

[[modes]]
name = "Default"

# Only triggers from the "pads" device
[[modes.mappings]]
trigger = { type = "Note", note = 36, device = "pads" }
action = { type = "Launch", app = "Finder" }

# Only triggers from the "keys" device
[[modes.mappings]]
trigger = { type = "Note", note = 36, device = "keys" }
action = { type = "Shell", command = "echo hello" }

# Triggers from any device (no device filter)
[[modes.mappings]]
trigger = { type = "Note", note = 60 }
action = { type = "Text", text = "any device" }

Priority Order

When an event arrives, Conductor checks rules in this order:

  1. Device-specific rules for the current mode (O(1) HashMap lookup)
  2. Any-device rules for the current mode (linear scan)
  3. Global device-specific rules
  4. Global any-device rules

The first match wins.

Hot-Plug Detection

Conductor automatically detects when devices are connected or disconnected (v4.22.0+). New ports are scanned every 5 seconds and matched against your [[devices]] configuration.

GUI Multi-Device Status

The GUI shows all connected devices with status indicators (v4.22.0+):

  • Green dot: Active and receiving events
  • Yellow dot: Muted (events ignored)
  • Red dot: Disconnected

You can mute/unmute individual devices from the GUI.

MCP Multi-Device Tools

Three MCP tools are available for LLM-assisted multi-device management (v4.23.0+):

  • conductor_list_device_bindings: List all device bindings and their status
  • conductor_set_device_enabled: Mute/unmute a specific device
  • conductor_scan_ports: Trigger a port rescan for hot-plug detection

Migrating from Legacy Config

If you have an existing [device] section, use the migration CLI:

# Preview what would change (dry-run)
conductorctl migrate-config

# Apply the migration (creates .bak backup)
conductorctl migrate-config --write

This converts:

[device]
name = "Mikro MK3"
auto_connect = true

Into:

[[devices]]
alias = "mikro-mk3"
matchers = [{ type = "NameContains", value = "Mikro MK3" }]

Config Validation

Conductor validates multi-device configs (v4.24.0+):

  • Device aliases must be unique and non-empty
  • Trigger device fields must reference a defined device alias
  • Invalid references produce clear error messages during config load

Complete Example

[[devices]]
alias = "pads"
matchers = [{ type = "NameContains", value = "Mikro" }]

[[devices]]
alias = "faders"
matchers = [{ type = "NameContains", value = "nanoKONTROL" }]

[[modes]]
name = "Production"

[[modes.mappings]]
trigger = { type = "Note", note = 36, device = "pads" }
action = { type = "Keystroke", keys = "space", modifiers = ["cmd"] }

[[modes.mappings]]
trigger = { type = "CC", cc = 0, device = "faders" }
action = { type = "VolumeControl", operation = "Set" }

[[modes]]
name = "Mixing"

[[modes.mappings]]
trigger = { type = "Note", note = 36, device = "pads" }
action = { type = "Keystroke", keys = "m" }

[[global_mappings]]
trigger = { type = "Note", note = 127 }
action = { type = "ModeChange", mode = "Production" }