Rust MIDI-CI / Property Exchange responder stack — the control plane for MIDI 2.0.
# 1. Build the virtual responder
cargo build -p virtual-responder --release
# 2. Run it (creates a virtual ALSA UMP endpoint)
cargo run -p virtual-responder --release -- --device-name midici --endpoint-name midici-responder -v 1
# 3. In another terminal, list endpoints
aseqdump -l
# 4. Monitor traffic (replace <client:port> from step 3)
aseqdump -u 2 -p <client:port>The responder implements Discovery, PE Capabilities, Get, Set, Subscribe, Notify fan-out, ACK/NAK, MUID lifecycle, and Invalidate MUID — MIDI-CI v1.2 Management plus Property Exchange per M2-101/M2-103.
clap-autoprop demonstrates why this stack exists: a CLAP plugin whose
parameters auto-appear as MIDI-CI Property Exchange data — zero manual
MIDI-learn:
- Controller/host does Discovery → PE Get
ResourceList→ GetChCtrlList(the controller list resource) → auto-maps knobs - Controller subscribes to the resource (
0x38command: "start") - When the host flushes a param change, the plugin's control thread calls
clap_autoprop::flush_param_change(engine, "ChCtrlList", partial_json), which fans out acommand: "partial"update to every subscribed peer — the audio thread never touches the engine
The CLAP event bridge (RT ring + host-timer glue) and the full plugin binary land with the Phase-6/8 transport work; the PE subscription plumbing they will drive is complete and tested at the wire level.
See docs/guide/integrating-a-clap-plugin.md for the full walkthrough.
Asciinema recording of the autoprop demo — pending (HUMAN GATE G9a). Recording
goes to docs/media/autoprop.cast; this link will be added once the file exists.
┌────────────────────────────────────────────────────────┐
│ Application (plugin params, device model, resources) │
├────────────────────────────────────────────────────────┤
│ midici-responder poll(now) / feed() / drain() │
├──────────────────────┬─────────────────────────────────┤
│ midici-core │ midici-pe │
│ Discovery, MUID, │ Chunker/Reassembler, Mcoded7, │
│ ACK/NAK, tx mgr │ Resource registry, Subs │
├──────────────────────┴─────────────────────────────────┤
│ midi2 crate (SysEx7 / UMP message types) │
├────────────────────────────────────────────────────────┤
│ Transport adapters: ALSA UMP · CLAP events · (CoreMIDI│
│ / Windows MIDI Services later) │
└────────────────────────────────────────────────────────┘
Sans-io core — zero I/O, deterministic, no_std + alloc capable. Transport adapters are thin.
Audio thread does exactly one thing: move bytes.
- CLAP RT side: copies inbound SysEx bytes into wait-free SPSC ring, pops outbound ring, emits CLAP MIDI events. No parsing, no JSON, no allocation, no locks, no logging.
- Control thread: 10 ms timer drives
poll(), drains rings, runs all state machines + JSON. - Reassembly buffers: pre-reserved, capped at 64 KiB per (peer, requestId), max 4 concurrent per peer, LRU-evicted on 3 s timeout.
- Verified:
assert_no_allocon RT path in debug; loom/miri pass on ring wrapper.
| Crate | Role |
|---|---|
midici-core |
Sans-io CI state machines (Discovery, MUID, ACK/NAK, version masking) + PE wire message types |
midici-pe |
PE Capabilities/Get/Set/Subscriptions/Notify, chunker, reassembler, Mcoded7, resource registry, ResponderEngine façade |
midici-responder |
Reserved for the ergonomic event-pump façade (use midici_pe::ResponderEngine today) |
midici-transport-alsa |
ALSA UMP sequencer adapter (Linux) |
midici-transport-clap |
CLAP adapter core: miri-verified wait-free SPSC ring (Ring/Producer/Consumer) |
midici-conformance |
Golden transcripts, fuzz targets, interop harness |
| Area | Status | Spec |
|---|---|---|
| Discovery / Reply | ✅ full | M2-101 §5.5–5.6 |
| Endpoint Inquiry/Reply | ✅ inquiry only | M2-101 §5.7–5.8 |
| ACK / NAK | ✅ full | M2-101 §5.10–5.11 |
| Invalidate MUID | ✅ full | M2-101 §5.9 |
| PE Capabilities | ✅ full | M2-101 §8.5–8.6 |
| PE Get / GetReply | ✅ full | M2-101 §8.7–8.8 |
| PE Set / SetReply | ✅ full (per-resource write policy) | M2-101 §8.9–8.10 |
| PE Subscribe / updates | ✅ full (0x38/0x39; partial/full/notify fan-out) |
M2-101 §8.11–8.12 · M2-103 §11 |
| Notify (0x3F) | ✅ receive-only (deprecated in v1.2; status 144 honored) | M2-101 §8.13 · M2-103 §12 |
| Profiles | 🔄 inquiry-only | M2-101 §6–7 |
| Encodings: ASCII, Mcoded7, zlib+Mcoded7 | ✅ / ✅ / feature zlib |
M2-103 §6 |
Rust 1.97.1 (pinned in rust-toolchain.toml).
MIT. See LICENSE.