MIDI Control
shiloh-mixer includes a UDP MIDI listener that drives a vim-style modal state machine.
A USB MIDI keyboard connected to any machine on the network can control channel faders,
mutes, and Ardour transport without touching the browser.
Architecture
USB piano (any machine)
│ USB MIDI class-compliant device
▼
shiloh-midi-sender (runs on the PC with the keyboard)
│ raw MIDI note bytes in UDP datagrams → stg-srv001:19999
▼
shiloh-mixer MIDI listener
│
├──► modal state machine (fader/mute changes on mixer channels)
└──► Ardour :3819 (OSC transport / marker commands)
Ardour WebSocket ws://localhost:3818/
└──► mixer_web (DAW strip fader/mute feedback)
All transport is fire-and-forget UDP. No acknowledgement or retransmit.
| Port | Direction | Purpose |
|---|---|---|
| 19999 | keyboard PC → mixer | Raw MIDI datagrams from shiloh-midi-sender |
| 3819 | mixer → Ardour | OSC transport, navigation, and fader commands |
| 3818 | Ardour → mixer_web | Ardour WebSocket surface (strip gain/mute feedback) |
Sending MIDI from Your PC
On the machine with the keyboard:
shiloh-midi-sender \
--device "USB MIDI" \
--server stg-srv001.bq.shilohbv.com:19999
--device takes a case-insensitive substring of the MIDI input port name. For most
class-compliant USB keyboards the default (USB MIDI) works. List available devices:
shiloh-midi-sender --list-devices
The sender logs every note-on it forwards:
[midi] note_on: note=60 (C4) vel=100
Modal State Machine
The mixer interprets note-ons through a modal machine. You stay in a mode until you
explicitly switch. A3 (MIDI 57) is the universal reset to Idle from any mode.
Idle ─ C3 ─► ChannelSelect ─ <channel key (C4 octave)> ─► ChannelControl{id}
│
│ action keys (C5 octave)
│ fader_down / fader_up / mute_toggle
Idle ─ G3 ─► Transport
│ transport keys (C6 octave)
│ play / stop / record / goto_start / markers
A3 from anywhere ──► Idle (universal reset)
Modes do not auto-timeout by default (configurable via idle_timeout_ms in [midi]).
Use A3 whenever you feel stuck or want to start a fresh selection.
Default Key Map
The map starts at C3 (MIDI 48) to avoid the unreliable low C2 on our instrument.
Mode entry — C3 octave
| Note | MIDI # | Effect |
|---|---|---|
| A3 | 57 | Reset to Idle (works from any mode) |
| C3 | 48 | Enter ChannelSelect |
| G3 | 55 | Enter Transport |
Channel selection — C4 octave (in ChannelSelect)
White keys correspond to channels left-to-right in the UI:
| Note | MIDI # | Channel |
|---|---|---|
| C4 | 60 | Studio (bcast1) |
| D4 | 62 | Laptop (bcast2) |
| E4 | 64 | Guest (bcast3) |
| F4 | 65 | Desktop audio |
| G4 | 67 | Mopidy |
| A4 | 69 | USB mic L |
| B4 | 71 | USB mic R |
Channel actions — C5 octave (in ChannelControl)
| Note | MIDI # | Action |
|---|---|---|
| C5 | 72 | Fader down (−fader_step, default −0.05 linear) |
| D5 | 74 | Fader up (+fader_step) |
| E5 | 76 | Mute toggle (main bus) |
Transport — C6 octave (in Transport mode)
| Note | MIDI # | Ardour action |
|---|---|---|
| C6 | 84 | Play |
| D6 | 86 | Stop |
| E6 | 88 | Record enable toggle |
| F6 | 89 | Goto start |
| G6 | 91 | Goto end |
| A6 | 93 | Previous marker |
| B6 | 95 | Next marker |
| C7 | 96 | Add marker |
NOTE: Ardour has no pause primitive. Stop keeps the playhead in place; a subsequent
play resumes from there.
Configuring Custom Mappings in TOML
Key maps are defined in shiloh-mixer.toml under [midi]. Note names use the
convention C4 = 60 (middle C). Sharps use # or s suffix; flats use b.
[midi]
listen_port = 19999
ardour_osc = "127.0.0.1:3819"
idle_timeout_ms = 0 # 0 = no timeout
fader_step = 0.05 # linear step per fader_down/up press
# Mode-entry keys (in Idle mode)
[midi.mode_keys]
"C3" = "channel_select"
"G3" = "transport"
"A3" = "idle"
# Channel selection keys (in ChannelSelect mode)
[midi.channel_keys]
"C4" = "bcast1"
"D4" = "bcast2"
"E4" = "bcast3"
"F4" = "desktop"
"G4" = "mopidy"
"A4" = "lmic"
# Channel action keys (in ChannelControl mode)
[midi.channel_action_keys]
"C5" = "fader_down"
"D5" = "fader_up"
"E5" = "mute_toggle"
# Transport keys (in Transport mode)
[midi.transport_keys]
"C6" = "play"
"D6" = "stop"
"E6" = "record"
"F6" = "goto_start"
"G6" = "goto_end"
"A6" = "prev_marker"
"B6" = "next_marker"
"C7" = "add_marker"
Valid action names:
mode_keys:idle,channel_select,transport,navigationchannel_action_keys:fader_down,fader_up,mute_toggletransport_keys:play,stop,play_stop,record,goto_start,goto_end,
prev_marker,next_marker,add_markerchannel_keys: anyidfrom[[channels]]
Unknown action names log a warning at startup and silently no-op at runtime.
After editing the config, restart the mixer:
systemctl --user restart shiloh-mixer
Ardour Setup
For bidirectional Ardour integration:
- Open Edit → Preferences → Control Surfaces → Open Sound Control → Show Protocol Settings.
- Enable OSC. Set the listen port to 3819.
- Under Feedback Settings, enable fader and mute feedback on strips/master.
For the WebSocket surface (fader/mute feedback to mixer_web):
The mixer connects to ws://localhost:3818/ automatically if the Ardour WebSocket server
is enabled. Enable it in Ardour’s preferences under Control Surfaces → WebSockets Server.
The mixer matches Ardour strip names by the prefix convention "NN Name" (e.g.
"01 Studio", "02 Laptop").
Troubleshooting MIDI
Keys appear in MIDI panel but action does nothing
- Check which mode you are in (
MODEbadge in the MIDI panel). - The key may not be mapped in the current mode — the badge shows
—if unmapped. - Press
A3to reset to Idle and try again from ChannelSelect.
No key activity in MIDI panel at all
- Check
:19999label in the MIDI panel — confirms the mixer is listening. - On the server:
sudo tcpdump -i any -nn udp port 19999— look for packets when you
press keys. - On the keyboard PC: check
shiloh-midi-senderlogs — it prints every note-on forwarded. - If
shiloh-midi-sendershows no output, USB MIDI isn’t routing to it. Try listing
devices:shiloh-midi-sender --list-devices.
Ardour transport does nothing
- Check the mixer log for
[midi] transport → play(or similar) lines. - If those appear but Ardour ignores them: confirm Ardour OSC is enabled and listening
on port 3819. - Use
tcpdump -i any -nn udp port 3819on the server to confirm packets are being sent.
DAW fader doesn’t follow Ardour
- Check the mixer log for
osc feedback listenerorardour web surfaceat startup. - Verify the Ardour WebSocket server is enabled (not OSC feedback — we use WebSocket).
- In
mixer_webat/diag, check the Ardour Web Surface card: look for connection
state and strip count. If strip count is 0, Ardour’s track names may not match the
"NN Name"prefix convention.