Files
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

197 lines
6.5 KiB
Go

package amrnb
// MR475 joint two-subframe gain quantization, ported from opencore-amrnb
// qgain475.cpp (MR475_gain_quant, MR475_quant_store_results,
// MR475_update_unq_pred) and the target-energy helper calc_en.cpp
// (calc_target_energy). The 4.75 kbit/s mode quantizes the pitch and code gains
// of two consecutive subframes jointly with a single 8-bit index into
// table_gain_MR475, using an "unquantized" gain predictor (predUnq) to drive the
// search and the real predictor (pred) for the reconstructed gains. Bit-exact
// with the reference.
const (
cMIN_QUA_ENER = -5443
cMIN_QUA_ENER_MR122 = -32768
cMAX_QUA_ENER = 3037
cMAX_QUA_ENER_MR122 = 18284
)
// calcTargetEnergy returns the energy of the LTP target xn as a normalized
// (exponent, fraction) pair. Mirrors calc_target_energy.
func calcTargetEnergy(xn []int16) (enExp, enFrac int16) {
var s int32
for i := 0; i < cL_SUBFR; i++ {
s += int32(xn[i]) * int32(xn[i]) // 32-bit MAC, wraps like the reference
}
if s < 0 { // overflow wrapped negative
s = maxInt32
}
exp := norm_l(s)
enFrac = int16(L_shl(s, exp) >> 16)
enExp = 16 - exp
return
}
// mr475StoreResults dequantizes one subframe's gains from a table entry p
// (p[0]=gain_pit, p[1]=gain_code) given the predicted code gain gcode0, and
// advances the predictor with the resulting quantized energy error. Mirrors
// MR475_quant_store_results.
func (pred *gcPredState) mr475StoreResults(p []int16, gcode0, expGcode0 int16) (gainPit, gainCod int16) {
gainPit = p[0]
gCode := p[1]
L := (int32(gCode) * int32(gcode0)) << 1
L = L_shr(L, 10-expGcode0)
gainCod = int16(L >> 16)
exp, frac := log2(int32(gCode))
exp -= 12
tmp := shr_r(frac, 5)
quaEnerMR122 := (exp << 10) + tmp
L2 := mpy32_16(exp, frac, 24660) << 13
quaEner := int16((L2 + 0x00008000) >> 16)
pred.update(quaEnerMR122, quaEner)
return
}
// mr475UpdateUnqPred advances the "unquantized" predictor with the optimum
// (unquantized) code gain of a subframe. Mirrors MR475_update_unq_pred.
func (pred *gcPredState) mr475UpdateUnqPred(expGcode0, fracGcode0, codGainExp, codGainFrac int16) {
var quaEner, quaEnerMR122 int16
if codGainFrac <= 0 {
quaEner = cMIN_QUA_ENER
quaEnerMR122 = cMIN_QUA_ENER_MR122
} else {
fg := int16(pow2(14, fracGcode0))
if codGainFrac >= fg {
codGainFrac >>= 1
codGainExp++
}
frac := div_s(codGainFrac, fg)
tmp := codGainExp - expGcode0 - 1
exp, fr := log2(int32(frac))
exp += tmp
quaEnerMR122 = shr_r(fr, 5) + (exp << 10)
if quaEnerMR122 > cMAX_QUA_ENER_MR122 {
quaEner = cMAX_QUA_ENER
quaEnerMR122 = cMAX_QUA_ENER_MR122
} else {
L := L_shl(mpy32_16(exp, fr, 24660), 13)
quaEner = round_(L)
}
}
pred.update(quaEnerMR122, quaEner)
}
// mr475GainQuant runs the joint pitch+code gain VQ for a subframe pair: it
// searches table_gain_MR475 for the entry minimizing the combined weighted
// error of both subframes (respecting the pitch-gain limit), stores the
// reconstructed gains for both subframes, and returns the table index. The sf0
// energy terms were stashed in st by the even-subframe pass; the sf1 terms are
// passed in. Mirrors MR475_gain_quant.
func (st *coderState) mr475GainQuant(sf1Code []int16, sf1ExpGcode0, sf1FracGcode0 int16,
sf1ExpCoeff, sf1FracCoeff []int16, sf1ExpTargetEn, sf1FracTargetEn, gpLimit int16) (index, sf0GainPit, sf0GainCod, sf1GainPit, sf1GainCod int16) {
sf0Gcode0 := int16(pow2(14, st.sf0FracGcode0))
sf1Gcode0 := int16(pow2(14, sf1FracGcode0))
// per-coefficient exponents (0..4: sf0, 5..9: sf1)
var expMax [10]int16
exp := st.sf0ExpGcode0 - 11
expMax[0] = st.sf0ExpCoeff[0] - 13
expMax[1] = st.sf0ExpCoeff[1] - 14
expMax[2] = st.sf0ExpCoeff[2] + (15 + (exp << 1))
expMax[3] = st.sf0ExpCoeff[3] + exp
expMax[4] = st.sf0ExpCoeff[4] + (1 + exp)
exp = sf1ExpGcode0 - 11
expMax[5] = sf1ExpCoeff[0] - 13
expMax[6] = sf1ExpCoeff[1] - 14
expMax[7] = sf1ExpCoeff[2] + (15 + (exp << 1))
expMax[8] = sf1ExpCoeff[3] + exp
expMax[9] = sf1ExpCoeff[4] + (1 + exp)
// scale the two target energies to a common exponent, then nudge the sf0
// coefficient exponents so the two subframes' errors are comparable.
sf0FracTargetEn := st.sf0FracTargetEn
if e := st.sf0ExpTargetEn - sf1ExpTargetEn; e > 0 {
sf1FracTargetEn >>= uint(e)
} else {
sf0FracTargetEn >>= uint(-e)
}
adj := int16(0)
if tmp := shr_r(sf1FracTargetEn, 1); tmp > sf0FracTargetEn {
adj = 1
} else if (sf0FracTargetEn+3)>>2 > sf1FracTargetEn {
adj = -1
}
for i := 0; i < 5; i++ {
expMax[i] += adj
}
// common exponent for all 10 coefficients
mx := expMax[0]
for i := 9; i > 0; i-- {
if expMax[i] > mx {
mx = expMax[i]
}
}
mx++ // avoid overflow
var coeff, coeffLo [10]int16
for i := 0; i < 10; i++ {
var fc int16
if i < 5 {
fc = st.sf0FracCoeff[i]
} else {
fc = sf1FracCoeff[i-5]
}
L := L_shr(int32(fc)<<16, mx-expMax[i])
coeff[i] = int16(L >> 16)
coeffLo[i] = int16((L >> 1) - ((L >> 16) << 15))
}
// exhaustive search over the 256-entry joint codebook
distMin := int32(maxInt32)
index = 0
for i := 0; i < cMR475_VQ_SIZE; i++ {
b := i * 4
gPitch := table_gain_MR475[b]
gCode := int16((int32(table_gain_MR475[b+1]) * int32(sf0Gcode0)) >> 15)
g2Pitch := int16((int32(gPitch) * int32(gPitch)) >> 15)
g2Code := int16((int32(gCode) * int32(gCode)) >> 15)
gPitCod := int16((int32(gCode) * int32(gPitch)) >> 15)
L := mpy32_16(coeff[0], coeffLo[0], g2Pitch) +
mpy32_16(coeff[1], coeffLo[1], gPitch) +
mpy32_16(coeff[2], coeffLo[2], g2Code) +
mpy32_16(coeff[3], coeffLo[3], gCode) +
mpy32_16(coeff[4], coeffLo[4], gPitCod)
tmp := gPitch - gpLimit
gPitch1 := table_gain_MR475[b+2]
if tmp <= 0 && gPitch1 <= gpLimit {
gCode1 := int16((int32(table_gain_MR475[b+3]) * int32(sf1Gcode0)) >> 15)
g2Pitch = int16((int32(gPitch1) * int32(gPitch1)) >> 15)
g2Code = int16((int32(gCode1) * int32(gCode1)) >> 15)
gPitCod = int16((int32(gCode1) * int32(gPitch1)) >> 15)
L += mpy32_16(coeff[5], coeffLo[5], g2Pitch) +
mpy32_16(coeff[6], coeffLo[6], gPitch1) +
mpy32_16(coeff[7], coeffLo[7], g2Code) +
mpy32_16(coeff[8], coeffLo[8], gCode1) +
mpy32_16(coeff[9], coeffLo[9], gPitCod)
if L < distMin {
distMin = L
index = int16(i)
}
}
}
// reconstruct sf0 gains (updates the real predictor), re-predict, then sf1
base := int(index) << 2
sf0GainPit, sf0GainCod = st.pred.mr475StoreResults(table_gain_MR475[base:], sf0Gcode0, st.sf0ExpGcode0)
gp := st.pred.gcPred(dMR475, sf1Code)
sf1Gcode0 = int16(pow2(14, gp.fracGcode0))
sf1GainPit, sf1GainCod = st.pred.mr475StoreResults(table_gain_MR475[base+2:], sf1Gcode0, gp.expGcode0)
return
}