use crate::ics_info::WindowSequence;
use crate::{Error, Result};
pub const AOT_AAC_MAIN: u8 = 1;
pub const AOT_AAC_LC: u8 = 2;
pub const AOT_AAC_SSR: u8 = 3;
pub const AOT_AAC_LTP: u8 = 4;
pub const AOT_ER_AAC_LD: u8 = 23;
const FS_INDEX_FIRST_LE_32K: u8 = 5;
const TNS_MAX_BANDS_LONG_NON_PQF: [u8; 12] = [31, 31, 34, 40, 42, 51, 46, 46, 42, 42, 42, 39];
const TNS_MAX_BANDS_SHORT_NON_PQF: [u8; 12] = [9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14];
const TNS_MAX_BANDS_LONG_PQF: [u8; 12] = [28, 28, 27, 26, 26, 26, 29, 29, 23, 23, 23, 19];
const TNS_MAX_BANDS_SHORT_PQF: [u8; 12] = [7, 7, 7, 6, 6, 6, 7, 7, 8, 8, 8, 7];
pub const TNS_MAX_BANDS_LD_480: [Option<u8>; 12] = [
None, None, None, Some(31), Some(32), Some(37), Some(30), Some(30), None, None, None, None, ];
pub const TNS_MAX_BANDS_LD_512: [Option<u8>; 12] = [
None, None, None, Some(31), Some(32), Some(37), Some(31), Some(31), None, None, None, None, ];
pub fn tns_max_order(aot: u8, window_sequence: WindowSequence, fs_index: u8) -> Result<u8> {
if fs_index >= 13 {
return Err(Error::IcsInfoUnsupportedSampleRateIndex(fs_index));
}
if window_sequence.is_eight_short() {
return Ok(7);
}
let above_32k = fs_index < FS_INDEX_FIRST_LE_32K;
Ok(match aot {
AOT_AAC_MAIN => 20,
AOT_AAC_LC => 12,
AOT_AAC_SSR => 12,
_ => {
if above_32k {
20
} else {
12
}
}
})
}
pub fn tns_max_bands(aot: u8, window_sequence: WindowSequence, fs_index: u8) -> Result<u8> {
let idx = fs_index as usize;
if idx >= TNS_MAX_BANDS_LONG_NON_PQF.len() {
return Err(Error::IcsInfoUnsupportedSampleRateIndex(fs_index));
}
let table = match (aot, window_sequence.is_eight_short()) {
(AOT_AAC_SSR, false) => &TNS_MAX_BANDS_LONG_PQF,
(AOT_AAC_SSR, true) => &TNS_MAX_BANDS_SHORT_PQF,
(_, false) => &TNS_MAX_BANDS_LONG_NON_PQF,
(_, true) => &TNS_MAX_BANDS_SHORT_NON_PQF,
};
Ok(table[idx])
}
pub fn tns_max_bands_family(
family: crate::swb_offset::FrameFamily,
aot: u8,
window_sequence: WindowSequence,
fs_index: u8,
) -> Result<u8> {
use crate::swb_offset::FrameFamily;
match family {
FrameFamily::Lc1024 | FrameFamily::Lc960 => tns_max_bands(aot, window_sequence, fs_index),
FrameFamily::Ld512 | FrameFamily::Ld480 => {
if window_sequence.is_eight_short() {
return Err(Error::LdShortWindow);
}
let slot = match fs_index {
0..=3 => 3,
4..=7 => fs_index,
8..=11 => 7,
other => return Err(Error::IcsInfoUnsupportedSampleRateIndex(other)),
};
if family == FrameFamily::Ld512 {
tns_max_bands_ld_512(slot)
} else {
tns_max_bands_ld_480(slot)
}
}
}
}
pub fn tns_max_bands_ld_480(fs_index: u8) -> Result<u8> {
let idx = fs_index as usize;
if idx >= TNS_MAX_BANDS_LD_480.len() {
return Err(Error::IcsInfoUnsupportedSampleRateIndex(fs_index));
}
TNS_MAX_BANDS_LD_480[idx].ok_or(Error::IcsInfoUnsupportedSampleRateIndex(fs_index))
}
pub fn tns_max_bands_ld_512(fs_index: u8) -> Result<u8> {
let idx = fs_index as usize;
if idx >= TNS_MAX_BANDS_LD_512.len() {
return Err(Error::IcsInfoUnsupportedSampleRateIndex(fs_index));
}
TNS_MAX_BANDS_LD_512[idx].ok_or(Error::IcsInfoUnsupportedSampleRateIndex(fs_index))
}
pub fn clamp_tns_order(
order: u8,
aot: u8,
window_sequence: WindowSequence,
fs_index: u8,
) -> Result<u8> {
let cap = tns_max_order(aot, window_sequence, fs_index)?;
Ok(order.min(cap))
}
pub fn clamp_tns_band(
band: u8,
max_sfb: u8,
aot: u8,
window_sequence: WindowSequence,
fs_index: u8,
) -> Result<u8> {
let cap = tns_max_bands(aot, window_sequence, fs_index)?;
Ok(band.min(cap).min(max_sfb))
}
pub fn clamp_tns_band_family(
band: u8,
max_sfb: u8,
family: crate::swb_offset::FrameFamily,
aot: u8,
window_sequence: WindowSequence,
fs_index: u8,
) -> Result<u8> {
let cap = tns_max_bands_family(family, aot, window_sequence, fs_index)?;
Ok(band.min(cap).min(max_sfb))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::WindowSequence;
#[test]
fn order_short_window_is_7_for_every_aot() {
for aot in [AOT_AAC_MAIN, AOT_AAC_LC, AOT_AAC_SSR, AOT_AAC_LTP, 17] {
for fs in 0..=12_u8 {
assert_eq!(
tns_max_order(aot, WindowSequence::EightShort, fs).unwrap(),
7,
"AOT {aot} fs {fs} short windows",
);
}
}
}
#[test]
fn order_aac_main_long_window_is_20_for_all_rates() {
for fs in 0..=12_u8 {
for ws in [
WindowSequence::OnlyLong,
WindowSequence::LongStart,
WindowSequence::LongStop,
] {
assert_eq!(tns_max_order(AOT_AAC_MAIN, ws, fs).unwrap(), 20);
}
}
}
#[test]
fn order_aac_lc_long_window_is_12_for_all_rates() {
for fs in 0..=12_u8 {
assert_eq!(
tns_max_order(AOT_AAC_LC, WindowSequence::OnlyLong, fs).unwrap(),
12,
);
}
}
#[test]
fn order_aac_ssr_long_window_is_12_for_all_rates() {
for fs in 0..=12_u8 {
assert_eq!(
tns_max_order(AOT_AAC_SSR, WindowSequence::OnlyLong, fs).unwrap(),
12,
);
}
}
#[test]
fn order_other_aot_long_window_splits_at_32k_threshold() {
for aot in [AOT_AAC_LTP, 17, 19, 20, 21, 22, 23] {
for fs in 0..=4_u8 {
assert_eq!(
tns_max_order(aot, WindowSequence::OnlyLong, fs).unwrap(),
20,
"AOT {aot} fs {fs} long > 32 kHz",
);
}
for fs in 5..=12_u8 {
assert_eq!(
tns_max_order(aot, WindowSequence::OnlyLong, fs).unwrap(),
12,
"AOT {aot} fs {fs} long <= 32 kHz",
);
}
}
}
#[test]
fn order_rejects_out_of_range_fs_index() {
assert!(matches!(
tns_max_order(AOT_AAC_LC, WindowSequence::OnlyLong, 13),
Err(Error::IcsInfoUnsupportedSampleRateIndex(13))
));
assert!(matches!(
tns_max_order(AOT_AAC_LC, WindowSequence::OnlyLong, 15),
Err(Error::IcsInfoUnsupportedSampleRateIndex(15))
));
}
#[test]
fn bands_long_non_pqf_matches_table_row_by_row() {
let expected: [(u8, u8); 12] = [
(0, 31), (1, 31), (2, 34), (3, 40), (4, 42), (5, 51), (6, 46), (7, 46), (8, 42), (9, 42), (10, 42), (11, 39), ];
for (fs, expected_bands) in expected {
assert_eq!(
tns_max_bands(AOT_AAC_LC, WindowSequence::OnlyLong, fs).unwrap(),
expected_bands,
"fs {fs} long non-PQF",
);
}
}
#[test]
fn bands_short_non_pqf_matches_table_row_by_row() {
let expected: [(u8, u8); 12] = [
(0, 9),
(1, 9),
(2, 10),
(3, 14),
(4, 14),
(5, 14),
(6, 14),
(7, 14),
(8, 14),
(9, 14),
(10, 14),
(11, 14),
];
for (fs, expected_bands) in expected {
assert_eq!(
tns_max_bands(AOT_AAC_LC, WindowSequence::EightShort, fs).unwrap(),
expected_bands,
"fs {fs} short non-PQF",
);
}
}
#[test]
fn bands_long_pqf_aac_ssr_matches_table_row_by_row() {
let expected: [(u8, u8); 12] = [
(0, 28),
(1, 28),
(2, 27),
(3, 26),
(4, 26),
(5, 26),
(6, 29),
(7, 29),
(8, 23),
(9, 23),
(10, 23),
(11, 19),
];
for (fs, expected_bands) in expected {
assert_eq!(
tns_max_bands(AOT_AAC_SSR, WindowSequence::OnlyLong, fs).unwrap(),
expected_bands,
"fs {fs} long PQF",
);
}
}
#[test]
fn bands_short_pqf_aac_ssr_matches_table_row_by_row() {
let expected: [(u8, u8); 12] = [
(0, 7),
(1, 7),
(2, 7),
(3, 6),
(4, 6),
(5, 6),
(6, 7),
(7, 7),
(8, 8),
(9, 8),
(10, 8),
(11, 7),
];
for (fs, expected_bands) in expected {
assert_eq!(
tns_max_bands(AOT_AAC_SSR, WindowSequence::EightShort, fs).unwrap(),
expected_bands,
"fs {fs} short PQF",
);
}
}
#[test]
fn bands_dispatches_long_start_and_stop_to_long_column() {
for ws in [WindowSequence::LongStart, WindowSequence::LongStop] {
assert_eq!(tns_max_bands(AOT_AAC_LC, ws, 4).unwrap(), 42);
assert_eq!(tns_max_bands(AOT_AAC_SSR, ws, 4).unwrap(), 26);
}
}
#[test]
fn bands_rejects_fs_12_and_above() {
assert!(matches!(
tns_max_bands(AOT_AAC_LC, WindowSequence::OnlyLong, 12),
Err(Error::IcsInfoUnsupportedSampleRateIndex(12))
));
assert!(matches!(
tns_max_bands(AOT_AAC_LC, WindowSequence::OnlyLong, 13),
Err(Error::IcsInfoUnsupportedSampleRateIndex(13))
));
assert!(matches!(
tns_max_bands(AOT_AAC_LC, WindowSequence::OnlyLong, 15),
Err(Error::IcsInfoUnsupportedSampleRateIndex(15))
));
}
#[test]
fn bands_aot_other_treats_as_non_pqf() {
for aot in [AOT_AAC_LTP, 17, 19, 20, 21, 22, 23] {
assert_eq!(
tns_max_bands(aot, WindowSequence::OnlyLong, 4).unwrap(),
42,
"AOT {aot} long non-PQF",
);
assert_eq!(
tns_max_bands(aot, WindowSequence::EightShort, 4).unwrap(),
14,
"AOT {aot} short non-PQF",
);
}
}
#[test]
fn ld_480_matches_table_4_119_row_by_row() {
assert_eq!(tns_max_bands_ld_480(3).unwrap(), 31); assert_eq!(tns_max_bands_ld_480(4).unwrap(), 32); assert_eq!(tns_max_bands_ld_480(5).unwrap(), 37); assert_eq!(tns_max_bands_ld_480(6).unwrap(), 30); assert_eq!(tns_max_bands_ld_480(7).unwrap(), 30); }
#[test]
fn ld_512_matches_table_4_120_row_by_row() {
assert_eq!(tns_max_bands_ld_512(3).unwrap(), 31); assert_eq!(tns_max_bands_ld_512(4).unwrap(), 32); assert_eq!(tns_max_bands_ld_512(5).unwrap(), 37); assert_eq!(tns_max_bands_ld_512(6).unwrap(), 31); assert_eq!(tns_max_bands_ld_512(7).unwrap(), 31); }
#[test]
fn ld_480_rejects_uncovered_rates() {
for fs in [0_u8, 1, 2, 8, 9, 10, 11] {
assert!(matches!(
tns_max_bands_ld_480(fs),
Err(Error::IcsInfoUnsupportedSampleRateIndex(_))
));
}
}
#[test]
fn ld_512_rejects_uncovered_rates() {
for fs in [0_u8, 1, 2, 8, 9, 10, 11] {
assert!(matches!(
tns_max_bands_ld_512(fs),
Err(Error::IcsInfoUnsupportedSampleRateIndex(_))
));
}
}
#[test]
fn ld_accessors_reject_out_of_range_fs_index() {
assert!(matches!(
tns_max_bands_ld_480(12),
Err(Error::IcsInfoUnsupportedSampleRateIndex(12))
));
assert!(matches!(
tns_max_bands_ld_480(15),
Err(Error::IcsInfoUnsupportedSampleRateIndex(15))
));
assert!(matches!(
tns_max_bands_ld_512(13),
Err(Error::IcsInfoUnsupportedSampleRateIndex(13))
));
}
#[test]
fn clamp_order_floors_to_cap() {
assert_eq!(
clamp_tns_order(20, AOT_AAC_LC, WindowSequence::OnlyLong, 3).unwrap(),
12,
);
assert_eq!(
clamp_tns_order(5, AOT_AAC_LC, WindowSequence::OnlyLong, 3).unwrap(),
5,
);
assert_eq!(
clamp_tns_order(12, AOT_AAC_LC, WindowSequence::OnlyLong, 3).unwrap(),
12,
);
assert_eq!(
clamp_tns_order(31, AOT_AAC_MAIN, WindowSequence::EightShort, 3).unwrap(),
7,
);
}
#[test]
fn clamp_order_propagates_fs_error() {
assert!(matches!(
clamp_tns_order(5, AOT_AAC_LC, WindowSequence::OnlyLong, 13),
Err(Error::IcsInfoUnsupportedSampleRateIndex(13))
));
}
#[test]
fn clamp_band_takes_three_way_min() {
assert_eq!(
clamp_tns_band(50, 49, AOT_AAC_LC, WindowSequence::OnlyLong, 4).unwrap(),
42,
);
assert_eq!(
clamp_tns_band(50, 30, AOT_AAC_LC, WindowSequence::OnlyLong, 4).unwrap(),
30,
);
assert_eq!(
clamp_tns_band(10, 49, AOT_AAC_LC, WindowSequence::OnlyLong, 4).unwrap(),
10,
);
}
#[test]
fn clamp_band_propagates_fs_error() {
assert!(matches!(
clamp_tns_band(5, 49, AOT_AAC_LC, WindowSequence::OnlyLong, 12),
Err(Error::IcsInfoUnsupportedSampleRateIndex(12))
));
}
#[test]
fn every_non_ld_table_has_12_entries() {
assert_eq!(TNS_MAX_BANDS_LONG_NON_PQF.len(), 12);
assert_eq!(TNS_MAX_BANDS_SHORT_NON_PQF.len(), 12);
assert_eq!(TNS_MAX_BANDS_LONG_PQF.len(), 12);
assert_eq!(TNS_MAX_BANDS_SHORT_PQF.len(), 12);
}
#[test]
fn every_ld_table_has_12_entries() {
assert_eq!(TNS_MAX_BANDS_LD_480.len(), 12);
assert_eq!(TNS_MAX_BANDS_LD_512.len(), 12);
}
}