// 计算增益表函数
int32_t WebRtcAgc_CalculateGainTable(int32_t *gainTable, // Q16格式的增益表
int16_t digCompGaindB, // 数字补偿增益,Q0格式
int16_t targetLevelDbfs, // 目标电平(分贝满标度),Q0格式
uint8_t limiterEnable, // 限制器启用标志
int16_t analogTarget) // 模拟目标电平,Q0格式
{
uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox;
int32_t inLevel, limiterLvl;
int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32;
const uint16_t kLog10 = 54426; // log2(10)的Q14表示
const uint16_t kLog10_2 = 49321; // 10*log10(2)的Q14表示
const uint16_t kLogE_1 = 23637; // log2(e)的Q14表示
uint16_t constMaxGain;
uint16_t tmpU16, intPart, fracPart;
const int16_t kCompRatio = 3; // 压缩比
int16_t limiterOffset = 0; // 限制器偏移
int16_t limiterIdx, limiterLvlX;
int16_t constLinApprox, maxGain, diffGain;
int16_t i, tmp16, tmp16no1;
int zeros, zerosScale;
// 计算最大数字增益和零增益级
tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1);
tmp16no1 = analogTarget - targetLevelDbfs;
tmp16no1 += DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
maxGain = MAX(tmp16no1, (analogTarget - targetLevelDbfs));
// 根据设定条件计算限制器级别和索引
if ((digCompGaindB <= analogTarget) && (limiterEnable)) {
limiterOffset = 0;
}
// 计算最大增益与0dB0v增益之间的差
tmp32no1 = digCompGaindB * (kCompRatio - 1);
diffGain = DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
// 如果差值超出表大小,返回错误
if (diffGain < 0 || diffGain >= kGenFuncTableSize) {
assert(0);
return -1;
}
limiterLvlX = analogTarget - limiterOffset;
limiterIdx = 2 + DivW32W16ResW16((int32_t)limiterLvlX * (1 << 13),
kLog10_2 / 2);
tmp16no1 = DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio);
limiterLvl = targetLevelDbfs + tmp16no1;
// 通过查表获取常数最大增益
constMaxGain = kGenFuncTable[diffGain];
// 计算用于线性函数近似的常数(片段线性函数)
constLinApprox = 22817;
// 计算从分贝到线性尺度转换的分母
den = ((int32_t)(int16_t)(20) * (uint16_t)(constMaxGain));
for (i = 0; i < 32; i++) {
// 计算输入级(压缩器)
tmp16 = (int16_t)((kCompRatio - 1) * (i - 1));
tmp32 = ((int32_t)(int16_t)(tmp16) * (uint16_t)(kLog10_2)) + 1;
inLevel = DivW32W16(tmp32, kCompRatio);
inLevel = (int32_t)diffGain * (1 << 14) - inLevel;
// 对输入级绝对值进行计算并查表获取近似对数值
absInLevel = (uint32_t)(((int32_t)(inLevel) >= 0) ? ((int32_t)(inLevel)) : -((int32_t)(inLevel)));
intPart = (uint16_t)(absInLevel >> 14);
fracPart = (uint16_t)(absInLevel & 0x00003FFF);
tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart];
tmpU32no1 = tmpU16 * fracPart;
tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14;
logApprox = tmpU32no1 >> 8;
// 根据对数值计算增益表中的具体增益值
if (inLevel < 0) {
zeros = NormU32(absInLevel);
zerosScale = 0;
if (zeros < 15) {
tmpU32no2 = absInLevel >> (15 - zeros);
tmpU32no2 = ((uint32_t)((uint32_t)(tmpU32no2) * (uint16_t)(kLogE_1)));
if (zeros < 9) {
zerosScale = 9 - zeros;
tmpU32no1 >>= zerosScale;
}
else {
tmpU32no2 >>= zeros - 9;
}
}
else {
tmpU32no2 = ((uint32_t)((uint32_t)(absInLevel) * (uint16_t)(kLogE_1)));
tmpU32no2 >>= 6;
}
logApprox = 0;
if (tmpU32no2 < tmpU32no1) {
logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale);
}
}
numFIX = (maxGain * constMaxGain) * (1 << 6);
numFIX -= (int32_t)logApprox * diffGain;
// 调整增益计算中的数值尺度,避免溢出
if (numFIX > (den >> 8) || -numFIX > (den >> 8)) {
zeros = NormW32(numFIX);
}
else {
zeros = NormW32(den) + 8;
}
numFIX *= 1 << zeros;
tmp32no1 = SHIFT_W32(den, zeros - 9);
y32 = numFIX / tmp32no1;
y32 = y32 >= 0 ? (y32 + 1) >> 1 : -((-y32 + 1) >> 1);
// 根据限制器启用状态进行最后的调整
if (limiterEnable && (i < limiterIdx)) {
tmp32 = ((int32_t)(int16_t)(i - 1) * (uint16_t)(kLog10_2));
tmp32 -= limiterLvl * (1 << 14);
y32 = DivW32W16(tmp32 + 10, 20);
}
// 计算增益值并填充增益表
if (y32 > 39000) {
tmp32 = (y32 >> 1) * kLog10 + 4096;
tmp32 >>= 13;
}
else {
tmp32 = y32 * kLog10 + 8192;
tmp32 >>= 14;
}
tmp32 += 16 << 14;
if (tmp32 > 0) {
intPart = (int16_t)(tmp32 >> 14);
fracPart = (uint16_t)(tmp32 & 0x00003FFF);
if ((fracPart >> 13) != 0) {
tmp16 = (2 << 14) - constLinApprox;
tmp32no2 = (1 << 14) - fracPart;
tmp32no2 *= tmp16;
tmp32no2 >>= 13;
tmp32no2 = (1 << 14) - tmp32no2;
}
else {
tmp16 = constLinApprox - (1 << 14);
tmp32no2 = (fracPart * tmp16) >> 13;
}
fracPart = (uint16_t)tmp32no2;
gainTable[i] = (1 << intPart) + SHIFT_W32(fracPart, intPart - 14);
}
else {
gainTable[i] = 0;
}
}
return 0;
}
// 初始化数字AGC模块
int32_t WebRtcAgc_InitDigital(DigitalAgc *stt, int16_t agcMode) {
if (agcMode == kAgcModeFixedDigital) {
// 如果AGC模式是固定数字模式,则从最小增益开始,以便更快地找到正确的增益值
stt->capacitorSlow = 0;
}
else {
// 否则从0分贝增益开始
stt->capacitorSlow = 134217728; // 等于0.125 * 32768.0 * 32768.0
}
stt->capacitorFast = 0; // 快电容器初始值设为0
stt->gain = 65536; // 初始增益设置为1(65536在Q16格式下表示1.0)
stt->gatePrevious = 0; // 之前的门限值设为0
stt->agcMode = agcMode; // 设置AGC模式
#ifdef WEBRTC_AGC_DEBUG_DUMP
stt->frameCounter = 0; // 调试模式下,帧计数器初始化为0
#endif
// 初始化语音活动检测器(VAD)
WebRtcAgc_InitVad(&stt->vadNearend); // 初始化近端VAD
WebRtcAgc_InitVad(&stt->vadFarend); // 初始化远端VAD
return 0;
}
// 将远端信号添加到数字AGC处理中
int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc *stt,
const int16_t *in_far,
size_t nrSamples) {
assert(stt); // 断言,确保stt指针非空
// 对远端信号进行语音活动检测
WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
return 0;
}
/* 处理数字自动增益控制
* stt - 数字AGC状态实例的指针
* in_near - 近端信号的输入数组
* num_bands - 频带数量
* out - 输出处理后的音频数组
* FS - 采样频率
* lowlevelSignal - 低级别信号标志
*/
int32_t WebRtcAgc_ProcessDigital(DigitalAgc *stt,
const int16_t *const *in_near,
size_t num_bands,
int16_t *const *out,
uint32_t FS,
int16_t lowlevelSignal) {
int32_t gains[11]; // 每毫秒的增益数组(包括起始和结束)
int32_t out_tmp, tmp32;
int32_t env[10]; // 每个子帧的环境音量数组
int32_t max_nrg; // 最大能量
int32_t cur_level;
int32_t gain32, delta;
int16_t logratio; // 日志比率
int16_t lower_thr, upper_thr; // 上下阈值
int16_t zeros = 0, zeros_fast, frac = 0;
int16_t decay; // 衰减
int16_t gate, gain_adj;
int16_t k;
size_t n, i, L;
int16_t L2; // 子帧的样本数
// 根据采样率确定每毫秒的样本数
if (FS == 8000) {
L = 8;
L2 = 3;
}
else if (FS == 16000 || FS == 32000 || FS == 48000) {
L = 16;
L2 = 4;
}
else {
return -1;
}
// 如果输入和输出的指针不同,则复制数据
for (i = 0; i < num_bands; ++i) {
if (in_near[i] != out[i]) {
memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0]));
}
}
// 近端语音活动检测(VAD)
logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out[0], L * 10);
// 远端VAD的影响
if (stt->vadFarend.counter > 10) {
tmp32 = 3 * logratio;
logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2);
}
// 根据VAD决定衰减因子
upper_thr = 1024; // Q10格式
lower_thr = 0; // Q10格式
if (logratio > upper_thr) {
decay = -65;
}
else if (logratio < lower_thr) {
decay = 0;
}
else {
tmp32 = (lower_thr - logratio) * 65;
decay = (int16_t)(tmp32 >> 10);
}
// 长时间静音时调整衰减因子,仅在自适应模式中执行
if (stt->agcMode != kAgcModeFixedDigital) {
if (stt->vadNearend.stdLongTerm < 4000) {
decay = 0;
}
else if (stt->vadNearend.stdLongTerm < 8096) {
tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay;
decay = (int16_t)(tmp32 >> 12);
}
if (lowlevelSignal != 0) {
decay = 0;
}
}
#ifdef WEBRTC_AGC_DEBUG_DUMP
// 调试信息
stt->frameCounter++;
fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100,
logratio, decay, stt->vadNearend.stdLongTerm);
#endif
// 找到每个子帧的最大振幅
for (k = 0; k < 10; k++) {
max_nrg = 0;
for (n = 0; n < L; n++) {
int32_t nrg = out[0][k * L + n] * out[0][k * L + n];
if (nrg > max_nrg) {
max_nrg = nrg;
}
}
env[k] = max_nrg;
}
// 计算每个子帧的增益
gains[0] = stt->gain; // 初始化增益数组的首个元素为当前增益
for (k = 0; k < 10; k++) {
// 快速包络跟踪器
// 衰减时间 = -131000 / -1000 = 131 毫秒
stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
if (env[k] > stt->capacitorFast) {
stt->capacitorFast = env[k]; // 如果环境能量大于快速包络,则更新快速包络
}
// 慢速包络跟踪器
if (env[k] > stt->capacitorSlow) {
// 增加慢速包络
stt->capacitorSlow = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow);
}
else {
// 减少慢速包络
stt->capacitorSlow = AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
}
// 使用两个包络中的较大者作为当前级别
if (stt->capacitorFast > stt->capacitorSlow) {
cur_level = stt->capacitorFast;
}
else {
cur_level = stt->capacitorSlow;
}
// 将信号级别转换为增益,使用分段线性近似
zeros = NormU32((uint32_t)cur_level); // 计算前导零
if (cur_level == 0) {
zeros = 31; // 如果当前级别为0,则前导零为31
}
tmp32 = ((uint32_t)cur_level << zeros) & 0x7FFFFFFF;
frac = (int16_t)(tmp32 >> 19); // Q12格式
tmp32 = (stt->gainTable[zeros - 1] - stt->gainTable[zeros]) * frac;
gains[k + 1] = stt->gainTable[zeros] + (tmp32 >> 12); // 计算增益
#ifdef WEBRTC_AGC_DEBUG_DUMP
if (k == 0) {
// 调试信息输出
fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros);
}
#endif
}
// 门控处理(在没有语音时降低增益)
zeros = (zeros << 9) - (frac >> 3);
zeros_fast = NormU32((uint32_t)stt->capacitorFast); // 快速包络的前导零
if (stt->capacitorFast == 0) {
zeros_fast = 31; // 如果快速包络为0,则前导零为31
}
tmp32 = ((uint32_t)stt->capacitorFast << zeros_fast) & 0x7FFFFFFF;
zeros_fast <<= 9;
zeros_fast -= (int16_t)(tmp32 >> 22);
gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm; // 计算门控值
if (gate < 0) {
stt->gatePrevious = 0;
}
else {
tmp32 = stt->gatePrevious * 7;
gate = (int16_t)((gate + tmp32) >> 3); // 平滑门控值
stt->gatePrevious = gate;
}
if (gate > 0) {
if (gate < 2500) {
gain_adj = (2500 - gate) >> 5; // 计算增益调整值
}
else {
gain_adj = 0;
}
for (k = 0; k < 10; k++) {
if ((gains[k + 1] - stt->gainTable[0]) > 8388608) {
// 防止包络溢出
tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8;
tmp32 *= 178 + gain_adj;
}
else {
tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj);
tmp32 >>= 8;
}
gains[k + 1] = stt->gainTable[0] + tmp32; // 应用增益调整
}
}
// 限制增益以避免过载失真
for (k = 0; k < 10; k++) {
// 防止环绕
zeros = 10;
if (gains[k + 1] > 47453132) {
zeros = 16 - NormW32(gains[k + 1]); // 计算需要的归一化位数
}
gain32 = (gains[k + 1] >> zeros) + 1;
gain32 *= gain32; // 计算增益平方
// 检查是否溢出
while (AGC_MUL32((env[k] >> 12) + 1, gain32) > SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) {
// 乘以 253/256,约等于 -0.1 dB
if (gains[k + 1] > 8388607) {
// 防止环绕
gains[k + 1] = (gains[k + 1] / 256) * 253;
}
else {
gains[k + 1] = (gains[k + 1] * 253) / 256;
}
gain32 = (gains[k + 1] >> zeros) + 1;
gain32 *= gain32; // 重新计算增益平方
}
}
// 增益减少应比增益增加提前1毫秒执行
for (k = 1; k < 10; k++) {
if (gains[k] > gains[k + 1]) {
gains[k] = gains[k + 1];
}
}
// 保存下一帧的起始增益
stt->gain = gains[10];
// 应用增益
// 单独处理第一个子帧
delta = (gains[1] - gains[0]) * (1 << (4 - L2));
gain32 = gains[0] * (1 << 4); // 初始化增益
// 遍历样本
for (n = 0; n < L; n++) {
for (i = 0; i < num_bands; ++i) {
tmp32 = out[i][n] * ((gain32 + 127) >> 7);
out_tmp = tmp32 >> 16;
if (out_tmp > 4095) {
out[i][n] = (int16_t)32767;
}
else if (out_tmp < -4096) {
out[i][n] = (int16_t)-32768;
}
else {
tmp32 = out[i][n] * (gain32 >> 4);
out[i][n] = (int16_t)(tmp32 >> 16);
}
}
gain32 += delta; // 更新增益
}
// 遍历子帧
for (k = 1; k < 10; k++) {
delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2)); // 计算增益变化
gain32 = gains[k] * (1 << 4); // 初始化增益
// 遍历样本
for (n = 0; n < L; n++) {
for (i = 0; i < num_bands; ++i) {
int64_t tmp64 = ((int64_t)(out[i][k * L + n])) * (gain32 >> 4);
tmp64 = tmp64 >> 16;
if (tmp64 > 32767) {
out[i][k * L + n] = 32767;
}
else if (tmp64 < -32768) {
out[i][k * L + n] = -32768;
}
else {
out[i][k * L + n] = (int16_t)(tmp64);
}
}
gain32 += delta; // 更新增益
}
}
return 0; // 返回成功标志
}
// 初始化VAD(声音活动检测)
void WebRtcAgc_InitVad(AgcVad *state) {
int16_t k;
state->HPstate = 0; // 高通滤波器的状态
state->logRatio = 0; // 活动状态与非活动状态的概率对数比
// 长期平均输入水平 (Q10)
state->meanLongTerm = 15 << 10;
// 长期输入水平的方差 (Q8)
state->varianceLongTerm = 500 << 8;
state->stdLongTerm = 0; // 长期输入水平的标准偏差(分贝)
// 短期平均输入水平 (Q10)
state->meanShortTerm = 15 << 10;
// 短期输入水平的方差 (Q8)
state->varianceShortTerm = 500 << 8;
state->stdShortTerm =
0; // 短期输入水平的标准偏差(分贝)
state->counter = 3; // 更新计数
for (k = 0; k < 8; k++) {
// 降采样滤波器状态
state->downState[k] = 0;
}
}
// 处理VAD
// vadInst: VAD状态(指向AgcVad结构体实例的指针)
// in: 指向输入语音信号的指针
// nrSamples: 输入信号的样本数量
int16_t WebRtcAgc_ProcessVad(AgcVad *state, // (i) VAD 状态
const int16_t *in, // (i) 语音信号
size_t nrSamples) // (i) 样本数量
{
uint32_t nrg;
int32_t out, tmp32, tmp32b;
uint16_t tmpU16;
int16_t k, subfr, tmp16;
int16_t buf1[8];
int16_t buf2[4];
int16_t HPstate;
int16_t zeros, dB;
// 在 10 个 1 毫秒的子帧中处理(为了节省内存)
nrg = 0;
HPstate = state->HPstate;
for (subfr = 0; subfr < 10; subfr++) {
// 下采样到 4 kHz
if (nrSamples == 160) {
for (k = 0; k < 8; k++) {
tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
tmp32 >>= 1;
buf1[k] = (int16_t)tmp32;
}
in += 16;
downsampleBy2(buf1, 8, buf2, state->downState);
}
else {
downsampleBy2(in, 8, buf2, state->downState);
in += 8;
}
// 高通滤波并计算能量
for (k = 0; k < 4; k++) {
out = buf2[k] + HPstate;
tmp32 = 600 * out;
HPstate = (int16_t)((tmp32 >> 10) - buf2[k]);
// 以不溢出的方式将 'out * out / 2**6' 加到 'nrg' 上
// 只要 'out * out / 2**6' 可以适应 int32_t 就能工作
nrg += out * (out / (1 << 6));
nrg += out * (out % (1 << 6)) / (1 << 6);
}
}
state->HPstate = HPstate;
// 找出前导零的数量
if (!(0xFFFF0000 & nrg)) {
zeros = 16;
}
else {
zeros = 0;
}
if (!(0xFF000000 & (nrg << zeros))) {
zeros += 8;
}
if (!(0xF0000000 & (nrg << zeros))) {
zeros += 4;
}
if (!(0xC0000000 & (nrg << zeros))) {
zeros += 2;
}
if (!(0x80000000 & (nrg << zeros))) {
zeros += 1;
}
// 能量级别(范围 {-32..30})(Q10)
dB = (15 - zeros) * (1 << 11);
// 更新统计数据
if (state->counter < kAvgDecayTime) {
// 衰减时间 = AvgDecTime * 10 毫秒
state->counter++;
}
// 更新短期平均能量级别估计(Q10)
tmp32 = state->meanShortTerm * 15 + dB;
state->meanShortTerm = (int16_t)(tmp32 >> 4);
// 更新短期能量级别方差估计(Q8)
tmp32 = (dB * dB) >> 12;
tmp32 += state->varianceShortTerm * 15;
state->varianceShortTerm = tmp32 / 16;
// 更新短期能量级别标准偏差估计(Q10)
tmp32 = state->meanShortTerm * state->meanShortTerm;
tmp32 = (state->varianceShortTerm << 12) - tmp32;
state->stdShortTerm = (int16_t)fast_sqrt(tmp32);
// 更新长期平均能量级别估计(Q10)
tmp32 = state->meanLongTerm * state->counter + dB;
state->meanLongTerm =
DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
// 更新长期能量级别方差估计(Q8)
tmp32 = (dB * dB) >> 12;
tmp32 += state->varianceLongTerm * state->counter;
state->varianceLongTerm =
DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
// 更新长期能量级别标准偏差估计(Q10)
tmp32 = state->meanLongTerm * state->meanLongTerm;
tmp32 = (state->varianceLongTerm << 12) - tmp32;
state->stdLongTerm = (int16_t)fast_sqrt(tmp32);
// 更新语音活动测量(Q10)
tmp16 = 3 << 12;
tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm);
tmp32 = DivW32W16(tmp32, state->stdLongTerm);
tmpU16 = (13 << 12);
tmp32b = ((int32_t)(int16_t)(state->logRatio) * (uint16_t)(tmpU16));
tmp32 += tmp32b >> 10;
state->logRatio = (int16_t)(tmp32 >> 6);
// 限制
if (state->logRatio > 2048) {
state->logRatio = 2048;
}
if (state->logRatio < -2048) {
state->logRatio = -2048;
}
return state->logRatio; // Q10
}
//添加并处理麦克风输入信号
int WebRtcAgc_AddMic(void *state,
int16_t *const *in_mic,
size_t num_bands,
size_t samples) {
int32_t nrg, max_nrg, sample, tmp32;
int32_t *ptr;
uint16_t targetGainIdx, gain;
size_t i;
int16_t n, L, tmp16, tmp_speech[16];
LegacyAgc *stt;
stt = (LegacyAgc *)state;
// 根据采样率设置不同的处理长度
if (stt->fs == 8000) {
L = 8;
if (samples != 80) {
return -1; // 如果样本数不符合预期,则返回错误
}
}
else {
L = 16;
if (samples != 160) {
return -1; // 如果样本数不符合预期,则返回错误
}
}
/* 应用缓慢变化的数字增益 */
if (stt->micVol > stt->maxAnalog) {
/* |maxLevel| 严格大于等于 |micVol|,确保不会出现除以零的情况 */
assert(stt->maxLevel > stt->maxAnalog);
/* Q1 */
tmp16 = (int16_t)(stt->micVol - stt->maxAnalog);
tmp32 = (GAIN_TBL_LEN - 1) * tmp16;
tmp16 = (int16_t)(stt->maxLevel - stt->maxAnalog);
targetGainIdx = tmp32 / tmp16;
assert(targetGainIdx < GAIN_TBL_LEN);
/* 按目标增益逐渐调整增益表 */
if (stt->gainTableIdx < targetGainIdx) {
stt->gainTableIdx++;
}
else if (stt->gainTableIdx > targetGainIdx) {
stt->gainTableIdx--;
}
/* Q12 */
gain = kGainTableAnalog[stt->gainTableIdx];
for (i = 0; i < samples; i++) {
size_t j;
for (j = 0; j < num_bands; ++j) {
sample = (in_mic[j][i] * gain) >> 12;
if (sample > 32767) {
in_mic[j][i] = 32767;
}
else if (sample < -32768) {
in_mic[j][i] = -32768;
}
else {
in_mic[j][i] = (int16_t)sample;
}
}
}
}
else {
stt->gainTableIdx = 0;
}
/* 计算包络 */
if (stt->inQueue > 0) {
ptr = stt->env[1];
}
else {
ptr = stt->env[0];
}
for (i = 0; i < kNumSubframes; i++) {
/* 遍历样本 */
max_nrg = 0;
for (n = 0; n < L; n++) {
nrg = in_mic[0][i * L + n] * in_mic[0][i * L + n];
if (nrg > max_nrg) {
max_nrg = nrg;
}
}
ptr[i] = max_nrg;
}
/* 计算能量 */
if (stt->inQueue > 0) {
ptr = stt->Rxx16w32_array[1];
}
else {
ptr = stt->Rxx16w32_array[0];
}
for (i = 0; i < kNumSubframes / 2; i++) {
if (stt->fs == 16000) {
downsampleBy2(&in_mic[0][i * 32], 32, tmp_speech,
stt->filterState);
}
else {
memcpy(tmp_speech, &in_mic[0][i * 16], 16 * sizeof(short));
}
/* 计算16个样本的能量 */
ptr[i] = DotProductWithScale(tmp_speech, tmp_speech, 16, 4);
}
/* 更新队列信息 */
if (stt->inQueue == 0) {
stt->inQueue = 1;
}
else {
stt->inQueue = 2;
}
/* 调用 VAD (仅使用低频带) */
WebRtcAgc_ProcessVad(&stt->vadMic, in_mic[0], samples);
return 0;
}
//调用错误检查和将远端音频信号添加到数字 AGC 处理中
int WebRtcAgc_AddFarend(void *state, const int16_t *in_far, size_t samples) {
LegacyAgc *stt = (LegacyAgc *)state; // 强制转换 state 参数为 LegacyAgc 类型的指针
// 检查添加远端信号是否会产生错误
int err = WebRtcAgc_GetAddFarendError(state, samples);
// 如果有错误,直接返回错误代码
if (err != 0)
return err;
// 如果没有错误,将远端信号添加到数字 AGC
return WebRtcAgc_AddFarendToDigital(&stt->digitalAgc, in_far, samples);
}
//验证传入的音频样本是否符合当前系统设置的采样率要求
int WebRtcAgc_GetAddFarendError(void *state, size_t samples) {
LegacyAgc *stt; // 定义一个 LegacyAgc 类型的指针
stt = (LegacyAgc *)state; // 强制转换 state 参数为 LegacyAgc 类型的指针
// 检查状态指针是否为空,如果为空,则返回错误
if (stt == NULL)
return -1;
// 根据采样率检查样本数是否正确
if (stt->fs == 8000) {
if (samples != 80)
return -1; // 对于 8000 Hz 采样率,样本数应为 80
}
else if (stt->fs == 16000 || stt->fs == 32000 || stt->fs == 48000) {
if (samples != 160)
return -1; // 对于 16000, 32000, 48000 Hz 采样率,样本数应为 160
}
else {
return -1; // 如果采样率不是上述任何一种,返回错误
}
return 0; // 如果所有检查都通过,返回 0 表示没有错误
}
/* 处理虚拟麦克风的输入
* agcInst - AGC状态实例的指针
* in_near - 多频带的输入音频数据
* num_bands - 输入音频的频带数
* samples - 每个频带的样本数
* micLevelIn - 输入的麦克风电平
* micLevelOut - 输出的麦克风电平的指针
*/
int WebRtcAgc_VirtualMic(void *agcInst,
int16_t *const *in_near,
size_t num_bands,
size_t samples,
int32_t micLevelIn,
int32_t *micLevelOut) {
int32_t tmpFlt, micLevelTmp, gainIdx;
uint16_t gain;
size_t ii, j;
LegacyAgc *stt;
uint32_t nrg;
size_t sampleCntr;
uint32_t frameNrg = 0;
uint32_t frameNrgLimit = 5500;
int16_t numZeroCrossing = 0;
const int16_t kZeroCrossingLowLim = 15;
const int16_t kZeroCrossingHighLim = 20;
stt = (LegacyAgc *)agcInst;
/* 判断是否是低级别信号,低级别信号不适用数字AGC调整 */
if (stt->fs != 8000) {
frameNrgLimit = frameNrgLimit << 1; // 非8kHz采样率时调整能量限制
}
/* 计算帧能量 */
frameNrg = (uint32_t)(in_near[0][0] * in_near[0][0]);
for (sampleCntr = 1; sampleCntr < samples; sampleCntr++) {
// 如果当前帧能量小于限制,则累加能量值
if (frameNrg < frameNrgLimit) {
nrg = (uint32_t)(in_near[0][sampleCntr] * in_near[0][sampleCntr]);
frameNrg += nrg;
}
// 计算过零点数量
numZeroCrossing += ((in_near[0][sampleCntr] ^ in_near[0][sampleCntr - 1]) < 0);
}
/* 根据能量和过零点数判断信号是否低级别 */
if ((frameNrg < 500) || (numZeroCrossing <= 5)) {
stt->lowLevelSignal = 1;
}
else if (numZeroCrossing <= kZeroCrossingLowLim) {
stt->lowLevelSignal = 0;
}
else if (frameNrg <= frameNrgLimit) {
stt->lowLevelSignal = 1;
}
else if (numZeroCrossing >= kZeroCrossingHighLim) {
stt->lowLevelSignal = 1;
}
else {
stt->lowLevelSignal = 0;
}
micLevelTmp = micLevelIn << stt->scale; // 放大输入的麦克风电平
/* 设置期望增益级别 */
gainIdx = stt->micVol;
if (stt->micVol > stt->maxAnalog) {
gainIdx = stt->maxAnalog; // 限制最大增益
}
if (micLevelTmp != stt->micRef) {
/* 如果物理级别发生变化,重置 */
stt->micRef = micLevelTmp;
stt->micVol = 127;
*micLevelOut = 127;
stt->micGainIdx = 127;
gainIdx = 127;
}
/* 预处理信号以模拟麦克风级别 */
if (gainIdx > 127) {
gain = kGainTableVirtualMic[gainIdx - 128]; // 使用增益表调整
}
else {
gain = kSuppressionTableVirtualMic[127 - gainIdx]; // 使用衰减表调整
}
for (ii = 0; ii < samples; ii++) {
tmpFlt = (in_near[0][ii] * gain) >> 10;
if (tmpFlt > 32767) {
tmpFlt = 32767;
gainIdx--;
gain = gainIdx >= 127 ? kGainTableVirtualMic[gainIdx - 127] : kSuppressionTableVirtualMic[127 - gainIdx];
}
if (tmpFlt < -32768) {
tmpFlt = -32768;
gainIdx--;
gain = gainIdx >= 127 ? kGainTableVirtualMic[gainIdx - 127] : kSuppressionTableVirtualMic[127 - gainIdx];
}
in_near[0][ii] = (int16_t)tmpFlt; // 应用增益调整
for (j = 1; j < num_bands; ++j) { // 对所有频带重复上述过程
tmpFlt = (in_near[j][ii] * gain) >> 10;
if (tmpFlt > 32767) {
tmpFlt = 32767;
}
if (tmpFlt < -32768) {
tmpFlt = -32768;
}
in_near[j][ii] = (int16_t)tmpFlt;
}
}
/* 设置最终使用的电平 */
stt->micGainIdx = gainIdx;
*micLevelOut = stt->micGainIdx >> stt->scale; // 输出调整后的麦克风电平
/* 假设输出是真实麦克风的输出 */
if (WebRtcAgc_AddMic(agcInst, in_near, num_bands, samples) != 0) {
return -1; // 如果处理失败,返回错误
}
return 0;
}