WebRTC_ANS——noise_suppression.c基础讲解(4)

分析核心模块

void WebRtcNs_AnalyzeCore(NoiseSuppressionC *self, const int16_t *speechFrame) {
	size_t i;
	const size_t kStartBand = 5;  // 在估算时跳过最初的频率带。
	int updateParsFlag; // 更新参数标志
	float energy; // 能量
	float signalEnergy = 0.f; // 信号能量
	float sumMagn = 0.f; // 总幅度
	float tmpFloat1, tmpFloat2, tmpFloat3; // 临时浮点数变量
	float winData[ANAL_BLOCKL_MAX]; // 窗口数据
	float lmagn[HALF_ANAL_BLOCKL]; // 对数幅度
	float magn[HALF_ANAL_BLOCKL], noise[HALF_ANAL_BLOCKL]; // 幅度和噪声
	float snrLocPost[HALF_ANAL_BLOCKL], snrLocPrior[HALF_ANAL_BLOCKL], logSnrLocPrior[HALF_ANAL_BLOCKL]; // 后验SNR,先验SNR和其对数值
	float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; // 实部和虚部
	// 启动期间的变量。
	float sum_log_i = 0; // 对数i的总和
	float sum_log_i_square = 0; // 对数i平方的总和
	float sum_log_magn = 0; // 对数幅度的总和
	float sum_log_i_log_magn = 0; // 对数i和对数幅度乘积的总和
	float parametric_exp = 0; // 参数化指数
	float parametric_num = 0; // 参数化分子
	//float db[HALF_ANAL_BLOCKL]; // 存储计算得到的dB值   3-12
	// 检查是否已完成初始化。
	assert(1 == self->initFlag); // 断言初始化标志为1
	updateParsFlag = self->modelUpdatePars[0]; // 更新参数标志

	// 更新L频带的分析缓冲区。
	UpdateBuffer(speechFrame, self->blockLen, self->anaLen, self->analyzeBuf); // 更新缓冲区
	energy = WindowingEnergy(self->window, self->analyzeBuf, self->anaLen, winData); // 计算窗口能量
	if (energy == 0.0) {
		// 在这种情况下我们希望避免更新统计数据:
		// 当我们只有零值时更新特征统计数据会导致
		// 阈值移向零信号情况。这反过来会有
		// 效果是一旦信号“开启”(非零值)一切
		// 都将被视为语音并且没有噪声抑制效果。
		// 根据非活动信号的持续时间,系统学习什么是噪声和
		// 什么是语音需要相当长的时间。
		return; // 如果能量为0,则返回
	}

	self->blockInd++;  // 当我们处理一个块时,只更新块索引。

	FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn, lmagn, 1, &signalEnergy, &sumMagn); // 快速傅里叶变换
	if (self->blockInd < END_STARTUP_SHORT) { // 如果在启动的短期结束之前
		for (i = kStartBand; i < self->magnLen; i++) {
			sum_log_i += self->log_lut[i]; // 对数查找表的总和
			sum_log_i_square += self->log_lut_sqr[i]; // 对数查找表平方的总和
			sum_log_magn += lmagn[i]; // 对数幅度的总和
			sum_log_i_log_magn += self->log_lut[i] * lmagn[i]; // 对数i和对数幅度乘积的总和
		}
	}
	signalEnergy *= self->normMagnLen; // 信号能量乘以归一化幅度长度
	self->signalEnergy = signalEnergy; // 设置信号能量
	self->sumMagn = sumMagn; // 设置总幅度

	// 噪声估计分位数。
	NoiseEstimation(self, lmagn, noise); // 噪声估计
	const float norm = 1.f / (self->blockInd + 1.f); // 归一化因子
	const float norm_end = 1.f / END_STARTUP_SHORT; // 短期结束的归一化因子
	// 在启动期间计算简化的噪声模型。
	if (self->blockInd < END_STARTUP_SHORT) // 如果在短期启动结束之前
	{
		// 估计白噪声。
		self->whiteNoiseLevel += sumMagn * self->normMagnLen * self->overdrive; // 更新白噪声水平
		// 估计粉红噪声参数。
		tmpFloat1 = sum_log_i_square * (self->magnLen - kStartBand); // 计算临时浮点数1
		tmpFloat1 -= (sum_log_i * sum_log_i); // 计算临时浮点数1
		tmpFloat2 = (sum_log_i_square * sum_log_magn - sum_log_i * sum_log_i_log_magn); // 计算临时浮点数2
		tmpFloat3 = tmpFloat2 / tmpFloat1; // 计算临时浮点数3
		// 约束估计的谱线为正值。
		if (tmpFloat3 < 0.f) {
			tmpFloat3 = 0.f; // 如果临时浮点数3小于0,则设置为0
		}
		self->pinkNoiseNumerator += tmpFloat3; // 更新粉红噪声分子
		tmpFloat2 = (sum_log_i * sum_log_magn); // 计算临时浮点数2
		tmpFloat2 -= (self->magnLen - kStartBand) * sum_log_i_log_magn; // 计算临时浮点数2
		tmpFloat3 = tmpFloat2 / tmpFloat1; // 计算临时浮点数3
		// 约束粉红噪声功率在区间[0, 1]内。
		if (tmpFloat3 < 0.f) {
			tmpFloat3 = 0.f; // 如果临时浮点数3小于0,则设置为0
		}
		if (tmpFloat3 > 1.f) {
			tmpFloat3 = 1.f; // 如果临时浮点数3大于1,则设置为1
		}
		self->pinkNoiseExp += tmpFloat3; // 更新粉红噪声指数
		if (self->pinkNoiseExp == 0.f) {
			for (i = 0; i < self->magnLen; i++) {
				// 使用白噪声和粉红噪声参数估计背景噪声。
				self->parametricNoise[i] = self->whiteNoiseLevel; // 设置参数化噪声
				// 用模型噪声加权分位数噪声。
				noise[i] *= (self->blockInd); // 加权噪声
				tmpFloat2 = self->parametricNoise[i] * (END_STARTUP_SHORT - self->blockInd); // 计算临时浮点数2
				noise[i] += tmpFloat2 * norm; // 更新噪声
				noise[i] *= norm_end; // 归一化噪声
			}
		}
		else {
			// 计算参数化噪声估计的频率独立部分。

			// 使用粉红噪声估计。
			parametric_num = expf(self->pinkNoiseNumerator * norm); // 计算参数化分子
			parametric_num *= (float)(self->blockInd + 1); // 计算参数化分子
			parametric_exp = self->pinkNoiseExp * norm; // 计算参数化指数
			for (i = 0; i < self->magnLen; i++) {
				// 使用白噪声和粉红噪声参数估计背景噪声。
				// 使用粉红噪声估计。
				float use_band = (float)(i < kStartBand ? kStartBand : i); // 计算使用频带
				self->parametricNoise[i] = parametric_num / powf(use_band, parametric_exp); // 设置参数化噪声
				// 用模型噪声加权分位数噪声。
				noise[i] *= (self->blockInd); // 加权噪声
				tmpFloat2 = self->parametricNoise[i] * (END_STARTUP_SHORT - self->blockInd); // 计算临时浮点数2
				noise[i] += tmpFloat2 * norm; // 更新噪声
				noise[i] *= norm_end; // 归一化噪声
			}
		}
	}

	// 在END_STARTUP_LONG时间内计算平均信号:
	// 用于标准化频谱差异测量。
	if (self->blockInd < END_STARTUP_LONG) { // 如果在长期启动结束之前
		self->featureData[5] *= self->blockInd; // 更新特征数据5
		self->featureData[5] += signalEnergy; // 更新特征数据5
		self->featureData[5] *= norm; // 归一化特征数据5
	}

	// 计算后验SNR和先验SNR,这些对于SpeechNoiseProb是必需的。
	ComputeSnr(self, magn, noise, snrLocPrior, logSnrLocPrior, snrLocPost); // 计算SNR


    FeatureUpdate(self, magn, lmagn, updateParsFlag);// 更新噪声特征。
    SpeechNoiseProb(self, self->speechProb, snrLocPrior, logSnrLocPrior, snrLocPost);// 计算语音/噪声概率。
    UpdateNoiseEstimate(self, magn, noise);// 更新噪声估计。

	float sumOfPowers = 0; // 用于累加所有幅度值的平方,因为分贝值是基于功率的
	for (int i = 0; i < HALF_ANAL_BLOCKL; i++) {
		sumOfPowers += pow(noise[i], 2); // 将幅度值转换为功率值(假设幅度正比于根号下的功率)
	}

	float averagePower = sumOfPowers / HALF_ANAL_BLOCKL; // 计算平均功率
	float averageDB;

	if (averagePower == 0) {
		averageDB = -INFINITY; // 如果平均功率为0,则分贝值为负无穷
	}
	else {
		averageDB = 10 * log10(averagePower); // 将平均功率转换为分贝值
	}

	printf("Average dB for the frame: %f\n", averageDB);
	// 更新全局噪声估计值
	g_averageDB = averageDB;

    // Keep track of noise spectrum for next frame.
    memcpy(self->noise, noise, sizeof(*noise) * self->magnLen);
    memcpy(self->magnPrevAnalyze, magn, sizeof(*magn) * self->magnLen);
}

