// celt_energy.mbt
//
// CELT 能量包络解码(RFC 6716 §4.3.2):粗量化 → 细量化 → 收尾。
//
// 能量以 2 为底的对数表示(粗量化固定 6 dB/步),与参考实现 float 构建
// 的 celt_glog 同域。能量状态跨帧保留,由调用方持有 Array[Double]——
// 布局同参考实现的 oldBandE,单声道只用前 nbEBands 项。
//
// 参考实现对 C==1 另在后半段存一份副本,帧首取两者较大值;那份副本只在
// 丢包容错路径里会与本体分叉,正常解码下两者恒等,故此处只保留一份。
// 丢包容错(PLC、anti-collapse 的能量侧状态)不在本模块范围。
//
// 三步之间的细能量位数与优先级由比特分配(§4.3.3)产出,本模块只消费。
///|
/// 收尾阶段单带最多再补的细能量位数(quant_bands.c 的 MAX_FINE_BITS)。
pub const CEL_MAX_FINE_BITS : Int = 8
///|
/// Laplace 分布中 0 的最小概率(1/32768)与保证可表示的最小幅度。
const CEL_LAPLACE_MINP : Int = 1
///|
const CEL_LAPLACE_LOG_MINP : Int = 0
///|
const CEL_LAPLACE_NMIN : Int = 16
///|
/// §4.3.2.1 Laplace pdf 中幅度 1 的频率(参考实现 laplace.c 同名函数)。
///
/// `ft` = 总量扣除 0 与两侧各 NMIN 个最小概率格后的剩余质量,再按
/// `(16384-decay)>>15` 的 Q15 衰减取其份额。参数域内乘积不超过 2**31。
fn cel_laplace_get_freq1(fs : Int, decay : Int) -> Int {
let ft = 32768 - CEL_LAPLACE_MINP * (2 * CEL_LAPLACE_NMIN) - fs
(ft * (16384 - decay)) >> 15
}
///|
/// 解一个 Laplace 分布的符号增量(§4.3.2.1,参考实现 laplace.c 的
/// ec_laplace_decode)。总量 32768:`fs` 是 0 的频率,`decay` 是衰减率。
///
/// pdf 关于 0 对称,先在 [0,∞) 上按衰减链定位幅度、再由 fm 落在 0 的
/// 正侧还是负侧定符号;状态更新统一放在最后,与参考实现一致——中途
/// 只用 decode_bin_fs 取 fs,不推进解码器。
fn cel_laplace_decode(dec : RangeDecoder, fs : Int, decay : Int) -> Int {
let fm = dec.decode_bin_fs(15)
let mut fs = fs
let mut fl = 0
let mut val = 0
if fm >= fs {
val += 1
fl = fs
fs = cel_laplace_get_freq1(fs, decay) + CEL_LAPLACE_MINP
// 沿衰减链逐级展开:fm 未落进当前格就继续往更大幅度走。
while fs > CEL_LAPLACE_MINP && fm >= fl + 2 * fs {
fs = fs * 2
fl = fl + fs
fs = ((fs - 2 * CEL_LAPLACE_MINP) * decay) >> 15
fs = fs + CEL_LAPLACE_MINP
val += 1
}
// 衰减链耗尽后的尾部:每一格恰好 LAPLACE_MINP 的概率质量。
if fs <= CEL_LAPLACE_MINP {
let di = (fm - fl) >> (CEL_LAPLACE_LOG_MINP + 1)
val += di
fl = fl + 2 * di * CEL_LAPLACE_MINP
}
if fm < fl + fs {
val = -val
} else {
fl = fl + fs
}
}
let fh = if fl + fs < 32768 { fl + fs } else { 32768 }
dec.update(fl, fh, 32768)
val
}
///|
/// §4.3.2.1 粗能量解码:逐带解 Laplace 增量(或位数不足时的降级路径),
/// 再按 2-D 预测滤波器把能量状态推进一帧。
///
/// `old_e` 进入时是上一帧的最终能量,逐带被本帧结果覆盖。带内还能不能
/// 塞下一个 Laplace 符号,由解码器自身的载荷长度决定(参考实现取
/// ec_dec 的 storage × 8);`lm` 是帧长索引(0..3 对应 120/240/480/960)。
///
/// 预测式为 §4.3.2.1 的 (1-alpha·z_l^-1)(1-z_b^-1)/(1-beta·z_b^-1):
/// 时间方向取上帧能量乘 coef(intra 时为 0),频率方向由 prev 累积本帧
/// 已解出的量化误差并按 beta 衰减。浮点构建下 SHL32 是恒等的,故
/// q 就等于解出的整数 qi。
pub fn cel_unquant_coarse_energy(
dec : RangeDecoder,
old_e : Array[Double],
start : Int,
end : Int,
intra : Bool,
lm : Int,
) -> Unit {
// e_prob_model 扁平布局 [lm][intra][42],42 = 2 × 21 带(每带一对参数)
let model = (if intra { lm * 2 + 1 } else { lm * 2 }) * 42
let mut coef = 0.0
let mut beta = CEL_BETA_INTRA
if !intra {
coef = cel_pred_coef[lm]
beta = cel_beta_coef[lm]
}
let budget = dec.nbytes * 8
let mut prev = 0.0
for i in start..= 15 {
// 带号超过 20 后共用最后一对参数(参考实现 IMIN(i, 20))
let pi = 2 * (if i < 20 { i } else { 20 })
cel_laplace_decode(
dec,
cel_e_prob_model[model + pi].to_int() * 128,
cel_e_prob_model[model + pi + 1].to_int() * 64,
)
} else if budget - tell >= 2 {
// 小能量的固定 2 位 icdf,解出后按 zigzag 还原符号
let v = dec.decode_icdf(cel_small_energy_icdf, 2)
(v >> 1) ^ -(v & 1)
} else if budget - tell >= 1 {
-dec.decode_bit_logp(1)
} else {
-1
}
// 一位都不剩:不读比特,直接按 -1 处理
let q = qi.to_double()
let pred_src = if old_e[i] < -9.0 { -9.0 } else { old_e[i] }
let tmp = coef * pred_src + prev + q
old_e[i] = tmp
prev = prev + q - beta * q
}
}
///|
/// §4.3.2.2 细能量解码:把比特分配给出的 `extra_quant[i]` 位裸比特解释为
/// 粗能量的修正量 `(f+1/2)/2**B_i - 1/2`。
///
/// 位数为 0 或剩余比特不够的带直接跳过(判据同参考实现)。QEXT 扩展
/// 的 prev 缩放因子在此恒为 1,故不出现。
pub fn cel_unquant_fine_energy(
dec : RangeDecoder,
old_e : Array[Double],
start : Int,
end : Int,
extra_quant : Array[Int],
) -> Unit {
for i in start.. dec.nbytes * 8 {
continue
}
let f = dec.dec_bits(extra)
// 先算出修正量再累加——与参考实现 `(a += offset)` 同序,
// 浮点下换序会改变末位。
let offset = (f.to_double() + 0.5) / (1 << extra).to_double() - 0.5
old_e[i] = old_e[i] + offset
}
}
///|
/// §4.3.2.2 收尾:把所有标志位解完后剩下的比特,按优先级 0→1 依次给
/// 各带再补 1 位细能量;补不动的位就留着不用。
///
/// `bits_left` 是本帧尚余的比特数(参考实现取 len*8 - ec_tell)。
pub fn cel_unquant_energy_finalise(
dec : RangeDecoder,
old_e : Array[Double],
start : Int,
end : Int,
fine_quant : Array[Int],
fine_priority : Array[Int],
bits_left : Int,
) -> Unit {
let mut left = bits_left
for prio in 0..<2 {
let mut i = start
while i < end && left >= 1 {
if fine_quant[i] >= CEL_MAX_FINE_BITS || fine_priority[i] != prio {
i += 1
continue
}
let q2 = dec.dec_bits(1)
let offset = (q2.to_double() - 0.5) /
(1 << (fine_quant[i] + 1)).to_double()
old_e[i] = old_e[i] + offset
left -= 1
i += 1
}
}
}