将信号从时域转换到频域
// 输入:
// * |time_data| 是时域中的信号。
// * |time_data_length| 是分析缓冲区的长度。
// * |magnitude_length| 是频谱幅度的长度,等于|real|和|imag|的长度(time_data_length / 2 + 1)。
// 输出:
// * |time_data| 是频域中的信号。
// * |real| 是频域中的实部。
// * |imag| 是频域中的虚部。
// * |magn| 是频域中信号的计算幅度。
static void FFT(NoiseSuppressionC *self,
float *time_data,
size_t time_data_length,
size_t magnitude_length,
float *real,
float *imag,
float *magn, float *lmagn, int prev_calc, float *signalEnergy, float *sumMagn) {
size_t i;
// 确保幅度长度正确。
assert(magnitude_length == time_data_length / 2 + 1);
// 执行实数FFT变换。
WebRtc_rdft(time_data_length, 1, time_data, self->ip, self->wfft);
// 初始化频域中的第一个和最后一个频率分量。
imag[0] = 0;
real[0] = time_data[0];
magn[0] = fabsf(real[0]);
imag[magnitude_length - 1] = 0;
real[magnitude_length - 1] = time_data[1];
magn[magnitude_length - 1] = fabsf(real[magnitude_length - 1]);
// 指向时域数据的指针,开始处理。
float *time_data_ptr = time_data + 2;
// 如果之前已经计算过,则执行下面的逻辑。
if (prev_calc == 1) {
// 计算第一个和最后一个频率分量的能量。
float first = real[0] * real[0] + imag[0] * imag[0];
float last = real[magnitude_length - 1] * real[magnitude_length - 1] +
imag[magnitude_length - 1] * imag[magnitude_length - 1];
*signalEnergy = first + last;
*sumMagn = sqrtf(first + epsilon_squ) + 2.f + sqrtf(last + epsilon_squ);
lmagn[0] = log1pf(magn[0]);
lmagn[magnitude_length - 1] = log1pf(magn[magnitude_length - 1]);
// 遍历处理除了第一个和最后一个之外的频率分量。
for (i = 1; i < magnitude_length - 1; ++i) {
real[i] = time_data_ptr[0];
imag[i] = time_data_ptr[1];
// 计算能量。
const float energy = real[i] * real[i] + imag[i] * imag[i];
*signalEnergy += energy;
// 计算幅度谱。
magn[i] = sqrtf(energy + epsilon_squ);
*sumMagn += magn[i];
lmagn[i] = log1pf(magn[i]);
time_data_ptr += 2;
}
}
else {
// 如果之前没有计算过,则只计算幅度谱。
for (i = 1; i < magnitude_length - 1; ++i) {
real[i] = time_data_ptr[0];
imag[i] = time_data_ptr[1];
// 计算幅度谱。
magn[i] = sqrtf(real[i] * real[i] + imag[i] * imag[i] + epsilon_squ);
time_data_ptr += 2;
}
}
}
定义一个静态函数 IFFT,用于将信号从频率域转换到时间域
// 输入参数:
// * |real| 是频率域的实部。
// * |imag| 是频率域的虚部。
// * |magnitude_length| 是频谱幅度的长度,等于 |real| 和 |imag| 的长度。
// * |time_data_length| 是分析缓冲区的长度(2 * (magnitude_length - 1))。
// 输出参数:
// * |time_data| 是时间域中的信号。
static void IFFT(NoiseSuppressionC *self,
const float *real,
const float *imag,
size_t magnitude_length,
size_t time_data_length,
float *time_data) {
size_t i;
// 确认时间数据长度是否正确
assert(time_data_length == 2 * (magnitude_length - 1));
// 初始化时间数据的前两个元素
time_data[0] = real[0];
time_data[1] = real[magnitude_length - 1];
// 指向时间数据数组的指针,从第三个元素开始
float *time_data_ptr = time_data + 2;
// 遍历频率数据,转换到时间域数据,跳过了第一个和最后一个因为它们已被赋值
for (i = 1; i < magnitude_length - 1; ++i) {
time_data_ptr[0] = real[i];
time_data_ptr[1] = imag[i];
time_data_ptr += 2; // 移动到下一个时间域数据位置
}
// 调用 WebRtc 的 rdft 函数执行实数反离散傅立叶变换
WebRtc_rdft(time_data_length, -1, time_data, self->ip, self->wfft);
// 归一化因子,用于FFT缩放
float norm = 2.f / time_data_length;
// 对时间域数据进行归一化处理
for (i = 0; i < time_data_length; ++i) {
time_data[i] *= norm; // FFT 缩放。
}
}
定义一个计算窗函数能量的静态函数
// 输入参数:
// * |window| 是窗函数。
// * |data| 是原始数据。
// * |length| 是数据的长度。
// * |data_windowed| 是应用了窗函数后的数据。
// 返回值:
// * 返回窗函数应用后数据的能量。
static float WindowingEnergy(const float *window,
const float *data,
size_t length,
float *data_windowed) {
size_t i;
float energy = 0.f;
// 遍历数据,应用窗函数,并计算能量
for (i = 0; i < length; ++i) {
data_windowed[i] = window[i] * data[i]; // 应用窗函数
energy += data_windowed[i] * data_windowed[i]; // 累加能量
}
return energy; // 返回计算出的能量
}
计算一个缓冲区的能量
// 输入参数:
// * |buffer| 是需要计算能量的缓冲区。
// * |length| 是缓冲区的长度。
// 返回计算出的能量。
static float Energy(const float *buffer, size_t length) {
size_t i;
float energy = 0.f;
// 遍历缓冲区的每个元素,累加其平方到能量中
for (i = 0; i < length; ++i) {
energy += buffer[i] * buffer[i];
}
return energy; // 返回累加后的能量值
}
估计先前的信噪比(SNR),决策导向,并计算基于决策导向(DD)的维纳滤波器
// 输入参数:
// * |magn| 是信号幅度谱估计。
// 输出参数:
// * |theFilter| 是计算出的维纳滤波器的频率响应。
static void ComputeDdBasedWienerFilter(const NoiseSuppressionC *self,
const float *magn,
float *theFilter) {
size_t i;
float snrPrior, previousEstimateStsa, currentEstimateStsa;
// 遍历所有频率点
for (i = 0; i < self->magnLen; i++) {
// 上一帧的估计:基于之前帧和增益滤波器。
// 这里使用了平滑处理后的信号与噪声之比,epsilon 用于防止除以零。
previousEstimateStsa = self->magnPrevProcess[i] * self->smooth[i] / (self->noisePrev[i] + epsilon);
// 后验和先验信噪比。
currentEstimateStsa = 0.f;
// 如果当前幅度大于噪声水平,则计算当前估计的STSA(短时谱幅比)
if (magn[i] > self->noise[i]) {
currentEstimateStsa = (magn[i] - self->noise[i]) / (self->noise[i] + epsilon);
}
// DD估计是两个项的和:当前估计和之前的估计。
// 决策导向更新先验SNR。
snrPrior = DD_PR_SNR * previousEstimateStsa +
(1.f - DD_PR_SNR) * currentEstimateStsa;
// 计算增益滤波器,这是一个基于估计SNR的维纳滤波。
theFilter[i] = snrPrior / (self->overdrive + snrPrior);
} // 结束频率的循环。
}
更改噪声抑制方法的攻击性
// |mode| = 0 是轻度 (6dB),|mode| = 1 是中等 (10dB) 和 |mode| = 2 是
// 激进的 (15dB)。
// 成功返回0,否则返回-1。
int WebRtcNs_set_policy_core(NoiseSuppressionC *self, int mode) {
// 允许模式:0, 1, 2, 3。
if (mode == 0) {
self->overdrive = 1.f; // 过驱动系数
self->denoiseBound = 0.5f; // 降噪边界
self->gainmap = 0; // 增益映射
}
else if (mode == 1) {
// self->overdrive = 1.25f; // 原来的过驱动值
self->overdrive = 1.f; // 修改后的过驱动值
self->denoiseBound = 0.25f; // 降噪边界
self->gainmap = 1; // 增益映射
}
else if (mode == 2) {
// self->overdrive = 1.25f; // 原来的过驱动值
self->overdrive = 1.1f; // 修改后的过驱动值
self->denoiseBound = 0.125f; // 降噪边界
self->gainmap = 1; // 增益映射
}
else if (mode == 3) {
// self->overdrive = 1.3f; // 原来的过驱动值
self->overdrive = 1.25f; // 修改后的过驱动值
self->denoiseBound = 0.09f; // 降噪边界
self->gainmap = 1; // 增益映射
}
else {
return -1; // 如果模式不是0、1、2、3,则返回-1
}
self->aggrMode = mode; // 设置攻击模式
return 0; // 成功返回0
}