// celt_alloc.mbt
//
// CELT 比特分配(RFC 6716 §4.3.3)的解码路径。
//
// 规范要求分配必须 bit-exact:分配偏一拍,后面所有带的频谱解码都会错位。
// 三段结构与参考实现 rate.c 的 clt_compute_allocation /
// interp_bits2pulses 一一对应:
//
// 1. 算每带的阈值与 trim 倾斜,在 11 条静态分配向量上二分;
// 2. 在 6 步分辨率上二次二分插值出每带位数,再从比特流读 skip 标志
// 逐带回收位数(**这一步会读范围解码器**,故本函数必须带 dec);
// 3. 逐带重平衡,得出细能量位 ebits 与收尾优先级 fine_priority,剩下的
// 位数给 PVQ。
//
// 单声道(C=1)路径:intensity / dual_stereo / LOG2_FRAC_TABLE 都只在
// 立体声分支用到,编码端专用的 prev 与 signalBandwidth 也不参与解码。
///|
/// 位分辨率:范围解码器按 1/8 bit 计位(celt/entcode.h 的 BITRES)。
pub const CEL_BITRES : Int = 3
///|
/// 第二次二分的步数(rate.c 的 ALLOC_STEPS)。
const CEL_ALLOC_STEPS : Int = 6
///|
/// 细能量偏移量(celt/rate.h 的 FINE_OFFSET)。
const CEL_FINE_OFFSET : Int = 21
///|
/// 静态分配向量的条数(模式结构体的 nbAllocVectors,对应 Table 57 的 11 列)。
const CEL_NB_ALLOC_VECTORS : Int = 11
///|
/// 分配结果。字段含义与参考实现的同名形参一致。
pub struct CeltAllocation {
/// 每带交给 PVQ 的位数,单位 1/8 bit(对应参考实现的 pulses/bits)
pulses : Array[Int]
/// 每带的细能量位数(0..CEL_MAX_FINE_BITS)
ebits : Array[Int]
/// 每带是否进入收尾细能量轮(1 = 优先)
fine_priority : Array[Int]
/// 实际编码的带数,其后的带不编码
coded_bands : Int
/// 重平衡后剩下的位数,单位 1/8 bit(交由 quant_all_bands 再平衡)
balance : Int
}
///|
/// 本帧可用于分配的位数,单位 1/8 bit。
///
/// `frame_bytes` 是本帧载荷字节数;`< Int {
frame_bytes * 8 * (1 << CEL_BITRES) - dec.tell_frac() - 1
}
///|
/// 无符号 32 位除法(参考实现的 celt_udiv)。
///
/// 余量分配处的 `left` 在规范外情况下可能为负;参考实现把它按 opus_uint32
/// 重解释后再除,这里照搬同一位型语义以保证两端一致。对非负输入它就是
/// 普通整除。结果按 32 位回写成 Int,对应 C 把 uint32 存回 opus_int32。
fn celt_udiv(n : Int, d : Int) -> Int {
let un = n.to_int64() & 0xFFFFFFFFL
let q = un / (d.to_int64() & 0xFFFFFFFFL)
let lo = q & 0xFFFFFFFFL
(if lo >= 0x80000000L { lo - 0x100000000L } else { lo }).to_int()
}
///|
/// §4.3.3 第二段:二次二分插值、skip 决策、余量分摊与逐带重平衡。
///
/// 入参 `pulses` 是输出兼中间量(参考实现的 `bits`),返回
/// `(codedBands, balance)`。跳过决策会从 `dec` 读 1 个二元符号。
fn celt_interp_bits2pulses(
dec : RangeDecoder,
start : Int,
end : Int,
skip_start : Int,
bits1 : Array[Int],
bits2 : Array[Int],
thresh : Array[Int],
cap : Array[Int],
pulses : Array[Int],
ebits : Array[Int],
fine_priority : Array[Int],
total : Int,
skip_rsv : Int,
lm : Int,
) -> (Int, Int) {
let m = 1 << lm
let mut total = total // 带回分配点时要把预留的 skip 位还回去
let alloc_floor = 1 << CEL_BITRES // 单声道:C << BITRES
let log_m = lm << CEL_BITRES
let floor_gate = alloc_floor + (1 << CEL_BITRES)
// ---- 二次二分:在 bits1..bits2 之间找不超过 total 的插值点 ----
let mut lo = 0
let mut hi = 1 << CEL_ALLOC_STEPS
for _ in 0..> 1
let mut psum = 0
let mut done = false
let mut j = end - 1
while j >= start {
let tmp = bits1[j] + ((mid * bits2[j]) >> CEL_ALLOC_STEPS)
if tmp >= thresh[j] || done {
done = true
psum += if tmp < cap[j] { tmp } else { cap[j] }
} else if tmp >= alloc_floor {
psum += alloc_floor
}
j -= 1
}
if psum > total {
hi = mid
} else {
lo = mid
}
}
// 用选定的插值点落出每带位数
let mut psum = 0
let mut done = false
let mut j = end - 1
while j >= start {
let mut tmp = bits1[j] + ((lo * bits2[j]) >> CEL_ALLOC_STEPS)
if tmp < thresh[j] && !done {
tmp = if tmp >= alloc_floor { alloc_floor } else { 0 }
} else {
done = true
}
tmp = if tmp < cap[j] { tmp } else { cap[j] }
pulses[j] = tmp
psum += tmp
j -= 1
}
// ---- 从尾部逐带决定跳过 ----
// 跳过决策本身要占 1 bit,所以只有位数够的带才有资格被跳(否则是被
// 强制跳过的,不花位)。循环到 skip_start 为止;读到 1 就停止跳过。
let mut coded_bands = end
let mut keep = false
while !keep && coded_bands > skip_start + 1 {
let jb = coded_bands - 1
let span = celt_eband(coded_bands, m) - celt_eband(start, m)
let mut left = total - psum
let percoeff = celt_udiv(left, span)
left -= span * percoeff
let used = celt_eband(jb, m) - celt_eband(start, m)
let rem = if left - used > 0 { left - used } else { 0 }
let band_width = celt_eband(coded_bands, m) - celt_eband(jb, m)
let mut band_bits = pulses[jb] + percoeff * band_width + rem
let gate = if thresh[jb] > floor_gate { thresh[jb] } else { floor_gate }
if band_bits >= gate {
// 解码路径:读到 1 表示这个带不跳过
if dec.decode_bit_logp(1) != 0 {
keep = true
} else {
psum += 1 << CEL_BITRES
band_bits -= 1 << CEL_BITRES
}
}
if !keep {
// 回收这个带原本分到的位数(单声道 intensity_rsv 恒为 0)
psum -= pulses[jb]
if band_bits >= alloc_floor {
// 至少够每声道一个细能量位,就用它
psum += alloc_floor
pulses[jb] = alloc_floor
} else {
pulses[jb] = 0
}
coded_bands -= 1
}
}
if !keep {
// 走到 skip_start 了:「跳带到此结束」的信号位用不着,还回去
total += skip_rsv
}
// ---- 余量按带宽分摊,不够整份的逐带补齐 ----
let mut left = total - psum
let span = celt_eband(coded_bands, m) - celt_eband(start, m)
let percoeff = celt_udiv(left, span)
left -= span * percoeff
for jb in start.. 1 {
excess = if bit > cap[jb] { bit - cap[jb] } else { 0 }
pulses[jb] = bit - excess
let den = n // 单声道 C=1,故 den = C*N
let nclogn = den * (cel_log_n[jb] + log_m)
// 相比「公平份额」的偏移:log2(N)/2 + FINE_OFFSET
let mut offset = (nclogn >> 1) - den * CEL_FINE_OFFSET
// N=2 是曲线上唯一不吻合的点
if n == 2 {
offset += den << CEL_BITRES >> 2
}
if pulses[jb] + offset < (den * 2) << CEL_BITRES {
offset += nclogn >> 2
} else if pulses[jb] + offset < (den * 3) << CEL_BITRES {
offset += nclogn >> 3
}
// 带舍入的除法:先加 den<<(BITRES-1) 再除
let mut fine = pulses[jb] + offset + (den << (CEL_BITRES - 1))
if fine < 0 {
fine = 0
}
fine = celt_udiv(fine, den) >> CEL_BITRES
// 别超过这个带实际有的位数(单声道 stereo=0)
if fine > pulses[jb] >> CEL_BITRES {
fine = pulses[jb] >> CEL_BITRES
}
// 参考实现此处还有 `fine > MAX_FINE_BITS` 的封顶。单声道解码路径
// 下它不可达:重平衡保证 pulses[jb] <= cap[jb],而对全部 (LM, 带)
// 组合按此约束算出的 fine 最大为 8。故不搬这段不会改变结果,也免得
// 留下一段永远测不到的分支。
ebits[jb] = fine
// 舍入向下或被封顶的带,进收尾细能量轮
fine_priority[jb] = if fine * (den << CEL_BITRES) >= pulses[jb] + offset {
1
} else {
0
}
// 细能量的位数先扣掉,剩下的归 PVQ
pulses[jb] -= fine << CEL_BITRES
} else {
// N=1:除了一个符号位,其余全给细能量
excess = if bit > 1 << CEL_BITRES { bit - (1 << CEL_BITRES) } else { 0 }
pulses[jb] = bit - excess
ebits[jb] = 0
fine_priority[jb] = 1
}
// 超出 cap 的部分改投细能量(参考实现注释:细能量吃不到
// quant_all_bands 的再平衡,所以在这儿补)
if excess > 0 {
let mut extra_fine = excess >> CEL_BITRES // 单声道 stereo=0
let room = CEL_MAX_FINE_BITS - ebits[jb]
if extra_fine > room {
extra_fine = room
}
ebits[jb] += extra_fine
let extra_bits = extra_fine << CEL_BITRES
fine_priority[jb] = if extra_bits >= excess - balance { 1 } else { 0 }
excess -= extra_bits
}
balance = excess
jb += 1
}
// 被跳过的带:位数全给细能量
while jb < end {
ebits[jb] = pulses[jb] >> CEL_BITRES
pulses[jb] = 0
fine_priority[jb] = if ebits[jb] < 1 { 1 } else { 0 }
jb += 1
}
(coded_bands, balance)
}
///|
/// 比特分配(§4.3.3):恢复本帧每带的 PVQ 位数、细能量位与收尾优先级。
///
/// `offsets` 是 dynalloc 的加成(单位 1/8 bit,长度 21),`cap` 由
/// `celt_init_caps` 给出,`alloc_trim` 是解出的 0..10(§4.3.3:5 表示不加
/// 不减),`total` 用 `celt_alloc_total` 算(单位 1/8 bit)。范围解码器会被
/// 推进——skip 决策要读符号,顺序与帧内其它符号一致。
pub fn celt_compute_allocation(
dec : RangeDecoder,
start : Int,
end : Int,
offsets : Array[Int],
cap : Array[Int],
alloc_trim : Int,
total : Int,
lm : Int,
) -> CeltAllocation {
let m = 1 << lm
let len = CEL_NB_EBANDS
let pulses = Array::make(len, 0)
let ebits = Array::make(len, 0)
let fine_priority = Array::make(len, 0)
let thresh = Array::make(len, 0)
let trim_offset = Array::make(len, 0)
let bits1 = Array::make(len, 0)
let bits2 = Array::make(len, 0)
let mut total = if total > 0 { total } else { 0 }
let mut skip_start = start
// 预留 1 bit 表示「手动跳带到此结束」
let skip_rsv = if total >= 1 << CEL_BITRES { 1 << CEL_BITRES } else { 0 }
total -= skip_rsv
// 单声道不需要 intensity / dual_stereo 的预留位
for jb in start..> 4
thresh[jb] = if floor_bits > scaled { floor_bits } else { scaled }
// 分配曲线的倾斜
trim_offset[jb] = (
n0 * (alloc_trim - 5 - lm) * (end - jb - 1) * (1 << (lm + CEL_BITRES))
) >>
6
// 单系数带给的分辨率低一些:它们一个粗值就够
if n0 << lm == 1 {
trim_offset[jb] -= 1 << CEL_BITRES
}
}
// ---- 第一次二分:在 11 条静态分配向量上找不超过 total 的那条 ----
// do-while:先算后判,故用 cont 标志复刻「至少跑一轮」。
let mut lo = 1
let mut hi = CEL_NB_ALLOC_VECTORS - 1
let mut cont = true
while cont {
let mid = (lo + hi) >> 1
let mut psum = 0
let mut done = false
let mut jb = end - 1
while jb >= start {
let n0 = celt_eband(jb + 1, m) - celt_eband(jb, m)
let mut bitsj = (n0 * cel_alloc_vectors[mid * len + jb]) << lm >> 2
if bitsj > 0 {
bitsj = if bitsj + trim_offset[jb] > 0 {
bitsj + trim_offset[jb]
} else {
0
}
}
bitsj += offsets[jb]
if bitsj >= thresh[jb] || done {
done = true
psum += if bitsj < cap[jb] { bitsj } else { cap[jb] }
} else if bitsj >= 1 << CEL_BITRES {
psum += 1 << CEL_BITRES
}
jb -= 1
}
if psum > total {
hi = mid - 1
} else {
lo = mid + 1
}
cont = lo <= hi
}
// 退出时 lo 是刚越过的那条,故有效插值区间是 [lo-1, lo]
let lo_edge = lo - 1
let hi_edge = lo
for jb in start..> 2
let mut b2 = if hi_edge >= CEL_NB_ALLOC_VECTORS {
cap[jb]
} else {
(n0 * cel_alloc_vectors[hi_edge * len + jb]) << lm >> 2
}
if b1 > 0 {
b1 = if b1 + trim_offset[jb] > 0 { b1 + trim_offset[jb] } else { 0 }
}
if b2 > 0 {
b2 = if b2 + trim_offset[jb] > 0 { b2 + trim_offset[jb] } else { 0 }
}
if lo_edge > 0 {
b1 += offsets[jb]
}
b2 += offsets[jb]
// 被 dynalloc 加过成的带不能被手动跳过
if offsets[jb] > 0 {
skip_start = jb
}
b2 = if b2 - b1 > 0 { b2 - b1 } else { 0 }
bits1[jb] = b1
bits2[jb] = b2
}
let cb = celt_interp_bits2pulses(
dec, start, end, skip_start, bits1, bits2, thresh, cap, pulses, ebits, fine_priority,
total, skip_rsv, lm,
)
CeltAllocation::{
pulses,
ebits,
fine_priority,
coded_bands: cb.0,
balance: cb.1,
}
}