use crate::celt_alloc_trim::{ALLOC_TRIM_MAX, ALLOC_TRIM_MIN};
use crate::celt_band_layout::{celt_band_bins_per_channel, CeltFrameSize, CELT_NUM_BANDS};
pub const TRIM_OFFSETS_BIAS: i32 = 5;
pub const TRIM_OFFSETS_NUMERATOR_SCALE: i32 = 8;
pub const TRIM_OFFSETS_DIVISOR: i32 = 64;
pub const TRIM_OFFSETS_WIDTH_ONE_BINS_PER_CHANNEL: u16 = 1;
pub const TRIM_OFFSETS_WIDTH_ONE_PER_CHANNEL_EIGHTH_BITS: i32 = 8;
pub const TRIM_OFFSETS_MONO_CHANNELS: i32 = 1;
pub const TRIM_OFFSETS_STEREO_CHANNELS: i32 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrimOffsetError {
AllocTrimOutOfRange {
provided: u8,
max: u8,
},
BandOutOfRange {
band: usize,
},
}
pub const fn shortest_frame_size() -> CeltFrameSize {
CeltFrameSize::Ms2_5
}
pub fn band_n_shortest(band: usize) -> Option<u16> {
celt_band_bins_per_channel(band, shortest_frame_size())
}
pub fn band_trim_offset(
alloc_trim: u8,
lm: CeltFrameSize,
is_stereo: bool,
n_shortest: u16,
n_per_channel: u16,
remaining_bands: u32,
) -> Result<i32, TrimOffsetError> {
if alloc_trim > ALLOC_TRIM_MAX {
return Err(TrimOffsetError::AllocTrimOutOfRange {
provided: alloc_trim,
max: ALLOC_TRIM_MAX,
});
}
const _: () = assert!(ALLOC_TRIM_MIN == 0);
let lm_idx = lm.column_index() as i32;
let channels = if is_stereo {
TRIM_OFFSETS_STEREO_CHANNELS
} else {
TRIM_OFFSETS_MONO_CHANNELS
};
let trim_term = (alloc_trim as i32) - TRIM_OFFSETS_BIAS - lm_idx;
let two_pow_lm = 1i32 << lm_idx;
let numerator = trim_term
* channels
* (n_shortest as i32)
* (remaining_bands as i32)
* two_pow_lm
* TRIM_OFFSETS_NUMERATOR_SCALE;
let base = numerator / TRIM_OFFSETS_DIVISOR;
let offset = if n_per_channel == TRIM_OFFSETS_WIDTH_ONE_BINS_PER_CHANNEL {
base - TRIM_OFFSETS_WIDTH_ONE_PER_CHANNEL_EIGHTH_BITS * channels
} else {
base
};
Ok(offset)
}
pub fn band_trim_offset_for_band(
band: usize,
alloc_trim: u8,
frame_size: CeltFrameSize,
is_stereo: bool,
remaining_bands: u32,
) -> Result<i32, TrimOffsetError> {
if band >= CELT_NUM_BANDS {
return Err(TrimOffsetError::BandOutOfRange { band });
}
let n_shortest = band_n_shortest(band).expect("§4.3 band < CELT_NUM_BANDS by guard above");
let n_per_channel =
celt_band_bins_per_channel(band, frame_size).expect("§4.3 band < CELT_NUM_BANDS");
band_trim_offset(
alloc_trim,
frame_size,
is_stereo,
n_shortest,
n_per_channel,
remaining_bands,
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::celt_alloc_trim::ALLOC_TRIM_DEFAULT;
#[test]
fn bias_matches_rfc_subtract_5() {
assert_eq!(TRIM_OFFSETS_BIAS, 5);
}
#[test]
fn bias_equals_alloc_trim_default() {
assert_eq!(TRIM_OFFSETS_BIAS as u8, ALLOC_TRIM_DEFAULT);
}
#[test]
fn numerator_scale_matches_rfc_8() {
assert_eq!(TRIM_OFFSETS_NUMERATOR_SCALE, 8);
}
#[test]
fn divisor_matches_rfc_64() {
assert_eq!(TRIM_OFFSETS_DIVISOR, 64);
}
#[test]
fn net_scale_keeps_eighth_bit_units() {
assert_eq!(
TRIM_OFFSETS_NUMERATOR_SCALE * 8,
TRIM_OFFSETS_DIVISOR,
"8/64 = 1/8 keeps Q3 units",
);
}
#[test]
fn width_one_trigger_matches_rfc_one_bin() {
assert_eq!(TRIM_OFFSETS_WIDTH_ONE_BINS_PER_CHANNEL, 1);
}
#[test]
fn width_one_per_channel_subtraction_is_one_whole_bit() {
assert_eq!(TRIM_OFFSETS_WIDTH_ONE_PER_CHANNEL_EIGHTH_BITS, 8);
assert_eq!(TRIM_OFFSETS_WIDTH_ONE_PER_CHANNEL_EIGHTH_BITS / 8, 1);
}
#[test]
fn channel_multipliers_match_audio_layout() {
assert_eq!(TRIM_OFFSETS_MONO_CHANNELS, 1);
assert_eq!(TRIM_OFFSETS_STEREO_CHANNELS, 2);
}
#[test]
fn shortest_frame_size_is_2_5_ms() {
assert_eq!(shortest_frame_size(), CeltFrameSize::Ms2_5);
assert_eq!(shortest_frame_size().column_index(), 0);
}
#[test]
fn n_shortest_band0_is_one() {
assert_eq!(band_n_shortest(0), Some(1));
}
#[test]
fn n_shortest_band20_is_twenty_two() {
assert_eq!(band_n_shortest(20), Some(22));
}
#[test]
fn n_shortest_band21_returns_none() {
assert_eq!(band_n_shortest(21), None);
assert_eq!(band_n_shortest(100), None);
}
#[test]
fn n_shortest_matches_table55_column0_for_every_band() {
for band in 0..CELT_NUM_BANDS {
let want = celt_band_bins_per_channel(band, CeltFrameSize::Ms2_5).unwrap();
assert_eq!(band_n_shortest(band).unwrap(), want, "band={band}");
}
}
#[test]
fn default_trim_lm0_no_width1_yields_zero() {
let v = band_trim_offset(
5,
CeltFrameSize::Ms2_5,
false,
1,
2,
21,
)
.unwrap();
assert_eq!(v, 0);
}
#[test]
fn default_trim_lm0_width1_mono_subtracts_eight() {
let v = band_trim_offset(5, CeltFrameSize::Ms2_5, false, 1, 1, 21).unwrap();
assert_eq!(v, -8);
}
#[test]
fn default_trim_lm0_width1_stereo_subtracts_sixteen() {
let v = band_trim_offset(5, CeltFrameSize::Ms2_5, true, 1, 1, 21).unwrap();
assert_eq!(v, -16);
}
#[test]
fn high_trim_lm0_positive_base_mono() {
let v = band_trim_offset(10, CeltFrameSize::Ms2_5, false, 1, 2, 1).unwrap();
assert_eq!(v, 0);
}
#[test]
fn high_trim_lm0_positive_base_large_factors() {
let v = band_trim_offset(10, CeltFrameSize::Ms2_5, true, 22, 22, 21).unwrap();
assert_eq!(v, 577);
}
#[test]
fn low_trim_negative_base_mono() {
let v = band_trim_offset(0, CeltFrameSize::Ms20, false, 22, 22, 21).unwrap();
assert_eq!(v, -3_696);
}
#[test]
fn low_trim_negative_base_stereo_width1() {
let v = band_trim_offset(0, CeltFrameSize::Ms20, true, 1, 1, 21).unwrap();
assert_eq!(v, -352);
}
#[test]
fn lm_factor_doubles_with_each_increment() {
let v0 = band_trim_offset(10, CeltFrameSize::Ms2_5, false, 16, 16, 4).unwrap();
let v1 = band_trim_offset(10, CeltFrameSize::Ms5, false, 16, 16, 4).unwrap();
let v2 = band_trim_offset(10, CeltFrameSize::Ms10, false, 16, 16, 4).unwrap();
let v3 = band_trim_offset(10, CeltFrameSize::Ms20, false, 16, 16, 4).unwrap();
assert_eq!(v0, 40);
assert_eq!(v1, 64);
assert_eq!(v2, 96);
assert_eq!(v3, 128);
}
#[test]
fn channel_factor_scales_linearly_when_no_width1() {
let mono = band_trim_offset(10, CeltFrameSize::Ms2_5, false, 16, 16, 4).unwrap();
let stereo = band_trim_offset(10, CeltFrameSize::Ms2_5, true, 16, 16, 4).unwrap();
assert_eq!(stereo, 2 * mono);
}
#[test]
fn n_shortest_factor_scales_linearly() {
let v_n16 = band_trim_offset(10, CeltFrameSize::Ms20, false, 16, 32, 1).unwrap();
let v_n8 = band_trim_offset(10, CeltFrameSize::Ms20, false, 8, 32, 1).unwrap();
assert_eq!(v_n16, 32);
assert_eq!(v_n8, 16);
assert_eq!(v_n16, 2 * v_n8);
}
#[test]
fn remaining_bands_factor_scales_linearly() {
let v_r4 = band_trim_offset(10, CeltFrameSize::Ms20, false, 16, 32, 4).unwrap();
let v_r2 = band_trim_offset(10, CeltFrameSize::Ms20, false, 16, 32, 2).unwrap();
assert_eq!(v_r4, 128);
assert_eq!(v_r2, 64);
assert_eq!(v_r4, 2 * v_r2);
}
#[test]
fn zero_remaining_bands_yields_just_width_correction() {
let v_no_w1 = band_trim_offset(10, CeltFrameSize::Ms2_5, true, 22, 22, 0).unwrap();
assert_eq!(v_no_w1, 0);
let v_w1 = band_trim_offset(10, CeltFrameSize::Ms2_5, true, 22, 1, 0).unwrap();
assert_eq!(v_w1, -16);
}
#[test]
fn trim_minus_lm_zero_zeros_kernel() {
for n_shortest in [1u16, 4, 16, 22] {
for remaining in [1u32, 4, 21] {
for stereo in [false, true] {
let v = band_trim_offset(
5,
CeltFrameSize::Ms2_5,
stereo,
n_shortest,
n_shortest, remaining,
)
.unwrap();
if n_shortest == 1 {
let expected = if stereo { -16 } else { -8 };
assert_eq!(v, expected, "n_shortest=1 width-1 active");
} else {
assert_eq!(
v, 0,
"n_shortest={n_shortest} remaining={remaining} stereo={stereo}"
);
}
}
}
}
}
#[test]
fn lm_eq_trim_minus_5_kernel_collapses_at_high_trims() {
let v = band_trim_offset(8, CeltFrameSize::Ms20, true, 16, 16, 8).unwrap();
assert_eq!(v, 0);
}
#[test]
fn width_one_subtraction_is_additive_to_base() {
let base_no_w1 = band_trim_offset(10, CeltFrameSize::Ms20, true, 8, 8, 4).unwrap();
let with_w1 = band_trim_offset(10, CeltFrameSize::Ms20, true, 8, 1, 4).unwrap();
assert_eq!(with_w1, base_no_w1 - 16);
}
#[test]
fn width_one_only_triggers_at_per_channel_eq_one() {
for n_per_channel in [0u16, 2, 3, 22, 176] {
let v = band_trim_offset(10, CeltFrameSize::Ms20, true, 8, n_per_channel, 4).unwrap();
assert_eq!(v, 128, "n_per_channel={n_per_channel} should not trigger");
}
let triggered = band_trim_offset(10, CeltFrameSize::Ms20, true, 8, 1, 4).unwrap();
assert_eq!(triggered, 128 - 16);
}
#[test]
fn negative_division_truncates_toward_zero() {
let v_exact = band_trim_offset(0, CeltFrameSize::Ms20, false, 1, 2, 1).unwrap();
assert_eq!(v_exact, -8);
let v_small = band_trim_offset(4, CeltFrameSize::Ms2_5, false, 1, 2, 1).unwrap();
assert_eq!(v_small, 0);
let v_truncating = band_trim_offset(3, CeltFrameSize::Ms2_5, false, 5, 2, 1).unwrap();
assert_eq!(v_truncating, -1);
}
#[test]
fn alloc_trim_above_max_rejected() {
let err = band_trim_offset(11, CeltFrameSize::Ms20, false, 16, 16, 4).unwrap_err();
assert_eq!(
err,
TrimOffsetError::AllocTrimOutOfRange {
provided: 11,
max: ALLOC_TRIM_MAX,
}
);
}
#[test]
fn alloc_trim_far_above_max_rejected() {
let err = band_trim_offset(255, CeltFrameSize::Ms20, false, 16, 16, 4).unwrap_err();
assert_eq!(
err,
TrimOffsetError::AllocTrimOutOfRange {
provided: 255,
max: ALLOC_TRIM_MAX,
}
);
}
#[test]
fn alloc_trim_zero_accepted() {
band_trim_offset(0, CeltFrameSize::Ms20, false, 16, 16, 4).unwrap();
}
#[test]
fn alloc_trim_max_accepted() {
band_trim_offset(ALLOC_TRIM_MAX, CeltFrameSize::Ms20, false, 16, 16, 4).unwrap();
}
#[test]
fn for_band_rejects_band_at_or_above_celt_num_bands() {
let err = band_trim_offset_for_band(21, 5, CeltFrameSize::Ms20, false, 21).unwrap_err();
assert_eq!(err, TrimOffsetError::BandOutOfRange { band: 21 });
let err = band_trim_offset_for_band(100, 5, CeltFrameSize::Ms20, false, 21).unwrap_err();
assert_eq!(err, TrimOffsetError::BandOutOfRange { band: 100 });
}
#[test]
fn for_band_matches_explicit_inputs() {
for band in 0..CELT_NUM_BANDS {
for frame_size in [
CeltFrameSize::Ms2_5,
CeltFrameSize::Ms5,
CeltFrameSize::Ms10,
CeltFrameSize::Ms20,
] {
for is_stereo in [false, true] {
let n_shortest = band_n_shortest(band).unwrap();
let n_per_channel = celt_band_bins_per_channel(band, frame_size).unwrap();
let want =
band_trim_offset(5, frame_size, is_stereo, n_shortest, n_per_channel, 7)
.unwrap();
let got = band_trim_offset_for_band(band, 5, frame_size, is_stereo, 7).unwrap();
assert_eq!(
got, want,
"band={band} frame_size={frame_size:?} stereo={is_stereo}"
);
}
}
}
}
#[test]
fn for_band_propagates_alloc_trim_error() {
let err = band_trim_offset_for_band(0, 11, CeltFrameSize::Ms20, false, 21).unwrap_err();
assert_eq!(
err,
TrimOffsetError::AllocTrimOutOfRange {
provided: 11,
max: ALLOC_TRIM_MAX,
}
);
}
#[test]
fn for_band_width1_triggers_when_table55_says_so() {
let mono = band_trim_offset_for_band(0, 5, CeltFrameSize::Ms2_5, false, 21).unwrap();
assert_eq!(mono, -8);
let stereo = band_trim_offset_for_band(0, 5, CeltFrameSize::Ms2_5, true, 21).unwrap();
assert_eq!(stereo, -16);
}
#[test]
fn for_band_no_width1_at_higher_bands() {
let v = band_trim_offset_for_band(20, 5, CeltFrameSize::Ms20, false, 21).unwrap();
assert_eq!(v, -1_386);
}
#[test]
fn determinism_across_repeats() {
for trim in [0u8, 3, 5, 7, 10] {
for fs in [
CeltFrameSize::Ms2_5,
CeltFrameSize::Ms5,
CeltFrameSize::Ms10,
CeltFrameSize::Ms20,
] {
for band in 0..CELT_NUM_BANDS {
for stereo in [false, true] {
for remaining in [0u32, 1, 4, 21] {
let a = band_trim_offset_for_band(band, trim, fs, stereo, remaining)
.unwrap();
let b = band_trim_offset_for_band(band, trim, fs, stereo, remaining)
.unwrap();
assert_eq!(a, b);
}
}
}
}
}
}
#[test]
fn output_fits_well_within_i32_for_full_legal_input_space() {
for trim in [0u8, ALLOC_TRIM_MAX] {
for fs in [
CeltFrameSize::Ms2_5,
CeltFrameSize::Ms5,
CeltFrameSize::Ms10,
CeltFrameSize::Ms20,
] {
for band in 0..CELT_NUM_BANDS {
for stereo in [false, true] {
for remaining in [0u32, 21] {
let v = band_trim_offset_for_band(band, trim, fs, stereo, remaining)
.unwrap();
assert!(v.abs() < 20_000, "v={v} band={band}");
}
}
}
}
}
}
#[test]
fn debug_format_renders() {
let err = TrimOffsetError::AllocTrimOutOfRange {
provided: 99,
max: 10,
};
let s = format!("{err:?}");
assert!(s.contains("AllocTrimOutOfRange"));
let err = TrimOffsetError::BandOutOfRange { band: 22 };
let s = format!("{err:?}");
assert!(s.contains("BandOutOfRange"));
}
}