噪声抑制的主要处理函数

void WebRtcNs_ProcessCore(NoiseSuppressionC *self,
	const int16_t *const *speechFrame,
	size_t num_bands,
	int16_t *const *outFrame) {
	// 主要的噪声减少例程。
	int flagHB = 0;
	size_t i, j;

	float energy1, energy2, gain, factor, factor1, factor2;
	float fout[BLOCKL_MAX]; // 输出缓冲区
	float winData[ANAL_BLOCKL_MAX]; // 窗函数处理后的数据
	float magn[HALF_ANAL_BLOCKL]; // 频谱幅度
	float theFilter[HALF_ANAL_BLOCKL], theFilterTmp[HALF_ANAL_BLOCKL]; // 滤波器系数
	float real[ANAL_BLOCKL_MAX], imag[HALF_ANAL_BLOCKL]; // FFT结果的实部和虚部
	float norm_end = 1.f / END_STARTUP_SHORT; // 初始化期结束后的归一化系数
	// 宽带变量
	int deltaBweHB = 1;
	int deltaGainHB = 1;
	float decayBweHB = 1;
	float gainMapParHB = 1;
	float avgProbSpeechHB, avgProbSpeechHBTmp, avgFilterGainHB, gainModHB;
	float sumMagnAnalyze, sumMagnProcess;

	// 检查是否已完成初始化。
	assert(1 == self->initFlag);
	assert(num_bands - 1 <= NUM_HIGH_BANDS_MAX);

	const int16_t *const *speechFrameHB = NULL; // 高频带的语音帧
	int16_t *const *outFrameHB = NULL; // 高频带的输出帧
	size_t num_high_bands = 0; // 高频带的数量
	if (num_bands > 1) {
		speechFrameHB = &speechFrame[1];
		outFrameHB = &outFrame[1];
		num_high_bands = num_bands - 1;
		flagHB = 1;
		// 用于平均低频带量以获得高带增益的范围。
		deltaBweHB = (int)self->magnLen / 4;
		deltaGainHB = deltaBweHB;
	}

	// 更新L带的分析缓冲区。
	UpdateBuffer(speechFrame[0], self->blockLen, self->anaLen, self->dataBuf);

	if (flagHB == 1) {
		// 更新高频带的分析缓冲区。
		for (i = 0; i < num_high_bands; ++i) {
			UpdateBuffer(speechFrameHB[i],
				self->blockLen,
				self->anaLen,
				self->dataBufHB[i]);
		}
	}
	// 计算窗函数处理后的能量。
	energy1 = WindowingEnergy(self->window, self->dataBuf, self->anaLen, winData);
	if (energy1 == 0.0) {
		// 特殊情况处理:输入为零。
		// 读出完全处理过的段。
		for (i = self->windShift; i < self->blockLen + self->windShift; i++) {
			fout[i - self->windShift] = self->syntBuf[i];
		}
		// 更新合成缓冲区。
		UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf);

		for (i = 0; i < self->blockLen; ++i)
			outFrame[0][i] =
			SPL_SAT(32767, fout[i], (-32768));

		// 对于高带的时域增益处理。
		if (flagHB == 1) {
			for (i = 0; i < num_high_bands; ++i) {
				for (j = 0; j < self->blockLen; ++j) {
					outFrameHB[i][j] = SPL_SAT(32767,
						self->dataBufHB[i][j],
						(-32768));
				}
			}
		}

		return;
	}

	// 执行FFT,得到频谱。
	FFT(self, winData, self->anaLen, self->magnLen, real, imag, magn, NULL, 0, NULL, NULL);

	// 如果还在启动阶段,更新初始幅度估计。
	if (self->blockInd < END_STARTUP_SHORT) {
		for (i = 0; i < self->magnLen; i++) {
			self->initMagnEst[i] += magn[i];
		}
	}

	// 计算基于决策导向的Wiener滤波器。
	ComputeDdBasedWienerFilter(self, magn, theFilter);
	// 在启动阶段结束前,调整滤波器系数。
	if (self->blockInd < END_STARTUP_SHORT) {
		for (i = 0; i < self->magnLen; i++) {
			theFilterTmp[i] = 1.f - (self->overdrive * self->parametricNoise[i]) / (self->initMagnEst[i] + epsilon);
			// 底部限制。
			if (theFilterTmp[i] < self->denoiseBound) {
				theFilterTmp[i] = self->denoiseBound;
			}
			// 顶部限制。
			if (theFilterTmp[i] > 1.f) {
				theFilterTmp[i] = 1.f;
			}
			// 权衡两个抑制滤波器。
			theFilter[i] *= (self->blockInd);
			theFilterTmp[i] *= (END_STARTUP_SHORT - self->blockInd);
			theFilter[i] += theFilterTmp[i];
			theFilter[i] *= norm_end;
			self->smooth[i] = theFilter[i];
			real[i] *= self->smooth[i];
			imag[i] *= self->smooth[i];
		}
	}
	else {
		// 启动阶段结束后的处理。
		for (i = 0; i < self->magnLen; i++) {
			// 底部限制。
			if (theFilter[i] < self->denoiseBound) {
				theFilter[i] = self->denoiseBound;
			}
			// 顶部限制。
			if (theFilter[i] > 1.f) {
				theFilter[i] = 1.f;
				self->smooth[i] = theFilter[i];
			}
			else {
				self->smooth[i] = theFilter[i];
				real[i] *= self->smooth[i];
				imag[i] *= self->smooth[i];
			}
		}
	}
	// 为下一帧保留|magn|频谱。
	memcpy(self->magnPrevProcess, magn, sizeof(*magn) * self->magnLen);
	memcpy(self->noisePrev, self->noise, sizeof(self->noise[0]) * self->magnLen);
	// 返回时域。
	IFFT(self, real, imag, self->magnLen, self->anaLen, winData);

	// 缩放因子:只在END_STARTUP_LONG时间后做。
	factor = 1.f;

	// 如果开启增益映射并且已超过启动长期阶段。
	if (self->gainmap == 1 && self->blockInd > END_STARTUP_LONG) {
		factor1 = 1.f;
		factor2 = 1.f;
		// 计算增益。
		energy2 = Energy(winData, self->anaLen);
		gain = sqrtf(energy2 / (energy1 + epsilon) + epsilon_squ);

		// 新版本的缩放处理。
		if (gain > B_LIM) {
			factor1 = 1.f + 1.3f * (gain - B_LIM);
			if (gain * factor1 > 1.f) {
				factor1 = 1.f / gain;
			}
		}
		if (gain < B_LIM) {
			// 对于暂停区域,不要太多减小缩放:
			// 这里的衰减应该由底限控制。
			if (gain <= self->denoiseBound) {
				gain = self->denoiseBound;
			}
			factor2 = 1.f - 0.3f * (B_LIM - gain);
		}
		// 结合两种缩放与语音/噪声概率:
		// 注意,先验概率(priorSpeechProb)不依赖于频率。
		factor = self->priorSpeechProb * factor1 +
			(1.f - self->priorSpeechProb) * factor2;
	}  // self->gainmap == 1的处理结束。

	// 合成。
	for (i = 0; i < self->anaLen; i++) {
		self->syntBuf[i] += factor * winData[i] * self->window[i];
	}
	// 读出完全处理过的段。
	for (i = self->windShift; i < self->blockLen + self->windShift; i++) {
		fout[i - self->windShift] = self->syntBuf[i];
	}
	// 更新合成缓冲区。
	UpdateBuffer(NULL, self->blockLen, self->anaLen, self->syntBuf);

	for (i = 0; i < self->blockLen; ++i)
		outFrame[0][i] =
		SPL_SAT(32767, fout[i], (-32768));

	// 对于高带的时域增益。
	if (flagHB == 1) {
		// 从低带平均语音概率。
		// 在频谱的后半部分(即,4->8kHz)上平均。
		avgProbSpeechHB = 0;
		for (i = self->magnLen - deltaBweHB - 1; i < self->magnLen - 1; i++) {
			avgProbSpeechHB += self->speechProb[i];
		}
		avgProbSpeechHB = avgProbSpeechHB / ((float)deltaBweHB);
		// 如果语音被Analyze和Process之间的组件抑制,比如AEC,则不应该考虑为高带抑制目的的语音。
		sumMagnAnalyze = 0;
		sumMagnProcess = 0;
		for (i = 0; i < self->magnLen; ++i) {
			sumMagnAnalyze += self->magnPrevAnalyze[i];
			sumMagnProcess += self->magnPrevProcess[i];
		}
		avgProbSpeechHB *= sumMagnProcess / sumMagnAnalyze;
		// 从低带平均滤波器增益。
		// 在频谱的后半部分(即,4->8kHz)上平均。
		avgFilterGainHB = 0;
		for (i = self->magnLen - deltaGainHB - 1; i < self->magnLen - 1; i++) {
			avgFilterGainHB += self->smooth[i];
		}
		avgFilterGainHB = avgFilterGainHB / ((float)(deltaGainHB));
		avgProbSpeechHBTmp = 2.f * avgProbSpeechHB - 1.f;
		// 基于语音概率的增益。
		gainModHB = 0.5f + 0.5f * tanhf(gainMapParHB * avgProbSpeechHBTmp);
		// 结合低带增益。
		float gainTimeDomainHB = 0.5f * (gainModHB + avgFilterGainHB);
		if (avgProbSpeechHB >= 0.5f) {
			gainTimeDomainHB = 0.25f * gainModHB + 0.75f * avgFilterGainHB;
		}
		gainTimeDomainHB = gainTimeDomainHB * decayBweHB;
		// 确保增益在限制范围内。
		// 底部限制。
		if (gainTimeDomainHB < self->denoiseBound) {
			gainTimeDomainHB = self->denoiseBound;
		}
		// 顶部限制。
		if (gainTimeDomainHB > 1.f) {
			gainTimeDomainHB = 1.f;
		}
		// 应用增益。
		for (i = 0; i < num_high_bands; ++i) {
			for (j = 0; j < self->blockLen; j++) {
				outFrameHB[i][j] =
					SPL_SAT(32767,
						gainTimeDomainHB * self->dataBufHB[i][j],
						(-32768));
			}
		}
	}  // 高带增益计算结束。
}

创建一个噪声抑制实例,并返回其句柄

NsHandle *WebRtcNs_Create() {
	// 分配噪声抑制实例的内存。
	NoiseSuppressionC *self = (NoiseSuppressionC *)malloc(sizeof(NoiseSuppressionC));
	if (self != NULL) {
		// 初始化标志设置为0,表示尚未初始化。
		self->initFlag = 0;
	}
	// 将实例转换为通用句柄类型并返回。
	return (NsHandle *)self;
}

释放噪声抑制实例占用的内存

void WebRtcNs_Free(NsHandle *NS_inst) {
	// 如果实例非空,则释放其占用的内存。
	if (NS_inst)
		free(NS_inst);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值