port: check-capture needed two numbers -- the rate alone passed a 50%-silent file
The Decoder found a blind spot in the bar I shipped last iteration. Raising the PulseAudio client buffer keeps cutting the gap RATE while total silence bottoms out and then doubles -- an over-large buffer starves in a few enormous holes instead of many small ones. Its 500 ms capture scores 1.3 gaps/s, better than a genuine music bed at 3.3, while being 50% silence. My 20/s bar passed it. Same shape as the level table that cannot see a duplicated channel: one number, blind to the failure next door. I did not set a bar on their numbers, because I do not hold those files and the last two bars in this tool were wrong precisely from being invented. Instead I built a control in that regime -- `bigholes`, a real bed with 350 ms holes punched in -- and set the rule from four controls I can run: real music+SFX bed 1.1% silence, 3.3 gaps/s PASS voice track, mono, pauses 53.2% silence, 0.3 gaps/s PASS bed with 350 ms holes 46.3% silence, 3.2 gaps/s FAIL the starved capture 35.6% silence, 30.9 gaps/s FAIL Rate alone cannot separate rows 2 and 3; silence alone cannot separate 1 and 3. The pair does: fail when >=10% is silent on every channel AND there is at least one gap per second. Real audio is either mostly not silent, or silent in a few long stretches -- not both at once. AND THE REGIME IT STILL CANNOT JUDGE IS PRINTED RATHER THAN PASSED. High silence with very few gaps is what a real voice track looks like and what an over-buffered capture looks like; nothing here separates them, so the tool says UNJUDGED and tells the reader to check against a known source. Inventing a bar for a regime with no control in it is how the previous two bars came to be wrong. A CONTROL THAT DOES NOT EXECUTE IS NOT A CONTROL: the tool returned immediately for single-channel input, so the mono voice track -- one of the four controls -- was never run through the check it was meant to control. Mono now skips only the duplicate test. Also recorded: the Decoder has withdrawn "the monitor-sink route cannot be fixed by configuration". A ~200 ms client buffer is worth a retry BEFORE anyone spends a session on a Canary rebuild. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
@@ -215,19 +215,62 @@ it produced said nothing about the game.
|
||||
gaps at a median of 1.4 ms** — quiet 16-bit passages really are zero for
|
||||
milliseconds — so neither statistic separates it from a starved file.
|
||||
|
||||
**What does separate them is the RATE**, by an order of magnitude in both
|
||||
directions:
|
||||
3. 🔴 **The gap RATE alone.** This one shipped, and the Decoder found it: raising
|
||||
the client buffer keeps cutting the rate while total silence **bottoms out and
|
||||
then doubles**, because an over-large buffer starves in a few enormous holes
|
||||
instead of many small ones. Its `PULSE_LATENCY_MSEC=500` capture scores
|
||||
**1.3 gaps/s — better than a genuine music bed at 3.3 — while being 50 %
|
||||
silence**, and a 20/s bar passed it.
|
||||
|
||||
| | gaps/s | median gap | all-channel silence |
|
||||
**It takes two numbers, because either one alone is blind to the failure next
|
||||
door** — the same shape as a level table that cannot see a duplicated channel.
|
||||
Reproduced on a file held here (`bigholes`: a real bed with 350 ms holes punched
|
||||
into it) so the regime is controlled rather than quoted:
|
||||
|
||||
| control | all-channel silence | gaps/s | verdict |
|
||||
|---|---|---|---|
|
||||
| the starved capture | **32.9** | 3.9 ms | 35.6 % |
|
||||
| a real music+SFX bed | **3.3** | 1.4 ms | 1.1 % |
|
||||
| a voice track, 53 % pauses | **0.03** | — | — |
|
||||
| real music+SFX bed | 1.1 % | 3.3 | **PASS** |
|
||||
| voice track, mono, real pauses | 53.2 % | 0.3 | **PASS** |
|
||||
| bed with 350 ms holes | **46.3 %** | 3.2 | **FAIL** |
|
||||
| the starved capture | **35.6 %** | 30.9 | **FAIL** |
|
||||
|
||||
The bar is **20 gaps/s** — 1.6× below the bad case, 6× above the worst good one,
|
||||
and set from those controls rather than chosen and then justified.
|
||||
Rate alone cannot separate rows 2 and 3; silence alone cannot separate rows 1 and
|
||||
3. **The pair does:** fail when ≥ 10 % of the file is silent on every channel
|
||||
*and* there is at least 1 gap per second. Real audio is either mostly not silent,
|
||||
or silent in a few long stretches — not both at once.
|
||||
|
||||
### 🔴 The monitor-sink route cannot be fixed by configuration
|
||||
### ⚠️ The regime this tool cannot judge, and says so
|
||||
|
||||
**High silence with very few gaps is what a real voice track looks like (53.2 %
|
||||
in 0.3 gaps/s) and also what an over-buffered capture looks like.** No statistic
|
||||
here separates them. The tool prints `UNJUDGED` and tells you to check the file
|
||||
against a known source rather than passing it silently — because inventing a bar
|
||||
for a regime with no control in it is how the two bars above came to be wrong.
|
||||
|
||||
⚠️ **A control that does not execute is not a control.** An earlier version
|
||||
returned immediately for a single-channel file, so the mono voice track — one of
|
||||
the four controls — was never actually run through the check it was meant to
|
||||
control. Mono now skips only the duplicate test.
|
||||
|
||||
### 🟡 The monitor-sink route may be fixable after all — retry before rebuilding
|
||||
|
||||
An earlier version of this section said the route *"cannot be fixed by
|
||||
configuration"*. **Withdrawn.** That inferred from the holes that the guest runs
|
||||
below real time, without testing the alternative: **the client buffer is simply
|
||||
tiny.** Xenia asks SDL for 256 samples — **5.33 ms** at 6 ch — against a stock
|
||||
`daemon.conf` with no fragment tuning.
|
||||
|
||||
| client buffer | silence | gaps/s |
|
||||
|---|---|---|
|
||||
| Xenia default (~5.3 ms) | 39.3 % | 30.5 |
|
||||
| `PULSE_LATENCY_MSEC=200` | **15.6 %** | 3.5 |
|
||||
| `PULSE_LATENCY_MSEC=500` | 50.1 % | 1.3 |
|
||||
|
||||
⚠️ Not clean, and not like-for-like — 88 s against 347 s, and the short run covers
|
||||
the splash logos where silence is real. But **the capture route deserves a retry
|
||||
at ~200 ms before anyone spends a session on a Canary rebuild.**
|
||||
|
||||
### The tap, if configuration is not enough
|
||||
|
||||
`parec` reads a monitor that advances at wall-clock rate and substitutes silence,
|
||||
so **every moment the emulator runs below real time is a hole**, and the timebase
|
||||
|
||||
@@ -38,8 +38,15 @@ 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
|
||||
|
||||
# ⚠️ MONO SKIPS THE DUPLICATE TEST AND STILL GETS THE STARVATION ONE. An earlier
|
||||
# version returned immediately for a single channel, so the mono voice track --
|
||||
# one of this tool's four controls -- was never actually run through the check it
|
||||
# was supposed to control. A control that does not execute is not a control.
|
||||
dupes=0
|
||||
if [ "$ch" -lt 2 ]; then
|
||||
echo " single channel -- no duplicate test, starvation still checked"
|
||||
else
|
||||
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
|
||||
@@ -57,7 +64,6 @@ for i in $(seq 0 $((ch-1))); do
|
||||
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
|
||||
@@ -66,6 +72,7 @@ for i in $(seq 0 $((ch-1))); do
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
# STARVATION: the second way a capture looks perfect and carries nothing.
|
||||
#
|
||||
@@ -147,11 +154,47 @@ print(" all-channel silence %.1f%%, %d gap(s) over 1 ms (%.1f/s), median gap %.
|
||||
# length separates them. The RATE does, by an order of magnitude in both
|
||||
# directions, and 20/s sits between with a 1.6x margin below the bad case and
|
||||
# 6x above the worst good one.
|
||||
if rate_per_s >= 20.0 and med < 50.0:
|
||||
print(" 🔴 STARVED: %.1f gaps per second at a median of %.1f ms."
|
||||
% (rate_per_s, med))
|
||||
print(" The producer was not keeping the sink fed.")
|
||||
# TWO NUMBERS, BECAUSE ONE CANNOT SEE THE FAILURE NEXT DOOR.
|
||||
#
|
||||
# The first version of this tested the gap RATE alone, at 20/s. The Decoder then
|
||||
# measured what a LARGER client buffer does, and the relationship is not
|
||||
# monotonic: raising `PULSE_LATENCY_MSEC` keeps cutting the rate while total
|
||||
# silence bottoms out and then doubles, because an over-large buffer starves in a
|
||||
# few enormous holes instead of many small ones. Its 500 ms capture scores
|
||||
# **1.3 gaps/s -- better than a genuine music bed at 3.3 -- while being 50 %
|
||||
# silence**, and my bar passed it. Reproduced here on a file I hold: `bigholes`,
|
||||
# a real bed with 350 ms holes punched in, is 46.3 % silence at 3.2 gaps/s.
|
||||
#
|
||||
# That is the same shape as the level table that could not see a duplicated
|
||||
# channel. One number, blind to the neighbouring failure.
|
||||
#
|
||||
# Controls, all four measured here:
|
||||
#
|
||||
# real music bed 1.1 % silence, 3.3 gaps/s PASS
|
||||
# voice track, mono 53.3 % silence, 0.3 gaps/s PASS (real pauses)
|
||||
# bed with big holes 46.3 % silence, 3.2 gaps/s FAIL
|
||||
# the starved capture 35.6 % silence, 30.9 gaps/s FAIL
|
||||
#
|
||||
# Rate alone cannot separate rows 2 and 3; silence alone cannot separate rows 1
|
||||
# and 3, nor 2 and 3. The pair does.
|
||||
if tot / float(n) >= 0.10 and rate_per_s >= 1.0:
|
||||
print(" 🔴 STARVED: %.1f%% of the file is silent on every channel, in %.1f gaps"
|
||||
% (100.0 * tot / n, rate_per_s))
|
||||
print(" per second (median %.1f ms). Real audio is either mostly not" % med)
|
||||
print(" silent, or silent in a few long stretches -- not both at once.")
|
||||
raise SystemExit(3)
|
||||
|
||||
# ⚠️ THE REGIME THIS TOOL CANNOT JUDGE, said out loud rather than passed
|
||||
# silently. High silence with FEW gaps is what a real voice track looks like
|
||||
# (53.3 % in 0.3 gaps/s) and also what an over-buffered capture looks like. No
|
||||
# statistic here separates them, and inventing a bar for a regime I have no
|
||||
# control in is how the last two bars in this file came to be wrong.
|
||||
if tot / float(n) >= 0.10:
|
||||
print(" ⚠️ %.1f%% silent in only %.1f gaps/s -- UNJUDGED. That is the shape of"
|
||||
% (100.0 * tot / n, rate_per_s))
|
||||
print(" a real voice track AND of an over-buffered capture, and this tool")
|
||||
print(" cannot tell them apart. Check it against a known source before")
|
||||
print(" concluding anything from it.")
|
||||
PYEOF
|
||||
starved=$?
|
||||
set -e
|
||||
|
||||
Reference in New Issue
Block a user