Files
Cyrene_v2/internal/capability/transcode/amrnb/dsp_agc.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

78 lines
2.0 KiB
Go

package amrnb
// Adaptive gain control for the post-filter, ported from opencore-amrnb agc.cpp.
// agc scales the post-filtered signal so its energy tracks the synthesis
// energy, with a per-sample gain smoothed by agc_fac across the subframe. The
// running gain is carried in agcState. Bit-exact with the reference.
const cAGC_FAC = 29491 // 0.9, the post-filter AGC factor
// agcState holds the smoothed per-sample gain across subframes.
type agcState struct {
pastGain int16
}
// energyOld accumulates the energy of a prescaled signal (used when energyNew
// would overflow). Mirrors energy_old.
func energyOld(in []int16, lTrm int) int32 {
var s int32
for i := 0; i < lTrm; i++ {
temp := in[i] >> 2
s = L_mac(s, temp, temp)
}
return s
}
// energyNew returns the energy of a signal (>>4 scaled), falling back to
// energyOld on saturation. Mirrors energy_new.
func energyNew(in []int16, lTrm int) int32 {
var s int32
for i := 0; i < lTrm; i++ {
s = L_mac(s, in[i], in[i])
}
if s != maxInt32 {
return s >> 4
}
return energyOld(in, lTrm)
}
// agc scales sigOut in place so its energy approaches sigIn's, applying a gain
// smoothed across the subframe by agcFac and carried in the state. Mirrors agc.
func (st *agcState) agc(sigIn, sigOut []int16, agcFac int16, lTrm int) {
s := energyNew(sigOut, lTrm)
if s == 0 {
st.pastGain = 0
return
}
exp := norm_l(s) - 1
gainOut := round_(L_shl(s, exp))
s = energyNew(sigIn, lTrm)
var g0 int16
if s == 0 {
g0 = 0
} else {
i := norm_l(s)
gainIn := round_(s << i)
exp -= i
temp := div_s(gainOut, gainIn)
ss := int32(temp) << 7
ss = L_shr(ss, exp)
ss = invSqrt(ss)
lTemp := ss << 9
ii := int16((lTemp + 0x8000) >> 16)
temp = 32767 - agcFac
g0 = int16((int32(ii) * int32(temp)) >> 15)
}
gain := st.pastGain
for i := 0; i < lTrm; i++ {
gain = int16((int32(gain) * int32(agcFac)) >> 15)
gain += g0
lTemp := (int32(sigOut[i]) * int32(gain)) << 1
sigOut[i] = int16(lTemp >> 13)
}
st.pastGain = gain
}