Companion module for the DiGiCo S31 with an iPad relay (v1)
Bitfocus Companion 5 connection module (ESM, @companion-module/base ~2.1.3, runtime node22) for the DiGiCo S21/S31. It occupies the console's single iPad slot, controls per-channel sends/assigns to one buss, aux send level/on/pan, mutes, faders and snapshots, and relays any number of DiGiCo iPad apps through to the console byte-for-byte. The S31's iPad extension was probed live: it speaks the SD/DiGiCo Pad OSC tree (console receives on 8000, replies only to the configured iPad port 9000). tools/mock-s31.js emulates the console and test/selftest.js runs the link + relay end to end against it. docs/design-v2.md is the brief for the generic v2 rewrite. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
# Developer tools
|
||||
|
||||
Two small, dependency-free Node.js tools (Node 22+) for developing the S31 module
|
||||
without a console. They share `osc-lite.js`, a tiny OSC encoder/decoder.
|
||||
|
||||
`tools/package.json` pins this folder to CommonJS, because the module's own
|
||||
`package.json` is `"type": "module"` and would otherwise make Node load these
|
||||
files as ESM. Keep it.
|
||||
|
||||
Both tools speak two OSC dialects, chosen with `--dialect`:
|
||||
|
||||
| Dialect | What it is |
|
||||
| --- | --- |
|
||||
| `sseries` (default) | The lowercase S-Series command list: `/console/ping`, `/channel/N/fader`, ... |
|
||||
| `sd` | The "DiGiCo Pad" dialect the **real S31 iPad extension actually speaks**: `/Console/Name/?`, `/Input_Channels/N/fader/?`, ... Verified against a live console. |
|
||||
|
||||
## `mock-s31.js` - fake S31 console
|
||||
|
||||
Emulates the console's OSC extension. It listens on one UDP port and sends
|
||||
**all** feedback to **one** configured controller IP:port, whoever asked and
|
||||
from whichever source port, like the real desk.
|
||||
|
||||
```
|
||||
node tools/mock-s31.js [--dialect sseries|sd] [--listen 8000] [--reply-to IP:PORT]
|
||||
[--channels N] [--auxes N] [--bool-int] [--live]
|
||||
[--types] [--quiet]
|
||||
```
|
||||
|
||||
| Flag | Meaning |
|
||||
| --- | --- |
|
||||
| `--dialect` | `sseries` (default) or `sd`. |
|
||||
| `--listen` | UDP port to bind (the console's receive port). Default 8000. |
|
||||
| `--reply-to` | Fixed feedback destination, e.g. `127.0.0.1:9000`. If omitted, locks onto the first sender's IP with port 9000 and prints that. |
|
||||
| `--channels` | Input count, 1-60. Default 48 (`sseries`) or 60 (`sd`). |
|
||||
| `--auxes` | Aux busses / sends per channel. Default 16, max 16 (`sseries`); default 25, max 25 (`sd`). |
|
||||
| `--bool-int` | `sseries` only: send booleans as int 0/1 instead of OSC `T`/`F`. |
|
||||
| `--live` | Every 3 s, change a random send level or send on/off and emit it. |
|
||||
| `--types` | Also print each message's OSC type tags, e.g. `/Input_Channels/1/fader ,f [-1.4375]`. |
|
||||
| `--quiet` | Don't print every message. |
|
||||
|
||||
Traffic is printed as `<- ADDRESS [args] from IP:PORT` and `-> ADDRESS [args]`.
|
||||
Whole-number floats print with a decimal (`-10.0`) so you can tell int from float.
|
||||
|
||||
### sseries dialect
|
||||
No args at a readable address = query (reply with the value); with an arg =
|
||||
set (store, then echo to the controller). `/console/resend` dumps the whole
|
||||
state at ~2 ms per packet. Values are clamped (fader -150..10, send level
|
||||
-90..10, pan -1..1). Addresses: `/console/{ping,channel/counts,resend}`,
|
||||
`/channel/N/{name,fader,mute,solo,pan}`, `/channel/N/total/gain` (send-only,
|
||||
sent with fader changes), `/channel/N/send/M/{level,enabled,pan}`, and
|
||||
`/digico/snapshots/fire[/next|/previous]` (logged only). Inputs are 1-60, aux
|
||||
busses 70..70+auxes-1.
|
||||
|
||||
### sd dialect
|
||||
A **query** is the address plus a trailing `/?` and no args; the reply is the
|
||||
same address without `/?` plus the value. A **set** is the address without `/?`
|
||||
and one arg; it is stored and echoed back in the console's reply types.
|
||||
|
||||
| Query | Reply type |
|
||||
| --- | --- |
|
||||
| `/Console/Name/?` | `s` ("S31 AB") |
|
||||
| `/Console/{Input_Channels,Aux_Outputs,Group_Outputs,Control_Groups,Matrix_Outputs,Matrix_Inputs,Graphic_EQ,Talkback_Outputs,Multis}/?` | `i` |
|
||||
| `/Console/Aux_Outputs/modes/?` | one `i` per aux (2 = stereo, 1 = mono) |
|
||||
| `/Input_Channels/N/Channel_Input/name/?`, `/Aux_Outputs/N/Buss_Trim/name/?`, `/Group_Outputs/N/Buss_Trim/name/?` | `s` (read-only). The 25 busses appear under both Aux_Outputs and Group_Outputs; a buss reports its name on the side matching its mode and `""` on the other (captured names). |
|
||||
| `/Input_Channels/N/fader/?`, `/Aux_Outputs/N/fader/?`, `/Group_Outputs/N/fader/?` | `f`, dB, -150 = -inf, max +10 (group 24 = -1.6875) |
|
||||
| `/Input_Channels/N/mute/?`, `/Aux_Outputs/N/mute/?`, `/Group_Outputs/N/mute/?` | `f` 0/1 (a float, not an int) |
|
||||
| `/Input_Channels/N/Aux_Send/M/send_level/?` | `f`, dB, -150..10 |
|
||||
| `/Input_Channels/N/Aux_Send/M/send_on/?` | `f` 0/1 |
|
||||
| `/Input_Channels/N/Aux_Send/M/send_pan/?` | `f`, 0..1, 0.5 = centre |
|
||||
| `/Input_Channels/N/Aux_Send/M/send_pre-post/?` | `i` (captured as an int, default 0; settable, clamped 0..1 - range is an assumption) |
|
||||
| `/Input_Channels/N/Group_Send/M/group/?` | `f` 0/1 group assign, M = 1..25. Default: inputs 1-8 and 18-21 on group 24, nothing else. The reply type is the single constant `SD_GROUP_ASSIGN_WIRE_TYPE` in `mock-s31.js` (`'f'`, assumed like mute/send_on; set `'i'` if the real console reports an int). |
|
||||
| `/Snapshots/Current_Snapshot/?` | `i` |
|
||||
| `/Snapshots/names/?` | one `/Snapshots/name ,iiis [index, cue*100, 0, "name"]` per snapshot |
|
||||
|
||||
Sets accept int or float for `mute`/`send_on` (any non-zero = on) and clamp
|
||||
`fader`/`send_level` to -150..10 and `send_pan` to 0..1. Like the real console
|
||||
it stays **silent** on everything else: all lowercase S-Series addresses,
|
||||
`/Console/Resend`, `/Console/Channels/?`, `/Input_Channels/N/Aux_Send/M/?`,
|
||||
`/Input_Channels/N/Aux_Send/?`, `/Macros/Buttons/?`, out-of-range channel/send
|
||||
numbers, and sets of read-only names. (`--bool-int` has no effect in `sd`.)
|
||||
|
||||
## `fake-ipad.js` - stand-in for the DiGiCo iPad app
|
||||
|
||||
Binds the receive port, sends its opening requests, repeats a keepalive every
|
||||
5 s, prints everything it receives and a packet-count summary every 10 s.
|
||||
|
||||
```
|
||||
node tools/fake-ipad.js --console IP --send-port 8000 --receive-port 9000
|
||||
[--dialect sseries|sd] [--name A] [--types] [--quiet]
|
||||
```
|
||||
|
||||
- `sseries`: sends `/console/ping`, then `/console/resend`; keepalive `/console/ping`; `/console/pong` counts as a pong.
|
||||
- `sd`: sends `/Console/Name/?`, `/Console/Input_Channels/?`, `/Console/Aux_Outputs/?`, `/Console/Aux_Outputs/modes/?`, names of inputs 1-8 and aux outputs 1-4, then `/Snapshots/names/?` (20 ms apart); keepalive `/Console/Name/?`; a `/Console/Name` reply counts as a pong.
|
||||
|
||||
Type `ADDRESS [arg ...]` lines on stdin to send your own messages
|
||||
(`true`/`false` = boolean, `5` = int, `5.0` or `-12.5` = float, anything else or
|
||||
`"quoted"` = string; `quit` exits):
|
||||
|
||||
```
|
||||
/channel/3/send/2/level -12.5 # sseries set
|
||||
/Input_Channels/3/Aux_Send/2/send_on 1 # sd set (echo comes back as ,f [1])
|
||||
/Input_Channels/3/Aux_Send/2/send_level/? # sd query
|
||||
/channel/3/name "Lead Vox"
|
||||
```
|
||||
|
||||
## End-to-end loops on one machine
|
||||
|
||||
The module sits between the console and the iPad: the console's feedback goes
|
||||
to the module (port 9000), and the module relays to and from the iPad. Use the
|
||||
ports the module is actually configured with; `9100` and `9200` below are just
|
||||
examples for "the relay's iPad-facing port" and "where the relay forwards
|
||||
feedback to the iPad".
|
||||
|
||||
### S-Series dialect
|
||||
|
||||
```sh
|
||||
# 1. Mock console: listens on 8000, replies to whatever listens on 9000
|
||||
node tools/mock-s31.js --listen 8000 --reply-to 127.0.0.1:9000
|
||||
|
||||
# 2. Start the Companion module (or any relay) listening on 9000, sending to 127.0.0.1:8000
|
||||
|
||||
# 3. Fake iPad pointed at the relay (sends to 9100, receives the relayed feedback on 9200)
|
||||
node tools/fake-ipad.js --console 127.0.0.1 --send-port 9100 --receive-port 9200
|
||||
```
|
||||
|
||||
### SD dialect (what the real S31 speaks)
|
||||
|
||||
```sh
|
||||
# 1. Mock console in SD mode: listens on 8000, replies to 127.0.0.1:9000
|
||||
node tools/mock-s31.js --dialect sd --listen 8000 --reply-to 127.0.0.1:9000 --types
|
||||
|
||||
# 2. Start the Companion module (or any relay) listening on 9000, sending to 127.0.0.1:8000
|
||||
|
||||
# 3. Fake iPad (SD) pointed at the relay
|
||||
node tools/fake-ipad.js --dialect sd --console 127.0.0.1 --send-port 9100 --receive-port 9200 --types
|
||||
```
|
||||
|
||||
No module yet? Skip step 2 and point the iPad straight at the mock to check the
|
||||
tools themselves, e.g.
|
||||
`node tools/fake-ipad.js --dialect sd --console 127.0.0.1 --send-port 8000 --receive-port 9000`.
|
||||
Reference in New Issue
Block a user