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

将信号从时域转换到频域

// 输入:
//   * |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
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值