197 lines
6.5 KiB
Go
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
|
|
}
|