From 1f3be111b8dc0b1d7d05195357df0889b5b507fe Mon Sep 17 00:00:00 2001 From: alehel Date: Fri, 13 Nov 2026 09:56:22 +0100 Subject: [PATCH] craft-audio: drop a short last SBR patch before the patch limit A header whose sixth patch is short (96 kHz, start 3, stop 9) was rejected, though that patch is dropped and five remain. Co-Authored-By: Claude Opus 5.5 --- crates/audio/src/aac/sbr/freq.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/audio/src/aac/sbr/freq.rs b/crates/audio/src/aac/sbr/freq.rs index d12fbdc02..7bb372dc4 100644 --- a/crates/audio/src/aac/sbr/freq.rs +++ b/crates/audio/src/aac/sbr/freq.rs @@ -211,8 +211,9 @@ fn patches(master: &[usize], k0: usize, kx: usize, m: usize, fs: u32) -> Option< let (mut msb, mut usb) = (k0, kx); let mut out: Vec<(usize, usize)> = Vec::new(); // each pass either adds a patch or lowers `msb` to `kx`; a stream that does neither twice - // running cannot finish - for _ in 0..2 * (MAX_PATCHES + 1) { + // running cannot finish. One patch over the limit may still be a short last one that is + // dropped below. + for _ in 0..2 * (MAX_PATCHES + 2) { // the highest master border the patch can reach from below k0 let mut j = k + 1; let (sb, odd) = loop { @@ -227,7 +228,7 @@ fn patches(master: &[usize], k0: usize, kx: usize, m: usize, fs: u32) -> Option< if count > 0 { let start = (k0 - odd).checked_sub(count)?; out.push((start, count)); - if out.len() > MAX_PATCHES { + if out.len() > MAX_PATCHES + 1 { return None; } (usb, msb) = (sb, sb); @@ -241,7 +242,7 @@ fn patches(master: &[usize], k0: usize, kx: usize, m: usize, fs: u32) -> Option< if out.len() > 1 && out.last().is_some_and(|p| p.1 < 3) { out.pop(); } - return (!out.is_empty()).then_some(out); + return (!out.is_empty() && out.len() <= MAX_PATCHES).then_some(out); } } None @@ -350,6 +351,15 @@ mod tests { assert_eq!((t.k0, t.kx, t.m), (32, 32, 32)); } + /// At 96 kHz this header builds patches of 6, 6, 6, 6, 5 and 2 channels: the short sixth is + /// dropped, as any short last patch is, and the five left are within the limit. + #[test] + fn a_short_sixth_patch_is_dropped_before_the_limit() { + let t = FreqTables::new(&header(3, 9, 0, 0, false, 2), 96_000).unwrap(); + assert_eq!((t.k0, t.kx + t.m), (8, 39)); + assert_eq!(t.patches.iter().map(|p| p.1).collect::>(), [6, 6, 6, 6, 5]); + } + /// A typical 44.1 kHz header: start channel 12 + offset, stop near 64, three noise bands. #[test] fn a_44k_header_has_the_expected_layout() {