Files
Sylpheed/tools/port/check-capture
Sylpheed port agent f23180c4eb port: a capture must pass a provenance check before anyone analyses it
The 6-channel capture I spent an iteration refuting was corrupt, and the Decoder
found the cause: PulseAudio remapping between two mismatched channel maps, where
a 6-channel remap SILENTLY DROPS AND DUPLICATES. Its control -- six channels, six
tones, same sink, same parec -- came back 400/3200/200/800/800/200 for an input
of 400/800/200/1600/3200/6400. Two source channels gone entirely.

So the negative was right, and the byte-identical pair I reported was the thread
that unravelled it. Worth recording precisely, because it nearly went unreported:
it began as an idle look at two channels whose peak AND RMS matched to six
decimals, and it only became evidence because a coincidence at six decimals is
cheaper to hash than to explain.

`tools/port/check-capture` makes that check one command -- split the file, hash
every channel, fail on any duplicate pair -- and AUDIO-VERIFICATION.md gains a
section 5 saying to run it FIRST, every time, plus the two conditions the same
incident produced: start the recorder before the process, and log what was on
screen against the recording's own clock so a miss is diagnosable.

Controlled both directions, because a checker nobody controlled is what this
incident is about: six distinct tones PASS; the remap's own output pattern FAILS
naming all four pairs; the corrupt capture FAILS on ch2 == ch5.

THE KNOWN-BAD CONTROL IS THE POINT. All six of its channels report a peak of
-18.063656 dB, identical to six decimals, while containing three duplicate pairs.
A level check cannot see this failure. That is why the tool hashes rather than
measures, and why the corrupt capture's "plausible per-channel levels" were never
evidence. The tool says of itself that it is necessary, not sufficient.

Withdrawn with the file, both the Decoder's: "all six channels carry signal", and
the non-zero-surround observation offered as weak support for 5.1. Unaffected:
the three-XMA-context concurrency result, read from the emulator's log rather
than the audio path, on two independent boots.

The corrupt file is dropped from the exchange so the next agent cannot pick it up
and repeat the work. Nothing in the export changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
2026-08-29 16:07:19 +00:00

78 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Provenance check for a multichannel capture, BEFORE anybody analyses it.
#
# tools/port/check-capture /path/to/capture.wav
#
# WHY THIS EXISTS. A 6-channel capture of the game's own output was analysed at
# length -- three controls, a drift test, a written-up negative -- and the file
# was corrupt. PulseAudio was remapping between two mismatched channel maps, and
# a 6-channel remap SILENTLY DROPS AND DUPLICATES: right duration, right channel
# count, plausible per-channel levels, no error anywhere. Two of the six channels
# were byte-identical copies of two others and two source channels were simply
# gone.
#
# The Decoder proved it with a control that needs no emulator and no disc: six
# channels each carrying a different tone through the same sink and the same
# `parec` invocation. Channels came back 400 / 3200 / 200 / 800 / 800 / 200 for
# an input of 400 / 800 / 200 / 1600 / 3200 / 6400 -- see
# `docs/re/audio-capture-channel-map-trap.md`. Setting the sink's `channel_map`
# to the guest's own and passing the same map to `parec` returns all six.
#
# THE DETECTABLE SIGNATURE IS AN EXACT DUPLICATE PAIR. Two channels of a real
# surround mix are never byte-identical over 70 s. Levels are not enough to catch
# it -- the corrupt file's per-channel peaks looked entirely reasonable, and it
# was only equal peak AND equal RMS to six decimals that prompted a hash.
#
# This is a NECESSARY check, not a sufficient one: passing it means the capture
# has no duplicated channels, not that it recorded the right thing.
set -euo pipefail
f="${1:?usage: check-capture FILE.wav}"
# Queried one field at a time. A combined `-show_entries` prints two values on
# ONE comma-separated line, and `read -r ch rate dur` then puts "48000,6" in
# `$ch` -- which every later arithmetic test rejects, in a script whose whole
# job is to be trusted about a file.
probe() { ffprobe -v error -select_streams a:0 -show_entries "$1" -of csv=p=0:nk=1 "$f" | head -1; }
ch=$(probe stream=channels)
rate=$(probe stream=sample_rate)
dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0:nk=1 "$f" | head -1)
printf '%s: %sch %sHz %.3fs\n' "$f" "$ch" "$rate" "$dur"
if [ "$ch" -lt 2 ]; then echo " single channel -- nothing to compare"; exit 0; fi
layout=5.1; [ "$ch" = 2 ] && layout=stereo
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
map=""; for i in $(seq 0 $((ch-1))); do map="$map -map [c$i] $tmp/c$i.wav"; done
split=""; for i in $(seq 0 $((ch-1))); do split="$split[c$i]"; done
# shellcheck disable=SC2086
ffmpeg -hide_banner -v error -y -i "$f" \
-filter_complex "channelsplit=channel_layout=$layout$split" $map
declare -a sums
for i in $(seq 0 $((ch-1))); do
s=$(ffmpeg -hide_banner -v error -i "$tmp/c$i.wav" -f md5 - | cut -d= -f2)
peak=$(ffmpeg -hide_banner -v info -i "$tmp/c$i.wav" -af astats -f null - 2>&1 \
| grep -m1 "Peak level dB" | sed 's/.*: //')
sums[i]="$s"
printf ' ch%-2d peak %-12s %s\n' "$i" "$peak" "$s"
done
dupes=0
for i in $(seq 0 $((ch-1))); do
for j in $(seq $((i+1)) $((ch-1))); do
if [ "${sums[i]}" = "${sums[j]}" ]; then
echo " 🔴 ch$i and ch$j are BYTE-IDENTICAL"
dupes=1
fi
done
done
if [ "$dupes" = 1 ]; then
echo "FAIL: duplicated channels. A surround remap drops and duplicates silently;"
echo " channels are missing from this file. Do not analyse it -- fix the"
echo " sink's channel_map and re-record. See docs/port/AUDIO-VERIFICATION.md."
exit 1
fi
echo "PASS: no duplicated channels. (Necessary, not sufficient -- this says"
echo " nothing about whether the right thing was recorded.)"