谱减法(Spectral Subtraction)进行降噪

谱减法是一种利用语音信号的短时平稳特性,从带噪声的语音信号短时谱中减去噪声的短时谱,从而得到纯净语音的频谱。
该方法是针对宽带声音信号去噪的方法,对加性噪声效果比较好。

下面是我个人总结的一个流程:
这里写图片描述

其中谱减部分具体有三种方法:
(1)利用幅度谱进行谱减。 (图中④式)
(2)利用功率谱进行谱减。 (图中⑤式,alpha取2)
(3)alpha-beta法进行改进,可以减少音乐噪声(图中⑤式)
关键部分的话就是谱减以及最后的声音恢复(4)。

欢迎大家进行讨论与批评

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个简单的C语言谱减法实现wav音频降噪的例子: ``` #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <math.h> #define PI 3.14159265358979323846 typedef struct { uint32_t ChunkID; uint32_t ChunkSize; uint32_t Format; uint32_t Subchunk1ID; uint32_t Subchunk1Size; uint16_t AudioFormat; uint16_t NumChannels; uint32_t SampleRate; uint32_t ByteRate; uint16_t BlockAlign; uint16_t BitsPerSample; uint32_t Subchunk2ID; uint32_t Subchunk2Size; } WavHeader; int16_t* read_wav_file(char* filename, WavHeader* header) { FILE* fp = fopen(filename, "rb"); if (fp == NULL) { printf("Error: cannot open file %s\n", filename); return NULL; } fread(header, sizeof(WavHeader), 1, fp); if (header->ChunkID != 0x46464952 || header->Format != 0x45564157 || header->Subchunk1ID != 0x20746D66 || header->AudioFormat != 1 || header->BitsPerSample != 16 || header->Subchunk2ID != 0x61746164) { printf("Error: invalid file format\n"); fclose(fp); return NULL; } uint32_t num_samples = header->Subchunk2Size / (header->NumChannels * (header->BitsPerSample / 8)); int16_t* buffer = (int16_t*)malloc(num_samples * sizeof(int16_t)); fread(buffer, sizeof(int16_t), num_samples, fp); fclose(fp); return buffer; } void write_wav_file(char* filename, WavHeader* header, int16_t* buffer) { FILE* fp = fopen(filename, "wb"); if (fp == NULL) { printf("Error: cannot create file %s\n", filename); return; } fwrite(header, sizeof(WavHeader), 1, fp); fwrite(buffer, sizeof(int16_t), header->Subchunk2Size/sizeof(int16_t), fp); fclose(fp); } double blackman_window(int n, int N) { return 0.42 - 0.5 * cos(2 * PI * n / (N-1)) + 0.08 * cos(4 * PI * n / (N-1)); } void spectral_subtraction(int16_t* buffer, uint32_t num_samples, uint32_t sample_rate, uint16_t window_size, uint16_t overlap, double reduction_factor) { uint16_t num_windows = (num_samples - window_size) / (window_size - overlap) + 1; double* power_spec = (double*)malloc(window_size/2 * sizeof(double)); double* noise_spec = (double*)malloc(window_size/2 * sizeof(double)); double* noise_estimate = (double*)calloc(window_size/2, sizeof(double)); double* noise_reduction = (double*)calloc(window_size/2, sizeof(double)); double temp; uint16_t i, j, k, idx; for (i = 0; i < num_windows; i++) { // apply blackman window to the current window for (j = 0; j < window_size; j++) { temp = (double)buffer[i*(window_size-overlap) + j] / 32768.0; temp *= blackman_window(j, window_size); power_spec[j] = temp * temp; } // calculate noise estimate for (j = 0; j < window_size/2; j++) { noise_spec[j] = 0; for (k = 0; k < num_windows; k++) { idx = k*(window_size-overlap) + j; temp = (double)buffer[idx] / 32768.0; temp *= blackman_window(idx-k*(window_size-overlap), window_size); noise_spec[j] += temp * temp; } noise_spec[j] /= num_windows; noise_estimate[j] = reduction_factor * noise_spec[j] + (1 - reduction_factor) * noise_estimate[j]; } // subtract noise estimate for (j = 0; j < window_size/2; j++) { if (power_spec[j] > noise_estimate[j]) { power_spec[j] -= noise_estimate[j]; } else { power_spec[j] = 0; } noise_reduction[j] = noise_estimate[j] / (noise_estimate[j] + power_spec[j]); } // apply inverse FFT to obtain denoised window // (here we skip the inverse FFT part for simplicity, and just use the original window) for (j = 0; j < window_size; j++) { temp = (double)buffer[i*(window_size-overlap) + j] / 32768.0; buffer[i*(window_size-overlap) + j] = (int16_t)(temp * noise_reduction[j] * 32768.0); } } free(power_spec); free(noise_spec); free(noise_estimate); free(noise_reduction); } int main() { WavHeader header; int16_t* buffer = read_wav_file("input.wav", &header); if (buffer == NULL) { return 1; } spectral_subtraction(buffer, header.Subchunk2Size/sizeof(int16_t), header.SampleRate, 512, 256, 0.5); write_wav_file("output.wav", &header, buffer); free(buffer); return 0; } ``` 这个例子实现了基于谱减法的简单音频降噪,输入为一个16位带符号整数的wav文件,输出也是一个wav文件。程序首先读入文件头信息和音频数据,然后对每个大小为512的窗口(前后重叠256)分别进行以下操作: 1. 对当前窗口应用黑曼窗口,将时域信号转换到频域 2. 用所有窗口的平均功率谱作为噪声功率谱的估计值 3. 根据给定的降噪因子,计算当前窗口的噪声功率谱估计值,用它和信号功率谱进行比较 4. 如果信号功率谱大于噪声功率谱估计值,则将信号功率谱减去噪声功率谱估计值,否则将信号功率谱置为0 5. 用之前估计的噪声功率谱估计值和减法结果计算噪声的红减系数 6. 将红减系数应用于当前窗口的频域信号,得到去噪后的窗口信号 注意,这个实现的FFT部分没有包含在代码中,需要自己实现或使用第三方库。而且,这个实现可能并不完美,因此需要根据具体情况进行优化。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值