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:
+28
-10
@@ -3,8 +3,7 @@
|
||||
* choice builders and the query plans used to load it.
|
||||
*
|
||||
* Entities: input, buss (aux and group are two views of the same 25 busses, named and levelled
|
||||
* on the side matching the buss mode), matrix (matrix outputs), minput (matrix inputs, no
|
||||
* name address), cg (control groups), geq (graphic EQs). All numbers are 1-based.
|
||||
* on the side matching the buss mode), matrix (matrix outputs), minput (matrix inputs), cg (control groups), geq (graphic EQs). All numbers are 1-based.
|
||||
*
|
||||
* applyMessage(address, args) is the single entry point for console traffic (and for the
|
||||
* module's own optimistic writes): it returns the parsed event annotated with
|
||||
@@ -13,7 +12,7 @@
|
||||
* stereo/mono flags alone never are)
|
||||
*/
|
||||
|
||||
import { LIMITS, K, addr, query, parse, fmtCue } from './protocol.js'
|
||||
import { LIMITS, K, addr, query, parse, fmtCue, cleanName } from './protocol.js'
|
||||
|
||||
const EPS = 1e-4
|
||||
|
||||
@@ -25,6 +24,10 @@ function setField(obj, field, value) {
|
||||
return old !== value
|
||||
}
|
||||
|
||||
function clampInt(v, lo, hi) {
|
||||
return Math.min(hi, Math.max(lo, v))
|
||||
}
|
||||
|
||||
function rec(map, key) {
|
||||
let r = map.get(key)
|
||||
if (!r) {
|
||||
@@ -77,6 +80,9 @@ export class Buss {
|
||||
}
|
||||
}
|
||||
|
||||
/** Peak tracking keeps, per meter, the last time a reading was at most k steps below full scale, for k = 0..LOUD_STEPS. */
|
||||
const LOUD_STEPS = 20
|
||||
|
||||
const FALLBACK_NAME = { input: 'Input', matrix: 'Matrix', cg: 'CG', geq: 'Graphic EQ', minput: 'Matrix In' }
|
||||
|
||||
export class State {
|
||||
@@ -93,6 +99,7 @@ export class State {
|
||||
this.inputs = new Map()
|
||||
this.busses = new Map()
|
||||
this.matrix = new Map()
|
||||
this.minputs = new Map() // matrix inputs: { name }
|
||||
this.cgs = new Map()
|
||||
this.geqs = new Map()
|
||||
this.sends = new Map() // "ch:buss" -> { level, on, pan }
|
||||
@@ -100,7 +107,7 @@ export class State {
|
||||
this.matrixSends = new Map() // "in:out" -> { level, on }
|
||||
this.params = new Map() // K.param(...) -> boolean | number (per-channel switches and values, loaded lazily)
|
||||
this.snapshots = { current: undefined, count: undefined, list: new Map() } // index -> { name, cue }
|
||||
this.meters = new Map() // meter id -> { value, at, peakAt }
|
||||
this.meters = new Map() // meter id -> { value, at, loud } (loud[k]: last time a reading was k steps below full scale or louder)
|
||||
}
|
||||
|
||||
// ---------- inbound ----------
|
||||
@@ -112,6 +119,7 @@ export class State {
|
||||
ev.structural = false
|
||||
switch (ev.kind) {
|
||||
case 'consoleName':
|
||||
ev.value = cleanName(ev.value)
|
||||
ev.changed = setField(this.console, 'name', ev.value)
|
||||
break
|
||||
case 'session':
|
||||
@@ -143,6 +151,7 @@ export class State {
|
||||
})
|
||||
break
|
||||
case 'name':
|
||||
ev.value = cleanName(ev.value) // the surface pads and wraps names: trim and collapse whitespace once, here
|
||||
ev.changed = this.#applyName(ev)
|
||||
ev.structural = ev.changed
|
||||
break
|
||||
@@ -189,6 +198,7 @@ export class State {
|
||||
this.snapshots.count = ev.value
|
||||
break
|
||||
case 'snapName': {
|
||||
ev.name = cleanName(ev.name)
|
||||
const old = this.snapshots.list.get(ev.index)
|
||||
this.snapshots.list.set(ev.index, { name: ev.name, cue: ev.cue })
|
||||
ev.changed = !old || old.name !== ev.name || old.cue !== ev.cue
|
||||
@@ -198,10 +208,10 @@ export class State {
|
||||
case 'meters': {
|
||||
const now = Date.now()
|
||||
for (const [id, value] of ev.pairs) {
|
||||
const m = this.meters.get(id) || { value: 0, at: 0, peakAt: 0 }
|
||||
const m = this.meters.get(id) || { value: 127, at: 0, loud: new Float64Array(LOUD_STEPS + 1) }
|
||||
m.value = value
|
||||
m.at = now
|
||||
if (value >= 127) m.peakAt = now
|
||||
for (let k = Math.max(0, value); k <= LOUD_STEPS; k++) m.loud[k] = now
|
||||
this.meters.set(id, m)
|
||||
}
|
||||
ev.changed = ev.pairs.length > 0
|
||||
@@ -254,6 +264,8 @@ export class State {
|
||||
return this.inputs
|
||||
case 'matrix':
|
||||
return this.matrix
|
||||
case 'minput':
|
||||
return this.minputs
|
||||
case 'cg':
|
||||
return this.cgs
|
||||
case 'geq':
|
||||
@@ -286,7 +298,7 @@ export class State {
|
||||
}
|
||||
}
|
||||
|
||||
/** An entity exists when its name answered (matrix inputs have no name: they exist while matrix outputs do). */
|
||||
/** An entity exists when its name answered. */
|
||||
has(ent, n) {
|
||||
if (!(n >= 1 && n <= this.limit(ent))) return false
|
||||
switch (ent) {
|
||||
@@ -294,8 +306,6 @@ export class State {
|
||||
const b = this.busses.get(n)
|
||||
return !!b && (b.auxName !== undefined || b.groupName !== undefined)
|
||||
}
|
||||
case 'minput':
|
||||
return this.limit('matrix') > 0 && (this.counts.matrixIn !== undefined || this.matrix.size > 0)
|
||||
default:
|
||||
return this.#map(ent).get(n)?.name !== undefined
|
||||
}
|
||||
@@ -319,7 +329,6 @@ export class State {
|
||||
/** The name as the console reports it (may be ""), or undefined while unknown. */
|
||||
rawName(ent, n) {
|
||||
if (ent === 'buss') return this.busses.get(n)?.name
|
||||
if (ent === 'minput') return undefined
|
||||
return this.#map(ent).get(n)?.name
|
||||
}
|
||||
|
||||
@@ -391,6 +400,13 @@ export class State {
|
||||
return this.meters.get(id)
|
||||
}
|
||||
|
||||
/** Last time meter `id` read at or above `thresholdDb` dBFS (0 = never; thresholds from -20 to 0 are tracked). */
|
||||
meterPeakAt(id, thresholdDb) {
|
||||
const m = this.meters.get(id)
|
||||
if (!m || !Number.isFinite(thresholdDb)) return 0
|
||||
return m.loud[clampInt(Math.floor(-thresholdDb), 0, LOUD_STEPS)]
|
||||
}
|
||||
|
||||
snapshotName(index) {
|
||||
return this.snapshots.list.get(index)?.name
|
||||
}
|
||||
@@ -458,6 +474,7 @@ export class State {
|
||||
if (want('buss', n, 'group')) q.push(query(addr.name('buss', n, 'group')))
|
||||
}
|
||||
for (let n = 1; n <= this.limit('matrix'); n++) if (want('matrix', n)) q.push(query(addr.name('matrix', n)))
|
||||
for (let n = 1; n <= this.limit('minput'); n++) if (want('minput', n)) q.push(query(addr.name('minput', n)))
|
||||
for (let n = 1; n <= this.limit('cg'); n++) if (want('cg', n)) q.push(query(addr.name('cg', n)))
|
||||
for (let n = 1; n <= this.limit('geq'); n++) if (want('geq', n)) q.push(query(addr.name('geq', n)))
|
||||
return q
|
||||
@@ -493,6 +510,7 @@ export class State {
|
||||
aux: this.namedBusses('aux').length,
|
||||
group: this.namedBusses('group').length,
|
||||
matrix: found('matrix'),
|
||||
matrixIn: found('minput'),
|
||||
cgs: found('cg'),
|
||||
geqs: found('geq'),
|
||||
snapshots: this.snapshots.list.size,
|
||||
|
||||
Reference in New Issue
Block a user