Name matrix inputs, fix the meter scale, add output meters

Found on the live S31 (read-only, plus meter subscriptions):

- Matrix inputs are named at /Matrix_Inputs/N/Channel_Input/name (an
  address missing from DiGiCo's 2014 list). Their names now appear in
  every matrix-send dropdown, preset and the new matrix_in_N_name
  variables, falling back to "Matrix In N" when empty. Matrix inputs also
  get trim and phase.
- Console names are space-padded where the surface wraps them; every name
  is now trimmed and its whitespace runs collapsed ("LEAD V RESI").
- Meter values are inverted: 127 is silence and lower is louder, about
  1 dB per step below full scale (silent channels read 127, the room mic
  36-48). The module treated 127 as peak. Level meters now read dBFS
  (approximate), gauges span -60..0 dBFS, signal and peak thresholds are
  in dBFS; gain reduction reads dB of reduction, 127 meaning none.
- Bus and matrix outputs have meters (/Aux_Outputs, /Group_Outputs and
  /Matrix_Outputs/N/Output/meter): new signal, peak, value and gauge
  feedbacks and "Buss meters" / "Matrix meters" presets.

3002 presets, 2.7 MB. npm test: 425 checks pass on Node 25 and on
Companion's Node 22. Deployed and checked on the booth Companion 5.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 10:25:39 -04:00
co-authored by Claude Opus 5.5
parent 9f64d20a24
commit 52777f3d7c
11 changed files with 436 additions and 152 deletions
+54 -10
View File
@@ -1,7 +1,8 @@
import { COLORS } from './feedbacks.js'
import { VAR_PREFIX } from './variables.js'
/** Hard limits agreed for the preset list (Companion keeps all of it in memory and sends it to the web UI). */
export const MAX_PRESETS = 3000
export const MAX_PRESETS = 3200
export const MAX_PRESET_BYTES = 4 * 1024 * 1024
const { white: WHITE, dark: DARK, red: RED, green: GREEN, amber: AMBER } = COLORS
@@ -30,7 +31,7 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
const L = inst.label
const presets = {}
const structure = []
const nameVar = (ent, n) => `$(${L}:${ent}_${n}_name)`
const nameVar = (ent, n) => `$(${L}:${VAR_PREFIX[ent]}_${n}_name)`
const localFeedback = (variableName, feedbackId, options) => ({ variableName, variableType: 'feedback', feedbackId, options })
const simple = (name, text, down, feedbacks = [], extra = {}) => ({
@@ -86,8 +87,11 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
feedbacks,
localVariables: [localFeedback('level', levelId, levelOptions), localFeedback('pos', posId, posOptions)],
})
/** Layered input meter: name on the left, a vertical gauge (green / amber / red zones) at the right edge. */
const layeredMeter = ({ name, title, options }) => ({
/**
* Layered meter: name on the left, a vertical gauge (green / amber / red zones) at the right edge; the position comes
* from `posFeedback`. A signal feedback tints the background dark green and, for level meters, a peak feedback red.
*/
const layeredMeter = ({ name, title, posFeedback, signal, peak }) => ({
type: 'layered',
name,
elements: [
@@ -115,8 +119,8 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
},
],
steps: [{ down: [], up: [] }],
feedbacks: [tint('input_signal', { ...options, threshold: 5 }, SIGNAL_BG), tint('input_peak', { ...options, hold: 2 }, MUTED_RED)],
localVariables: [localFeedback('pos', 'input_meter_pos', options)],
feedbacks: [tint(signal[0], signal[1], SIGNAL_BG), ...(peak ? [tint(peak[0], peak[1], MUTED_RED)] : [])],
localVariables: [localFeedback('pos', posFeedback[0], posFeedback[1])],
})
const group = (id, name, ids) => ({ id, type: 'simple', name, presets: ids })
@@ -146,7 +150,15 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
['comp', 'input_comp_gr', 'Compressor reduction'],
]) {
if (meter !== 'level' && !grMeters) continue
presets[`${prefix}_${n}`] = layeredMeter({ name: `${label}: ${tag}`, title: nameVar('input', n), options: { channel: n, meter } })
const options = { channel: n, meter }
presets[`${prefix}_${n}`] = layeredMeter({
name: `${label}: ${tag}`,
title: nameVar('input', n),
posFeedback: ['input_meter_pos', options],
signal: ['input_signal', { ...options, thresholdDb: -50 }],
// a gain reduction meter has no peak
peak: meter === 'level' ? ['input_peak', { ...options, peakDb: -1, hold: 2 }] : null,
})
meterIds[meter].push(`${prefix}_${n}`)
}
muteIds.push(`input_mute_${n}`)
@@ -290,14 +302,14 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
const onIds = []
const knobIds = []
for (const inN of s.numbers('minput')) {
const tag = `Matrix In ${inN} → ${out} · ${oname}`
const text = `Matrix In ${inN}\\n$(local:level) dB`
const tag = `${s.displayName('minput', inN)} → ${out} · ${oname}`
const text = `${nameVar('minput', inN)}\\n$(local:level) dB`
const options = { matrixIn: inN, matrixOut: out }
const lv = [localFeedback('level', 'matrix_send_level_value', options)]
presets[`msend_on_${inN}_${out}`] = simple(`Matrix send on/off: ${tag}`, text, [act('matrix_send_on', { ...options, state: 'toggle' })], [fb('matrix_send_on', options, GREEN)], { localVariables: lv })
presets[`msend_knob_${inN}_${out}`] = layeredKnob({
name: `Matrix send level (knob): ${tag}`,
title: `Matrix In ${inN}`,
title: nameVar('minput', inN),
press: [act('matrix_send_on', { ...options, state: 'toggle' })],
adjust: ['matrix_send_level_adjust', options],
level: ['matrix_send_level_value', options],
@@ -316,6 +328,38 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
}
}
// ---- Output meters: one layered meter per buss and per matrix output
{
const bussIds = []
for (const n of s.named('buss')) {
presets[`buss_meter_${n}`] = layeredMeter({
name: `Buss meter: ${n} · ${s.displayName('buss', n)} (${s.bussMode(n)})`,
title: nameVar('buss', n),
posFeedback: ['buss_meter_pos', { buss: n }],
signal: ['buss_signal', { buss: n, thresholdDb: -50 }],
peak: ['buss_peak', { buss: n, peakDb: -1, hold: 2 }],
})
bussIds.push(`buss_meter_${n}`)
}
if (bussIds.length) {
structure.push({ id: 'buss_meters', name: 'Buss meters', description: 'Meters subscribe on the console when the button first renders.', definitions: [group('buss_meters_level', 'Level', bussIds)] })
}
const matrixIds = []
for (const n of s.numbers('matrix')) {
presets[`matrix_meter_${n}`] = layeredMeter({
name: `Matrix meter: ${n} · ${s.displayName('matrix', n)}`,
title: nameVar('matrix', n),
posFeedback: ['matrix_meter_pos', { matrix: n }],
signal: ['matrix_signal', { matrix: n, thresholdDb: -50 }],
peak: ['matrix_peak', { matrix: n, peakDb: -1, hold: 2 }],
})
matrixIds.push(`matrix_meter_${n}`)
}
if (matrixIds.length) {
structure.push({ id: 'matrix_meters', name: 'Matrix meters', description: 'Meters subscribe on the console when the button first renders.', definitions: [group('matrix_meters_level', 'Level', matrixIds)] })
}
}
// ---- Graphic EQs: in / out
{
const ids = []