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

322 lines
9.2 KiB
Go

package amrnb
// Encoder LSF quantization, ported from opencore-amrnb q_plsf_3.cpp (Q_plsf_3,
// Vq_subvec3, Vq_subvec4) and lsfwt.cpp (Lsf_wt). Q_plsf_3 is the split-VQ LSF
// quantizer for all modes except 12.2 kbit/s: it converts the analysis LSPs to
// weighted LSFs, removes the MA prediction, and searches the LSF codebooks for
// the indices the bitstream carries. It is the exact encoder counterpart of the
// decoder's dPlsf3 — feeding its indices back through dPlsf3 reproduces the same
// quantized LSPs. Bit-exact with the reference.
// qPlsfState holds the encoder LSF MA-predictor memory. Mirrors Q_plsfState.
type qPlsfState struct {
pastRQ [cM]int16
}
func (st *qPlsfState) reset() {
for i := range st.pastRQ {
st.pastRQ[i] = 0
}
}
// lsfWt computes the LSF weighting factors (squared, Q13<<3). Mirrors Lsf_wt.
func lsfWt(lsf, wf []int16) {
wf[0] = lsf[1]
for i := 1; i <= 8; i++ {
wf[i] = lsf[i+1] - lsf[i-1]
}
wf[9] = 16384 - lsf[8] // reference's pointer ends at lsf[M-2] here, not lsf[M-1]
for i := 0; i < cM; i++ {
wgt := wf[i]
temp := wgt - 1843
if temp > 0 {
temp = int16((int32(temp) * 6242) >> 15)
wgt = 1843 - temp
} else {
temp = int16((int32(wgt) * 28160) >> 15)
wgt = 3427 - temp
}
wf[i] = shl(wgt, 3)
}
}
// vqSubvec3 finds the weighted nearest codebook entry for a 3-element LSF
// residual, writing the chosen entry back into lsfR1[0:3]. useHalf searches only
// every second codebook entry. Mirrors Vq_subvec3.
func vqSubvec3(lsfR1, dico, wf1 []int16, dicoSize int, useHalf bool) int16 {
distMin := int32(maxInt32)
index := int16(0)
stride := 0
if useHalf {
stride = 3
}
l0, l1, l2 := lsfR1[0], lsfR1[1], lsfR1[2]
w0, w1, w2 := wf1[0], wf1[1], wf1[2]
di := 0
advance := 3 + stride
for i := 0; i < dicoSize; i++ {
row := dico[di : di+3] // hoist the per-dim bounds checks
di += advance
t := int16((int32(w0) * int32(l0-row[0])) >> 15)
dist := int32(t) * int32(t)
t = int16((int32(w1) * int32(l1-row[1])) >> 15)
dist += int32(t) * int32(t)
t = int16((int32(w2) * int32(l2-row[2])) >> 15)
dist += int32(t) * int32(t)
if dist < distMin {
distMin = dist
index = int16(i)
}
}
pd := 3 * int(index)
if useHalf {
pd += 3 * int(index)
}
lsfR1[0] = dico[pd]
lsfR1[1] = dico[pd+1]
lsfR1[2] = dico[pd+2]
return index
}
// vqSubvec4 finds the weighted nearest codebook entry for a 4-element LSF
// residual, writing the chosen entry back into lsfR1[0:4]. Mirrors Vq_subvec4.
func vqSubvec4(lsfR1, dico, wf1 []int16, dicoSize int) int16 {
distMin := int32(maxInt32)
index := int16(0)
l0, l1, l2, l3 := lsfR1[0], lsfR1[1], lsfR1[2], lsfR1[3]
w0, w1, w2, w3 := wf1[0], wf1[1], wf1[2], wf1[3]
di := 0
for i := 0; i < dicoSize; i++ {
row := dico[di : di+4] // hoist the per-dim bounds checks
di += 4
t := int16((int32(w0) * int32(l0-row[0])) >> 15)
dist := int32(t) * int32(t)
t = int16((int32(w1) * int32(l1-row[1])) >> 15)
dist += int32(t) * int32(t)
t = int16((int32(w2) * int32(l2-row[2])) >> 15)
dist += int32(t) * int32(t)
t = int16((int32(w3) * int32(l3-row[3])) >> 15)
dist += int32(t) * int32(t)
if dist < distMin {
distMin = dist
index = int16(i)
}
}
pd := int(index) << 2
lsfR1[0] = dico[pd]
lsfR1[1] = dico[pd+1]
lsfR1[2] = dico[pd+2]
lsfR1[3] = dico[pd+3]
return index
}
// qPlsf3 quantizes the analysis LSPs for the given mode into three split-VQ
// indices and the quantized LSP vector, updating the predictor. Mirrors
// Q_plsf_3 (non-DTX path). Returns the three indices.
func (st *qPlsfState) qPlsf3(mode int, lsp1 []int16, lsp1q []int16) [3]int16 {
var lsf1, wf1, lsfP, lsfR1, lsf1q [cM]int16
lspLsf(lsp1, lsf1[:], cM)
lsfWt(lsf1[:], wf1[:])
for i := 0; i < cM; i++ {
temp := int16((int32(st.pastRQ[i]) * int32(pred_fac_3[i])) >> 15)
lsfP[i] = mean_lsf_3[i] + temp
lsfR1[i] = lsf1[i] - lsfP[i]
}
var indice [3]int16
switch {
case mode == dMR475 || mode == dMR515:
indice[0] = vqSubvec3(lsfR1[0:], dico1_lsf_3, wf1[0:], cDICO1_SIZE, false)
indice[1] = vqSubvec3(lsfR1[3:], dico2_lsf_3, wf1[3:], cDICO2_SIZE/2, true)
indice[2] = vqSubvec4(lsfR1[6:], mr515_3_lsf, wf1[6:], cMR515_3_SIZE)
case mode == dMR795:
indice[0] = vqSubvec3(lsfR1[0:], mr795_1_lsf, wf1[0:], cMR795_1_SIZE, false)
indice[1] = vqSubvec3(lsfR1[3:], dico2_lsf_3, wf1[3:], cDICO2_SIZE, false)
indice[2] = vqSubvec4(lsfR1[6:], dico3_lsf_3, wf1[6:], cDICO3_SIZE)
default: // MR59, MR67, MR74, MR102
indice[0] = vqSubvec3(lsfR1[0:], dico1_lsf_3, wf1[0:], cDICO1_SIZE, false)
indice[1] = vqSubvec3(lsfR1[3:], dico2_lsf_3, wf1[3:], cDICO2_SIZE, false)
indice[2] = vqSubvec4(lsfR1[6:], dico3_lsf_3, wf1[6:], cDICO3_SIZE)
}
for i := 0; i < cM; i++ {
lsf1q[i] = add(lsfR1[i], lsfP[i])
st.pastRQ[i] = lsfR1[i]
}
reorderLsf(lsf1q[:], cLSF_GAP, cM)
lsfLsp(lsf1q[:], lsp1q, cM)
return indice
}
// LSF-5 split-VQ codebook sizes (q_plsf_5_tbl.cpp).
const (
cDICO1_5_SIZE = 128
cDICO2_5_SIZE = 256
cDICO3_5_SIZE = 256
cDICO4_5_SIZE = 256
cDICO5_5_SIZE = 64
)
// vqSubvec finds the weighted nearest 4-element codebook entry (two LSFs from
// each of the two LSF vectors), writing it back into lsfR1[0:2]/lsfR2[0:2].
// Mirrors Vq_subvec.
func vqSubvec(lsfR1, lsfR2, dico, wf1, wf2 []int16, dicoSize int) int16 {
distMin := int32(maxInt32)
index := int16(0)
w10, w11 := wf1[0], wf1[1]
w20, w21 := wf2[0], wf2[1]
aux1 := int32(lsfR1[0]) * int32(w10)
aux2 := int32(lsfR1[1]) * int32(w11)
aux3 := int32(lsfR2[0]) * int32(w20)
aux4 := int32(lsfR2[1]) * int32(w21)
for i := 0; i < dicoSize; i++ {
row := dico[i*4 : i*4+4] // hoist the per-dim bounds checks
temp := int16((aux1 - int32(w10)*int32(row[0])) >> 15)
dist := int32(temp) * int32(temp)
if dist >= distMin {
continue
}
temp = int16((aux2 - int32(w11)*int32(row[1])) >> 15)
dist += int32(temp) * int32(temp)
if dist >= distMin {
continue
}
temp = int16((aux3 - int32(w20)*int32(row[2])) >> 15)
dist += int32(temp) * int32(temp)
if dist >= distMin {
continue
}
temp = int16((aux4 - int32(w21)*int32(row[3])) >> 15)
dist += int32(temp) * int32(temp)
if dist < distMin {
distMin = dist
index = int16(i)
}
}
pd := int(index) << 2
lsfR1[0] = dico[pd]
lsfR1[1] = dico[pd+1]
lsfR2[0] = dico[pd+2]
lsfR2[1] = dico[pd+3]
return index
}
// vqSubvecS is vqSubvec with sign selection: each codebook entry is tried as-is
// and negated; the index LSB encodes the sign. Mirrors Vq_subvec_s.
func vqSubvecS(lsfR1, lsfR2, dico, wf1, wf2 []int16, dicoSize int) int16 {
distMin := int32(maxInt32)
index := int16(0)
sign := int16(0)
l10, l11 := lsfR1[0], lsfR1[1]
l20, l21 := lsfR2[0], lsfR2[1]
w10, w11 := wf1[0], wf1[1]
w20, w21 := wf2[0], wf2[1]
for i := 0; i < dicoSize; i++ {
row := dico[i*4 : i*4+4] // hoist the per-dim bounds checks
temp := row[0]
t1 := int16((int32(w10) * int32(l10-temp)) >> 15)
t2 := int16((int32(w10) * int32(l10+temp)) >> 15)
dist1 := int32(t1) * int32(t1)
dist2 := int32(t2) * int32(t2)
temp = row[1]
t1 = int16((int32(w11) * int32(l11-temp)) >> 15)
t2 = int16((int32(w11) * int32(l11+temp)) >> 15)
dist1 += int32(t1) * int32(t1)
dist2 += int32(t2) * int32(t2)
if dist1 >= distMin && dist2 >= distMin {
continue
}
temp = row[2]
t1 = int16((int32(w20) * int32(l20-temp)) >> 15)
t2 = int16((int32(w20) * int32(l20+temp)) >> 15)
dist1 += int32(t1) * int32(t1)
dist2 += int32(t2) * int32(t2)
temp = row[3]
t1 = int16((int32(w21) * int32(l21-temp)) >> 15)
t2 = int16((int32(w21) * int32(l21+temp)) >> 15)
dist1 += int32(t1) * int32(t1)
dist2 += int32(t2) * int32(t2)
if dist1 < distMin {
distMin = dist1
index = int16(i)
sign = 0
}
if dist2 < distMin {
distMin = dist2
index = int16(i)
sign = 1
}
}
pd := int(index) << 2
index <<= 1
if sign != 0 {
lsfR1[0] = -dico[pd]
lsfR1[1] = -dico[pd+1]
lsfR2[0] = -dico[pd+2]
lsfR2[1] = -dico[pd+3]
index++
} else {
lsfR1[0] = dico[pd]
lsfR1[1] = dico[pd+1]
lsfR2[0] = dico[pd+2]
lsfR2[1] = dico[pd+3]
}
return index
}
// qPlsf5 quantizes two LSP vectors (MR122) into five indices and the two
// quantized LSP vectors, updating the predictor. The exact encoder counterpart
// of dPlsf5. Mirrors Q_plsf_5.
func (st *qPlsfState) qPlsf5(lsp1, lsp2, lsp1q, lsp2q []int16) [5]int16 {
var lsf1, lsf2, wf1, wf2, lsfP, lsfR1, lsfR2, lsf1q, lsf2q [cM]int16
lspLsf(lsp1, lsf1[:], cM)
lspLsf(lsp2, lsf2[:], cM)
lsfWt(lsf1[:], wf1[:])
lsfWt(lsf2[:], wf2[:])
for i := 0; i < cM; i++ {
lsfP[i] = mean_lsf_5[i] + int16((int32(st.pastRQ[i])*cLSP_PRED_FAC_MR122)>>15)
lsfR1[i] = lsf1[i] - lsfP[i]
lsfR2[i] = lsf2[i] - lsfP[i]
}
var indice [5]int16
indice[0] = vqSubvec(lsfR1[0:], lsfR2[0:], dico1_lsf_5, wf1[0:], wf2[0:], cDICO1_5_SIZE)
indice[1] = vqSubvec(lsfR1[2:], lsfR2[2:], dico2_lsf_5, wf1[2:], wf2[2:], cDICO2_5_SIZE)
indice[2] = vqSubvecS(lsfR1[4:], lsfR2[4:], dico3_lsf_5, wf1[4:], wf2[4:], cDICO3_5_SIZE)
indice[3] = vqSubvec(lsfR1[6:], lsfR2[6:], dico4_lsf_5, wf1[6:], wf2[6:], cDICO4_5_SIZE)
indice[4] = vqSubvec(lsfR1[8:], lsfR2[8:], dico5_lsf_5, wf1[8:], wf2[8:], cDICO5_5_SIZE)
for i := 0; i < cM; i++ {
lsf1q[i] = lsfR1[i] + lsfP[i]
lsf2q[i] = lsfR2[i] + lsfP[i]
st.pastRQ[i] = lsfR2[i]
}
reorderLsf(lsf1q[:], cLSF_GAP, cM)
reorderLsf(lsf2q[:], cLSF_GAP, cM)
lsfLsp(lsf1q[:], lsp1q, cM)
lsfLsp(lsf2q[:], lsp2q, cM)
return indice
}