设置特征提取参数的函数
static void set_feature_extraction_parameters(NoiseSuppressionC *self) {
// 直方图的bin大小设置。
self->featureExtractionParams.binSizeLrt = 0.1f; // LRT(长时平均比率测试)的bin大小。
self->featureExtractionParams.binSizeSpecFlat = 0.05f; // 频谱平坦度的bin大小。
self->featureExtractionParams.binSizeSpecDiff = 0.1f; // 频谱差异的bin大小。
// 计算LRT阈值的直方图范围。
self->featureExtractionParams.rangeAvgHistLrt = 1.f; // LRT直方图的平均范围。
// 缩放参数:通过缩放因子乘以直方图的主要峰值,以获取先验模型的阈值。
// 用于LRT和频谱差异的缩放因子。
self->featureExtractionParams.factor1ModelPars = 1.2f;
// 用于频谱平坦度:当噪声比语音更平坦时使用。
self->featureExtractionParams.factor2ModelPars = 0.9f;
// 频谱平坦度的峰值限制(在0到1之间变化)。
self->featureExtractionParams.thresPosSpecFlat = 0.6f;
// 直方图中两个最高峰之间间距的限制:间距由bin大小决定。
self->featureExtractionParams.limitPeakSpacingSpecFlat =
2 * self->featureExtractionParams.binSizeSpecFlat; // 频谱平坦度。
self->featureExtractionParams.limitPeakSpacingSpecDiff =
2 * self->featureExtractionParams.binSizeSpecDiff; // 频谱差异。
// 第二个峰值相关性的限制。
self->featureExtractionParams.limitPeakWeightsSpecFlat = 0.5f; // 频谱平坦度。
self->featureExtractionParams.limitPeakWeightsSpecDiff = 0.5f; // 频谱差异。
// LRT特征的波动限制。
self->featureExtractionParams.thresFluctLrt = 0.05f;
// 特征阈值的最大值和最小值的限制。
self->featureExtractionParams.maxLrt = 1.f; // LRT的最大值。
self->featureExtractionParams.minLrt = 0.2f; // LRT的最小值。
self->featureExtractionParams.maxSpecFlat = 0.95f; // 频谱平坦度的最大值。
self->featureExtractionParams.minSpecFlat = 0.1f; // 频谱平坦度的最小值。
self->featureExtractionParams.maxSpecDiff = 1.f; // 频谱差异的最大值。
self->featureExtractionParams.minSpecDiff = 0.16f; // 频谱差异的最小值。
// 接受/拒绝特征的直方图峰值权重标准。
self->featureExtractionParams.thresWeightSpecFlat =
(int)(0.3 * (self->modelUpdatePars[1])); // 频谱平坦度。
self->featureExtractionParams.thresWeightSpecDiff =
(int)(0.3 * (self->modelUpdatePars[1])); // 频谱差异。
}
初始化状态的函数
int WebRtcNs_InitCore(NoiseSuppressionC *self, uint32_t fs) {
int i;
// 检查指针是否有效。
if (self == NULL) {
return -1; // 如果self指针无效,返回-1。
}
// 初始化结构体。
if (fs == 8000 || fs == 16000) {
self->fs = fs; // 只支持8000Hz或16000Hz的采样率。
}
else {
return -1; // 如果采样率不是8000Hz或16000Hz,返回-1。
}
self->windShift = 0; // 初始化窗口偏移量。
// 我们只支持10ms帧。
if (fs == 8000) {
self->blockLen = 80; // 对于8000Hz采样率,每个块的长度设置为80个样本。
self->anaLen = 128; // 分析长度设置为128。
self->window = kBlocks80w128; // 使用特定于8000Hz的窗函数。
}
else {
self->blockLen = 160; // 对于16000Hz采样率,每个块的长度设置为160个样本。
self->anaLen = 256; // 分析长度设置为256。
self->window = kBlocks160w256; // 使用特定于16000Hz的窗函数。
}
self->magnLen = self->anaLen / 2 + 1; // 计算频率bin的数量。
self->normMagnLen = 1.f / self->magnLen; // 频率bin数量的倒数,用于归一化。
// 初始化FFT工作数组。
self->ip[0] = 0; // 设置这个值触发初始化。
memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX); // 初始化数据缓冲区。
WebRtc_rdft(self->anaLen, 1, self->dataBuf, self->ip, self->wfft); // 执行一次FFT以触发FFT初始化。
// 初始化各种缓冲区。
memset(self->analyzeBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
memset(self->dataBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
memset(self->syntBuf, 0, sizeof(float) * ANAL_BLOCKL_MAX);
// 高带处理的初始化。
memset(self->dataBufHB, 0, sizeof(float) * NUM_HIGH_BANDS_MAX * ANAL_BLOCKL_MAX);
// 用于量化噪声估计的初始化。
memset(self->quantile, 0, sizeof(float) * HALF_ANAL_BLOCKL);
for (i = 0; i < SIMULT * HALF_ANAL_BLOCKL; i++) {
self->lquantile[i] = 8.f; // 初始化长期量化阈值。
self->density[i] = 0.3f; // 初始化密度估计值。
}
// 初始化直方图计数器。
for (i = 0; i < SIMULT; i++) {
self->counter[i] = (int)floorf((float)(END_STARTUP_LONG * (i + 1)) / (float)SIMULT);
}
self->updates = 0; // 初始化更新计数器。
// 维纳滤波器的初始化。
for (i = 0; i < HALF_ANAL_BLOCKL; i++) {
self->smooth[i] = 1.f; // 初始化平滑参数。
self->log_lut[i] = log1pf((float)i); // 初始化对数查找表。
self->log_lut_sqr[i] = self->log_lut[i] * self->log_lut[i]; // 初始化对数查找表的平方。
}
// 设置默认的侵略模式。
self->aggrMode = 0;
// 初始化新方法的变量。
self->priorSpeechProb = 0.5f; // 语音/噪声的先验概率。
// 初始化各种频谱缓冲区。
memset(self->magnPrevAnalyze, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->magnPrevProcess, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->noise, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->noisePrev, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->magnAvgPause, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->speechProb, 0, sizeof(float) * HALF_ANAL_BLOCKL);
memset(self->initMagnEst, 0, sizeof(float) * HALF_ANAL_BLOCKL);
for (i = 0; i < HALF_ANAL_BLOCKL; i++) {
self->logLrtTimeAvg[i] = LRT_FEATURE_THR; // 初始化LRT特征的平滑长期比率测试阈值。
}
// 初始化特征量。
self->featureData[0] = SF_FEATURE_THR; // 频谱平坦度(从阈值开始)。
self->featureData[1] = 0.f; // 频谱熵:在这个版本中不使用。
self->featureData[2] = 0.f; // 频谱方差:在这个版本中不使用。
self->featureData[3] = LRT_FEATURE_THR; // 平均LRT因子(从阈值开始)。
self->featureData[4] = SF_FEATURE_THR; // 频谱模板差异(从阈值开始)。
self->featureData[5] = 0.f; // 频谱差异的归一化。
self->featureData[6] = 0.f; // 输入幅度谱的窗口时间平均值。
// 初始化直方图量:用于估计/更新特征的阈值。
memset(self->histLrt, 0, sizeof(int) * HIST_PAR_EST);
memset(self->histSpecFlat, 0, sizeof(int) * HIST_PAR_EST);
memset(self->histSpecDiff, 0, sizeof(int) * HIST_PAR_EST);
self->blockInd = -1; // 帧计数器初始化。
// 初始化模型参数。
self->priorModelPars[0] = LRT_FEATURE_THR;
self->priorModelPars[1] = 0.5f;
self->priorModelPars[2] = 1.f;
self->priorModelPars[3] = 0.5f;
self->priorModelPars[4] = 1.f;
self->priorModelPars[5] = 0.f;
self->priorModelPars[6] = 0.f;
// 初始化模型更新参数。
self->modelUpdatePars[0] = 2;
self->modelUpdatePars[1] = 500; // 更新窗口。
self->modelUpdatePars[2] = 0; // 保守噪声谱更新计数器。
self->modelUpdatePars[3] = self->modelUpdatePars[1]; // 特征阈值更新计数器。
// 初始化声音能量和其他统计量。
self->signalEnergy = 0;
self->sumMagn = 0;
self->whiteNoiseLevel = 0;
self->pinkNoiseNumerator = 0;
self->pinkNoiseExp = 0;
// 设置特征提取参数。
set_feature_extraction_parameters(self);
// 设置默认策略。
WebRtcNs_set_policy_core(self, 0);
self->initFlag = 1; // 标记初始化完成。
return 0; // 初始化成功返回0。
}
噪声估计函数
static void NoiseEstimation(NoiseSuppressionC *self, float *lmagn, float *noise) {
size_t i, s, offset = 0; // 声明循环计数器和偏移量变量。
float delta; // 声明用于更新量化估计的变量delta。
// 如果更新次数少于END_STARTUP_LONG,更新计数。
if (self->updates < END_STARTUP_LONG) {
self->updates++;
}
// 遍历所有同时估计。
for (s = 0; s < SIMULT; s++) {
offset = s * self->magnLen; // 计算当前估计的偏移量。
float norm_counter_weight = 1.f / (self->counter[s] + 1.f); // 计算归一化计数器权重。
float density_plus_weight = norm_counter_weight / (2.f * WIDTH); // 计算密度加权。
// 对每个频率bin进行新的量化估计。
for (i = 0; i < self->magnLen; i++) {
// 计算更新步长delta。
if (self->density[offset + i] > 1.0) {
delta = FACTOR / self->density[offset + i];
}
else {
delta = FACTOR;
}
// 更新对数量化估计值。
if (lmagn[i] > self->lquantile[offset + i]) {
self->lquantile[offset + i] += QUANTILE * delta * norm_counter_weight;
}
else {
self->lquantile[offset + i] -= (1.f - QUANTILE) * delta * norm_counter_weight;
}
// 更新密度估计值。
if (fabsf(lmagn[i] - self->lquantile[offset + i]) < WIDTH) {
self->density[offset + i] = self->counter[s] * self->density[offset + i] * norm_counter_weight + density_plus_weight;
}
} // 结束频谱幅度循环。
// 如果达到启动结束阶段,重置计数器。
if (self->counter[s] >= END_STARTUP_LONG) {
self->counter[s] = 0;
if (self->updates >= END_STARTUP_LONG) {
// 如果更新次数也超过启动阶段,更新噪声量化值。
for (i = 0; i < self->magnLen; i++) {
self->quantile[i] = expf(self->lquantile[offset + i]); // 将对数量化值转换回线性域。
}
}
}
self->counter[s]++; // 更新当前估计的计数器。
} // 结束所有同时估计的循环。
// 在启动期间序列更新噪声。
if (self->updates < END_STARTUP_LONG) {
// 在启动阶段使用最后一个“s”获取非零的噪声估计值。
for (i = 0; i < self->magnLen; i++) {
self->quantile[i] = expf(self->lquantile[offset + i]); // 将对数量化值转换回线性域。
}
memcpy(noise, self->quantile, self->magnLen * sizeof(*noise)); // 将噪声估计值复制到输出数组。
}
else {
memcpy(noise, self->quantile, self->magnLen * sizeof(*noise)); // 启动完成后,直接复制噪声估计值到输出数组。
}
}
特征参数提取函数定义
// 该函数用于更新直方图或计算阈值和权重。
static void FeatureParameterExtraction(NoiseSuppressionC *self, int flag) {
// 定义局部变量
int i, useFeatureSpecFlat, useFeatureSpecDiff, numHistLrt;
int maxPeak1, maxPeak2;
int weightPeak1SpecFlat, weightPeak2SpecFlat, weightPeak1SpecDiff,
weightPeak2SpecDiff;
float binMid, featureSum;
float posPeak1SpecFlat, posPeak2SpecFlat, posPeak1SpecDiff, posPeak2SpecDiff;
float fluctLrt, avgHistLrt, avgSquareHistLrt, avgHistLrtCompl;
// 定义三个特征:长时平均比(LRT)、平坦度(flatness)和差异(difference)。
// lrt_feature = self->featureData[3];
// flat_feature = self->featureData[0];
// diff_feature = self->featureData[4];
// 更新直方图。
if (flag == 0) {
// 长时平均比(LRT)
if ((self->featureData[3] <
HIST_PAR_EST * self->featureExtractionParams.binSizeLrt) &&
(self->featureData[3] >= 0.0)) {
i = (int)(self->featureData[3] /
self->featureExtractionParams.binSizeLrt);
self->histLrt[i]++;
}
// 频谱平坦度
if ((self->featureData[0] <
HIST_PAR_EST * self->featureExtractionParams.binSizeSpecFlat) &&
(self->featureData[0] >= 0.0)) {
i = (int)(self->featureData[0] /
self->featureExtractionParams.binSizeSpecFlat);
self->histSpecFlat[i]++;
}
// 频谱差异
if ((self->featureData[4] <
HIST_PAR_EST * self->featureExtractionParams.binSizeSpecDiff) &&
(self->featureData[4] >= 0.0)) {
i = (int)(self->featureData[4] /
self->featureExtractionParams.binSizeSpecDiff);
self->histSpecDiff[i]++;
}
}
// 如果flag为1,则执行参数提取操作。
if (flag == 1) {
// 初始化局部变量
avgHistLrt = 0;
avgHistLrtCompl = 0;
avgSquareHistLrt = 0;
numHistLrt = 0;
// 计算直方图的平均值和方差
for (i = 0; i < HIST_PAR_EST; i++) {
binMid = ((float)i + 0.5f) * self->featureExtractionParams.binSizeLrt;
if (binMid <= self->featureExtractionParams.rangeAvgHistLrt) {
avgHistLrt += self->histLrt[i] * binMid;
numHistLrt += self->histLrt[i];
}
avgSquareHistLrt += self->histLrt[i] * binMid * binMid;
avgHistLrtCompl += self->histLrt[i] * binMid;
}
// 计算平均值
if (numHistLrt > 0) {
avgHistLrt = avgHistLrt / ((float)numHistLrt);
}
avgHistLrtCompl = avgHistLrtCompl / ((float)self->modelUpdatePars[1]);
avgSquareHistLrt = avgSquareHistLrt / ((float)self->modelUpdatePars[1]);
fluctLrt = avgSquareHistLrt - avgHistLrt * avgHistLrtCompl;
// 计算LRT特征的阈值
if (fluctLrt < self->featureExtractionParams.thresFluctLrt) {
// 如果波动很小,认为是噪声
self->priorModelPars[0] = self->featureExtractionParams.maxLrt;
}
else {
self->priorModelPars[0] =
self->featureExtractionParams.factor1ModelPars * avgHistLrt;
// 检查值是否在最小和最大范围内
if (self->priorModelPars[0] < self->featureExtractionParams.minLrt) {
self->priorModelPars[0] = self->featureExtractionParams.minLrt;
}
if (self->priorModelPars[0] > self->featureExtractionParams.maxLrt) {
self->priorModelPars[0] = self->featureExtractionParams.maxLrt;
}
}
// 完成LRT特征的处理
// 对于频谱平坦度和频谱差异,计算直方图的主要峰值。
maxPeak1 = 0;
maxPeak2 = 0;
posPeak1SpecFlat = 0;
posPeak2SpecFlat = 0;
weightPeak1SpecFlat = 0;
weightPeak2SpecFlat = 0;
// 计算平坦度的峰值
for (i = 0; i < HIST_PAR_EST; i++) {
binMid = (i + 0.5f) * self->featureExtractionParams.binSizeSpecFlat;
if (self->histSpecFlat[i] > maxPeak1) {
// 发现新的“第一”峰值
maxPeak2 = maxPeak1;
weightPeak2SpecFlat = weightPeak1SpecFlat;
posPeak2SpecFlat = posPeak1SpecFlat;
maxPeak1 = self->histSpecFlat[i];
weightPeak1SpecFlat = self->histSpecFlat[i];
posPeak1SpecFlat = binMid;
}
else if (self->histSpecFlat[i] > maxPeak2) {
// 发现新的“第二”峰值
maxPeak2 = self->histSpecFlat[i];
weightPeak2SpecFlat = self->histSpecFlat[i];
posPeak2SpecFlat = binMid;
}
}
// 对于频谱差异也是同样的计算过程
maxPeak1 = 0;
maxPeak2 = 0;
posPeak1SpecDiff = 0;
posPeak2SpecDiff = 0;
weightPeak1SpecDiff = 0;
weightPeak2SpecDiff = 0;
for (i = 0; i < HIST_PAR_EST; i++) {
binMid =
((float)i + 0.5f) * self->featureExtractionParams.binSizeSpecDiff;
if (self->histSpecDiff[i] > maxPeak1) {
// 同上,寻找“第一”和“第二”峰值
maxPeak2 = maxPeak1;
weightPeak2SpecDiff = weightPeak1SpecDiff;
posPeak2SpecDiff = posPeak1SpecDiff;
maxPeak1 = self->histSpecDiff[i];
weightPeak1SpecDiff = self->histSpecDiff[i];
posPeak1SpecDiff = binMid;
}
else if (self->histSpecDiff[i] > maxPeak2) {
maxPeak2 = self->histSpecDiff[i];
weightPeak2SpecDiff = self->histSpecDiff[i];
posPeak2SpecDiff = binMid;
}
}
// 对于频谱平坦度特征的处理
useFeatureSpecFlat = 1;
// 如果两个峰值很接近,则合并这两个峰值
if ((fabsf(posPeak2SpecFlat - posPeak1SpecFlat) <
self->featureExtractionParams.limitPeakSpacingSpecFlat) &&
(weightPeak2SpecFlat >
self->featureExtractionParams.limitPeakWeightsSpecFlat *
weightPeak1SpecFlat)) {
weightPeak1SpecFlat += weightPeak2SpecFlat;
posPeak1SpecFlat = 0.5f * (posPeak1SpecFlat + posPeak2SpecFlat);
}
// 如果峰值的权重不够大,或峰值太小,则不使用此特征
if (weightPeak1SpecFlat <
self->featureExtractionParams.thresWeightSpecFlat ||
posPeak1SpecFlat < self->featureExtractionParams.thresPosSpecFlat) {
useFeatureSpecFlat = 0;
}
// 如果选中了此特征,计算阈值
if (useFeatureSpecFlat == 1) {
self->priorModelPars[1] =
self->featureExtractionParams.factor2ModelPars * posPeak1SpecFlat;
// 检查值是否在最小和最大范围内
if (self->priorModelPars[1] < self->featureExtractionParams.minSpecFlat) {
self->priorModelPars[1] = self->featureExtractionParams.minSpecFlat;
}
if (self->priorModelPars[1] > self->featureExtractionParams.maxSpecFlat) {
self->priorModelPars[1] = self->featureExtractionParams.maxSpecFlat;
}
}
// 完成平坦度特征的处理
// 对于频谱差异特征的处理
useFeatureSpecDiff = 1;
// 如果两个峰值很接近,也合并这两个峰值
if ((fabsf(posPeak2SpecDiff - posPeak1SpecDiff) <
self->featureExtractionParams.limitPeakSpacingSpecDiff) &&
(weightPeak2SpecDiff >
self->featureExtractionParams.limitPeakWeightsSpecDiff *
weightPeak1SpecDiff)) {
weightPeak1SpecDiff += weightPeak2SpecDiff;
posPeak1SpecDiff = 0.5f * (posPeak1SpecDiff + posPeak2SpecDiff);
}
// 计算阈值
self->priorModelPars[3] =
self->featureExtractionParams.factor1ModelPars * posPeak1SpecDiff;
// 如果峰值的权重不够大,则不使用此特征
if (weightPeak1SpecDiff <
self->featureExtractionParams.thresWeightSpecDiff) {
useFeatureSpecDiff = 0;
}
// 检查值是否在最小和最大范围内
if (self->priorModelPars[3] < self->featureExtractionParams.minSpecDiff) {
self->priorModelPars[3] = self->featureExtractionParams.minSpecDiff;
}
if (self->priorModelPars[3] > self->featureExtractionParams.maxSpecDiff) {
self->priorModelPars[3] = self->featureExtractionParams.maxSpecDiff;
}
// 完成频谱差异特征的处理
// 如果LRT特征的波动很小,大概率只是噪声状态,不使用频谱差异特征
if (fluctLrt < self->featureExtractionParams.thresFluctLrt) {
useFeatureSpecDiff = 0;
}
// 选择各特征之间的权重
featureSum = (float)(1 + useFeatureSpecFlat + useFeatureSpecDiff);
self->priorModelPars[4] = 1.f / featureSum; // LRT特征的权重
self->priorModelPars[5] = ((float)useFeatureSpecFlat) * self->priorModelPars[4]; // 频谱平坦度特征的权重
self->priorModelPars[6] = ((float)useFeatureSpecDiff) * self->priorModelPars[4]; // 频谱差异特征的权重
// 为下一次更新将直方图清零
if (self->modelUpdatePars[0] >= 1) {
for (i = 0; i < HIST_PAR_EST; i++) {
self->histLrt[i] = 0;
self->histSpecFlat[i] = 0;
self->histSpecDiff[i] = 0;
}
}
} // flag == 1的处理结束。
}
计算输入频谱的频谱平坦度
// magnIn 是幅度谱。
// 频谱平坦度结果存储在 self->featureData[0] 中。
static void ComputeSpectralFlatness(NoiseSuppressionC *self,
const float *magnIn, const float *logmagnIn) {
size_t i;
size_t shiftLP = 1; // 用于从频谱计算中移除第一个bin(或多个bin)的选项。
float avgSpectralFlatnessNum, avgSpectralFlatnessDen, spectralTmp;
// 计算频谱指标。
// 对于平坦度。
avgSpectralFlatnessNum = 0;
avgSpectralFlatnessDen = self->sumMagn;
// 从频谱指标中移除前 shiftLP 个bin的影响。
for (i = 0; i < shiftLP; i++) {
avgSpectralFlatnessDen -= magnIn[i];
}
// 计算几何平均数与算术平均数之比的对数:检查 log(0) 的情况。
for (i = shiftLP; i < self->magnLen; i++) {
if (magnIn[i] > 0.0) {
avgSpectralFlatnessNum += logmagnIn[i];
}
else {
// 如果某个频率分量的幅度为零,更新频谱平坦度特征,并返回。
self->featureData[0] -= SPECT_FL_TAVG * self->featureData[0];
return;
}
}
// 归一化。
avgSpectralFlatnessDen = avgSpectralFlatnessDen * self->normMagnLen;
avgSpectralFlatnessNum = avgSpectralFlatnessNum * self->normMagnLen;
// 计算比值和对数的逆:检查 log(0) 的情况。
spectralTmp = expf(avgSpectralFlatnessNum) / avgSpectralFlatnessDen;
// 频谱平坦度特征的时间平均更新。
self->featureData[0] += SPECT_FL_TAVG * (spectralTmp - self->featureData[0]);
// 完成平坦度特征的计算。
}