计算基于分位数噪声估计的先验和后验信噪比,以及先验SNR的DD估计
// 输入:
// * |magn| 是信号幅度谱估计。
// * |noise| 是幅度噪声谱估计。
// 输出:
// * |snrLocPrior| 是计算得到的先验SNR。
// * |snrLocPost| 是计算得到的后验SNR。
static void ComputeSnr(const NoiseSuppressionC *self,
const float *magn,
const float *noise,
float *snrLocPrior, float *logSnrLocPrior,
float *snrLocPost) {
size_t i;
for (i = 0; i < self->magnLen; i++) {
// 前一帧的后验SNR。
// 前一次估计:基于上一帧和增益滤波器。
float previousEstimateStsa = (self->magnPrevAnalyze[i] * self->smooth[i]) / (self->noisePrev[i] + epsilon);
// 后验SNR。
snrLocPost[i] = 0.f;
if (magn[i] > noise[i]) {
snrLocPost[i] = (magn[i] - noise[i]) / (noise[i] + epsilon);
}
// DD估计是两个项的和:当前估计和前一估计。
// 针对snrPrior的定向决策更新。
snrLocPrior[i] = 2.f * (
DD_PR_SNR * previousEstimateStsa + (1.f - DD_PR_SNR) * snrLocPost[i]);
logSnrLocPrior[i] = log1pf(snrLocPrior[i]);
} // 结束频率循环。
}
计算输入频谱与模板/学习到的噪声频谱之间的差异测量
// |magnIn| 是输入频谱。
// 参考/模板频谱是self->magnAvgPause[i]。
// 返回在self->featureData[4]中的(标准化的)频谱差异。
static void ComputeSpectralDifference(NoiseSuppressionC *self,
const float *magnIn) {
// avgDiffNormMagn = var(magnIn) - cov(magnIn, magnAvgPause)^2 /
// var(magnAvgPause)
size_t i;
float avgPause, avgMagn, covMagnPause, varPause, varMagn, avgDiffNormMagn;
avgPause = 0;
avgMagn = self->sumMagn;
// 计算平均量。
for (i = 0; i < self->magnLen; i++) {
// 从暂停帧中保守平滑噪声频谱。
avgPause += self->magnAvgPause[i];
}
avgPause *= self->normMagnLen;
avgMagn *= self->normMagnLen;
covMagnPause = 0;
varPause = 0;
varMagn = 0;
// 计算方差和协方差量。
for (i = 0; i < self->magnLen; i++) {
const float avgPauseDiff = self->magnAvgPause[i] - avgPause;
const float avgMagnDiff = magnIn[i] - avgMagn;
covMagnPause += avgMagnDiff * avgPauseDiff;
varPause += avgPauseDiff * avgPauseDiff;
varMagn += avgMagnDiff * avgMagnDiff;
}
covMagnPause *= self->normMagnLen;
varPause *= self->normMagnLen;
varMagn *= self->normMagnLen;
// 更新平均幅度频谱。
self->featureData[6] += self->signalEnergy;
avgDiffNormMagn =
varMagn - (covMagnPause * covMagnPause) / (varPause + epsilon);
// 标准化和计算差异特征的时间平均更新。
avgDiffNormMagn = avgDiffNormMagn / (self->featureData[5] + epsilon);
self->featureData[4] +=
SPECT_DIFF_TAVG * (avgDiffNormMagn - self->featureData[4]);
}
计算语音/噪声概率
// 语音/噪声概率返回在|probSpeechFinal|中。
// |magn| 是输入的幅度频谱。
// |noise| 是噪声频谱。
// |snrLocPrior| 是每个频率的先验SNR。
// |snrLocPost| 是每个频率的后验SNR。
static void SpeechNoiseProb(NoiseSuppressionC *self,
float *probSpeechFinal,
const float *snrLocPrior, const float *logSnrLocPrior,
const float *snrLocPost) {
size_t i;
int sgnMap;
float invLrt, gainPrior, indPrior;
float logLrtTimeAvgKsum, besselTmp;
float indicator0, indicator1, indicator2;
float weightIndPrior0, weightIndPrior1, weightIndPrior2;
float threshPrior0, threshPrior1, threshPrior2;
float widthPrior, widthPrior0, widthPrior1, widthPrior2;
// 初始化宽度参数
widthPrior0 = WIDTH_PR_MAP;
// 在暂停区域使用更大的宽度增加在tanh映射中的宽度。
widthPrior1 = 2.f * WIDTH_PR_MAP;
widthPrior2 = 2.f * WIDTH_PR_MAP; // 用于频谱差异测量。
// 阈值参数
threshPrior0 = self->priorModelPars[0];
threshPrior1 = self->priorModelPars[1];
threshPrior2 = self->priorModelPars[3];
// 平坦度特征的符号。
sgnMap = (int)(self->priorModelPars[2]);
// 特征权重参数。
weightIndPrior0 = self->priorModelPars[4];
weightIndPrior1 = self->priorModelPars[5];
weightIndPrior2 = self->priorModelPars[6];
// 计算基于平均LR因子的特征。
// 这是平滑后的对数LRT在所有频率上的平均值。
logLrtTimeAvgKsum = 0;
for (i = 0; i < self->magnLen; i++) {
besselTmp = (snrLocPost[i] * snrLocPrior[i] + snrLocPrior[i]) / (snrLocPrior[i] + 1.f + epsilon);
self->logLrtTimeAvg[i] += LRT_TAVG * (besselTmp - logSnrLocPrior[i] - self->logLrtTimeAvg[i]);
logLrtTimeAvgKsum += self->logLrtTimeAvg[i];
}
// 对数LRT时间平均的累积和,乘以标准化系数。
logLrtTimeAvgKsum = logLrtTimeAvgKsum * self->normMagnLen;
// 将计算结果保存到特征数据中。
self->featureData[3] = logLrtTimeAvgKsum;
// 计算指示函数。
// 平均LRT特征。
widthPrior = widthPrior0;
if (logLrtTimeAvgKsum < threshPrior0) {
// 在暂停区域使用更大的宽度。
widthPrior = widthPrior1;
}
// 计算指示函数:sigmoid映射。
indicator0 = 0.5f * tanhf(widthPrior * (logLrtTimeAvgKsum - threshPrior0)) + 0.5f;
// 频谱平坦度特征。
widthPrior = widthPrior0;
if ((sgnMap == 1 && (self->featureData[0] > threshPrior1)) ||
(sgnMap == -1 && (self->featureData[0] < threshPrior1))) {
widthPrior = widthPrior1;
}
// 计算指示函数:sigmoid映射。
indicator1 = 0.5f * tanhf((float)sgnMap * widthPrior * (threshPrior1 - self->featureData[0])) + 0.5f;
// 模板频谱差异。
widthPrior = widthPrior0;
if (self->featureData[4] < threshPrior2) {
widthPrior = widthPrior2;
}
// 计算指示函数:sigmoid映射。
indicator2 = 0.5f * tanhf(widthPrior * (self->featureData[4] - threshPrior2)) + 0.5f;
// 将指示函数与特征权重结合。
indPrior = weightIndPrior0 * indicator0 + weightIndPrior1 * indicator1 +
weightIndPrior2 * indicator2;
// 计算先验概率。
self->priorSpeechProb += PRIOR_UPDATE * (indPrior - self->priorSpeechProb);
if (self->priorSpeechProb > 1.f) {
self->priorSpeechProb = 1.f;
}
if (self->priorSpeechProb < 0.01f) {
self->priorSpeechProb = 0.01f;
}
// 最终语音概率:将先验模型与LR因子结合。
gainPrior = (1.f - self->priorSpeechProb) / (self->priorSpeechProb + epsilon);
for (i = 0; i < self->magnLen; i++) {
invLrt = expf(-self->logLrtTimeAvg[i]);
invLrt = gainPrior * invLrt;
probSpeechFinal[i] = 1.f / (1.f + invLrt);
}
}
更新噪声特征
// 输入:
// * |magn| 是信号幅度频谱估计。
// * |updateParsFlag| 是参数更新标志。
static void FeatureUpdate(NoiseSuppressionC *self,
const float *magn, const float *lmagn,
int updateParsFlag) {
// 计算输入频谱的频谱平坦度。
ComputeSpectralFlatness(self, magn, lmagn);
// 计算输入频谱与学习到的/估计的噪声频谱的差异。
ComputeSpectralDifference(self, magn);
// 计算参数决策的直方图(特征的阈值和权重)。
// 参数每过一段窗口时间就提取一次。
// (=self->modelUpdatePars[1])
if (updateParsFlag >= 1) {
// 计数器更新。
self->modelUpdatePars[3]--;
// 如果计数器大于0,则更新直方图。
if (self->modelUpdatePars[3] > 0) {
FeatureParameterExtraction(self, 0);
}
// 如果计数器为0,计算模型参数。
if (self->modelUpdatePars[3] == 0) {
FeatureParameterExtraction(self, 1);
// 重置计数器为窗口时间间隔。
self->modelUpdatePars[3] = self->modelUpdatePars[1];
// 如果希望只更新一次,将标志置为零。
if (updateParsFlag == 1) {
self->modelUpdatePars[0] = 0;
}
else {
// 每个窗口更新:
// 获取下一个窗口估计的频谱差异的归一化值。
self->featureData[6] =
self->featureData[6] / ((float)self->modelUpdatePars[1]);
// 将当前窗口和下一个窗口的频谱差异归一化值平均,以便平滑过渡。
self->featureData[5] =
0.5f * (self->featureData[6] + self->featureData[5]);
// 重置频谱差异归一化值。
self->featureData[6] = 0.f;
}
}
}
}
更新噪声估计
// 输入:
// * |magn| 是信号幅度频谱估计。
// * |snrLocPrior| 是先验SNR。
// * |snrLocPost| 是后验SNR。
// 输出:
// * |noise| 是更新后的噪声幅度频谱估计。
static void UpdateNoiseEstimate(NoiseSuppressionC *self,
const float *magn,
float *noise) {
size_t i;
float probSpeech, probNonSpeech;
// 噪声更新的时间平均参数。
float gammaNoiseTmp = NOISE_UPDATE;
float gammaNoiseOld;
float noiseUpdateTmp;
for (i = 0; i < self->magnLen; i++) {
// 语音概率。
probSpeech = self->speechProb[i];
// 非语音概率。
probNonSpeech = 1.f - probSpeech;
// 临时噪声更新:
// 如果更新值小于之前的值,则用于语音帧。
noiseUpdateTmp = gammaNoiseTmp * self->noisePrev[i] +
(1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] +
probSpeech * self->noisePrev[i]);
// 基于语音/噪声状态的时间常数。
gammaNoiseOld = gammaNoiseTmp;
gammaNoiseTmp = NOISE_UPDATE;
// 对于可能是语音的帧,增加gamma(即,减少噪声更新)。
if (probSpeech > PROB_RANGE) {
gammaNoiseTmp = SPEECH_UPDATE;
}
// 保守的噪声更新。
if (probSpeech < PROB_RANGE) {
// 对于暂停帧,更新平均暂停幅度频谱。
self->magnAvgPause[i] += GAMMA_PAUSE * (magn[i] - self->magnAvgPause[i]);
}
// 噪声更新。
if (gammaNoiseTmp == gammaNoiseOld) {
// 如果gamma没有变化,使用临时噪声更新结果。
noise[i] = noiseUpdateTmp;
}
else {
// 如果gamma变化了,根据新的gamma计算噪声更新。
noise[i] = gammaNoiseTmp * self->noisePrev[i] +
(1.f - gammaNoiseTmp) * (probNonSpeech * magn[i] +
probSpeech * self->noisePrev[i]);
// 允许噪声向下更新:
// 如果噪声更新导致噪声减小,则认为是安全的,因此允许这种更新发生。
if (noiseUpdateTmp < noise[i]) {
noise[i] = noiseUpdateTmp;
}
}
} // 结束频率循环。
}
使用新的|frame|更新|buffer|
// 输入:
// * |frame| 是一个新的语音帧,或者为NULL时设置为零。
// * |frame_length| 是新帧的长度。
// * |buffer_length| 是缓冲区的长度。
// 输出:
// * |buffer| 是更新后的缓冲区。
static void UpdateBuffer(const int16_t *frame,
size_t frame_length,
size_t buffer_length,
float *buffer) {
// 确保缓冲区长度小于两倍帧长,这是一个前提条件检查。
assert(buffer_length < 2 * frame_length);
// 将缓冲区中剩余的数据(即,不被新帧覆盖的部分)向前移动。
memcpy(buffer,
buffer + frame_length,
sizeof(*buffer) * (buffer_length - frame_length));
// 如果frame不是NULL,即有新的帧要添加。
if (frame) {
int i = 0;
// 将新帧的数据复制到缓冲区的末尾。
for (i = 0; i < frame_length; ++i) {
buffer[buffer_length - frame_length + i] = frame[i];
}
}
else {
// 如果frame为NULL,将要更新的缓冲区部分设置为零。
memset(buffer + buffer_length - frame_length,
0,
sizeof(*buffer) * frame_length);
}
}