Skip to content

pns: fix decoding window offset clamping for short sequences - #237

Merged
fabiangreffrath merged 1 commit into
knik0:masterfrom
nschimme:fix-pns-noise-dropout
Aug 27, 2026
Merged

pns: fix decoding window offset clamping for short sequences#237
fabiangreffrath merged 1 commit into
knik0:masterfrom
nschimme:fix-pns-noise-dropout

Conversation

@nschimme

Copy link
Copy Markdown
Contributor

Fixes a bug in pns_decode() where Perceptual Noise Substitution (PNS) band bounds (begin/end) were incorrectly clamped against ics->swb_offset_max instead of the full buffer size (frame_len).

For EIGHT_SHORT_SEQUENCE frames, swb_offset_max spans only a single short window (frame_len / 8). As a result, base = group * nshort evaluated to >= swb_offset_max for every window group after the first. Clamping against swb_offset_max caused min(base + swb_offset[sfb], swb_offset_max) to collapse to swb_offset_max, resulting in begin == end and silently skipping PNS noise generation across 7 of the 8 window groups in short frames.

Fix: Clamp band offsets against frame_len (already passed into pns_decode()) to reflect the actual bounds of the spectral array.

Fixes #191


Verification & Benchmark Results

Tested on a low-bitrate FAAC-encoded AAC-LC stream (32kbps, PNS enabled, using short-window block switching):

Metric faad2 (before) faad2 (after) ffmpeg (ref) Apple afconvert (ref)
>8kHz Energy Ratio vs. Source 1.52x 3.57x 3.57x 3.48x
Zimtohrli MOS 4.0887 4.5640 4.5519 4.5644
  • Decoder parity: After the fix, high-frequency energy alignment and perceptual quality (MOS) match reference decoders (ffmpeg and Apple afconvert).
  • Control check: Disabling PNS (--pns 0) yields identical PCM output between faad2 and ffmpeg.
  • Regressions: Gapless sample counting across the regression test suite remains unaffected.

Test Plan

  • Build succeeds clean with CMake/Ninja.
  • Verified high-frequency noise spectral energy matches reference implementations.
  • Verified Zimtohrli MOS improvement on PNS-heavy short-window test streams.
  • Verified existing regression suite passes without sample count drift.

@nschimme nschimme changed the title pns: Fix decoding window offset clamping for short sequences pns: fix decoding window offset clamping for short sequences Aug 27, 2026
In pns_decode(), band offsets (begin/end) were clamped against
ics->swb_offset_max. For EIGHT_SHORT_SEQUENCE frames, swb_offset_max
represents the offset limit of a single short window (frame_len / 8),
rather than the entire spectral buffer.

Because `base = group * nshort` exceeds swb_offset_max for all window
groups past the first, clamping against swb_offset_max collapsed
(base + swb_offset[sfb]) to swb_offset_max regardless of sfb. This
resulted in `begin == end`, silently dropping PNS noise generation
for 7 out of 8 window groups in short-window frames.

Fix this by clamping band offsets against frame_len instead, matching
the actual memory bound of the target spectrum buffer.
@nschimme
nschimme force-pushed the fix-pns-noise-dropout branch from 631c903 to dade5fc Compare August 27, 2026 03:35
@nschimme nschimme mentioned this pull request Aug 27, 2026
@fabiangreffrath
fabiangreffrath merged commit 6e4e056 into knik0:master Aug 27, 2026
5 of 6 checks passed
@nschimme
nschimme deleted the fix-pns-noise-dropout branch August 27, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Values begin, end variables in pns.c may not be correct in the case of short window sequences

2 participants