AB's meter appeared to halt for a day and a half. Nothing had halted: the capture child had run continuously since the machine was restarted, never stalled, never exited, and was delivering twenty frames a second. Every one of them was digital silence, because Dante Via stopped passing audio after the reboot — the Timers module's LTC listener, on channel 16 of the same device, had been reporting no signal for exactly as long. The module's part in that was reporting "ok" through all of it and drawing a steady -83, which is the -200 floor plus the +117 calibration offset. A plausible number on a wall is believed, and a green dot beside it says the number is worth believing. That is worse than showing nothing. So silence is now a state the module names. Below -150 dBFS unweighted the input is not quiet, it is absent: a microphone in a silent room still reads about -83, and even a muted converter dithers. Five seconds of that on every channel and health() goes to error naming the device and how long it has been dead, the tiles stop drawing a level and say "No audio" with how long, and the status dot goes red. Verified both directions, which is the half that matters: BlackHole with nothing routed into it is caught in five seconds, and a real microphone in a quiet room at -49 dBFS is never flagged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
155 lines
4.8 KiB
CSS
155 lines
4.8 KiB
CSS
/* SPL Meter — every colour comes from the shell's variables so themes and
|
|
per-tile tints reach in. Nothing here is hardcoded except the canvas
|
|
heat ramp, which client.js owns because it has to interpolate. */
|
|
|
|
[data-module="spl-meter"] .spl {
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* ── readout ──────────────────────────────────────────────────────── */
|
|
|
|
[data-module="spl-meter"] .spl-readout {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.25em;
|
|
min-height: 0;
|
|
padding: 0.25rem;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-value {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.2em;
|
|
line-height: 1;
|
|
color: var(--text);
|
|
}
|
|
|
|
/* A first-paint size only. client.js measures the text and sets the real
|
|
one inline, because CSS has to assume a digit count and gets it wrong the
|
|
moment the reading needs a sixth character: the coefficient that fills a
|
|
tile for "-45.9" pushes "-100.5" straight out through the side. */
|
|
[data-module="spl-meter"] .spl-num {
|
|
font-size: clamp(1.25rem, min(26cqw, 60cqh), 100rem);
|
|
font-variant-numeric: tabular-nums;
|
|
font-weight: 600;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-unit {
|
|
font-size: clamp(0.65rem, min(7cqw, 15cqh), 26rem);
|
|
color: var(--muted);
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-sub {
|
|
font-size: 0.7rem;
|
|
color: var(--muted);
|
|
text-align: center;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-readout[data-level="warn"] .spl-num { color: var(--warn); }
|
|
[data-module="spl-meter"] .spl-readout[data-level="alarm"] .spl-num { color: var(--danger); }
|
|
[data-module="spl-meter"] .spl-readout[data-level="idle"] .spl-num { color: var(--muted); }
|
|
|
|
/* A dead input reads danger, not muted: silence that is a fault should not
|
|
look like silence that is a quiet room. */
|
|
[data-module="spl-meter"] .spl-readout[data-level="dead"] .spl-num { color: var(--danger); }
|
|
[data-module="spl-meter"] .spl-readout[data-level="dead"] .spl-sub { color: var(--danger); }
|
|
|
|
/* ── level bars ───────────────────────────────────────────────────── */
|
|
|
|
[data-module="spl-meter"] .spl-bars {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: 0.35rem;
|
|
padding: 0.4rem;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar {
|
|
display: grid;
|
|
grid-template-columns: 1.4em 1fr 3.4em;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar-label { color: var(--muted); text-align: right; }
|
|
|
|
[data-module="spl-meter"] .spl-bar-track {
|
|
position: relative;
|
|
height: 0.65rem;
|
|
border-radius: 0.35rem;
|
|
background: color-mix(in srgb, var(--border) 60%, transparent);
|
|
overflow: hidden;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar-fill {
|
|
position: absolute;
|
|
inset: 0 auto 0 0;
|
|
width: 0;
|
|
border-radius: inherit;
|
|
background: var(--accent);
|
|
transition: width 60ms linear;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar[data-level="warn"] .spl-bar-fill { background: var(--warn); }
|
|
[data-module="spl-meter"] .spl-bar[data-level="alarm"] .spl-bar-fill { background: var(--danger); }
|
|
[data-module="spl-meter"] .spl-bar[data-level="idle"] .spl-bar-fill { background: var(--muted); }
|
|
[data-module="spl-meter"] .spl-bar[data-level="dead"] .spl-bar-value { color: var(--danger); }
|
|
|
|
/* Peak hold sits on the track rather than inside the fill, so it stays
|
|
visible when the bar falls away underneath it. */
|
|
[data-module="spl-meter"] .spl-bar-peak {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: var(--text);
|
|
opacity: 0.75;
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar-value {
|
|
font-variant-numeric: tabular-nums;
|
|
text-align: right;
|
|
color: var(--text);
|
|
}
|
|
|
|
[data-module="spl-meter"] .spl-bar.clip .spl-bar-value { color: var(--danger); }
|
|
|
|
/* ── canvas tiles ─────────────────────────────────────────────────── */
|
|
|
|
[data-module="spl-meter"] .spl-canvas {
|
|
flex: 1;
|
|
display: block;
|
|
width: 100%;
|
|
min-height: 0;
|
|
border-radius: 0.25rem;
|
|
}
|
|
|
|
/* The readout scales off both of the tile's dimensions, so the container
|
|
has to be sized on both axes — inline-size alone leaves cqh undefined. */
|
|
[data-module="spl-meter"] .spl { container-type: size; }
|
|
|
|
/* The sub-line is the first thing to go when the tile gets short: the
|
|
number is the point, and a clipped caption under it is worse than none. */
|
|
[data-module="spl-meter"] .spl-sub {
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
@container (max-height: 90px) {
|
|
[data-module="spl-meter"] .spl-sub { display: none; }
|
|
}
|