// celt_alloc_flags.mbt
//
// 比特分配之前的两个标志(RFC 6716 §4.3.3):band boost(dynalloc)与
// allocation trim。它们的产出正是 celt_compute_allocation 的两个输入
// (offsets 与 alloc_trim),所以在帧解码里紧接在 tf / spread 之后、
// 分配之前。
//
// 符号顺序:带内 boost 是一串极低概率的二元符号——首带首次 boost 要 6 bit,
// 之后每次降到 1 bit,同时把该带的初始成本往下压(最低 2 bit);trim 是
// 11 个符号的 icdf,默认 5 表示不加不减,且受门控(预算不够就不读)。
//
// 循环条件按参考实现取**递减后的** total_bits。RFC 正文的字面是
// 「total_bits + total_boost」,因每次 boost 同时让 total_bits 减 quanta、
// total_boost 加 quanta,那个和恒等于帧长——两种写法不同。此处跟参考实现
// 一致(真实比特流由参考实现的编码器产生,解码必须与之对齐);trim 的
// 门控两种理解下等价,因为门控用的正是这个递减后的值。
///|
/// 分配相关的两个标志。
pub struct CeltAllocFlags {
/// 每带的 boost 加成,单位 1/8 bit(长度 21,窗外为 0)
offsets : Array[Int]
/// 分配倾斜,0..10;5 表示不加不减,<5 偏低频、>5 偏高频
alloc_trim : Int
}
///|
/// 解 band boost 与 allocation trim。
///
/// `cap` 由 `celt_init_caps` 给出,`frame_bytes` 是本帧载荷字节数(决定
/// 预算与门控),`lm` 是帧长索引(决定每带的 boost 步长)。
/// 范围解码器会被推进,顺序与帧内其它符号一致。
pub fn celt_decode_alloc_flags(
dec : RangeDecoder,
start : Int,
end : Int,
cap : Array[Int],
frame_bytes : Int,
lm : Int,
) -> CeltAllocFlags {
let offsets = Array::make(CEL_NB_EBANDS, 0)
// 预算与门控都按 1/8 bit 计;循环里每 boost 一次就扣掉 quanta
let mut total_bits = frame_bytes * 8 * (1 << CEL_BITRES)
let mut tell = dec.tell_frac()
let mut dynalloc_logp = 6
for i in start.. width { 48 } else { width }
let wide = width << CEL_BITRES
let quanta = if wide < lo { wide } else { lo }
let mut loop_logp = dynalloc_logp
let mut boost = 0
let mut again = true
while again &&
tell + (loop_logp << CEL_BITRES) < total_bits &&
boost < cap[i] {
let flag = dec.decode_bit_logp(loop_logp)
tell = dec.tell_frac()
if flag == 0 {
again = false
} else {
boost += quanta
total_bits -= quanta
// 首次 boost 之后,本带后续的 boost 只花 1 bit
loop_logp = 1
}
}
offsets[i] = boost
// 动过 boost 的带,下一个带的初始成本降一档(最低 2 bit)
if boost > 0 && dynalloc_logp > 2 {
dynalloc_logp -= 1
}
}
// §4.3.3:预算不够就把 trim 留在默认值 5,不读符号
let mut alloc_trim = 5
if tell + (6 << CEL_BITRES) <= total_bits {
alloc_trim = dec.decode_icdf(cel_trim_icdf, 7)
}
CeltAllocFlags::{ offsets, alloc_trim, }
}