Files
Cyrene_v2/internal/capability/transcode/amrnb/dsp_gain.go
T
AskaEth 4a2d276881
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(capability): 转码能力包(vendor 纯 Go AMR-NB 解码器 + 黄金向量逐样本对照 + 8 哨兵 + 注册面)
2026-10-05 13:02:28 +08:00

183 lines
5.1 KiB
Go

package amrnb
// Fixed-codebook gain prediction, ported from opencore-amrnb gc_pred.cpp plus
// the 32-bit composite operators it needs (L_Extract, L_Comp, Mpy_32_16 from
// the ETSI basicop set). gc_pred runs the MA energy predictor that yields the
// predicted code gain (gcode0) used by the gain dequantizers; gc_pred_update
// feeds back the quantized energy each subframe. Bit-exact with the reference.
const (
cNPRED = 4
cMEAN_ENER_MR122 = 783741 // Q17
cMIN_ENERGY = -14336 // Q10
cMIN_ENERGY_MR122 = -2381 // Q10
)
// MA prediction coefficients (gc_pred.cpp).
var gcPred = [cNPRED]int16{5571, 4751, 2785, 1556}
var gcPredMR122 = [cNPRED]int16{44, 37, 22, 12}
// L_Extract splits a 32-bit value into a 16-bit high part and a 15-bit low
// part such that L ≈ (hi<<16) + (lo<<1). Mirrors L_Extract.
func L_Extract(L int32) (hi, lo int16) {
hi = int16(L >> 16)
lo = int16((L >> 1) - (int32(hi) << 15))
return hi, lo
}
// L_Comp recombines a (hi,lo) pair back into a 32-bit value. Mirrors L_Comp.
func L_Comp(hi, lo int16) int32 {
return L_mac(L_deposit_h(hi), lo, 1)
}
// mpy32_16 multiplies a 32-bit value (given as hi,lo) by a 16-bit value,
// matching the optimized opencore-amrnb Mpy_32_16 (truncated low-part shift).
func mpy32_16(hi, lo, n int16) int32 {
return L_add(L_mult(hi, n), ((int32(lo)*int32(n))>>15)<<1)
}
// gcPredState is the MA energy-predictor memory (two parallel histories: the
// 20·log10 form for most modes and the log2 form for MR122). Mirrors
// gc_predState.
type gcPredState struct {
pastQuaEn [cNPRED]int16 // Q10, 20*log10(qua_err)
pastQuaEnMR122 [cNPRED]int16 // Q10, log2(qua_err)
}
// reset initializes both predictor histories to their energy floor. Mirrors
// gc_pred_reset.
func (st *gcPredState) reset() {
for i := 0; i < cNPRED; i++ {
st.pastQuaEn[i] = cMIN_ENERGY
st.pastQuaEnMR122[i] = cMIN_ENERGY_MR122
}
}
// update shifts the new quantized energies into the predictor memory. Mirrors
// gc_pred_update.
func (st *gcPredState) update(quaEnerMR122, quaEner int16) {
st.pastQuaEn[3] = st.pastQuaEn[2]
st.pastQuaEnMR122[3] = st.pastQuaEnMR122[2]
st.pastQuaEn[2] = st.pastQuaEn[1]
st.pastQuaEnMR122[2] = st.pastQuaEnMR122[1]
st.pastQuaEn[1] = st.pastQuaEn[0]
st.pastQuaEnMR122[1] = st.pastQuaEnMR122[0]
st.pastQuaEnMR122[0] = quaEnerMR122
st.pastQuaEn[0] = quaEner
}
// gcPredResult carries gc_pred's outputs: the exponent/fraction of the
// predicted gain factor gcode0, and (MR795 only) of the innovation energy.
type gcPredResult struct {
expGcode0, fracGcode0 int16
expEn, fracEn int16
}
// gcPred computes the predicted fixed-codebook gain factor from the innovation
// energy and the predictor memory. Mirrors gc_pred (gc_pred.cpp).
func (st *gcPredState) gcPred(mode int, code []int16) gcPredResult {
var r gcPredResult
var enerCode int32
for i := cL_SUBFR >> 2; i != 0; i-- {
base := (cL_SUBFR>>2 - i) * 4
for k := 0; k < 4; k++ {
tmp := code[base+k]
enerCode += (int32(tmp) * int32(tmp)) >> 3
}
}
enerCode <<= 4
if enerCode>>31 != 0 { // saturation
enerCode = maxInt32
}
if mode == dMR122 {
enerCode = int32(round_(enerCode)) * 26214 << 1
exp, frac := log2(enerCode)
lTemp1 := int32(exp-30) << 16
enerCode = lTemp1 + (int32(frac) << 1)
ener := int32(cMEAN_ENER_MR122)
for i := 0; i < cNPRED; i++ {
lt := (int32(st.pastQuaEnMR122[i]) * int32(gcPredMR122[i])) << 1
ener = L_add(ener, lt)
}
lTemp1 = L_sub(ener, enerCode)
r.expGcode0 = int16(lTemp1 >> 17)
lTemp2 := int32(r.expGcode0) << 15
lTemp1 >>= 2
r.fracGcode0 = int16(lTemp1 - lTemp2)
return r
}
// all modes except 12.2
expCode := norm_l(enerCode)
enerCode = L_shl(enerCode, expCode)
exp, frac := log2Norm(enerCode, expCode)
lTemp2 := (int32(exp) * -24660) << 1
lTmp := (int32(frac) * -24660) >> 15
if lTmp&0x00010000 != 0 { // sign-extend
lTmp |= int32(-0x10000) // 0xffff0000
}
lTmp <<= 1
lTmp = L_add(lTmp, lTemp2)
switch mode {
case dMR102:
lTemp2 = int32(16678) << 7
lTmp = L_add(lTmp, lTemp2)
case dMR795:
r.fracEn = int16(enerCode >> 16)
r.expEn = int16(-11 - expCode)
lTemp2 = int32(17062) << 7
lTmp = L_add(lTmp, lTemp2)
case dMR74:
lTemp2 = int32(32588) << 6
lTmp = L_add(lTmp, lTemp2)
case dMR67:
lTemp2 = int32(32268) << 6
lTmp = L_add(lTmp, lTemp2)
default: // MR59, MR515, MR475
lTemp2 = int32(16678) << 7
lTmp = L_add(lTmp, lTemp2)
}
// gcode0 = sum(pred[i]*past_qua_en[i]) - ener_code + mean_ener (Q24)
if lTmp > 0x001fffff {
lTmp = maxInt32
} else if lTmp < -2097152 {
lTmp = minInt32
} else {
lTmp <<= 10
}
for i := 0; i < 4; i++ {
lTemp2 = (int32(gcPred[i]) * int32(st.pastQuaEn[i])) << 1
lTmp = L_add(lTmp, lTemp2)
}
gcode0 := int16(lTmp >> 16) // Q8
// gcode0 = pow(2, 0.166*gcode0)
if mode == dMR74 { // IS641 bit-exactness
lTmp = int32(gcode0) * 5439 << 1
} else {
lTmp = int32(gcode0) * 5443 << 1
}
if lTmp < 0 {
lTmp = ^((^lTmp) >> 8)
} else {
lTmp >>= 8 // -> Q16
}
r.expGcode0 = int16(lTmp >> 16)
var lTemp1 int32
if lTmp < 0 {
lTemp1 = ^((^lTmp) >> 1)
} else {
lTemp1 = lTmp >> 1
}
lTemp2 = int32(r.expGcode0) << 15
r.fracGcode0 = int16(L_sub(lTemp1, lTemp2))
return r
}