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:
@@ -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)),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { combineRgb } from '@companion-module/base'
|
||||
import { LEVEL, METERS, PARAMS, clamp, fmtDb, fmtPan, meterId } from '../protocol.js'
|
||||
import { GEQ_RANGE, LEVEL, METERS, PARAMS, clamp, fmtDb, fmtPan, fmtValue, meterId } from '../protocol.js'
|
||||
import { horizontalBar, verticalMeter } from '../image.js'
|
||||
import { opt, paramEntities, paramEntityOption } from './actions.js'
|
||||
|
||||
@@ -18,6 +18,9 @@ const n = (v) => Number(v)
|
||||
/** -60 dB .. +10 dB mapped to 0..1 for the level bars. */
|
||||
const levelFraction = (db) => clamp((db + 60) / (LEVEL.max + 60), 0, 1)
|
||||
|
||||
/** GEQ band gain -12 .. +12 dB mapped to 0..1 for the band bar. */
|
||||
const geqFraction = (db) => clamp((db - GEQ_RANGE.min) / (GEQ_RANGE.max - GEQ_RANGE.min), 0, 1)
|
||||
|
||||
const colorOption = { type: 'colorpicker', id: 'color', label: 'Bar colour', default: COLORS.bar, returnType: 'number' }
|
||||
|
||||
const KIND_VISIBLE = (kind) => `$(options:kind) == '${kind}'`
|
||||
@@ -30,6 +33,9 @@ const KIND_VISIBLE = (kind) => `$(options:kind) == '${kind}'`
|
||||
export const watchKey = {
|
||||
entity: (ent, num) => `${ent}:${num}`,
|
||||
send: (ch, buss) => `send:${ch}:${buss}`,
|
||||
msend: (inN, outN) => `msend:${inN}:${outN}`,
|
||||
geqOn: (n) => `geq:${n}:on`,
|
||||
geqGain: (n, band) => `geq:${n}:gain:${band}`,
|
||||
group: (ch, buss) => `group:${ch}:${buss}`,
|
||||
meter: (id) => `meter:${id}`,
|
||||
snapshot: 'snapshot',
|
||||
@@ -107,11 +113,38 @@ export function getFeedbackDefinitions(inst) {
|
||||
}
|
||||
const entityKey = (ent, id) => (o) => watchKey.entity(ent, n(o[id]))
|
||||
const sendKey = (o) => watchKey.send(n(o.channel), n(o.buss))
|
||||
const msendKey = (o) => watchKey.msend(n(o.matrixIn), n(o.matrixOut))
|
||||
const msendOf = (o) => {
|
||||
inst.ensureMatrixSend(n(o.matrixIn), n(o.matrixOut))
|
||||
return s.getMatrixSend(n(o.matrixIn), n(o.matrixOut))
|
||||
}
|
||||
const msendOptions = () => [opt.matrixIn(inst), opt.matrix(inst, 'matrixOut')]
|
||||
const geqOnKey = (o) => watchKey.geqOn(n(o.geq))
|
||||
const geqGainKey = (o) => watchKey.geqGain(n(o.geq), n(o.band))
|
||||
const geqGainOf = (o) => {
|
||||
inst.ensureGeqBand(n(o.geq), n(o.band))
|
||||
return s.getGeq(n(o.geq)).gains?.get(n(o.band))
|
||||
}
|
||||
|
||||
/** Boolean feedback for every switch in PARAMS, on the entity kinds that have it. */
|
||||
const paramFeedbacks = {}
|
||||
for (const [param, p] of Object.entries(PARAMS)) {
|
||||
if (p.type !== 'switch') continue
|
||||
if (p.type === 'value') {
|
||||
for (const ent of paramEntities(param)) {
|
||||
const entOption = paramEntityOption(inst, ent)
|
||||
paramFeedbacks[`${ent}_${param}_value`] = value(
|
||||
`${{ input: 'Input', buss: 'Buss', matrix: 'Matrix output' }[ent]}: ${p.label} (${p.unit} text)`,
|
||||
[entOption],
|
||||
entityKey(ent, entOption.id),
|
||||
({ options }) => {
|
||||
const target = n(options[entOption.id])
|
||||
inst.ensureParam(ent, target, param)
|
||||
return fmtValue(s.getParam(ent, target, param), p.unit) || '--'
|
||||
},
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for (const ent of paramEntities(param)) {
|
||||
const style = param === 'phantom' ? RED : GREEN
|
||||
const entOption = paramEntityOption(inst, ent)
|
||||
@@ -145,6 +178,13 @@ export function getFeedbackDefinitions(inst) {
|
||||
buss_solo: bool('Buss: solo on', AMBER, [opt.buss(inst)], entityKey('buss', 'buss'), ({ options }) => s.getSolo('buss', n(options.buss)) === true),
|
||||
matrix_muted: bool('Matrix output: muted', RED, [opt.matrix(inst)], entityKey('matrix', 'matrix'), ({ options }) => s.getMute('matrix', n(options.matrix)) === true),
|
||||
cg_muted: bool('Control group: muted', RED, [opt.cg(inst)], entityKey('cg', 'cg'), ({ options }) => s.getMute('cg', n(options.cg)) === true),
|
||||
matrix_solo: bool('Matrix output: solo on', AMBER, [opt.matrix(inst)], entityKey('matrix', 'matrix'), ({ options }) => s.getSolo('matrix', n(options.matrix)) === true),
|
||||
cg_solo: bool('Control group: solo on', AMBER, [opt.cg(inst)], entityKey('cg', 'cg'), ({ options }) => s.getSolo('cg', n(options.cg)) === true),
|
||||
matrix_send_on: bool('Matrix send: on', GREEN, msendOptions(), msendKey, ({ options }) => msendOf(options).on === true),
|
||||
geq_on: bool('Graphic EQ: in', GREEN, [opt.geq(inst)], geqOnKey, ({ options }) => {
|
||||
inst.ensureGeqOn(n(options.geq))
|
||||
return s.getGeq(n(options.geq)).on === true
|
||||
}),
|
||||
snapshot_current: bool('Snapshot: is the current one', GREEN, [opt.snapshot(inst)], () => watchKey.snapshot, ({ options }) => s.snapshots.current === Math.trunc(n(options.snapshot))),
|
||||
level_compare: bool(
|
||||
'Fader level: above/below',
|
||||
@@ -213,6 +253,12 @@ export function getFeedbackDefinitions(inst) {
|
||||
buss_fader_value: value('Buss: fader level (dB text)', [opt.buss(inst)], entityKey('buss', 'buss'), ({ options }) => fmtDb(s.getFader('buss', n(options.buss))) || '--'),
|
||||
matrix_fader_value: value('Matrix output: fader level (dB text)', [opt.matrix(inst)], entityKey('matrix', 'matrix'), ({ options }) => fmtDb(s.getFader('matrix', n(options.matrix))) || '--'),
|
||||
cg_fader_value: value('Control group: fader level (dB text)', [opt.cg(inst)], entityKey('cg', 'cg'), ({ options }) => fmtDb(s.getFader('cg', n(options.cg))) || '--'),
|
||||
send_pan_value: value('Aux send: pan (text)', [opt.channel(inst), opt.buss(inst, 'aux')], sendKey, ({ options }) => {
|
||||
inst.ensureSendPan(n(options.channel), n(options.buss))
|
||||
return fmtPan(s.getSend(n(options.channel), n(options.buss)).pan) || '--'
|
||||
}),
|
||||
matrix_send_level_value: value('Matrix send: level (dB text)', msendOptions(), msendKey, ({ options }) => fmtDb(msendOf(options).level) || '--'),
|
||||
geq_band_value: value('Graphic EQ: band gain (dB text)', [opt.geq(inst), opt.band()], geqGainKey, ({ options }) => fmtDb(geqGainOf(options)) || '--'),
|
||||
snapshot_name_value: value('Snapshot: current name', [], () => watchKey.snapshot, () => s.snapshotName(s.snapshots.current) ?? '--'),
|
||||
input_meter_value: value(
|
||||
'Input meter: value',
|
||||
@@ -250,6 +296,22 @@ export function getFeedbackDefinitions(inst) {
|
||||
const v = s.getFader('buss', n(fb.options.buss))
|
||||
return v === undefined ? {} : horizontalBar({ ...image, fraction: levelFraction(v), color: fb.options.color })
|
||||
}),
|
||||
matrix_fader_bar: bar('Matrix output: fader bar', [opt.matrix(inst)], entityKey('matrix', 'matrix'), (fb, image) => {
|
||||
const v = s.getFader('matrix', n(fb.options.matrix))
|
||||
return v === undefined ? {} : horizontalBar({ ...image, fraction: levelFraction(v), color: fb.options.color })
|
||||
}),
|
||||
cg_fader_bar: bar('Control group: fader bar', [opt.cg(inst)], entityKey('cg', 'cg'), (fb, image) => {
|
||||
const v = s.getFader('cg', n(fb.options.cg))
|
||||
return v === undefined ? {} : horizontalBar({ ...image, fraction: levelFraction(v), color: fb.options.color })
|
||||
}),
|
||||
matrix_send_level_bar: bar('Matrix send: level bar', msendOptions(), msendKey, (fb, image) => {
|
||||
const v = msendOf(fb.options).level
|
||||
return v === undefined ? {} : horizontalBar({ ...image, fraction: levelFraction(v), color: fb.options.color })
|
||||
}),
|
||||
geq_band_bar: bar('Graphic EQ: band gain bar', [opt.geq(inst), opt.band()], geqGainKey, (fb, image) => {
|
||||
const v = geqGainOf(fb.options)
|
||||
return v === undefined ? {} : horizontalBar({ ...image, fraction: geqFraction(v), color: fb.options.color })
|
||||
}),
|
||||
input_meter_bar: {
|
||||
type: 'advanced',
|
||||
name: 'Input meter: bar (right edge)',
|
||||
@@ -278,6 +340,10 @@ export function watchKeyForEvent(ev) {
|
||||
return watchKey.send(ev.ch, ev.buss)
|
||||
case 'group':
|
||||
return watchKey.group(ev.ch, ev.buss)
|
||||
case 'msend':
|
||||
return watchKey.msend(ev.input, ev.out)
|
||||
case 'geq':
|
||||
return ev.field === 'on' ? watchKey.geqOn(ev.n) : watchKey.geqGain(ev.n, ev.band)
|
||||
case 'snapCurrent':
|
||||
case 'snapName':
|
||||
return watchKey.snapshot
|
||||
@@ -292,14 +358,14 @@ export function feedbackIdsForEvent(ev) {
|
||||
case 'mute':
|
||||
return { input: ['input_muted'], buss: ['buss_muted'], matrix: ['matrix_muted'], cg: ['cg_muted'] }[ev.ent] || []
|
||||
case 'solo':
|
||||
return { input: ['input_solo'], buss: ['buss_solo'] }[ev.ent] || []
|
||||
return { input: ['input_solo'], buss: ['buss_solo'], matrix: ['matrix_solo'], cg: ['cg_solo'] }[ev.ent] || []
|
||||
case 'fader':
|
||||
return (
|
||||
{
|
||||
input: ['input_fader_value', 'input_fader_bar', 'level_compare'],
|
||||
buss: ['buss_fader_value', 'buss_fader_bar', 'level_compare'],
|
||||
matrix: ['matrix_fader_value', 'level_compare'],
|
||||
cg: ['cg_fader_value', 'level_compare'],
|
||||
matrix: ['matrix_fader_value', 'matrix_fader_bar', 'level_compare'],
|
||||
cg: ['cg_fader_value', 'cg_fader_bar', 'level_compare'],
|
||||
}[ev.ent] || []
|
||||
)
|
||||
case 'pan':
|
||||
@@ -307,11 +373,16 @@ export function feedbackIdsForEvent(ev) {
|
||||
case 'send':
|
||||
if (ev.field === 'level') return ['send_level_value', 'send_level_bar', 'send_level_compare']
|
||||
if (ev.field === 'on') return ['send_on', 'send_on_value']
|
||||
if (ev.field === 'pan') return ['send_pan_value']
|
||||
return []
|
||||
case 'msend':
|
||||
return ev.field === 'level' ? ['matrix_send_level_value', 'matrix_send_level_bar'] : ['matrix_send_on']
|
||||
case 'geq':
|
||||
return ev.field === 'on' ? ['geq_on'] : ['geq_band_value', 'geq_band_bar']
|
||||
case 'group':
|
||||
return ['group_assigned']
|
||||
case 'param':
|
||||
return PARAMS[ev.param].type === 'switch' ? [`${ev.ent}_${ev.param}_state`] : []
|
||||
return [`${ev.ent}_${ev.param}_${PARAMS[ev.param].type === 'switch' ? 'state' : 'value'}`]
|
||||
case 'snapCurrent':
|
||||
case 'snapName':
|
||||
return ['snapshot_current', 'snapshot_name_value']
|
||||
|
||||
+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