Files
michaelandClaude Opus 5.5 13092672f7 Fade at the iPad app's rate; relay rate report in debug mode
Measured on the booth relay with debug logging on: while dragging a
fader, the DiGiCo iPad app sends /Input_Channels/N/fader at most every
45 ms (about 22 per second; 16/s on average while moving). Fades now
step every 45 ms to match (was 50 ms). Verified on the S31 with a
switched-off matrix send: 23 steps in 1 s, landing exactly on target.

New debug aid: with "Debug logging" on, the module logs every 5 s, per
iPad, the busiest address patterns with their message rate and the
shortest gap between two messages to the same address.

The fade tests now derive their step counts from the exported
FADE_STEP_MS. npm test: 542 checks pass on Node 25 and on Companion's
Node 22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 15:54:07 -04:00

22 KiB
Raw Permalink Blame History

DiGiCo S-Series (S21 / S31): control and iPad relay

This connection does two things:

  1. Controls the console over OSC. On connect it asks the console what it has (inputs, busses, matrix outputs, control groups, graphic EQs, snapshots) and builds its dropdowns, variables and presets from the live names. Faders, mutes, solos, pans, aux sends, group assigns, matrix sends, control groups, graphic EQs, per-channel switches (phantom, EQ in, filters, inserts ...) and snapshots are covered, with feedbacks, variables, rotary-encoder presets and input and output meters.
  2. Relays the DiGiCo iPad app. An S-Series console talks to exactly one iPad at a time. Companion takes that slot and the iPad apps connect to this computer instead; everything an app sends is forwarded to the console unchanged, and everything the console sends is copied to every app, so several iPads can be used together with Companion.

Console setup (once, at the surface)

Main Menu, Extensions > iPad:

Console field Value
iPad IP address the computer running Companion
Send port (console to this computer) this connection's Local receive port (default 9000)
Receive port (this computer to the console) this connection's Console receive port (default 8000)
iPad connection enabled

The console sends every reply and every change to that one IP address and Send port, never to the port a query came from. That is why the connection binds a fixed local port.

iPad app setup

In the DiGiCo S app, for every iPad:

iPad field Value
Console IP the computer running Companion, not the console
Send port this connection's Relay port (default 8000)
Receive port this connection's iPad receive port (default 9000)

An iPad that stays quiet for Forget an iPad after seconds (0 = never) is dropped from the fan-out list, and at most 16 are kept. $(label:relay_clients) shows how many are live. Changing only the idle timeout, the iPad receive port or Debug logging does not interrupt the relay or the console link; changing the relay port reopens the relay and keeps the list of iPads. If the relay port is already in use the connection shows the warning "iPad relay: port N in use" and tries again every 10 seconds (a console-link port that cannot be bound shows a connection failure and is retried the same way). Other controllers share the console through the relay, and anything they change reaches this module too, so feedbacks follow moves made on the surface or on an iPad.

Settings

