SPL Meter 0.2.0: one measurement, many views

Every control asked for here is a view choice, so none of them may reach
the capture child — two dashboards watching the same room must be able to
disagree about how to draw it without disturbing the measurement or each
other. That set the shape of the change: the child now sends everything
anyone might ask for, once, and each tile folds it down.

Averaging (was "Reading") is Fast, Slow, Inf and None, and the child
computes and sends all four every frame. Fast and Slow are the IEC time
constants, Inf is Leq since reset, None is the block with no time constant
at all. The admin-wide time constant is gone — it was never a property of
the room.

Banding is 1/3 through 1/48 octave. The wire always carries 1/48, and
because every banding offered divides 48 a coarser band is an exact sum of
finer ones in the power domain. Nothing is resampled and no tile's choice
is visible to another. The transform grew to 16384 points to have bins fine
enough to mean it; below roughly 200 Hz a 1/48 band is still narrower than
one bin, which the Banding help says out loud rather than pretending.

Each tile kind now carries its own settings, which is the fix for the
complaint that started this: the RTA was offering an alarm threshold and a
metric picker that did nothing to it. An entry's instanceSchema replaces the
module's, so a gear popover only holds controls that change that tile.

Also: frequency labels in three tiers that thin out as a tile narrows
instead of smearing, gridlines off by default on both graphs, RTA smoothing
as a damping factor on the drawn value only, a title-bar toggle to swap the
spectrogram's axes, and the threshold blurb removed.

Settings saved by 0.1.0 are mapped forward — a layout on a wall should not
start reading dashes because a field was renamed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Waters Church
2026-09-17 15:56:27 -04:00
co-authored by Claude Opus 5
parent e90ae51495
commit d2d4207fe4
5 changed files with 496 additions and 169 deletions
+32 -45
View File
@@ -1,20 +1,33 @@
{
"id": "spl-meter",
"name": "SPL Meter",
"version": "0.1.0",
"version": "0.2.0",
"proddash": ">=1.5.0",
"description": "Calibrated sound level, Leq and RTA from an audio input on this machine",
"permissions": [
{ "kind": "microphone", "reason": "Measuring sound level from an audio input on this machine" }
{
"kind": "microphone",
"reason": "Measuring sound level from an audio input on this machine"
}
],
"client": "client.js",
"server": "server.js",
"style": "style.css",
"minSize": { "w": 2, "h": 1 },
"defaultSize": { "w": 3, "h": 2 },
"minSize": {
"w": 2,
"h": 1
},
"defaultSize": {
"w": 3,
"h": 2
},
"configGroups": {
"Input": { "toggle": "enabled" },
"Measurement": { "columns": 2 },
"Input": {
"toggle": "enabled"
},
"Measurement": {
"columns": 2
},
"Calibration": {
"collapsed": "whenSet",
"help": "A sound level calibrator fits over the microphone capsule and emits a known level, almost always 94 dB at 1 kHz. Put it on, press Measure beside the channel, and copy the offset it reports into the box. Without this the meter still works, but it reads in dB relative to full scale and every tile says so."
@@ -49,19 +62,18 @@
"group": "Measurement",
"default": "A",
"options": [
{ "value": "A", "label": "A — how loud it sounds" },
{ "value": "C", "label": "C — keeps the low end" },
{ "value": "Z", "label": "Z — no weighting at all" }
]
},
"filter": {
"type": "select",
"label": "Time constant",
"group": "Measurement",
"default": "fast",
"options": [
{ "value": "fast", "label": "Fast — 125 ms" },
{ "value": "slow", "label": "Slow — 1 s" }
{
"value": "A",
"label": "A — how loud it sounds"
},
{
"value": "C",
"label": "C — keeps the low end"
},
{
"value": "Z",
"label": "Z — no weighting at all"
}
]
},
"bands": {
@@ -69,7 +81,7 @@
"label": "Spectrum analysis",
"group": "Measurement",
"default": true,
"help": "Third-octave bands for the RTA and spectrograph tiles. Turn it off if you only ever show the number."
"help": "1/48-octave bands for the RTA and spectrogram tiles, which each fold them to their own banding. Turn it off if you only ever show the number."
},
"calibration": {
"type": "string",
@@ -78,30 +90,5 @@
"default": "",
"help": "Leave empty and every reading is dB relative to full scale, labelled dBFS so nobody mistakes it for real SPL."
}
},
"instanceGroups": {
"Thresholds": { "columns": 2, "help": "The readout turns amber then red as it crosses these. Set them to whatever this room has agreed is loud, not to a legal limit — this is not a compliance meter." }
},
"instanceSchema": {
"channel": {
"type": "select",
"label": "Channel",
"default": "",
"optionsRoute": "/channels"
},
"metric": {
"type": "select",
"label": "Reading",
"default": "spl",
"options": [
{ "value": "spl", "label": "SPL — right now" },
{ "value": "leq", "label": "Leq — average since reset" },
{ "value": "leq1m", "label": "Leq 1 min" },
{ "value": "leq10m", "label": "Leq 10 min" },
{ "value": "peakC", "label": "Peak, C-weighted" }
]
},
"warnAt": { "type": "number", "label": "Amber above", "group": "Thresholds", "default": 95 },
"alarmAt": { "type": "number", "label": "Red above", "group": "Thresholds", "default": 100 }
}
}