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:
+67
-15
@@ -9,8 +9,11 @@ const { white: WHITE, dark: DARK, red: RED, green: GREEN, amber: AMBER, bar: BAR
|
||||
const fb = (feedbackId, options, bg, color = WHITE) => ({ feedbackId, options, style: { bgcolor: bg, color } })
|
||||
const act = (actionId, options) => ({ actionId, options })
|
||||
|
||||
/** Build every preset from the live state. `withSendKnobs` is the first thing dropped when the list is too big. */
|
||||
function build(inst, withSendKnobs) {
|
||||
/**
|
||||
* Build every preset from the live state. When the list is too big `grMeters` (the gate / compressor reduction
|
||||
* meter presets) is dropped first, then `sendKnobs` (the per-input aux send level knobs).
|
||||
*/
|
||||
function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {}) {
|
||||
const s = inst.state
|
||||
const L = inst.label
|
||||
const presets = {}
|
||||
@@ -69,6 +72,7 @@ function build(inst, withSendKnobs) {
|
||||
['gate', 'input_gate_gr', 'Gate reduction'],
|
||||
['comp', 'input_comp_gr', 'Compressor reduction'],
|
||||
]) {
|
||||
if (meter !== 'level' && !grMeters) continue
|
||||
presets[`${prefix}_${n}`] = simple(
|
||||
`${label}: ${tag}`,
|
||||
nameVar('input', n),
|
||||
@@ -97,8 +101,7 @@ function build(inst, withSendKnobs) {
|
||||
description: 'Meters subscribe on the console when the button first renders.',
|
||||
definitions: [
|
||||
group('meters_input', 'Level', meterIds.level),
|
||||
group('meters_gate', 'Gate gain reduction', meterIds.gate),
|
||||
group('meters_comp', 'Compressor gain reduction', meterIds.comp),
|
||||
...(grMeters ? [group('meters_gate', 'Gate gain reduction', meterIds.gate), group('meters_comp', 'Compressor gain reduction', meterIds.comp)] : []),
|
||||
],
|
||||
})
|
||||
}
|
||||
@@ -217,6 +220,48 @@ function build(inst, withSendKnobs) {
|
||||
if (muteIds.length) structure.push({ id: 'cgs', name: 'Control groups', definitions: [group('cgs_mute', 'Mute', muteIds), group('cgs_knob', 'Fader (knob)', knobIds)] })
|
||||
}
|
||||
|
||||
// ---- Matrix sends: one section per matrix output, per matrix input an on/off button and a level knob
|
||||
for (const out of s.numbers('matrix')) {
|
||||
const oname = s.displayName('matrix', out)
|
||||
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 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}`] = knob(
|
||||
`Matrix send level (knob): ${tag}`,
|
||||
text,
|
||||
[act('matrix_send_on', { ...options, state: 'toggle' })],
|
||||
'matrix_send_level_adjust',
|
||||
options,
|
||||
lv,
|
||||
[fb('matrix_send_on', options, GREEN), { feedbackId: 'matrix_send_level_bar', options: { ...options, color: BAR } }],
|
||||
)
|
||||
onIds.push(`msend_on_${inN}_${out}`)
|
||||
knobIds.push(`msend_knob_${inN}_${out}`)
|
||||
}
|
||||
if (onIds.length) {
|
||||
structure.push({
|
||||
id: `msends_${out}`,
|
||||
name: `Matrix sends to ${oname}`,
|
||||
definitions: [group(`msends_${out}_on`, 'On/off', onIds), group(`msends_${out}_knob`, 'Level (knob)', knobIds)],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Graphic EQs: in / out
|
||||
{
|
||||
const ids = []
|
||||
for (const n of s.numbers('geq')) {
|
||||
presets[`geq_in_${n}`] = simple(`In/out: ${n} · ${s.displayName('geq', n)}`, `${s.displayName('geq', n)}\\nIN`, [act('geq_on', { geq: n, state: 'toggle' })], [fb('geq_on', { geq: n }, GREEN)])
|
||||
ids.push(`geq_in_${n}`)
|
||||
}
|
||||
if (ids.length) structure.push({ id: 'geqs', name: 'Graphic EQs', definitions: [group('geqs_in', 'In/out', ids)] })
|
||||
}
|
||||
|
||||
// ---- Snapshots
|
||||
{
|
||||
const fireIds = []
|
||||
@@ -247,19 +292,26 @@ function build(inst, withSendKnobs) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Presets for the current state: { structure, presets, stats }. If the list would exceed the agreed
|
||||
* limits the per-input send level knobs (the largest group) are left out and stats.droppedSendKnobs is set.
|
||||
* Presets for the current state: { structure, presets, stats }. If the list would exceed the agreed limits the
|
||||
* gate / compressor reduction meter presets are left out first, then the per-input aux send level knobs;
|
||||
* stats.dropped says which.
|
||||
*/
|
||||
export function getPresetDefinitions(inst) {
|
||||
let built = build(inst, true)
|
||||
let count = Object.keys(built.presets).length
|
||||
let bytes = JSON.stringify(built.presets).length
|
||||
let droppedSendKnobs = false
|
||||
if (count > MAX_PRESETS || bytes > MAX_PRESET_BYTES) {
|
||||
built = build(inst, false)
|
||||
droppedSendKnobs = true
|
||||
export function getPresetDefinitions(inst, limits = { count: MAX_PRESETS, bytes: MAX_PRESET_BYTES }) {
|
||||
const tiers = [
|
||||
{ options: { grMeters: true, sendKnobs: true }, dropped: [] },
|
||||
{ options: { grMeters: false, sendKnobs: true }, dropped: ['the gate / compressor reduction meter presets'] },
|
||||
{ options: { grMeters: false, sendKnobs: false }, dropped: ['the gate / compressor reduction meter presets', 'the per-input send level knobs'] },
|
||||
]
|
||||
let built
|
||||
let count = 0
|
||||
let bytes = 0
|
||||
let dropped = []
|
||||
for (const tier of tiers) {
|
||||
built = build(inst, tier.options)
|
||||
count = Object.keys(built.presets).length
|
||||
bytes = JSON.stringify(built.presets).length
|
||||
dropped = tier.dropped
|
||||
if (count <= limits.count && bytes <= limits.bytes) break
|
||||
}
|
||||
return { ...built, stats: { count, bytes, droppedSendKnobs } }
|
||||
return { ...built, stats: { count, bytes, dropped } }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user