// celt_anti_collapse.mbt
//
// 反塌缩处理(RFC 6716 §4.3.5 Anti-collapse Processing)。
//
// 瞬态帧的多个短 MDCT 可能让某些带在某些短块上能量归零,产生刺耳
// 伪影。解码端在瞬态且预算够时读一个反塌缩位;置位则对每个「塌缩」
// 的短块注入伪随机信号,幅度由该带两帧历史能量的较小者导出,注入后
// 对带区间重新归一化以保持能量守恒(§4.3.5 的两句要求)。
//
// 对应参考实现 celt_bands.c 的 anti_collapse(浮点路径)与
// celt_vq.c 的 renormalise_vector:
//
// - 能量域与 §4.3.2 状态一致(log2 幅度),Ediff = logE −
// min(prev1, prev2) 下限 0;单声道解码还要与 2*nbEBands 布局里
// 本带的声道 1 槽位取 max(参考实现 `!encode && C==1` 分支);
// - 注入幅度 r = min(thresh, 2·2^(−Ediff)·[LM=3 ×√2]) / √(N0<> LM / −8) 按 1/8 bit 深度衰减;
// - 塌缩判定读 collapse mask 的 bit k(短块序号),噪声符号来自
// celt_lcg_rand 的 bit15,整段按 uint32 环绕;
// - 频谱布局与 MDCT 一致:带 i 的带内下标是 (j< Int64 {
(seed * 1664525L + 1013904223L) & 0xFFFFFFFFL
}
///|
/// 带区间重新归一化(celt_vq.c 的 renormalise_vector,浮点路径):
/// `x *= gain / sqrt(EPSILON + Σx²)`,就地修改 `x[off.. Unit {
let mut e = 0.000000000000001
for j in 0..rng 的拷贝)。
pub fn celt_anti_collapse(
x : Array[Double],
collapse_masks : Array[Int],
lm : Int,
start : Int,
end : Int,
band_log_e : Array[Double],
prev1_log_e : Array[Double],
prev2_log_e : Array[Double],
pulses : Array[Int],
seed : Int64,
) -> Unit {
let m = 1 << lm
let nb = CEL_NB_EBANDS
let mut rng = seed
for i in start..> lm
let thresh = 0.5 *
@math.exp(0.6931471805599453094 * (-0.125 * depth.to_double()))
let sqrt_1 = 1.0 / @math.pow((n0 << lm).to_double(), 0.5)
// 单声道解码:prev 与 2*nbEBands 布局的声道 1 槽位取 max
let mut prev1 = prev1_log_e[i]
let mut prev2 = prev2_log_e[i]
if prev1_log_e[nb + i] > prev1 {
prev1 = prev1_log_e[nb + i]
}
if prev2_log_e[nb + i] > prev2 {
prev2 = prev2_log_e[nb + i]
}
let mut ediff = band_log_e[i] - (if prev1 < prev2 { prev1 } else { prev2 })
// 下限 0 照搬参考实现(其定点路径用它防 exp2 上溢)。浮点路径里这
// 一步不可观测:ediff≤0 时 r≥2 而 thresh≤0.5,随后 min 恒取 thresh,
// 截不截断结果相同——故变异验证不覆盖此行(等效变异)。
if ediff < 0.0 {
ediff = 0.0
}
let mut r = 2.0 * @math.exp(0.6931471805599453094 * -ediff)
if lm == 3 {
r = r * 1.41421356
}
if thresh < r {
r = thresh
}
r = r * sqrt_1
let base = celt_eband(i, m)
let mut renormalize = false
for k in 0..> k) & 1) == 0 {
for j in 0.. Int {
if is_transient && lm >= 2 && bits >= (lm + 2) << CEL_BITRES {
1 << CEL_BITRES
} else {
0
}
}
///|
/// 反塌缩标志位解码(celt_decoder.c:预留了才读,读 1 个 raw bit)。
pub fn celt_decode_anti_collapse_flag(dec : RangeDecoder, rsv : Int) -> Int {
if rsv > 0 {
dec.dec_bits(1)
} else {
0
}
}