F5: document the limiter's rate-leakage caveat in PLAN §3.1
Codex's review of the v0 design correctly pointed out that PLAN's
"hard −0.1 dBTP ceiling" claim only holds at the filter's output,
not at the speaker, when the real sink runs at a non-48 kHz native
rate. PipeWire inserts a resampler at the
`filter.playback → real-sink` edge, and any polynomial /
windowed-sinc reconstruction can elevate inter-sample peaks
slightly through its own math. The elevation is small in practice
(a few tenths of a dB for a clean band-limited resampler) and the
contract still holds where headroom is in control of the graph,
but the README and §3 had been silent on the leak.
This commit only edits docs:
- §3.1 grows a "Contract scope (caveat)" paragraph next to the
"Never bypassed, never disabled" hard-tier description, naming
the leak, its magnitude, and the fix-for-real (filter rate
matching).
- §11 picks up a new tracked follow-up entry alongside the
other dormant items. Scope: dynamic `FILTER_SAMPLE_RATE`,
kernel rebuild on real-sink change, Layer A's
block-period constant goes dynamic too. Gated on a multi-rate
hardware test bench — no point shipping the refactor without
something to validate it against. **v1 scope.**
No code changes; no tests; clippy and tests are unaffected.
This commit is contained in:
parent
04a005e1cd
commit
5143c07c99
1 changed files with 25 additions and 0 deletions
25
PLAN.md
25
PLAN.md
|
|
@ -233,6 +233,23 @@ downsampling) guarantee the contract numerically — the envelope can
|
|||
misbehave and the contract still holds. Never bypassed, never
|
||||
disabled.
|
||||
|
||||
**Contract scope (caveat).** The ≤ −0.1 dBTP guarantee holds at the
|
||||
*filter's output*, not at the speaker. The bus filter is hardcoded
|
||||
F32 stereo @ 48 kHz (`headroom-dsp::limiter`'s 4× oversampler is
|
||||
sized for 48 k); when the real sink negotiates a different rate
|
||||
(44.1 kHz, 96 kHz, 192 kHz), PipeWire inserts a downstream
|
||||
resampler between `filter.playback` and the sink. Polynomial /
|
||||
windowed-sinc resamplers can elevate inter-sample peaks slightly
|
||||
through their own reconstruction, so the limiter's true-peak
|
||||
guarantee leaks across that resampling stage. In practice the
|
||||
elevation is small (a few tenths of a dB worst case for a clean
|
||||
band-limited resampler), and the contract still holds at the bus
|
||||
output where headroom is in control. **For the contract to hold
|
||||
end-to-end the filter would need to match the real sink's rate
|
||||
and rebuild its DSP coefficients on rate-change** — that's the
|
||||
v1 work tracked as PLAN §11 "filter rate matching" (deferred from
|
||||
8d, gated on a multi-rate hardware test bench).
|
||||
|
||||
**Soft tier — the comfort cap.** Targets a *dynamic* ceiling computed
|
||||
as `program_lufs + max_psr_db`. Smooth attack/release envelope so the
|
||||
gain reduction sounds like volume riding, not a slap. Pulls transients
|
||||
|
|
@ -903,6 +920,14 @@ lost. Pick up by name when the trigger that gates them fires.
|
|||
for simplicity. Revisit if real users ask for it; the store-level
|
||||
change is a flag on the setter methods. **Dormant** — no user has
|
||||
asked through Phase 8.
|
||||
- **Filter rate matching to the real sink.** *(F5 follow-up.)* §3.1
|
||||
documents the contract leak when the real sink runs at a
|
||||
non-48 kHz native rate. Closing it requires dynamic
|
||||
`FILTER_SAMPLE_RATE`, kernel rebuild on real-sink change
|
||||
(compressor + limiter coefficients are rate-dependent), and
|
||||
Layer A's `LAYER_A_BLOCK_DT_S` constant becoming dynamic too.
|
||||
Gated on a multi-rate hardware test bench — no point shipping
|
||||
the refactor without something to validate it against. **v1 scope.**
|
||||
- ~~**Filter playback BUSY spikes (periodic, ~10 s cadence).**~~
|
||||
**Closed in 8e (`d52cd6d`).** The instrumentation added by 8e
|
||||
did not reproduce the ~8×-baseline outlier pattern in a ~3 min
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue