ATEM: RESI AUDIO SILENT when input 16 has no audio for 30 s; log what the switcher has wrong
2026-10-25 the Resi AJA passed no Dante audio and the panel stayed green (DANTE only sees the card on the network). atem_watch.js now asks the switcher for its Fairlight meters and reports a metered input stuck at the floor (-100 dB); the ATEM key turns red in auto-fix's hours or while a deck records. Differences (audio, camera, silence) are logged when they change, so a fault says what it was. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+33
-5
@@ -5,11 +5,14 @@
|
||||
//
|
||||
// reference.json (made by status_panel.py from the ATEM Software Control restore .xml):
|
||||
// {"audio": {"16/-65280": {"mixOption": 2, "faderGain": 0}, ...},
|
||||
// "camera": {"2/1/2": [4500, 0], ...}} // camera/category/parameter -> values
|
||||
// "camera": {"2/1/2": [4500, 0], ...}, // camera/category/parameter -> values
|
||||
// "levels": {"16": "Resi - RX1"}, "silence_seconds": 30} // inputs whose audio must never go silent
|
||||
// Prints a line "ATEM {json}" when the differences change (and every 10 s):
|
||||
// {"connected": true, "audio": ["16 Resi..: Off, should be On", ...], "camera": [...]}
|
||||
// {"connected": true, "audio": ["16 Resi..: Off, should be On", ...], "camera": [...],
|
||||
// "silence": ["input 16 Resi - RX1: no audio for 30 s"]}
|
||||
// Reads lines on stdin: "fix audio" / "fix camera" sends back only the settings that differ.
|
||||
// Nothing else is ever sent to the switcher.
|
||||
// Besides those fixes, the only thing ever sent is the request for the audio meters (as ATEM Software
|
||||
// Control's mixer does); nothing on the switcher changes.
|
||||
const [ip, lib, refPath] = process.argv.slice(2)
|
||||
const fs = require('fs')
|
||||
const readline = require('readline')
|
||||
@@ -18,6 +21,13 @@ const { Atem, Commands } = require(lib)
|
||||
const ref = JSON.parse(fs.readFileSync(refPath, 'utf8'))
|
||||
const MIX = { 1: 'Off', 2: 'On', 4: 'AFV' }
|
||||
const ccu = {} // "camera/category/parameter" -> {type, values}
|
||||
// Audio meters: the switcher reads -100.00 dB (-10000) on an input with nothing on it at all; a quiet room, a
|
||||
// pause or an open mic is far above that. So an input stuck at the floor means its audio is not arriving
|
||||
// (2026-10-25: the Resi AJA stopped passing Dante audio and nothing said so).
|
||||
const LEVELS = ref.levels || {}
|
||||
const FLOOR = -9000
|
||||
const heard = {} // input -> when it last had any audio
|
||||
let metersAt = 0 // when the switcher last sent meters (none: no verdict)
|
||||
const atem = new Atem({ disableMultithreaded: true })
|
||||
let connected = false
|
||||
|
||||
@@ -32,7 +42,17 @@ atem.on('receivedCommands', (cmds) => {
|
||||
}
|
||||
}
|
||||
})
|
||||
atem.on('connected', () => { connected = true })
|
||||
atem.on('levelChanged', (l) => {
|
||||
if (l.system !== 'fairlight' || l.type !== 'source') return
|
||||
metersAt = Date.now()
|
||||
const k = String(l.index)
|
||||
if (k in LEVELS && Math.max(l.levels.inputLeftLevel, l.levels.inputRightLevel) > FLOOR) heard[k] = Date.now()
|
||||
})
|
||||
atem.on('connected', () => {
|
||||
connected = true
|
||||
for (const k of Object.keys(LEVELS)) heard[k] = Date.now() // a fresh start gets the full grace time
|
||||
if (Object.keys(LEVELS).length) atem.startFairlightMixerSendLevels().catch(() => {})
|
||||
})
|
||||
atem.on('disconnected', () => { connected = false })
|
||||
atem.on('error', () => {})
|
||||
|
||||
@@ -68,10 +88,18 @@ function cameraDiffs() {
|
||||
return out
|
||||
}
|
||||
|
||||
function silence() {
|
||||
const now = Date.now()
|
||||
if (!connected || now - metersAt > 5000) return []
|
||||
const secs = ref.silence_seconds || 30
|
||||
return Object.entries(LEVELS).filter(([k]) => now - (heard[k] || now) >= secs * 1000)
|
||||
.map(([k, name]) => `input ${k} ${name}: no audio for ${secs} s or more`)
|
||||
}
|
||||
|
||||
let last = '', lastAt = 0
|
||||
function report() {
|
||||
const state = { connected, audio: connected ? audioDiffs().map((d) => d.text) : [], camera: connected ? cameraDiffs().map((d) => d.text) : [],
|
||||
camerasSeen: Object.keys(ccu).length }
|
||||
camerasSeen: Object.keys(ccu).length, silence: silence() }
|
||||
const s = JSON.stringify(state)
|
||||
if (s !== last || Date.now() - lastAt > 10000) {
|
||||
process.stdout.write('ATEM ' + s + '\n')
|
||||
|
||||
Reference in New Issue
Block a user