1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! SBR limiter frequency band table — ISO/IEC 14496-3 §4.6.18.3.2.3 /
//! Figure 4.41.
//!
//! `fTableLim` partitions the SBR range into the bands over which the
//! §4.6.18.7.5 gain limiter averages: either exactly one band
//! (`bs_limiter_bands == 0`) or approximately 1.2 / 2 / 3 bands per
//! octave. The table is a subset of the union of `fTableLow` and the
//! §4.6.18.6 patch borders; the Figure 4.41 walk merges neighbours
//! closer than `0.49 / limBands` octaves, always preferring to keep a
//! patch border over an envelope border (both being patch borders
//! keeps both).
//!
//! ## Provenance
//!
//! The construction is the Figure 4.41 flowchart of the staged spec,
//! with the `limiterBandsPerOctave = {1.2, 2, 3}` selector. No part of
//! this implementation is derived from any external decoder.
use crate::sbr_freq_bands::HiLoTables;
use crate::{Error, Result};
/// §4.6.18.3.2.3 / Figure 4.41 — build `fTableLim`.
///
/// * `bands` — the derived frequency tables (`fTableLow`, `k_x`, `m`).
/// * `patch_borders` — the §4.6.18.6 patch borders
/// ([`crate::sbr_hf_gen::Patches::borders`], starting at `k_x`).
/// * `bs_limiter_bands` — the 2-bit header field (`0..=3`).
///
/// Returns the border vector `fTableLim(0..=NL)`.
pub fn limiter_table(
bands: &HiLoTables,
patch_borders: &[i32],
bs_limiter_bands: u8,
) -> Result<Vec<i32>> {
let f_low = &bands.f_table_low;
if f_low.len() < 2 || bs_limiter_bands > 3 {
return Err(Error::SbrFreqBandInvalid);
}
// bs_limiter_bands == 0: one band over the whole SBR range.
if bs_limiter_bands == 0 {
return Ok(vec![f_low[0], f_low[f_low.len() - 1]]);
}
// limiterBandsPerOctave = {1.2, 2, 3}.
let lim_bands = [1.2f64, 2.0, 3.0][usize::from(bs_limiter_bands - 1)];
// limTable = fTableLow ∪ interior patch borders, sorted.
let num_patches = patch_borders.len().saturating_sub(1);
let mut lim_table: Vec<i32> = f_low.clone();
if num_patches > 1 {
lim_table.extend_from_slice(&patch_borders[1..num_patches]);
}
lim_table.sort_unstable();
// nrLim = NLow + numPatches - 1 (the last index of limTable).
let mut k = 1usize;
while k < lim_table.len() {
if lim_table[k] < 1 || lim_table[k - 1] < 1 {
return Err(Error::SbrFreqBandInvalid);
}
let n_octaves = (f64::from(lim_table[k]) / f64::from(lim_table[k - 1])).log2();
if n_octaves * lim_bands < 0.49 {
if lim_table[k] == lim_table[k - 1] {
// Duplicate border: drop one copy.
lim_table.remove(k);
} else if !patch_borders.contains(&lim_table[k]) {
// The upper border is droppable (an envelope border).
lim_table.remove(k);
} else if !patch_borders.contains(&lim_table[k - 1]) {
// The upper border is a patch border; drop the lower
// envelope border instead.
lim_table.remove(k - 1);
} else {
// Both are patch borders: keep both.
k += 1;
}
} else {
k += 1;
}
}
Ok(lim_table)
}
#[cfg(test)]
mod tests {
use super::*;
fn bands(f_low: Vec<i32>) -> HiLoTables {
let k_x = f_low[0];
let m = f_low[f_low.len() - 1] - k_x;
HiLoTables {
f_table_high: f_low.clone(),
f_table_low: f_low,
f_table_noise: vec![k_x, k_x + m],
m,
k_x,
}
}
/// bs_limiter_bands == 0 → exactly one band over the SBR range.
#[test]
fn zero_limiter_bands_is_one_band() {
let b = bands(vec![8, 12, 16, 20, 24]);
let t = limiter_table(&b, &[8, 16, 24], 0).unwrap();
assert_eq!(t, vec![8, 24]);
}
/// A single patch adds no interior borders: wide envelope bands
/// pass through untouched.
#[test]
fn single_patch_keeps_envelope_borders() {
let b = bands(vec![8, 12, 16, 20, 24]);
let t = limiter_table(&b, &[8, 24], 3).unwrap();
assert_eq!(t, vec![8, 12, 16, 20, 24]);
}
/// A patch border duplicating an envelope border collapses to one
/// entry.
#[test]
fn duplicate_border_removed() {
let b = bands(vec![8, 12, 16, 20, 24]);
// Interior patch border at 16 duplicates fLow's 16.
let t = limiter_table(&b, &[8, 16, 24], 3).unwrap();
assert_eq!(t, vec![8, 12, 16, 20, 24]);
}
/// A close pair drops the envelope border and keeps the patch
/// border.
#[test]
fn close_pair_keeps_patch_border() {
// fLow has 15 next to the interior patch border 16:
// log2(16/15)·3 ≈ 0.28 < 0.49 → merge, dropping 15.
let b = bands(vec![8, 12, 15, 20, 24]);
let t = limiter_table(&b, &[8, 16, 24], 3).unwrap();
assert!(t.contains(&16) && !t.contains(&15), "{t:?}");
// Borders stay sorted, spanning the SBR range.
assert_eq!(t.first(), Some(&8));
assert_eq!(t.last(), Some(&24));
assert!(t.windows(2).all(|w| w[0] < w[1]));
}
/// A close envelope pair (no patch border involved) drops the
/// upper border.
#[test]
fn close_envelope_pair_drops_upper() {
// 20 and 21 are ~0.07 octaves apart → merged; neither is a
// patch border so the upper (21) goes.
let b = bands(vec![8, 14, 20, 21, 28]);
let t = limiter_table(&b, &[8, 28], 2).unwrap();
assert_eq!(t, vec![8, 14, 20, 28]);
}
/// The coarsest per-octave setting (1.2) merges more bands than
/// the finest (3).
#[test]
fn coarser_setting_merges_more() {
let b = bands(vec![8, 9, 10, 12, 14, 17, 20, 24]);
let pb = [8, 24];
let t1 = limiter_table(&b, &pb, 1).unwrap();
let t3 = limiter_table(&b, &pb, 3).unwrap();
assert!(t1.len() <= t3.len(), "{t1:?} vs {t3:?}");
for t in [&t1, &t3] {
assert_eq!(t.first(), Some(&8));
assert_eq!(t.last(), Some(&24));
assert!(t.windows(2).all(|w| w[0] < w[1]));
}
}
}