Add a feedback for every controllable state, matrix send presets

A matrix-send knob looked dead on the booth Companion although the console
followed it (verified through Companion's rotate API: -90 -> -60 -> -59):
the module had matrix send actions but no matrix send feedbacks.

- Every action with a readable state now has a matching feedback: matrix
  send on/level text/level bar, matrix and control group solo and fader
  bars, graphic EQ in and band gain text/bar, aux send pan text, and value
  text for trim, gain and filter frequencies. A test checks the parity.
- New action: matrix output solo.
- Matrix sends use the console's -90 dB floor: adjusting down from -inf no
  longer writes -150 there.
- Presets: "Matrix sends to <output>" (on/off and knob per matrix input)
  and graphic EQ in/out. 2970 presets, 1.8 MB.

npm test: 368 checks pass on Node 25 and on Companion's Node 22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 15:44:43 -04:00
co-authored by Claude Opus 5.5
parent 57645be1e9
commit 90dee14444
9 changed files with 351 additions and 57 deletions
+21 -7
View File
@@ -29,13 +29,14 @@ export const opt = {
cg: (inst, id = 'cg', label = 'Control group') => opt.dropdown(id, label, inst.state.choices.cgs()),
geq: (inst, id = 'geq', label = 'Graphic EQ') => opt.dropdown(id, label, inst.state.choices.geqs()),
snapshot: (inst, id = 'snapshot', label = 'Snapshot') => opt.dropdown(id, label, inst.state.choices.snapshots(), { allowCustom: true, default: firstId(inst.state.choices.snapshots(), 0) }),
band: (id = 'band', label = 'Band') => ({ type: 'number', id, label, default: 1, min: 1, max: LIMITS.geqBands, step: 1 }),
state: (label = 'State') => ({ type: 'dropdown', id: 'state', label, choices: STATE_CHOICES, default: 'toggle' }),
level: (id = 'level', label = 'Level (dB)') => ({
level: (id = 'level', label = 'Level (dB)', min = LEVEL.min) => ({
type: 'number',
id,
label,
default: 0,
min: LEVEL.min,
min,
max: LEVEL.max,
step: 0.5,
showMinAsNegativeInfinity: true,
@@ -46,6 +47,9 @@ export const opt = {
}
/** Normalise a multidropdown / dropdown value to an array of numbers. */
/** Said in every level adjust action: a relative step cannot start from -inf, so the first step up jumps to -60 dB. */
const ADJUST_NOTE = 'From −∞ the first step up jumps to −60 dB.'
export const numbers = (v) => (Array.isArray(v) ? v : v === undefined || v === '' ? [] : [v]).map(Number).filter(Number.isFinite)
/** Which entity kinds get actions / feedbacks for a parameter: 'input', 'buss' and/or 'matrix'. */
@@ -130,7 +134,7 @@ export function getActionDefinitions(inst) {
},
input_fader_adjust: {
name: 'Input: adjust fader (relative)',
description: 'For rotary encoders. From -inf the first step up lands at -60 dB.',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.channel(inst), opt.delta()],
callback: async ({ options }) => inst.adjustFader('input', n(options.channel), n(options.delta)),
},
@@ -162,7 +166,7 @@ export function getActionDefinitions(inst) {
},
send_level_adjust: {
name: 'Aux send: adjust level (relative)',
description: 'For rotary encoders. From -inf the first step up lands at -60 dB.',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.channel(inst), opt.buss(inst, 'aux'), opt.delta()],
callback: async ({ options }) => inst.adjustSendLevel(n(options.channel), n(options.buss), n(options.delta)),
},
@@ -199,6 +203,7 @@ export function getActionDefinitions(inst) {
},
buss_fader_adjust: {
name: 'Buss: adjust fader (relative)',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.buss(inst), opt.delta()],
callback: async ({ options }) => inst.adjustFader('buss', n(options.buss), n(options.delta)),
},
@@ -208,6 +213,11 @@ export function getActionDefinitions(inst) {
options: [opt.matrix(inst), opt.state()],
callback: async ({ options }) => inst.setSwitch('matrix', 'mute', n(options.matrix), options.state),
},
matrix_solo: {
name: 'Matrix output: solo',
options: [opt.matrix(inst), opt.state()],
callback: async ({ options }) => inst.setSwitch('matrix', 'solo', n(options.matrix), options.state),
},
matrix_fader_set: {
name: 'Matrix output: set fader',
options: [opt.matrix(inst), opt.level()],
@@ -215,6 +225,7 @@ export function getActionDefinitions(inst) {
},
matrix_fader_adjust: {
name: 'Matrix output: adjust fader (relative)',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.matrix(inst), opt.delta()],
callback: async ({ options }) => inst.adjustFader('matrix', n(options.matrix), n(options.delta)),
},
@@ -225,11 +236,13 @@ export function getActionDefinitions(inst) {
},
matrix_send_level_set: {
name: 'Matrix send: set level',
options: [opt.matrixIn(inst), opt.matrix(inst, 'matrixOut'), opt.level()],
description: 'A matrix send goes down to −90 dB (−∞).',
options: [opt.matrixIn(inst), opt.matrix(inst, 'matrixOut'), opt.level('level', 'Level (dB)', LEVEL.matrixSendMin)],
callback: async ({ options }) => inst.setMatrixSendLevel(n(options.matrixIn), n(options.matrixOut), n(options.level)),
},
matrix_send_level_adjust: {
name: 'Matrix send: adjust level (relative)',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.matrixIn(inst), opt.matrix(inst, 'matrixOut'), opt.delta()],
callback: async ({ options }) => inst.adjustMatrixSendLevel(n(options.matrixIn), n(options.matrixOut), n(options.delta)),
},
@@ -251,6 +264,7 @@ export function getActionDefinitions(inst) {
},
cg_fader_adjust: {
name: 'Control group: adjust fader (relative)',
description: `For rotary encoders. ${ADJUST_NOTE}`,
options: [opt.cg(inst), opt.delta()],
callback: async ({ options }) => inst.adjustFader('cg', n(options.cg), n(options.delta)),
},
@@ -265,7 +279,7 @@ export function getActionDefinitions(inst) {
description: 'Band 1-32, gain in dB.',
options: [
opt.geq(inst),
{ type: 'number', id: 'band', label: 'Band', default: 1, min: 1, max: LIMITS.geqBands, step: 1 },
opt.band(),
{ type: 'number', id: 'gain', label: 'Gain (dB)', default: 0, min: -12, max: 12, step: 0.5 },
],
callback: async ({ options }) => inst.setGeqBand(n(options.geq), n(options.band), n(options.gain)),
@@ -274,7 +288,7 @@ export function getActionDefinitions(inst) {
name: 'Graphic EQ: adjust band gain (relative)',
options: [
opt.geq(inst),
{ type: 'number', id: 'band', label: 'Band', default: 1, min: 1, max: LIMITS.geqBands, step: 1 },
opt.band(),
{ type: 'number', id: 'delta', label: 'Change (dB, for encoders)', default: 1, min: -12, max: 12, step: 0.5 },
],
callback: async ({ options }) => inst.adjustGeqBand(n(options.geq), n(options.band), n(options.delta)),