Timed fades, mute-style matrix sends, meters on matrix send knobs
- Every set-level action (input, bus, matrix and control group faders, aux and matrix sends) has a fade time and a curve: fader travel (natural), fader travel with ease in/out, or linear in dB. Fades step at 20 Hz, land exactly on the target, never overshoot, and are cancelled by any new move on the same fader; new "Stop all fades" action, fades_running variable and fade_running feedback. Verified on the S31 with a switched-off matrix send: 20 writes in 1 s, ending exactly on target, then restored. - Matrix sends now look like mutes in the presets: red when the send is off, black when it is on (new matrix_send_off feedback, true only when the send is known to be off). - Matrix send knobs carry a meter column on the right. The S31 has no meter for matrix inputs, so it shows the destination output's meter. Meter gauges use a faint track so an empty meter reads as empty. - Test fixes: the meter-resubscribe check no longer depends on a silent channel changing value, plus back-to-back /Meters/clear coverage. npm test: 504 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:
+52
-20
@@ -54,24 +54,34 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
|
||||
const text = (id, x, y, width, height, value) => ({ type: 'text', id, x, y, width, height, text: value, fontsize: 100, fontsizeAllowShrink: true, color: TEXT, halign: 'center', valign: 'center' })
|
||||
/** Boolean feedback on a layered preset: it colours the background box. */
|
||||
const tint = (feedbackId, options, color) => ({ feedbackId, options, styleOverrides: [{ elementId: 'bg', elementProperty: 'color', override: color }] })
|
||||
/** Green / amber / red zones for a meter gauge on the -60..0 dBFS scale. */
|
||||
const meterStops = () => [
|
||||
{ value: 0, color: GAUGE_GREEN, gradient: false },
|
||||
{ value: 70, color: METER_AMBER, gradient: false },
|
||||
{ value: 90, color: METER_RED, gradient: false },
|
||||
]
|
||||
/** Width (%) of the optional meter column at the right edge of a knob preset. */
|
||||
const KNOB_METER_WIDTH = 13
|
||||
/** Track brightness (%) behind multi-colour meter gauges: faint, so an empty meter reads as empty. */
|
||||
const METER_TRACK_AMOUNT = 20
|
||||
/**
|
||||
* Layered rotary preset with a native Companion gauge along the bottom: black background, the entity name on top, the
|
||||
* dB text below it, and a gauge fed by the `pos` local variable. `level` and `pos` are the text and the numeric value
|
||||
* feedbacks for the same thing.
|
||||
* feedbacks for the same thing. With `meter: [feedbackId, options]` a vertical meter column (fed by the `meter` local
|
||||
* variable, a meter position feedback) runs down the right edge and the rest of the button narrows to make room.
|
||||
*/
|
||||
const layeredKnob = ({ name, title, press, adjust: [adjustActionId, adjustOptions], level: [levelId, levelOptions], pos: [posId, posOptions], feedbacks }) => ({
|
||||
type: 'layered',
|
||||
name,
|
||||
elements: [
|
||||
const layeredKnob = ({ name, title, press, adjust: [adjustActionId, adjustOptions], level: [levelId, levelOptions], pos: [posId, posOptions], meter, feedbacks }) => {
|
||||
const w = meter ? 100 - KNOB_METER_WIDTH : 100
|
||||
const elements = [
|
||||
{ type: 'box', id: 'bg', x: 0, y: 0, width: 100, height: 100, color: BLACK },
|
||||
text('title', 0, 0, 100, 55, title),
|
||||
text('level', 0, 52, 100, 32, '$(local:level) dB'),
|
||||
text('title', 0, 0, w, 55, title),
|
||||
text('level', 0, 52, w, 32, '$(local:level) dB'),
|
||||
{
|
||||
type: 'gauge',
|
||||
id: 'gauge',
|
||||
x: 0,
|
||||
y: 84,
|
||||
width: 100,
|
||||
width: w,
|
||||
height: 16,
|
||||
value: { isExpression: true, value: '$(local:pos)' },
|
||||
min: 0,
|
||||
@@ -82,11 +92,30 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
|
||||
multiColour: false,
|
||||
stops: [{ value: 0, color: GAUGE_LEVEL, gradient: false }],
|
||||
},
|
||||
],
|
||||
steps: knobSteps(press, adjustActionId, adjustOptions),
|
||||
feedbacks,
|
||||
localVariables: [localFeedback('level', levelId, levelOptions), localFeedback('pos', posId, posOptions)],
|
||||
})
|
||||
]
|
||||
const localVariables = [localFeedback('level', levelId, levelOptions), localFeedback('pos', posId, posOptions)]
|
||||
if (meter) {
|
||||
elements.push({
|
||||
type: 'gauge',
|
||||
id: 'meter',
|
||||
x: w + 1,
|
||||
y: 0,
|
||||
width: KNOB_METER_WIDTH - 1,
|
||||
height: 100,
|
||||
value: { isExpression: true, value: '$(local:meter)' },
|
||||
min: 0,
|
||||
max: 100,
|
||||
orientation: 'vertical',
|
||||
trackStyle: 'dimmed',
|
||||
fillEnabled: true,
|
||||
multiColour: true,
|
||||
trackAmount: METER_TRACK_AMOUNT,
|
||||
stops: meterStops(),
|
||||
})
|
||||
localVariables.push(localFeedback('meter', meter[0], meter[1]))
|
||||
}
|
||||
return { type: 'layered', name, elements, steps: knobSteps(press, adjustActionId, adjustOptions), feedbacks, localVariables }
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
@@ -111,11 +140,8 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
|
||||
trackStyle: 'dimmed',
|
||||
fillEnabled: true,
|
||||
multiColour: true,
|
||||
stops: [
|
||||
{ value: 0, color: GAUGE_GREEN, gradient: false },
|
||||
{ value: 70, color: METER_AMBER, gradient: false },
|
||||
{ value: 90, color: METER_RED, gradient: false },
|
||||
],
|
||||
trackAmount: METER_TRACK_AMOUNT,
|
||||
stops: meterStops(),
|
||||
},
|
||||
],
|
||||
steps: [{ down: [], up: [] }],
|
||||
@@ -306,7 +332,11 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
|
||||
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 })
|
||||
// A send that is off looks like a mute (red); an enabled send keeps the black background.
|
||||
presets[`msend_on_${inN}_${out}`] = simple(`Matrix send on/off: ${tag}`, text, [act('matrix_send_on', { ...options, state: 'toggle' })], [fb('matrix_send_off', options, MUTED_RED)], {
|
||||
style: { text, size: 'auto', color: WHITE, bgcolor: BLACK },
|
||||
localVariables: lv,
|
||||
})
|
||||
presets[`msend_knob_${inN}_${out}`] = layeredKnob({
|
||||
name: `Matrix send level (knob): ${tag}`,
|
||||
title: nameVar('minput', inN),
|
||||
@@ -314,7 +344,9 @@ function build(inst, { grMeters = true, sendKnobs: withSendKnobs = true } = {})
|
||||
adjust: ['matrix_send_level_adjust', options],
|
||||
level: ['matrix_send_level_value', options],
|
||||
pos: ['matrix_send_level_pos', options],
|
||||
feedbacks: [tint('matrix_send_on', options, ON_GREEN)],
|
||||
// The console has no meter for matrix inputs (no address answers), so the column shows the destination output.
|
||||
meter: ['matrix_meter_pos', { matrix: out }],
|
||||
feedbacks: [tint('matrix_send_off', options, MUTED_RED)],
|
||||
})
|
||||
onIds.push(`msend_on_${inN}_${out}`)
|
||||
knobIds.push(`msend_knob_${inN}_${out}`)
|
||||
|
||||
Reference in New Issue
Block a user