分析核心模块
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);
}