Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/midi-vectors/out-abs-same.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "in `abs` only a position that differs from the last one sent goes out",
"features": ["midi"],
"steps": [
{ "send": { "hand": { "knob": 2, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 25, 65], "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": 1 } }, "expect": { "out": [{ "midi": "<absent>", "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": -1 } }, "expect": { "out": [{ "midi": "<absent>", "within": 20 }] } },
{ "send": { "wait": 100 }, "expect": { "out": [{ "midi": "<absent>" }] } },
{ "send": { "hand": { "knob": 2, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 25, 66], "within": 20 }] } }
]
}
13 changes: 13 additions & 0 deletions docs/midi-vectors/out-buttons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "Encoder button 1–4 pressed",
"features": ["midi"],
"steps": [
{ "send": { "hand": { "knob": 1, "down": true } }, "expect": { "out": [{ "midi": [144, 60, 127], "within": 20 }] } },
{ "send": { "wait": 500 }, "expect": { "out": [{ "midi": "<absent>" }] } },
{ "send": { "hand": { "knob": 1, "down": false } }, "expect": { "out": [{ "midi": [128, 60, "<integer>"], "within": 20 }] } },
{ "send": { "hand": { "knob": 4, "down": true } }, "expect": { "out": [{ "midi": [144, 63, 127], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 4, "down": false } }, "expect": { "out": [{ "midi": [128, 63, "<integer>"], "within": 20 }, { "midi": "<absent>" }] } }
]
}
15 changes: 15 additions & 0 deletions docs/midi-vectors/out-cc-abs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "the value is a virtual position `0..127` the panel keeps per knob — starts at 64, moves by that knob's `outMul` steps per `outDiv` detents, clamps at the ends",
"features": ["midi"],
"steps": [
{ "send": { "hand": { "knob": 1, "turn": 3 } }, "expect": { "out": [{ "midi": [176, 24, 67], "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": -2 } }, "expect": { "out": [{ "midi": [176, 25, 62], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 1, "turn": 62 } }, "expect": { "out": [{ "midi": [176, 24, 127], "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": -63 } }, "expect": { "out": [{ "midi": [176, 25, 0], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 1, "turn": -1 } }, "expect": { "out": [{ "midi": [176, 24, 126], "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 25, 1], "within": 20 }] } }
]
}
13 changes: 13 additions & 0 deletions docs/midi-vectors/out-cc-div.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "The encoders have 24 detents a turn, so `1` is 24 steps per turn and `4` is 6.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outDiv=4&knob=1" } },
{ "send": { "hand": { "knob": 1, "turn": 24 } }, "expect": { "out": [{ "midi": [176, 24, 70], "within": 20 }] } },
{ "send": { "hand": { "knob": 2, "turn": 24 } }, "expect": { "out": [{ "midi": [176, 25, 88], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 1, "turn": 3 } }, "expect": { "out": [{ "midi": "<absent>", "within": 100 }] } },
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 24, 71], "within": 20 }] } }
]
}
12 changes: 12 additions & 0 deletions docs/midi-vectors/out-cc-mul.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "outbound sensitivity the other way: steps per detent, `1..8`.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outMul=8&knob=4" } },
{ "send": { "hand": { "knob": 4, "turn": 2 } }, "expect": { "out": [{ "midi": [176, 27, 80], "within": 20 }] } },
{ "send": { "hand": { "knob": 3, "turn": 2 } }, "expect": { "out": [{ "midi": [176, 26, 66], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 4, "turn": -1 } }, "expect": { "out": [{ "midi": [176, 27, 72], "within": 20 }] } }
]
}
12 changes: 12 additions & 0 deletions docs/midi-vectors/out-cc-rel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "`64 ± steps` since the previous message (binary offset)",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outMode=rel" } },
{ "send": { "hand": { "knob": 3, "turn": -2 } }, "expect": { "out": [{ "midi": [176, 26, 62], "within": 20 }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 3, "turn": 5 } }, "expect": { "out": [{ "midi": [176, 26, 69], "within": 20 }] } },
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 24, 65], "within": 20 }] } }
]
}
10 changes: 10 additions & 0 deletions docs/midi-vectors/out-hand-only.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "Encoder 1–4 turned by a hand.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/params?p1=900" } },
{ "send": { "wait": 100 }, "expect": { "out": [{ "midi": "<absent>" }], "status": { "params": [900, "<integer>", "<integer>", "<integer>"] } } },
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 24, 65], "within": 20 }] } }
]
}
13 changes: 13 additions & 0 deletions docs/midi-vectors/out-no-echo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "a CC or note the host itself sent is never reflected back",
"features": ["midi"],
"steps": [
{ "send": { "midi": [176, 20, 100] } },
{ "send": { "midi": [176, 25, 65] } },
{ "send": { "midi": [144, 62, 127] } },
{ "send": { "wait": 100 }, "expect": { "out": [{ "midi": "<absent>" }], "status": { "paramActive": [true, false, false, false] }, "input": { "given": { "deltas": [0, 1, 0, 0], "presses": [0, 0, 1, 0] } } } },
{ "send": { "midi": [128, 62, 0] } },
{ "send": { "wait": 100 }, "expect": { "out": [{ "midi": "<absent>" }] } }
]
}
11 changes: 11 additions & 0 deletions docs/midi-vectors/out-pacing.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "at most one message per knob every 50 ms (≤ 20/s per knob, `PF_MIDI_OUT_MIN_INTERVAL_MS`), trailing-edge so the final value always arrives",
"features": ["midi"],
"steps": [
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 24, 65], "within": 20 }] } },
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": "<absent>", "within": 20 }] } },
{ "send": { "hand": { "knob": 1, "turn": 1 } }, "expect": { "out": [{ "midi": "<absent>", "within": 10 }] } },
{ "send": { "wait": 50 }, "expect": { "out": [{ "midi": [176, 24, 67] }, { "midi": "<absent>" }] } }
]
}
10 changes: 10 additions & 0 deletions docs/midi-vectors/out-program-change.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "The pattern changed (by anyone: knob, console, OSC, MIDI, a show), for `n ≤ 127`",
"features": ["midi"],
"steps": [
{ "send": { "http": "GET /api/patterns/select?index=2" }, "expect": { "out": [{ "midi": [192, 2], "within": 20 }] } },
{ "send": { "midi": [192, 5] }, "expect": { "out": [{ "midi": [192, 5], "within": 50 }] } },
{ "send": { "wait": 100 }, "expect": { "out": [{ "midi": "<absent>" }] } }
]
}
17 changes: 17 additions & 0 deletions docs/midi-vectors/out-rel-sum.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"contract": "docs/midi-spec.md#device--host",
"says": "in `rel` the steps sum between messages and a sum leaving ±63 is sent at once",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outMode=rel" } },
{ "send": { "hand": { "knob": 4, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 27, 65], "within": 20 }] } },
{ "send": { "hand": { "knob": 4, "turn": 2 } }, "expect": { "out": [{ "midi": "<absent>", "within": 20 }] } },
{ "send": { "hand": { "knob": 4, "turn": 3 } }, "expect": { "out": [{ "midi": "<absent>", "within": 10 }] } },
{ "send": { "wait": 50 }, "expect": { "out": [{ "midi": [176, 27, 69] }, { "midi": "<absent>" }] } },
{ "send": { "wait": 100 } },
{ "send": { "hand": { "knob": 4, "turn": 1 } }, "expect": { "out": [{ "midi": [176, 27, 65], "within": 20 }] } },
{ "send": { "hand": { "knob": 4, "turn": 40 } }, "expect": { "out": [{ "midi": "<absent>", "within": 20 }] } },
{ "send": { "hand": { "knob": 4, "turn": 40 } }, "expect": { "out": [{ "midi": [176, 27, 127], "within": 20 }] } },
{ "send": { "wait": 60 }, "expect": { "out": [{ "midi": [176, 27, 81] }, { "midi": "<absent>" }] } }
]
}
9 changes: 9 additions & 0 deletions docs/rest-api-vectors/midi-caps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"contract": "docs/rest-api.md#conventions-for-future-additions",
"says": "A feature's endpoints exist only when that feature is composed in — the route itself is absent otherwise, so clients probe `caps` rather than expecting a soft 404.",
"features": ["midi"],
"steps": [
{ "send": { "http": "GET /api/status" }, "expect": { "reply": { "body": { "caps": ["patterns", "params", "sleep", "midi"] } } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "status": 200, "headers": { "content-type": "application/json" } } } }
]
}
19 changes: 19 additions & 0 deletions docs/rest-api-vectors/midi-get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "channel, `outDiv`, `outMul`, `outMode`, `host`, session state, counters",
"features": ["midi"],
"steps": [
{
"send": { "http": "GET /api/midi" },
"expect": {
"reply": {
"status": 200,
"body": {
"channel": 1, "outDiv": [1, 1, 1, 1], "outMul": ["<integer>", "<integer>", "<integer>", "<integer>"], "outMode": "abs", "host": "<string>",
"rtpPeers": "<integer>", "rtpPeer": "<string>", "rx": "<integer>", "tx": "<integer>"
}
}
}
}
]
}
11 changes: 11 additions & 0 deletions docs/rest-api-vectors/midi-host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "Empty string clears it.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?host=192.168.0.23" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "host": "192.168.0.23" } }, "status": { "midi": { "host": "192.168.0.23" } } } },
{ "send": { "http": { "request": "POST /api/midi", "form": "host=" } }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "host": "" } } } }
]
}
12 changes: 12 additions & 0 deletions docs/rest-api-vectors/midi-knob.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "Apply `outMul`/`outDiv` to one knob; without it, to all four.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outDiv=4&knob=3" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "POST /api/midi?outMul=2&knob=2" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [1, 1, 4, 1], "outMul": [1, 2, 1, 1] } } } },
{ "send": { "http": "POST /api/midi?outDiv=2" } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [2, 2, 2, 2], "outMul": [1, 1, 1, 1] } } } }
]
}
11 changes: 11 additions & 0 deletions docs/rest-api-vectors/midi-out-div.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "outbound sensitivity: detents per outbound step, `1..16`, persisted on the panel.",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outDiv=16" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [16, 16, 16, 16] } }, "status": { "midi": { "outDiv": [16, 16, 16, 16] } } } },
{ "send": { "http": "POST /api/midi?outDiv=1" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [1, 1, 1, 1] } } } }
]
}
12 changes: 12 additions & 0 deletions docs/rest-api-vectors/midi-out-mode.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "knobs out as a virtual position (default) or as relative steps.",
"features": ["midi"],
"steps": [
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outMode": "abs" } } } },
{ "send": { "http": "POST /api/midi?outMode=rel" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outMode": "rel" } }, "status": { "midi": { "outMode": "rel" } } } },
{ "send": { "http": { "request": "POST /api/midi", "form": "outMode=abs" } }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outMode": "abs" } }, "status": { "midi": { "outMode": "abs" } } } }
]
}
13 changes: 13 additions & 0 deletions docs/rest-api-vectors/midi-out-mul.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"contract": "docs/midi-spec.md#settings",
"says": "Setting one of `outMul`/`outDiv` above 1 resets the other to 1",
"features": ["midi"],
"steps": [
{ "send": { "http": "POST /api/midi?outDiv=4" } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [4, 4, 4, 4] } } } },
{ "send": { "http": "POST /api/midi?outMul=8" }, "expect": { "reply": { "status": 200 } } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [1, 1, 1, 1], "outMul": [8, 8, 8, 8] } } } },
{ "send": { "http": "POST /api/midi?outDiv=2" } },
{ "send": { "http": "GET /api/midi" }, "expect": { "reply": { "body": { "outDiv": [2, 2, 2, 2], "outMul": [1, 1, 1, 1] } } } }
]
}
37 changes: 37 additions & 0 deletions docs/rest-api-vectors/midi-rx-detail.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"contract": "docs/midi-spec.md#status",
"says": "Unreleased 1.2 adds `rxDetail` to both `midi` in `/api/status` and the root of `GET /api/midi`.",
"features": ["midi"],
"steps": [
{
"send": { "http": "GET /api/status" },
"expect": {
"reply": {
"body": {
"midi": {
"rxDetail": {
"worker": "<boolean>", "abs": ["<integer>", "<integer>", "<integer>", "<integer>"], "rel": ["<integer>", "<integer>", "<integer>", "<integer>"],
"queueDropped": "<integer>", "txQueueDropped": "<integer>", "packetGaps": "<integer>", "parseErrors": "<integer>", "bufferOverflows": "<integer>",
"pollMaxGapUs": "<integer>", "pollMaxUs": "<integer>", "queueMaxAgeUs": "<integer>", "stackMin": "<integer>"
}
}
}
}
}
},
{
"send": { "http": "GET /api/midi" },
"expect": {
"reply": {
"body": {
"rxDetail": {
"worker": "<boolean>", "abs": ["<integer>", "<integer>", "<integer>", "<integer>"], "rel": ["<integer>", "<integer>", "<integer>", "<integer>"],
"queueDropped": "<integer>", "txQueueDropped": "<integer>", "packetGaps": "<integer>", "parseErrors": "<integer>", "bufferOverflows": "<integer>",
"pollMaxGapUs": "<integer>", "pollMaxUs": "<integer>", "queueMaxAgeUs": "<integer>", "stackMin": "<integer>"
}
}
}
}
}
]
}
24 changes: 24 additions & 0 deletions docs/rest-api-vectors/midi-status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"contract": "docs/midi-spec.md#status",
"says": "`GET /api/status` on a build with this feature lists `\"midi\"` in `caps` and carries",
"features": ["midi"],
"steps": [
{
"send": { "http": "GET /api/status" },
"expect": {
"reply": {
"status": 200,
"body": {
"caps": ["patterns", "params", "sleep", "midi"],
"midi": {
"runtime": "<boolean>", "channel": "<integer>", "outMode": "<string>", "host": "<string>",
"outDiv": ["<integer>", "<integer>", "<integer>", "<integer>"],
"outMul": ["<integer>", "<integer>", "<integer>", "<integer>"],
"rtpPeers": "<integer>", "rtpPeer": "<string>", "rx": "<integer>", "tx": "<integer>"
}
}
}
}
}
]
}
Loading