Expand description
Audio provenance (v2.3.7 “Overtone”) — why does this moment sound like that?
The APU analogue of the PPU’s rustynes_ppu::provenance (a plain code span,
not an intra-doc link: rustynes-apu does not depend on rustynes-ppu, and a
bracketed link to a crate outside the graph fails RUSTDOCFLAGS=-D warnings
while clippy stays green), and deliberately
built to the same shape: a register-attribution half that answers “what
wrote this, and from which instruction”, and a per-cycle mix trace that
answers “what were the channels actually doing”.
§What was missing before this
Every other ingredient already shipped. The frontend’s audio scope plots the
per-channel waveforms, the audio mixer exposes per-channel gain,
crate::Apu::pulse1_out and its siblings expose live channel outputs, and
the trace logger has PC and cycle. What did not exist anywhere is the link
between a sample and the instruction that caused it — exactly the gap the
pixel-provenance design identified for video.
§Cadence, and why it is per CPU cycle rather than per output sample
The mix is computed once per CPU cycle (1.789 MHz NTSC) and handed to the
band-limited blip decimator, which produces output samples at 44.1 kHz —
about one per 40.6 CPU cycles. Recording at output rate would therefore
require choosing which of those 40 mixes “is” the sample, and band-limited
synthesis makes that choice ill-posed: an output sample is a weighted sum of
transitions across the filter kernel, not a copy of one instant.
So this records what was genuinely mixed, at the cadence it was mixed. The panel maps a clicked output sample back to its CPU-cycle window; the doc says plainly that the window is a kernel width, not a point. A provenance tool that answers a question it cannot actually answer is worse than one that declines — the whole argument v2.3.6 was built on.
The cost is smaller than it sounds: 29,781 records per NTSC frame against the pixel store’s 61,440 — 0.48x the record count of the video side.
§Determinism
Output-only. Nothing here is read back into synthesis, none of it is part of
the save state, and every store is behind a runtime arm that is off by
default. With the arm off the cost is one Option discriminant test per
register write and per mixed cycle.
Structs§
- Audio
Provenance - Everything audio provenance owns, behind ONE pointer.
- Audio
Provenance Stash - Both audio stores, lifted out of the APU so a same-timeline restore can put them back.
- MixRecord
- The five channel outputs that went into one mixed CPU-cycle sample, plus the result.
- MixTrace
- One frame’s worth of per-CPU-cycle mix records.
- RegWrite
- One register write: the byte, the CPU cycle, and the instruction that did it.
- Register
Attribution - Last write to each of
$4000-$4017, with its cause.
Enums§
- Write
Origin - What performed a register write.
Constants§
- MIX_CAP
- Largest CPU-cycle count in one frame across every supported region, which is
what
MixTraceis sized for. - REG_
BASE - First APU/IO register address covered by
RegisterAttribution. - REG_
COUNT - Number of register slots tracked:
$4000-$4017inclusive. - REG_
COUNT_ U16 REG_COUNTas au16, so address arithmetic never needs a cast.