WebRTC_AGC——agc.c基础讲解(1)

// 计算增益表函数
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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值