Setting Default Meaning
Console IP The console's address
Console receive port 8000 UDP port the module sends to
Local receive port 9000 UDP port the module listens on (the console's Send port)
Enable iPad relay on Turns the relay on
Relay port 8000 Where iPad apps send
iPad receive port 9000 Where relayed console traffic goes (0 = the app's own source port)
Forget an iPad after 3600 s Idle time before an iPad is dropped (0 = never; an iPad that only watches sends nothing). At most 16 iPads are kept; the least recently seen is dropped first
Debug logging off Logs every new address pattern once and all traffic at debug level

What it discovers

On every connect (and on the Reload action, and automatically after the console was silent for 20 seconds and comes back) the module asks for the console name, the matrix, control group and graphic EQ counts, the buss types and modes, every name (inputs, busses, matrix inputs and outputs, control groups, graphic EQs), the session name, the snapshot list, and the fader, mute, solo and pan of everything that answered. The console does not answer the input and buss counts, so an entity exists when its name answered; entities that never answer are hidden from dropdowns, variables and presets. Dropdown entries read 1 · KICK In, 24 · Monitors (group), 3 · Message · cue 4. The console pads names with trailing spaces and wraps long ones with runs of spaces; every name is trimmed and its whitespace collapsed to single spaces when it arrives, so dropdowns, variables and presets show Studio TB, not Studio TB . A matrix input with no name reads Matrix In N. When a name, a buss mode or the snapshot list changes, the definitions are rebuilt a moment later. Sends, group assigns, matrix sends, graphic EQ gains and the per-channel switches are not bulk-loaded: each is read the first time a button or action needs it.

The 25 busses are each an aux or a group, according to /Console/Aux_Outputs/types (1 = aux, 0 = group). Aux busses take sends (level, on, pan); group busses take input assigns. A buss's fader, mute and solo are on the Aux_Outputs or Group_Outputs side that matches its mode. The module has separate Group and Aux master actions, feedbacks and presets for them (their dropdowns list only busses of that type); a buss that changes type on the console moves to the other dropdowns and preset sections when the definitions are rebuilt.

Actions

Level actions take dB (-inf at the bottom of the number field). Every level has a set and a relative adjust version for rotary encoders. A relative step cannot start from -inf: from −∞ the first step up jumps to −60 dB. A matrix send has its floor at -90 dB (the console reports off matrix sends as -90, and the module never writes lower); faders and aux sends go down to -150 dB. Toggles read the current state first when it is not known yet.

Group Actions
Inputs mute, solo (several inputs at once), fader set/adjust, pan set/adjust, set name
Aux sends on/off, level set/adjust, pan set/adjust
Groups assign input on/off
Groups Group: mute, solo (several), fader set (with fade) / adjust; input assign on/off is Group: assign input above
Aux masters Aux master: mute, solo (several), fader set (with fade) / adjust (the master fader of an aux buss; the sends to it are under Aux sends)
Any buss Any buss (aux or group): mute, solo (several), fader set / adjust, with one dropdown of all 25 busses. These are the original buss actions, kept so existing buttons keep working; use the Group and Aux master actions for new buttons
Matrix output mute, fader set/adjust; matrix send on/off, level set/adjust
Control groups mute, solo, fader set/adjust
Graphic EQ in/out, band gain set/adjust (bands 1-32)
Channel strip per input and per buss: phantom, phase, pad, EQ in, compressor in, gate in, high-pass in, low-pass in, delay in, insert A/B in (on/off); trim (-40 to +40 dB), analog gain (0 to 60 dB), filter frequencies (20 to 20000 Hz) (set/adjust; trim also on matrix outputs and matrix inputs, phase also on matrix inputs)
Snapshots fire (pick from the list or type an index), next, previous
Console reload, raw OSC, relay: forget all iPads, stop all fades

The channel-strip values are what the console reports (trim and gain in dB, filters in Hz). In the console's own naming lo_filter is the high-pass filter and hi_filter the low-pass filter; the actions are named for what they do. Delay times and the send pre/post fader switch are not offered: their units and meaning were never verified.

Fades. Every set level action (input, group, aux master, any-buss, matrix output and control group faders, aux send level, matrix send level) has a Fade time (ms, 0 = instant), 0 to 60000 ms. With 0 the level jumps to the target at once, as before; above 0 the module moves the level there smoothly, starting from where it is now (it reads the level first when it is not known yet; if the console does not answer, the target is set at once and a warning is logged). A new level is written 20 times a second at most (every 45 ms, about 22 per second, the same rate the DiGiCo iPad app sends a fader drag at; steps that would change the level by less than 0.05 dB are left out), and the last write is exactly the target. The Fade curve (shown once the time is above 0) is Fader travel (natural), which moves the way a fader's travel does (the same taper as the gauges, so the level change sounds even; the default), Fader travel, ease in/out (the same with a slow start and a slow end), or Linear in dB (the same number of dB every step; a fade from or to −∞ runs between −90 dB, where sound stops, and the target, so no part of it is a silent stretch; a fade to −∞ still ends at exactly −∞). A matrix send never goes below −90 dB. There is one fade per level: starting another fade, a plain set or an adjust on the same level ends the running one where it is, and the new one takes over from that level. The action returns at once and the fade runs on in the background; the Stop all fades action ends every fade where it is. Fades end too when the connection to the console is closed or reconfigured, and while one runs the module keeps writing its level, so a fader moved at the console in the meantime is overwritten. The variable fades_running is the number of fades running and the feedback A fade is running is on while there is one.

Encoders and writes. Every write updates the module's state at once, so a fast encoder turn accumulates. For 0.4 seconds after the last write to a value, a different value arriving for it is treated as a late echo of an earlier step and ignored; then one query confirms what the console has. The snapshot Fire action sends the snapshot's list position (0 = the first); verify it once on your console before using it live.

Macros: the console never answered a macro query, so there is no macro action. If your console supports it, the Send raw OSC action can send /Macros/Buttons/press with an integer argument (0-255).

Feedbacks

Every action that changes something the console reports has a feedback for it: a boolean for every switch, a value (text for a button's local variable) and a numeric value for a gauge for every level. The state a feedback needs (sends, matrix sends, graphic EQ bands, channel-strip values) is loaded the first time one is shown, and each button re-checks only when its own value changes.

  • Boolean (button colour): input muted / solo; aux send on; group assigned; group and aux master muted / solo (and the older any-buss versions); matrix output muted / solo; control group muted / solo; matrix send on; matrix send is off (red like a mute; false while the state is not yet known); graphic EQ in; snapshot is current; fader level above/below a threshold; aux send level above/below; console link alive; relay has an iPad; a level fade is running; every channel-strip switch (phantom, phase, pad, EQ in, compressor in, gate in, high-pass in, low-pass in, delay in, insert A/B in; phase also per matrix input); and the meter feedbacks (signal present, peak hold) for inputs, group and aux master outputs, busses and matrix outputs.
  • Value (put live numbers in button text with a preset's local variable): input fader and pan; aux send level, On/Off and pan; group / aux master / buss / matrix / control group fader; matrix send level; graphic EQ band gain; trim, analog gain and filter frequency per input and buss (trim also per matrix output and matrix input); current snapshot name; meter level text (see Meters). Levels read -inf at or below -90 dB.
  • Group and aux master feedbacks: group_muted, group_solo, group_fader_value, group_fader_pos, group_meter_value, group_meter_pos, group_signal, group_peak, and the same eight with the prefix aux_ for aux masters. They have the same options and behave exactly like the older buss_* ones (which stay, named "Any buss (aux or group): ...") with the dropdown limited to busses of that type. There are no image-bar versions of the new ones.
  • Numeric, for gauges (see Gauges below): the *_pos position feedbacks, input_pan_num / send_pan_num and geq_band_db.
  • Advanced image bars (legacy image, deprecated by Companion 5.1, kept so existing buttons keep working): input fader, aux send level, any-buss / matrix output / control group fader, matrix send level (-60 to +10 dB) and graphic EQ band gain (-12 to +12 dB) draw a horizontal bar along the bottom of the button; the input meter draws a vertical bar at the right edge. Use a gauge on new buttons.

Not covered by a feedback: free-form Send raw OSC, and Set name (the name is the variable input_N_name). There is no matrix-output solo action, only its feedback.

Gauges

Companion 5.x has a native Gauge element, and every knob and meter preset uses one. Each position feedback is a number you feed to a gauge through a button local variable:

Feedback Range Meaning
input_fader_pos, group_fader_pos, aux_fader_pos, buss_fader_pos, matrix_fader_pos, cg_fader_pos, send_level_pos, matrix_send_level_pos 0-100 fader / send position on a fader taper; unknown reads 0
input_pan_num, send_pan_num 0-1 the console's pan, 0.5 = centre (unknown reads 0.5)
geq_band_db -12 to +12 graphic EQ band gain in dB (unknown reads 0)
input_meter_pos, group_meter_pos, aux_meter_pos, buss_meter_pos, matrix_meter_pos 0-100 meter position: levels over -60..0 dBFS (-inf = 0), gain reduction over 0..20 dB; the input meter has the Meter option level / gate / compressor reduction

The fader taper maps +10 dB = 100, +5 = 87.5, 0 = 75, -5 = 65, -10 = 55, -20 = 40, -30 = 28, -40 = 18, -50 = 11 and -60 = 6 (straight lines in between), falls to about 1 just above -90 dB, and reads 0 at or below -90 dB (off).

To add a gauge to your own button in Companion 5.1:

  1. Local Variables tab: add a feedback variable, name it pos, and pick the position feedback (for example input_fader_pos with its input).
  2. Style tab: +, then Gauge. Set Value to the expression $(local:pos), Min 0, Max 100 (fader and meter positions).
  3. For pan use input_pan_num / send_pan_num with Min 0, Max 1, Symmetric on and Origin 0.5. For a graphic EQ band use geq_band_db with Min -12, Max 12, symmetric, Origin 0.

Variables

input_N_name|fader|mute|pan, buss_N_name|mode|stereo|fader|mute, matrix_N_name|fader|mute, matrix_in_N_name, cg_N_name|fader|mute (only for entities that exist), snapshot_index|name|cue|count, console_name, session_name, connected, relay_clients, relay_client_ips, fades_running (number of level fades running) and last_change. Use them as $(label:input_1_name) with the connection's label. There are no per-send variables: use the value feedbacks.

Presets

Presets are named with the live entity names and rebuilt when names load. The knob and meter presets are layered (black background, name, dB text, gauge) and a muted / on state tints their background; the plain toggles are simple buttons.

Preset group What the buttons do
Inputs: Mute One button per input; red when muted
Inputs: Fader (knob) Rotary: turn = fader -/+1 dB, press = mute; shows name and dB with a gauge along the bottom
Inputs: Solo One button per input
Groups Per named group buss: a Mute button (red when muted) and a Fader (knob) (turn = fader, press = mute, with a gauge)
Aux masters The same for each named aux buss's master fader (the sends to an aux are in Sends to aux name)
Sends to aux name One section per named aux buss: per input an On/off button (green when on, shows the level) and a Level (knob) (turn = send level, press = on/off, with a gauge)
Group group name One section per named group buss: per input an Assign toggle, green when assigned
Matrix, Control groups Mute and fader knob (with a gauge) per output / group
Matrix sends to output name One section per matrix output: per matrix input an On/off button (black when the send is on, red when it is off, like a mute; shows the matrix input's name and the level) and a Level (knob) (turn = send level, press = on/off, turns red when the send is off, with a level gauge along the bottom and a meter column on the right; the S31 has no meter for matrix inputs, so that column shows the destination matrix output's meter)
Graphic EQs One In/out toggle per graphic EQ, green when in
Snapshots Fire per snapshot (green when current), next, previous, current (display)
Input meters Level, gate gain reduction and compressor gain reduction buttons per input, each with a vertical gauge at the right edge (green below 70, amber 70-90, red above 90)
Group meters, Aux meters, Matrix meters One level meter button per named group buss, per named aux buss and per matrix output, same vertical gauge
Console Reload, connection status, relay status

Busses and control groups with no name are unused slots and get no presets. If the preset list would exceed 3200 entries or 4 MB, the per-input gate / compressor reduction meter presets are left out first, then the per-input aux send level knobs, and a warning is logged.

Meters

Meters are subscribed on the console the first time a meter feedback renders (ids start at 5000, so they never collide with an iPad's: input meters 5000-5260, buss meters 5400 + N, matrix output meters 5450 + N). The console then streams integer values 0-127 several times a second, only for the ones that changed, and the module re-checks the meter feedbacks at most 10 times a second, and only the buttons showing the meter that moved.

The scale is inverted and approximate. A level meter value is about the dB below full scale: 0 = full scale, 42 = about -42 dBFS, 127 = silence (-inf). A gain reduction meter value is about the dB of reduction (0-12 is typical, 127 = inactive). It has not been calibrated against a reference tone, so treat the numbers as roughly 1 dB per step, not as a measurement.

Feedback What it shows
input_meter_value, group_meter_value, aux_meter_value, buss_meter_value, matrix_meter_value text: -42 or -inf (dBFS, approx.); gain reduction meters: 3 (dB)
input_meter_pos, group_meter_pos, aux_meter_pos, buss_meter_pos, matrix_meter_pos gauge position 0-100: levels over -60..0 dBFS, gain reduction over 0..20 dB
input_signal, group_signal, aux_signal, buss_signal, matrix_signal signal present: level above a dBFS threshold (option, default -50; gain reduction meters: any reduction)
input_peak, group_peak, aux_peak, buss_peak, matrix_peak level at or above a dBFS value (option, default -1) held for the Hold time; not for gain reduction meters

Input meters are level (post-fader), gate gain reduction and compressor gain reduction. Output meters exist for aux busses, group busses (the address follows the buss's current aux / group side) and matrix outputs; they are level meters. Control groups and matrix inputs have no meter. The console keeps one subscription table: an iPad connecting clears it (/Meters/clear), and the module re-subscribes half a second after it sees that pass through the relay (and after a reconnect). Two iPads watching different banks both receive the mixed stream. The module never sends /Meters/clear itself.

Troubleshooting

  • No reply from the console. The console only answers to the iPad IP address and Send port set on the console. Check them, and that nothing else on this computer owns the local receive port.
  • Dropdowns show generic names. The load has not finished or the console did not answer the names; press Reload. The log shows load complete with the counts when it is done.
  • Something changed on the console is not shown. Press Reload after a session change; sends and per-channel values are only read when first needed.
  • Debug logging lists every new address pattern once (Connections log), so touching a control on the surface shows its address; $(label:last_change) shows the last change reported.

Protocol (condensed)

A query is the address plus /? with no argument; the reply is the same address without /?. All 0/1 switches are reported and written as floats (0.0 / 1.0), verified on an S31 on 2026-10-15: integer and OSC T/F writes to a switch are silently ignored (no echo, no change), unlike the 2014 DiGiCo list. Levels, sends and pans are floats too. Numbers are 1-based, snapshot indexes 0-based.

Address Reply Notes
/Console/Name s also the keepalive (every 5 s)
/Console/{Matrix_Outputs,Matrix_Inputs,Control_Groups,Graphic_EQ} i counts that answer
/Console/{Input_Channels,Aux_Outputs,Group_Outputs} i pushed once after first contact, never answered
/Console/Aux_Outputs/types 25 x i 1 = aux, 0 = group
/Console/{Aux_Outputs,Group_Outputs,Input_Channels}/modes i per buss / input 2 = stereo, 1 = mono
/Console/Session/Filename s
/Input_Channels/N/Channel_Input/name, /Matrix_Inputs/N/Channel_Input/name, /Aux_Outputs/N/Buss_Trim/name, /Group_Outputs/N/Buss_Trim/name, /Matrix_Outputs/N/Buss_Trim/name, /Control_Groups/N/name, /Graphic_EQ/N/name s names arrive padded; the module cleans them
/{Input_Channels,Aux_Outputs,Group_Outputs,Matrix_Outputs,Control_Groups}/N/{fader,mute,solo} f fader in dB, -150 = -inf
/Input_Channels/N/Panner/pan f 0..1, 0.5 = centre
/Input_Channels/N/Aux_Send/M/{send_level,send_on,send_pan} f
/Input_Channels/N/Group_Send/M/group f
/Matrix_Inputs/N/Matrix_Send/M/{send_level,send_on} f a matrix send floors at -90 dB
/Matrix_Inputs/N/Channel_Input/{trim,phase} f
/Graphic_EQ/N/{geq_in,geq_gain_1..32} f
/Input_Channels/N/{Channel_Input,Filters,Channel_Delay,EQ,Dynamics,Insert}/... and the buss equivalents f the channel-strip switches and values above
/Snapshots/Current_Snapshot, /Snapshots/count i
/Snapshots/names one /Snapshots/name iiis per snapshot: index, cue x 100, ?, name
/Snapshots/Fire_Snapshot_number, /Snapshots/Fire_Next_Snapshot, /Snapshots/Fire_Prev_Snapshot none write-only int
/Input_Channels/N/Channel_Input/post_meter, /Input_Channels/N/Dynamics/GR_meter, /{Aux_Outputs,Group_Outputs,Matrix_Outputs}/N/Output/meter none send ,ii [id, kind] (kind 1 level and gate and outputs, 4 compressor) to subscribe
/Meters/values ii... pushed pairs [id, value], value 0..127: inverted scale, 127 = silence / inactive
/Meters/clear none clears every subscription on the console; sent by iPad apps, never by this module