Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 17s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Failing after 54s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Failing after 2m33s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 3m8s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
wai-audit collector and player integration / does this change touch it (push) Has been cancelled
wai-audit collector and player integration / collector · linux (push) Has been cancelled
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Has been cancelled
WAI operation catalogue / does this change touch it (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-quantum for JavaScript / does this change touch it (push) Has been cancelled
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
wai-quantum studio and desktop / does this change touch it (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-quantum studio and desktop / desktop app · macos (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
wai-sink-android native sink library / does this change touch it (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
wai-sink-swift native sink package / does this change touch it (push) Has been cancelled
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Has been cancelled
WAI Studio / does this change touch it (push) Has been cancelled
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Has been cancelled
WAI Studio / Studio in Chromium and Firefox · macos (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai-ledger service / does this change touch it (push) Has been cancelled
wai-ledger service / service · linux (push) Has been cancelled
wai-ledger service / service · macos (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Has been cancelled
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Has been cancelled
wai-app and Create / wai-app · does this change touch it (push) Has been cancelled
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Has been cancelled
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Has been cancelled
wai-fleet service / does this change touch it (push) Has been cancelled
wai-fleet service / service · linux (push) Has been cancelled
wai-fleet service / service · macos (push) Has been cancelled
The reference synthesiser gains what a short channel sting needs, all in the wai.det.fixed64 floor and trig-free, every committed score rendering byte for byte as before (the three first-revision vectors keep their mixdown hash, PCM SHA-256 and score hash): - score §2.2, §3.1: waveforms 4 to 6, the saw, square and 25 %-pulse with each step corrected by a polynomial (PolyBLEP); 7, a sine by a refined parabola; 8, noise held from a 32-bit LCG between wraps of the phase. - score §2.1, §3.4: a contract of two channels mixes down to interleaved frames; a reader refuses any count but 1 and 2; a score with no voices centres each note. - score §2.4, §3.3: an optional third section, one 73-byte voice per note: a constant-power pan by the profile's integer square root, curved attack, decay and release (the release from the level at note-off), up to four one-pole low-pass sections that follow the envelope, a linear glide, the noise seed, and up to eight echoes at alternating pans. The 65-byte note record is unchanged; the score hash binds the voices when present. - wai score build|render: channels 2, waveforms to 8, each note's optional voice; render prints frames and channels. Receipt blocks are 4096 frames. - score-conformance: five vectors at 48 kHz (band-limited, two channels, voiced in one, a two-second sting, a voiced scale); multi_verify.py's independent synth agrees with all eight. - packaging §5.8: a score of one or two channels packs beside the lead with its voices, its floor the FLAC of the mixdown in its channels; verify holds the floor to the base's channels and rate. pack-conformance/multi/multi-sting-01 is a voiced two-channel score at 48 kHz. - The composing sink synthesises and plays a score in its contract's channels; its reference package's score is now two channels at 48 kHz, voiced. Co-Authored-By: Claude Opus 5.5 <[email protected]>
311 lines
17 KiB
Markdown
311 lines
17 KiB
Markdown
# WAI Extension: Score (Music as Instructions)
|
||
|
||
> Status: Draft. Phase 6 — the deepest audio lever. Ship the
|
||
> composition, not the recording: a score of note events the sink
|
||
> synthesizes. Conformance is **mixdown-equivalence**, the music
|
||
> counterpart of spatial audio. Reference impl: the `score` feature of
|
||
> `wai-rs`; corpus: `score-conformance/`; live sink:
|
||
> wai.transaction.science/score.
|
||
Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
|
||
|
||
## 1. Scope and model
|
||
|
||
A `wai.audio.score` object carries **no recorded audio** — only note
|
||
events. Where `wai.audio.scene` mixes recorded sound *objects*, a score
|
||
is reconstructed from instructions: a deterministic reference synth
|
||
turns the notes into a canonical PCM mixdown of one channel or two, and a
|
||
sink with a richer instrument (soundfont, neural) renders the same score
|
||
for presentation. A note may carry a voice (§2.4): its place between the
|
||
two channels, curved envelope segments, a low-pass filter that follows
|
||
its envelope, a pitch glide, a noise seed and echoes.
|
||
It is lever 3 (instructions-at-the-sink) for music — a minute of music
|
||
is kilobytes of notes, not megabytes of samples.
|
||
|
||
In scope: the container, the note model, the deterministic reference
|
||
synthesis, the **mixdown-equivalence** conformance criterion, the
|
||
receipt profile. Out of scope: a transport; the sink's instrument
|
||
(soundfonts, neural synths — presentation, not conformance); MIDI-file
|
||
import (an authoring concern).
|
||
|
||
Relationship to the core spec: mirrors spatial audio. The reconstruct
|
||
runs in the `wai.det.fixed64` floor; `media` takes the value `"audio"`;
|
||
`wai.audio.score` is authoritative.
|
||
|
||
## 2. Container (`WSCR`)
|
||
|
||
```
|
||
"WSCR" | u16 n_sections | section table (u8 kind, u32 off, u32 len) | sections
|
||
```
|
||
|
||
| kind | section | required |
|
||
|---|---|---|
|
||
| `0x01` | contract (canonical JSON) | REQUIRED |
|
||
| `0x02` | note events | REQUIRED |
|
||
| `0x03` | voices, one per note (§2.4) | OPTIONAL |
|
||
|
||
`off` counts from the end of the section table; the reference writer writes the sections
|
||
in this order, each padded with zero bytes to a multiple of 8. A score with a voices
|
||
section is **voiced**. A reader skips a section of a kind the table does not name: it does
|
||
not enter the mixdown. A score of the first revision's two sections renders as it always
|
||
has (§3.2).
|
||
|
||
### 2.1 Contract
|
||
|
||
```json
|
||
{ "channels": 1, "duration": 34000, "numeric": "wai.det.fixed64",
|
||
"sample_rate": 22050 }
|
||
```
|
||
|
||
`duration` is the rendered mixdown length in samples of each channel (sample frames).
|
||
`channels` is 1 or 2; a reader refuses any other count. A mix of two channels is the
|
||
left and the right, interleaved (§3.4).
|
||
|
||
**Reader limit.** A reader MUST NOT refuse a score for its length while `duration ×
|
||
channels` is at most 134 217 728 (2²⁷, the audio limit of SPEC §2), and MAY refuse one
|
||
past it (`duration_too_long`), when it reads the contract, before it renders anything.
|
||
A conforming encoder writes no score past it.
|
||
|
||
### 2.2 Notes
|
||
|
||
`n_notes u32`, then per note (65 bytes):
|
||
`start u64 | dur u64 | phase_inc i64 Fx | gain i64 Fx | waveform u8 |
|
||
attack u64 | decay u64 | sustain i64 Fx | release u64`.
|
||
|
||
`phase_inc` is the per-sample phase advance `freq / sample_rate`,
|
||
authored off-line from the pitch — so the floor never evaluates an
|
||
exponential to turn a note into a frequency (the same authoring
|
||
discipline as the avatar quaternion). `waveform` selects a reference
|
||
timbre (§3.1):
|
||
|
||
| code | timbre |
|
||
|---|---|
|
||
| `0` | saw, piecewise-linear |
|
||
| `1` | square, piecewise-linear |
|
||
| `2` | triangle, piecewise-linear |
|
||
| `3` | 25 %-pulse, piecewise-linear |
|
||
| `4` | saw, band-limited (each step corrected, PolyBLEP) |
|
||
| `5` | square, band-limited |
|
||
| `6` | 25 %-pulse, band-limited |
|
||
| `7` | sine (a refined parabola) |
|
||
| `8` | noise (a generator's value held between wraps of the phase) |
|
||
|
||
A note of another code is silent. The band-limited timbres are the piecewise-linear
|
||
ones with the step at each discontinuity spread over the samples beside it, so a high
|
||
note does not fold its harmonics back below the Nyquist frequency.
|
||
|
||
### 2.3 Authoring form (informative)
|
||
|
||
`wai score build` writes a `WSCR` from a JSON document that holds every
|
||
field as an integer, so the committed document is the definition and no
|
||
platform's floating-point `pow` can change a pitch:
|
||
|
||
```json
|
||
{ "sample_rate": 22050, "channels": 1, "duration": 34000,
|
||
"numeric": "wai.det.fixed64",
|
||
"notes": [ { "start": 0, "dur": 3500, "phase_inc": 50960238,
|
||
"gain": 1717986918, "waveform": 0, "attack": 220,
|
||
"decay": 600, "sustain": 2791728742, "release": 1500 } ] }
|
||
```
|
||
|
||
A voiced score of two channels at 48 000 Hz: one band-limited saw (A4,
|
||
`phase_inc` = round(440 / 48 000 × 2³²)), panned half left, its decay and
|
||
release curved, through two low-pass sections that open with its envelope, with
|
||
three echoes a quarter second apart crossing from right to left:
|
||
|
||
```json
|
||
{ "sample_rate": 48000, "channels": 2, "duration": 72000,
|
||
"numeric": "wai.det.fixed64",
|
||
"notes": [ { "start": 0, "dur": 24000, "phase_inc": 39370534,
|
||
"gain": 1288490189, "waveform": 4, "attack": 480,
|
||
"decay": 24000, "sustain": 2147483648, "release": 9600,
|
||
"voice": { "pan": -2147483648, "decay_curve": 4,
|
||
"release_curve": 4, "lp_poles": 2,
|
||
"lp_a": 214748365, "lp_env": 1717986918,
|
||
"echo_taps": 3, "echo_delay": 12000,
|
||
"echo_gain": 1717986918,
|
||
"echo_pan": 3006477107 } } ] }
|
||
```
|
||
|
||
`phase_inc`, `gain`, `sustain` and a voice's `pan`, `lp_a`, `lp_env`, `glide_to`,
|
||
`echo_gain` and `echo_pan` are the raw Q32.32 bits (a pan negative for the left).
|
||
`numeric` may be left out. `voice` is optional on each note, and each of its members
|
||
is optional, a member left out taking the default voice's value (§2.4); a score in
|
||
which any note has a `voice` is voiced, and a note of it with none takes the default
|
||
voice. The tool refuses a number with a fraction or an exponent, a member it does not
|
||
name, a member given twice (readers differ on which of the two values they keep), a
|
||
channel count other than 1 or 2, a `duration × channels` past 2²⁶, a `phase_inc` or a
|
||
`glide_to` of a whole cycle or more, a `gain` or `sustain` above 1.0, a waveform above
|
||
8, a voice member out of the range §2.4 gives, a `pan` or an `echo_pan` other than 0
|
||
in one channel, a `glide` with no `glide_to`, an echo of no delay, and a note whose
|
||
release, or last echo, would end after `duration`, so the mixdown always holds every
|
||
note whole. `score-conformance/notes/` holds each vector in this form, and `wai score
|
||
build` turns it back into the committed `WSCR` byte for byte.
|
||
|
||
### 2.4 Voices
|
||
|
||
A voices section holds one 73-byte record per note, in the notes' order, and nothing
|
||
else: its length is `n_notes × 73` (it has no count of its own). Each record:
|
||
|
||
```
|
||
pan i64 Fx | attack_curve u8 | decay_curve u8 | release_curve u8 | lp_poles u8 |
|
||
lp_a i64 Fx | lp_env i64 Fx | glide_to i64 Fx | glide u64 | seed u32 |
|
||
echo_taps u8 | echo_delay u64 | echo_gain i64 Fx | echo_pan i64 Fx
|
||
```
|
||
|
||
| field | meaning | range |
|
||
|---|---|---|
|
||
| `pan` | the note's place in a two-channel mix, −1.0 left to 1.0 right | −1.0 to 1.0; 0 in one channel |
|
||
| `attack_curve`, `decay_curve`, `release_curve` | the exponent of each envelope segment (§3.3); 1 is a line | 1 to 8 |
|
||
| `lp_poles` | one-pole low-pass sections in series; 0 is none | 0 to 4 |
|
||
| `lp_a` | each section's coefficient at an envelope of 0 | 0 to 1.0 |
|
||
| `lp_env` | what the coefficient gains per unit of envelope | 0 to 1.0 |
|
||
| `glide_to` | the phase increment the note glides to | 0 to below 1.0 |
|
||
| `glide` | the glide's length in samples; 0 is none (`glide_to` is then not read) | any |
|
||
| `seed` | the noise generator's seed (waveform 8) | any |
|
||
| `echo_taps` | how many echoes of the note | 0 to 8 |
|
||
| `echo_delay` | samples from one echo to the next | 1 to 2²⁷ when `echo_taps` > 0 |
|
||
| `echo_gain` | each echo's level against the one before it | 0 to 1.0 |
|
||
| `echo_pan` | the pan of the odd echoes; the even ones sit at its negation | −1.0 to 1.0; 0 in one channel |
|
||
|
||
A reader refuses a voices section of another length, and a record with a field out of
|
||
its range. The **default voice** is `pan` 0, the three curves 1, `lp_poles` 0, `lp_a`
|
||
1.0, `lp_env` 0, `glide_to` 0, `glide` 0, `seed` 0, `echo_taps` 0, `echo_delay` 0,
|
||
`echo_gain` 0 and `echo_pan` 0. A voiced note sounds from `start` to its **end**,
|
||
`start + dur + release + echo_taps × echo_delay` (each sum exact); a note of a score with
|
||
no voices to `start + dur + release`.
|
||
|
||
## 3. Reference synthesis (trig-free)
|
||
|
||
Every value is a `wai.det.fixed64` Q32.32 number: `+` and `−` saturate, `×` and `÷` are
|
||
the profile's (computed in 128 bits, rounded toward negative infinity, then saturated),
|
||
a negation of the least value is the greatest, and √ is the profile's (the floor square
|
||
root of the raw bits times 2³², and 0 for a value of 0 or below). An expression is
|
||
evaluated as written, `×` and `÷` before `+` and `−` and otherwise left to right, each
|
||
operation rounded as it is made; `1` is 1.0, `½` is 2³¹ raw, `¼` is 2³⁰ raw, and an
|
||
integer `n` of samples used as a value is `n · 2³²` raw. The whole path is `+ − × ÷ √`
|
||
in the `Fx` floor — bit-identical on every machine.
|
||
|
||
### 3.1 Oscillators
|
||
|
||
A note's phase `p` starts at 0. At each sample the oscillator gives a value from `p` and
|
||
the sample's increment `dt` (the note's `phase_inc`, or its glide's, §3.3); then
|
||
`p ← p + dt`, and where `p ≥ 1`, `p ← p − 1` once (the phase **wraps**).
|
||
|
||
- `0` saw: `2·p − 1`. `1` square: `1` for `p < ½`, else `−1`. `2` triangle: `4·p − 1`
|
||
for `p < ½`, else `3 − 4·p`. `3` pulse: `1` for `p < ¼`, else `−1`.
|
||
- The step correction `B(t, dt)`: `0` for `dt ≤ 0`; for `t < dt`, with `x = t ÷ dt`,
|
||
`x + x − x·x − 1`; for `t > 1 − dt`, with `x = (t − 1) ÷ dt`, `x·x + x + x + 1`;
|
||
otherwise `0`. With `W(q)` = `q − 1` for `q ≥ 1` and `q` otherwise:
|
||
`4` saw: `2·p − 1 − B(p, dt)`; `5` square: `s + B(p, dt) − B(W(p + ½), dt)`, `s` the
|
||
square's value; `6` pulse: `s + B(p, dt) − B(W(p + ½ + ¼), dt)`, `s` the pulse's value.
|
||
- `7` sine: `u = 2·p − 1`, `y = 4·u·(1 − |u|)`, `y ← P·(y·|y| − y) + y` with `P` =
|
||
966 367 642 raw (0.225), and the value is `−y` (sin 2π`p` to within a thousandth).
|
||
- `8` noise: the value **held**, from a 32-bit state `s`, set to the voice's `seed` (0
|
||
in a score with no voices) when the note starts, and advanced once then and again each
|
||
time the phase wraps: `s ← (1 664 525 · s + 1 013 904 223) mod 2³²`, the held value
|
||
then `((s >> 8) − 2²³) · 2⁹` raw, in `[−1, 1)`. A `phase_inc` just below 1 draws a new
|
||
value every sample after the first.
|
||
|
||
### 3.2 A score with no voices
|
||
|
||
Each note's envelope at `t` samples into it is the linear ADSR: for `attack > 0` and
|
||
`t < attack`, `t ÷ attack`; else for `t < attack + decay`, `1 − (1 − sustain) ×
|
||
(t − attack) ÷ decay` (or `sustain` when `decay` is 0); else for `t < dur`, `sustain`;
|
||
else for `release > 0` and `t < dur + release`, `sustain × (1 − (t − dur) ÷ release)`;
|
||
else 0. The note's sample is `osc × env × gain`, for `t` from 0 while `start + t` is
|
||
below both `start + dur + release` and `duration`. In one channel it is added to the
|
||
mix; in two, each channel adds it times `√((1 − 0) / 2)` (the centre, §3.3's pan of 0).
|
||
|
||
### 3.3 A voiced score
|
||
|
||
For each note and its voice, with `F(t, n)` = ⌊`t` · 2³² / `n`⌋ raw (exact, for `t < n`)
|
||
and `v^k` = `v` multiplied by `v` `k − 1` times in turn:
|
||
|
||
1. *Glide.* With `glide` = 0, `dt = phase_inc`. Otherwise `dt = phase_inc + ⌊(glide_to −
|
||
phase_inc) · min(t, glide) / glide⌋` raw, the difference of the raw values times
|
||
`min(t, glide)`, divided by `glide` toward negative infinity: the increment moves in a
|
||
line to `glide_to` and holds there.
|
||
2. *Envelope.* The held level `H(t)`: for `attack > 0` and `t < attack`,
|
||
`1 − (1 − F(t, attack))^attack_curve`; else, with `u = t − attack`, for `decay > 0`
|
||
and `u < decay`, `sustain + (1 − sustain) × (1 − F(u, decay))^decay_curve`; else
|
||
`sustain`. The envelope `E(t)` is `H(t)` for `t < dur`; for `release > 0` and
|
||
`t − dur < release`, `H(dur) × (1 − F(t − dur, release))^release_curve`; else 0. The
|
||
release starts from the level the note had at note-off, so a note let go during its
|
||
attack or decay does not jump.
|
||
3. *Low-pass.* With `lp_poles` = `k` > 0, the coefficient is `a = lp_a + lp_env × E(t)`,
|
||
held to `[0, 1]`; each section `i` of `1..k`, its state 0 when the note starts, makes
|
||
`y_i ← y_i + a × (x − y_i)` from its input `x` (the oscillator's value for the first,
|
||
the section before's `y` for the rest), and the last section's `y` is the filtered
|
||
value. With `lp_poles` 0 the oscillator's value passes.
|
||
4. *Gain.* `x = filtered × E(t) × gain`.
|
||
5. *Pan.* The gains of a pan `q` are `(L, R) = (√((1 − q) ⁄ 2), √((1 + q) ⁄ 2))`, each
|
||
`⁄ 2` the raw value shifted right one bit (floor): `L² + R²` is 1 at every pan, to
|
||
within the rounding, and the centre is 3 037 000 499 raw (√½) on each side. In two channels the note adds
|
||
`x × L` to the left and `x × R` to the right at frame `start + t`, by its `pan`; in
|
||
one channel it adds `x`.
|
||
6. *Echoes.* Echo `j` of `1..echo_taps` has the level `g_j = g_{j−1} × echo_gain`, `g_0
|
||
= 1`, and the pan `echo_pan` for odd `j`, `−echo_pan` for even `j`. After the note's
|
||
own sample, each echo in order adds `x × g_j` at frame `start + t + j × echo_delay`,
|
||
by its pan (in one channel, `x × g_j`), while that frame is below `duration`.
|
||
|
||
`t` runs from 0 while `start + t` is below both `start + dur + release` and `duration`;
|
||
each sample the oscillator's value is taken, then the phase advances by that sample's
|
||
`dt` (§3.1).
|
||
|
||
### 3.4 Mixdown
|
||
|
||
The mix is `duration × channels` values, each 0 to start, two channels interleaved left,
|
||
right (frame `f`'s left is value `2f`, its right `2f + 1`). The notes are rendered in
|
||
their order; within a note, sample by sample as above, each addition saturating as it
|
||
is made. Each value is then quantized to `i16` (`round(v · 32767)`, half away from zero,
|
||
`v` first clamped to `[−1, 1]`) in integer space.
|
||
|
||
## 4. Conformance — mixdown-equivalence
|
||
|
||
> Given the same `wai.audio.score`, a conforming sink MUST reconstruct
|
||
> the identical canonical `i16` PCM via the reference synth, hence the
|
||
> identical BLAKE3 — on every machine, no tolerance parameter.
|
||
|
||
```
|
||
mixdown_hash = BLAKE3("wai:score-mixdown\x01" || i16le PCM)
|
||
score_hash = BLAKE3("wai:score\x01" || contract || notes [|| voices]) // identity
|
||
```
|
||
|
||
The PCM is the mix of §3.4, two channels interleaved. `contract`, `notes` and `voices`
|
||
are the sections' bytes; a score with no voices section hashes as the first revision's
|
||
did. A conformance vector also states `pcm_sha256`, the SHA-256 of the same `i16le` PCM
|
||
(the audio buffer of SPEC §7 *Digests*), so a verifier that holds only SHA-256 checks
|
||
the samples. `wai score render` prints both, with `samples` the frames and `channels`
|
||
the contract's, and, with `--pcm`, writes the samples.
|
||
|
||
A sink MAY play a soundfont/neural instrument instead; that is
|
||
presentation and does not affect conformance, exactly as the audio-scene
|
||
HRTF render or the worlds renderer does not.
|
||
|
||
In a package ([packaging](packaging.md) §5, *Components beside the lead*), a score is cut at the lead's piece starts:
|
||
each piece a `WSCR` of the notes that sound in it (to their end, §2.4, echoes included),
|
||
its starts relative to the piece and their voices with them, whose mixdown is the
|
||
whole's for the piece's frames; its entry states the SHA-256 of that mixdown, and its
|
||
floor is the mixdown as FLAC in the score's channels.
|
||
|
||
## 5. Receipt (JWP profile)
|
||
|
||
`score_hash` is the identity; the group's objects are the mix PCM blocks
|
||
(4096 frames each, every channel of them; the last may be short), Merkle-bound; one Ed25519 signature (verified as
|
||
SPEC §7 *Signatures* requires) over score_hash + root + exact work (= duration × notes) + carried
|
||
`joules_micro`; `parent_receipt_hash` chains — the worlds/audio receipt
|
||
shape reused. The figure's acquisition class rides beside it as an optional signed label
|
||
([energy-measurement](energy-measurement.md) §2.8): absent, the receipt keeps
|
||
its legacy bytes and the figure is unlabelled; present, it is inside the
|
||
signature. The reference meter labels every figure it measures
|
||
`OnChipCounter`, with its declared uncertainty.
|
||
|
||
## Appendix
|
||
|
||
Mixdown-equivalence is spatial audio's criterion with the recorded
|
||
objects replaced by note events: both render a deterministic `i16` mix
|
||
in the `Fx` floor and hash it. The score is the most extreme
|
||
instructions-at-the-sink case in audio — there is no recording anywhere
|
||
in the payload, only the composition. Sight, hearing, touch, motion,
|
||
state, captured-content provenance — and now the composition itself —
|
||
are one floor, one verification.
|