use crate::celt_band_layout::CELT_NUM_BANDS;
pub const STATIC_ALLOC_Q_COUNT: usize = 11;
pub const STATIC_ALLOC_Q_MIN: u32 = 0;
pub const STATIC_ALLOC_Q_MAX: u32 = 10;
pub const STATIC_ALLOC_RIGHT_SHIFT: u32 = 2;
pub const STATIC_ALLOC_INTERP_STEPS: u32 = 64;
pub const STATIC_ALLOC_TOTAL_CELLS: usize = CELT_NUM_BANDS * STATIC_ALLOC_Q_COUNT;
#[rustfmt::skip]
pub const STATIC_ALLOC: [[u8; STATIC_ALLOC_Q_COUNT]; CELT_NUM_BANDS] = [
[0, 90, 110, 118, 126, 134, 144, 152, 162, 172, 200],
[0, 80, 100, 110, 119, 127, 137, 145, 155, 165, 200],
[0, 75, 90, 103, 112, 120, 130, 138, 148, 158, 200],
[0, 69, 84, 93, 104, 114, 124, 132, 142, 152, 200],
[0, 63, 78, 86, 95, 103, 113, 123, 133, 143, 200],
[0, 56, 71, 80, 89, 97, 107, 117, 127, 137, 200],
[0, 49, 65, 75, 83, 91, 101, 111, 121, 131, 200],
[0, 40, 58, 70, 78, 85, 95, 105, 115, 125, 200],
[0, 34, 51, 65, 72, 78, 88, 98, 108, 118, 198],
[0, 29, 45, 59, 66, 72, 82, 92, 102, 112, 193],
[0, 20, 39, 53, 60, 66, 76, 86, 96, 106, 188],
[0, 18, 32, 47, 54, 60, 70, 80, 90, 100, 183],
[0, 10, 26, 40, 47, 54, 64, 74, 84, 94, 178],
[0, 0, 20, 31, 39, 47, 57, 67, 77, 87, 173],
[0, 0, 12, 23, 32, 41, 51, 61, 71, 81, 168],
[0, 0, 0, 15, 25, 35, 45, 55, 65, 75, 163],
[0, 0, 0, 4, 17, 29, 39, 49, 59, 69, 158],
[0, 0, 0, 0, 12, 23, 33, 43, 53, 63, 153],
[0, 0, 0, 0, 1, 16, 26, 36, 46, 56, 148],
[0, 0, 0, 0, 0, 10, 15, 20, 30, 45, 129],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 20, 104],
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StaticAllocError {
BandOutOfRange { band: u32 },
QualityOutOfRange { q: u32 },
ChannelsOutOfRange { channels: u32 },
LmOutOfRange { lm: u32 },
}
pub fn static_alloc_cell(band: u32, q: u32) -> Result<u8, StaticAllocError> {
if band >= CELT_NUM_BANDS as u32 {
return Err(StaticAllocError::BandOutOfRange { band });
}
if q >= STATIC_ALLOC_Q_COUNT as u32 {
return Err(StaticAllocError::QualityOutOfRange { q });
}
Ok(STATIC_ALLOC[band as usize][q as usize])
}
pub fn static_alloc_row(
band: u32,
) -> Result<&'static [u8; STATIC_ALLOC_Q_COUNT], StaticAllocError> {
if band >= CELT_NUM_BANDS as u32 {
return Err(StaticAllocError::BandOutOfRange { band });
}
Ok(&STATIC_ALLOC[band as usize])
}
pub fn static_alloc_eighth_bits(
band: u32,
q: u32,
channels: u32,
n_bins: u32,
lm: u32,
) -> Result<u32, StaticAllocError> {
if channels == 0 || channels > 2 {
return Err(StaticAllocError::ChannelsOutOfRange { channels });
}
if lm >= 4 {
return Err(StaticAllocError::LmOutOfRange { lm });
}
let cell = static_alloc_cell(band, q)? as u32;
let scaled = channels * n_bins * cell;
Ok((scaled << lm) >> STATIC_ALLOC_RIGHT_SHIFT)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::celt_band_layout::{celt_band_bins_per_channel, CeltFrameSize};
#[test]
fn table_shape_constants_match_struct() {
assert_eq!(STATIC_ALLOC_Q_COUNT, 11);
assert_eq!(STATIC_ALLOC_Q_MIN, 0);
assert_eq!(STATIC_ALLOC_Q_MAX, 10);
assert_eq!(STATIC_ALLOC_TOTAL_CELLS, 231);
assert_eq!(CELT_NUM_BANDS, 21);
assert_eq!(STATIC_ALLOC.len(), CELT_NUM_BANDS);
for row in STATIC_ALLOC.iter() {
assert_eq!(row.len(), STATIC_ALLOC_Q_COUNT);
}
}
#[test]
fn unit_conversion_constants_match_rfc() {
assert_eq!(STATIC_ALLOC_RIGHT_SHIFT, 2);
assert_eq!(STATIC_ALLOC_INTERP_STEPS, 64);
}
#[test]
fn corner_top_left_is_zero() {
assert_eq!(STATIC_ALLOC[0][0], 0);
assert_eq!(static_alloc_cell(0, 0).unwrap(), 0);
}
#[test]
fn corner_top_right_is_two_hundred() {
assert_eq!(STATIC_ALLOC[0][10], 200);
assert_eq!(static_alloc_cell(0, 10).unwrap(), 200);
}
#[test]
fn corner_bottom_left_is_zero() {
assert_eq!(STATIC_ALLOC[20][0], 0);
assert_eq!(static_alloc_cell(20, 0).unwrap(), 0);
}
#[test]
fn corner_bottom_right_is_one_hundred_four() {
assert_eq!(STATIC_ALLOC[20][10], 104);
assert_eq!(static_alloc_cell(20, 10).unwrap(), 104);
}
#[test]
fn band_0_q_1_is_ninety() {
assert_eq!(STATIC_ALLOC[0][1], 90);
}
#[test]
fn band_8_q_10_is_one_hundred_ninety_eight() {
assert_eq!(STATIC_ALLOC[8][10], 198);
}
#[test]
fn band_13_q_1_is_zero() {
assert_eq!(STATIC_ALLOC[13][1], 0);
assert_eq!(STATIC_ALLOC[13][2], 20);
}
#[test]
fn band_20_low_q_columns_pin_to_one() {
assert_eq!(STATIC_ALLOC[20][5], 1);
assert_eq!(STATIC_ALLOC[20][6], 1);
assert_eq!(STATIC_ALLOC[20][7], 1);
assert_eq!(STATIC_ALLOC[20][8], 1);
assert_eq!(STATIC_ALLOC[20][9], 20);
}
#[test]
fn every_row_is_monotone_non_decreasing_in_q() {
for (band, row) in STATIC_ALLOC.iter().enumerate() {
for w in row.windows(2) {
assert!(
w[0] <= w[1],
"STATIC_ALLOC[{band}] not monotone non-decreasing at pair {w:?}",
);
}
}
}
#[test]
fn column_zero_is_uniformly_zero() {
for (band, row) in STATIC_ALLOC.iter().enumerate() {
assert_eq!(row[0], 0, "STATIC_ALLOC[{band}][0] != 0");
}
}
#[test]
fn column_ten_top_twelve_bands_are_two_hundred() {
for (band, row) in STATIC_ALLOC.iter().enumerate().take(8) {
assert_eq!(
row[10], 200,
"STATIC_ALLOC[{band}][10] expected 200 at saturation"
);
}
assert_eq!(STATIC_ALLOC[8][10], 198);
}
#[test]
fn cell_accessor_matches_array_indexing() {
for band in 0..(CELT_NUM_BANDS as u32) {
for q in 0..(STATIC_ALLOC_Q_COUNT as u32) {
assert_eq!(
static_alloc_cell(band, q).unwrap(),
STATIC_ALLOC[band as usize][q as usize],
);
}
}
}
#[test]
fn row_accessor_borrows_full_row() {
for band in 0..(CELT_NUM_BANDS as u32) {
let row = static_alloc_row(band).unwrap();
assert_eq!(row.len(), STATIC_ALLOC_Q_COUNT);
assert_eq!(row, &STATIC_ALLOC[band as usize]);
}
}
#[test]
fn cell_rejects_band_out_of_range() {
assert_eq!(
static_alloc_cell(21, 0).unwrap_err(),
StaticAllocError::BandOutOfRange { band: 21 },
);
assert_eq!(
static_alloc_cell(u32::MAX, 0).unwrap_err(),
StaticAllocError::BandOutOfRange { band: u32::MAX },
);
}
#[test]
fn cell_rejects_quality_out_of_range() {
assert_eq!(
static_alloc_cell(0, 11).unwrap_err(),
StaticAllocError::QualityOutOfRange { q: 11 },
);
assert_eq!(
static_alloc_cell(0, u32::MAX).unwrap_err(),
StaticAllocError::QualityOutOfRange { q: u32::MAX },
);
}
#[test]
fn row_rejects_band_out_of_range() {
assert_eq!(
static_alloc_row(21).unwrap_err(),
StaticAllocError::BandOutOfRange { band: 21 },
);
}
#[test]
fn unit_conversion_q_zero_is_zero() {
for band in 0..(CELT_NUM_BANDS as u32) {
for &channels in &[1u32, 2] {
for &n_bins in &[1u32, 4, 88, 176] {
for lm in 0..4 {
assert_eq!(
static_alloc_eighth_bits(band, 0, channels, n_bins, lm).unwrap(),
0,
"band {band} q 0 channels {channels} n_bins {n_bins} lm {lm}",
);
}
}
}
}
}
#[test]
fn unit_conversion_worked_example_lm_zero() {
assert_eq!(static_alloc_eighth_bits(0, 1, 1, 1, 0).unwrap(), 22);
assert_eq!(static_alloc_eighth_bits(0, 1, 2, 1, 0).unwrap(), 45);
assert_eq!(static_alloc_eighth_bits(0, 1, 1, 4, 0).unwrap(), 90);
}
#[test]
fn unit_conversion_worked_example_lm_three() {
assert_eq!(static_alloc_eighth_bits(0, 10, 2, 4, 3).unwrap(), 3200);
}
#[test]
fn unit_conversion_lm_shift_doubles_per_step() {
let cell = STATIC_ALLOC[5][5] as u32; let channels = 1u32;
let n_bins = 4u32;
let base = (channels * n_bins * cell) >> 2; for lm in 0..4u32 {
let got = static_alloc_eighth_bits(5, 5, channels, n_bins, lm).unwrap();
assert_eq!(got, base << lm, "LM = {lm} doubling failed");
}
}
#[test]
fn unit_conversion_against_band_layout_widths() {
let n0_lm3 = celt_band_bins_per_channel(0, CeltFrameSize::Ms20).unwrap() as u32;
assert_eq!(n0_lm3, 8);
let got = static_alloc_eighth_bits(0, 5, 2, n0_lm3, 3).unwrap();
assert_eq!(got, 4288);
let n20_lm3 = celt_band_bins_per_channel(20, CeltFrameSize::Ms20).unwrap() as u32;
assert_eq!(n20_lm3, 176);
let got = static_alloc_eighth_bits(20, 9, 1, n20_lm3, 3).unwrap();
assert_eq!(got, 7040);
}
#[test]
fn unit_conversion_rejects_channels_out_of_range() {
assert_eq!(
static_alloc_eighth_bits(0, 1, 0, 1, 0).unwrap_err(),
StaticAllocError::ChannelsOutOfRange { channels: 0 },
);
assert_eq!(
static_alloc_eighth_bits(0, 1, 3, 1, 0).unwrap_err(),
StaticAllocError::ChannelsOutOfRange { channels: 3 },
);
}
#[test]
fn unit_conversion_rejects_lm_out_of_range() {
assert_eq!(
static_alloc_eighth_bits(0, 1, 1, 1, 4).unwrap_err(),
StaticAllocError::LmOutOfRange { lm: 4 },
);
assert_eq!(
static_alloc_eighth_bits(0, 1, 1, 1, u32::MAX).unwrap_err(),
StaticAllocError::LmOutOfRange { lm: u32::MAX },
);
}
#[test]
fn unit_conversion_propagates_band_and_quality_errors() {
assert_eq!(
static_alloc_eighth_bits(21, 0, 1, 1, 0).unwrap_err(),
StaticAllocError::BandOutOfRange { band: 21 },
);
assert_eq!(
static_alloc_eighth_bits(0, 11, 1, 1, 0).unwrap_err(),
StaticAllocError::QualityOutOfRange { q: 11 },
);
}
#[test]
fn low_band_dominates_high_band_at_low_q() {
assert_eq!(STATIC_ALLOC[0][1], 90);
for (band, row) in STATIC_ALLOC.iter().enumerate().skip(13) {
assert_eq!(row[1], 0, "STATIC_ALLOC[{band}][1] expected 0 at low q",);
}
}
#[test]
fn saturation_column_declines_after_band_seven() {
assert_eq!(STATIC_ALLOC[7][10], 200);
assert_eq!(STATIC_ALLOC[8][10], 198);
assert!(
STATIC_ALLOC[8][10] < STATIC_ALLOC[7][10],
"saturation column failed to decline at band 7→8",
);
for band in 8..20 {
assert!(
STATIC_ALLOC[band + 1][10] <= STATIC_ALLOC[band][10],
"saturation column non-monotone at band {} → {}",
band,
band + 1,
);
}
assert_eq!(STATIC_ALLOC[20][10], 104);
}
}