维纳滤波是一种滤波器,它根据全部过去的和当前的观察数据来估计信号的当前值,它的解是以均方误差最小条件下所得到的系统的传递函数H(z)或单位样本响应h(n)的形式给出的,因此维纳滤波器也称为最佳线性滤波器。
可以使用MATLAB语言实现应用维纳滤波来实现语音信号去噪。代码中FFT是快速傅里叶变换,IFFT为傅里叶逆变换,是将信号频域表达转换为信号时域表达的算法。
% 读取带噪音的语音信号
[x, fs] = audioread('采煤机与人声混合右2架位置.wav');
% 设置参数
frameLength = 256; % 窗口长度
overlap = 128; % 窗口重叠
win = hamming(frameLength); % 加窗函数
N = length(x); % 信号长度
M = floor((N-frameLength)/overlap) + 1; % 窗口数
% 初始化去噪后的语音信号
y = zeros(N, 1);
for i = 1:M
startIndex = (i-1)*overlap + 1;
endIndex = startIndex + frameLength - 1;
frame = x(startIndex:endIndex);
X = fft(frame);
% 估计噪声功率谱密度
noisePower = mean(abs(X(1:floor(frameLength/2))).^2);
% 维纳滤波器参数设置
alpha = 2; % 维纳滤波器增益因子
X_denoised = X ./ (1 + alpha * (noisePower./abs(X).^2));
frame_denoised = real(ifft(X_denoised));
y(startIndex:endIndex) = y(startIndex:endIndex) + frame_denoised .* win;
end
audiowrite('采煤机与人声混合右2架位置2.wav', y, fs);
将MATLAB代码转换为C++代码涉及到多个方面,包括文件I/O、信号处理算法、FFT和IFFT的实现等,以下是转换后的C++代码。
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <fftw3.h> // 需要安装FFTW库
// WAV文件头部结构体
struct WAVHeader {
char chunkID;
unsigned long chunkSize;
char format;
char subchunk1ID;
unsigned long subchunk1Size;
unsigned short audioFormat;
unsigned short numChannels;
unsigned long sampleRate;
unsigned long byteRate;
unsigned short blockAlign;
unsigned short bitsPerSample;
char subchunk2ID;
unsigned long subchunk2Size;
};
using namespace std;
// 使用FFTW库进行FFT和IFFT
void applyWienerFilter(const vector<double>& input, vector<double>& output, int frameLength, int overlap, double alpha){
int N = input.size();
int M = (N - frameLength) / overlap +1; output. assign(N, 0.0);
// FFTW计划
fftw_complex* in, *out;
fftw_plan p;
in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * frameLength);
out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * frameLength) ;
p = fftw_plan_dft_1d(frameLength, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
for (int i= 0; i < M; ++i) {
int startIndex = i * overlap;
int endIndex = startIndex + frameLength;
// 填充FFT输入
for (int j = startIndex; j < endIndex; ++j) {
in[j - startIndex]= input[j] * (0.54 - 0.46 * cos(2 * M_PI * (j - startIndex) / (frameLength-1))); //汉明窗
in[j - startIndex] = 0.0;
}
// 执行FFT
fftw_execute(p);
// 估计噪声功率谱密度
double noisePower = 0.0;
for (int j = 0; j < frameLength / 2; ++j) {
noisePower += (out[j] * out[j] + out[j] * out[j]);
}
noisePower /= (frameLength / 2);
// 应用维纳滤波器
for (int j = 0; j < frameLength; ++j) {
double magnitude = sqrt(out[j] * out[j] + out[j] * out[j]);
double factor = 1.0 / (1.0 + alpha * (noisePower / (magnitude * magnitude)));
out [j] *= factor;
out [j] *= factor
}
// IFFT计划
fftw_plan q = fftw_plan_dft_1d(frameLength, out, in, FFTW_BACKWARD,FFTW_ESTIMATE);
// 执行IFFT
fftw_execute(q);
// 叠加到输出信号
for(int j = startIndex; j < endIndex; ++j) {
output[j] += in[j - startIndex] / frameLength; // FFTW的IFFT结果需要除以N
}
fftw_destroy_plan(q);
}
fftw_destroy_plan(p);
fftw_free(in);
fftw_free(out);
}
vector<double> ReadWaveFile(ifstream& inFile) {
if (!inFile) {
cerr << "无法打开文件" << endl;
vector<double> emptyAudioData;
return emptyAudioData;
}
WAVHeader wavHeader;
inFile.read(reinterpret_cast<char*>(&wavHeader), sizeof(WAVHeader));
// 检查格式是否为PCM
if (wavHeader.audioFormat != 1) {
cerr << "不支持的音频格式”<< endl;
vector<double> emptyAudioData;
return emptyAudioData;
}
// 读取音频数据
vector<double> audioData(wavHeader.subchunk2Size /(wavHeader.bitsPerSample / 8));
inFile.read(reinterpret_cast<char*>(audioData.data()), wavHeader.subchunk2Size);
inFile.close();
return audioData;
}
void WriteWaveFile(ofstream& outFile, const vector<double>& audioData, int sampleRate) {
if (!outFile) {
cerr << "无法打开文件" << endl;
return;
}
WAVHeader wavHeader;
// 填充WAV头部信息
memcpy((void*)wavHeader.chunkID, "RIFF", 4);
wavHeader.chunkSize = 36 + audioData.size() * sizeof(double);
memcpy((void*)wavHeader.format, "WAVE", 4);
memcpy((void*)wavHeader.subchunk1ID, "fmt ", 4);
wavHeader.subchunk1Size = 16;
wavHeader.audioFormat = 1;
wavHeader.numChannels = 1;
wavHeader.sampleRate = sampleRate;
wavHeader.byteRate = sampleRate * sizeof(double);
wavHeader.blockAlign = sizeof(double);
wavHeader.bitsPerSample = 8 * sizeof(double);
memcpy((void*)wavHeader, subchunk2ID,"data",4);
wavHeader.subchunk2Size = audioData.size() * sizeof (double);
// 写入头部信息
outFile.write(reinterpret_cast<const char*>(&wavHeader), sizeof(WAVHeader));
// 写入音频数据
outFile.write(reinterpret_cast<const char*>(audioData.data()),audioData.size() * sizeof(double));
outFile.close();
}
int main() {
// 读取带噪音的语音信号
ifstream inFile("采煤机与人声混合右2架位置.wav",ios::binary);
// 读取WAV文件代码
vector<double> x = ReadWaveFile(inFile); // 从WAV文件中读取的信号数据
int fs; //采样率
// 设置参数
int frameLength = 256; //窗口长度
int overlap = 128; // 窗口重叠
double alpha = 2.0; // 维纳滤波器增益因子
// 初始化去噪后的语音信号
vector<double> y;
// 应用维纳滤波器
applyWienerFilter(x, y, frameLength, overlap, alpha);
// 写入去噪后的语音信号到WAV文件
ofstream outFile("采煤机与人声混合右2架位置2.wav",ios::binary);
// 写入WAV文件代码
WriteWaveFile(outFile, y, fs);
return 0;
}