feat: add random meters in demo

This commit is contained in:
Lucas
2026-09-30 16:09:18 +02:00
parent f4650ece00
commit e661022329
7 changed files with 217 additions and 74 deletions
+67 -12
View File
@@ -617,26 +617,81 @@ delay = delay_min + (raw / 255) × (delay_max - delay_min)
| Field | Value |
|---|---|
| **Purpose** | Per-channel level + peak metering |
| **Size** | 3 bytes per channel |
| **Total** | 12 channels × 3 bytes = 36 bytes |
| **Purpose** | Per-channel RMS + peak metering, with clip flags |
| **Payload size** | 38 bytes (exactly; any other length is an error) |
| **Layout** | 12 channels × 3 bytes = 36 bytes, followed by 2 clip-flag bytes |
| **Channel order** | Channels 0–3 are inputs, channels 4–11 are outputs, each mapped in enum order |
#### Channel record (3 bytes, at offset `i = channel × 3`)
| Byte offset | Field | Format |
|---|---|---|
| 0–1 | Level | IEEE754 float16, little-endian |
| 2 | Peak | 1-byte peak value (format not specified in source) |
| `i + 0` | RMS low byte | Low byte of the RMS float16 |
| `i + 1` | Shared high byte | High byte of **both** the RMS and Peak float16s |
| `i + 2` | Peak low byte | Low byte of the Peak float16 |
Both values are IEEE 754 half-precision floats (little-endian pairs). The peak is not a separate 1-byte value: it reuses byte `i + 1` as its high byte, so its byte order is reversed relative to the offset order.
**Encoding (value → raw):**
```text
level_bytes = float16_encode(level) // 2 bytes, little-endian
peak_byte = peak_encode(peak) // format undefined — see note below
raw = level_bytes + peak_byte
rms_raw = float16_decode(lo = payload[i], hi = payload[i + 1])
peak_raw = float16_decode(lo = payload[i + 2], hi = payload[i + 1])
```
**Decoding (raw → value):**
If a float16 fails to decode, the raw value defaults to `0.0`, which converts to `MIN_DB` (-60 dB).
#### Clip flags (bytes 36–37)
| Channel index | Flag location |
|---|---|
| 0–3 | `payload[36]`, bit `index` |
| 4–11 | `payload[37]`, bit `index - 4` |
```text
level = float16_decode(raw[0:2]) // little-endian
peak = peak_decode(raw[2]) // format undefined — see note below
clip = (flags_byte & (1 << bit)) != 0
```
#### Raw → dB conversion
Raw values are converted with a piecewise-linear curve, then clamped.
| Constant | Value | Meaning |
|---|---|---|
| `X0` | -25.0 dB | Breakpoint |
| `Y0` | 1.0 | Raw value at the breakpoint |
| `K1` | 0.0104 | Raw units per dB below the breakpoint (`raw < Y0`) |
| `K2` | 0.0208 | Raw units per dB above the breakpoint (`raw >= Y0`) |
| `MIN_DB` | -60.0 dB | Lower clamp |
| `MAX_DB` | 12.0 dB | Upper clamp |
**Decoding (raw → dB):**
```text
if raw < Y0: db = X0 + (raw - Y0) / K1
else: db = X0 + (raw - Y0) / K2
db = clamp(db, MIN_DB, MAX_DB)
```
**Encoding (dB → raw):** the inverse of the above. Values outside the clamp range are not recoverable.
```text
db = clamp(db, MIN_DB, MAX_DB)
if db < X0: raw = Y0 + (db - X0) * K1
else: raw = Y0 + (db - X0) * K2
```
#### Full decode
```text
require len(payload) == 38
for index in 0..11:
i = index * 3
rms_db = raw_to_db(float16_decode(payload[i], payload[i + 1]))
peak_db = raw_to_db(float16_decode(payload[i + 2], payload[i + 1]))
clip = index < 4 ? payload[36] bit index
: payload[37] bit (index - 4)
meter[index] = { rms: rms_db, peak: peak_db, clip }
inputs = meter[0..3] → InputChannel enum order
outputs = meter[4..11] → OutputChannel enum order
```
---