diff --git a/docs/re/audio-capture-channel-map-trap.md b/docs/re/audio-capture-channel-map-trap.md new file mode 100644 index 00000000..7a29addf --- /dev/null +++ b/docs/re/audio-capture-channel-map-trap.md @@ -0,0 +1,92 @@ +# 🔴 A 6-channel PulseAudio capture SCRAMBLES AND DUPLICATES channels unless the maps match + +**Classification: measured**, on the capture chain itself rather than on the +game. Recorded because a capture taken with this defect was handed to the port +as evidence, cost them a full controlled analysis, and the negative they +correctly reported was **my instrument, not the guest**. + +## What happened + +`adv-game-output-6ch.wav` was captured from Canary through a PulseAudio null +sink and shared as "what the game emits during `ADV`". The port could not match +it against **anything** — the `ADV` bed, any of the three XMA voice streams, +`BGM_103`, `S00A` — with best-vs-runner-up margins of 0.001–0.016 everywhere, +i.e. plateaux rather than peaks. They controlled that three ways (their +instrument finds `bed` vs `bed` at r=1.000 margin +0.115; their `.ogv` reference +matches the disc's `.wmv` at r=1.000 margin +0.114; and drift was excluded by +windowed lags scattering across the movie). They also observed that **capture +channels 3 and 6 were byte-identical, same MD5**. + +That duplicate pair is the tell, and it is reproducible without the emulator. + +## The control I should have run first + +Six channels, each a **different** tone, so any reorder, drop or duplication +shows up as a wrong frequency. Played to the sink with `paplay`, recorded from +its monitor with the same `parec` invocation the `ADV` capture used. + +**Sink map `FL,FR,RL,RR,FC,LFE`, i.e. NOT the stream's map — the original setup:** + +| channel | expected | captured | | +|---|---|---|---| +| 0 | 400 Hz | 400 Hz | ok | +| 1 | 800 Hz | **3200 Hz** | wrong | +| 2 | 200 Hz | 200 Hz | ok | +| 3 | 1600 Hz | **800 Hz** | wrong | +| 4 | 3200 Hz | **800 Hz** | wrong | +| 5 | 6400 Hz | **200 Hz** | wrong | + +`ch2 == ch5`, **byte-identical** — the same artefact the port found. The 6400 Hz +and 1600 Hz channels are **gone entirely**, replaced by duplicates. + +**Sink map made identical to Canary's own stream map** +(`front-left,front-right,front-center,lfe,rear-left,rear-right`), and the same +map given to `parec` explicitly: + +| channel | expected | captured | +|---|---|---| +| 0–5 | 400 / 800 / 200 / 1600 / 3200 / 6400 | **400 / 800 / 200 / 1600 / 3200 / 6400** | + +No duplicates. **CONTROL PASSED.** + +## The rule + +⚠️ **A null sink whose `channel_map` differs from the client's makes PulseAudio +remap, and a 6-channel remap silently loses channels and duplicates others.** +There is no error, no warning, and the WAV has the right length, the right +channel count and plausible per-channel levels. Set the sink's map to the +client's, and pass the same map to `parec`: + +```bash +MAP=front-left,front-right,front-center,lfe,rear-left,rear-right +pactl load-module module-null-sink sink_name=cap channels=6 channel_map=$MAP +parec -d cap.monitor --channels=6 --rate=48000 --format=s16le --channel-map=$MAP … +``` + +## 🔴 What this withdraws + +* **`adv-game-output-6ch.wav` is withdrawn as evidence.** Its channels are + scrambled and one pair is a duplicate. Nothing should be concluded from it, + in either direction — it is not evidence that the game emits something + unexpected, and the port's inability to match it is fully explained. +* **"All six channels carry signal"** — withdrawn. One of the six was a copy of + another. +* **"The surround and LFE channels are not zero, which a stereo guest padded + into a 6-channel frame would give"** — withdrawn. It was offered as weak + support for the 5.1 reading of a voice cue's three streams + ([`voice-three-streams-are-concurrent.md`](structures/voice-three-streams-are-concurrent.md)), + and it is worth nothing. The port said a duplicated channel is not an + independent one, and they were right before this control existed. + +✅ **Unaffected:** the three-XMA-context concurrency result. That is read from +the emulator's own log, not from the audio path, and it reproduced on two +independent boots. + +## The lesson, in the form it should have been applied + +The corpus's own rule is *run your instrument through a control first*. Here the +control needed no emulator, no disc and 30 seconds: **play a known signal through +the capture chain and check that it comes back.** It was not run, an artefact was +published, and the person who found the defect was the one who could not see the +instrument. ⚠️ **A capture is an instrument, not just an output** — the same +scrutiny a parser or an estimator gets.