C++应用维纳滤波实现语音信号的去噪

维纳滤波是一种滤波器,它根据全部过去的和当前的观察数据来估计信号的当前值,它的解是以均方误差最小条件下所得到的系统的传递函数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;
}

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-07 22:00:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-07 22:00:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-07 22:00:03       82 阅读
  4. Python语言-面向对象

    2024-06-07 22:00:03       91 阅读

热门阅读

  1. React 18

    React 18

    2024-06-07 22:00:03      29 阅读
  2. refault distance算法的一点理解

    2024-06-07 22:00:03       32 阅读
  3. 【HarmonyOS】取消页面转场动画

    2024-06-07 22:00:03       32 阅读
  4. react-draft-wysiwyg富文本编辑器使用常见问题解答

    2024-06-07 22:00:03       32 阅读
  5. Python3 笔记:字符串的 strip()、lstrip()、rstrip()

    2024-06-07 22:00:03       29 阅读
  6. 【Python】模块和包

    2024-06-07 22:00:03       32 阅读
  7. 【pytest】为什么不能使用__init__

    2024-06-07 22:00:03       26 阅读
  8. 【C#】转换8位或16位像素值为Bitmap

    2024-06-07 22:00:03       26 阅读
  9. vue+Django接入钉钉登录

    2024-06-07 22:00:03       28 阅读
  10. 在Linux/Ubuntu/Debian中使用lscpu命令查看CPU信息

    2024-06-07 22:00:03       28 阅读
  11. GOPATH和Go Modules的关系

    2024-06-07 22:00:03       33 阅读
  12. Docker面试整理-Docker的网络是如何工作的?

    2024-06-07 22:00:03       29 阅读