语音降噪算法详解:MATLAB实现谱减、维纳滤波与小波去噪

发布时间:2026/10/23 21:15:18
语音降噪算法详解:MATLAB实现谱减、维纳滤波与小波去噪 简介语音降噪是语音信号处理中的关键环节直接影响语音识别、大数据分析及人工智能系统的鲁棒性。这套资源为《语音信号处理实验教程》第05章配套MATLAB源代码包面向需要动手实践的高校学生、算法研究者与入门工程师。压缩包共35个文件含26个.m源码、5张示意图和4个wav测试语音整体仅409KB结构紧凑、便于本地快速运行。内容覆盖5.1自适应滤波器法LMS、5.2谱减法、5.3维纳滤波法、5.4小波分解法每个算法均配有可执行脚本并包含分帧、重叠相加、端点检测、SNR计算等辅助函数方便对比不同降噪策略的效果。包内文件目录清晰按章节划分适合从理论过渡到代码实践也可作为语音处理课程实验或项目改造的基础。目前已有210人学习下载对希望深入理解语音降噪原理并快速上手的读者很有帮助。1. 语音信号处理实验包里最值得先跑的部分一套能出听感的降噪链路做语音信号处理实验很多人第一反应是找深度学习模型真打开 MATLAB 把 wav 读进来之后才发现最卡人的反而是“把带噪语音变成干净语音”这条基础链路。这套语音信号处理实验教程MATLAB 源代码语音降噪资源把读音频、分帧加窗、短时傅里叶变换、谱减法、维纳滤波和小波去噪整理成了一整套可运行的 MATLAB 源码每一步都有对应的脚本和参数。对要交课程实验报告的在校生或者要给语音识别前端做数据预处理的一线开发者这是比直接跑黑盒模型更值得研究的参考实现。2. 降噪实验的底层逻辑从短时平稳假设到频域增益2.1 为什么先分帧加窗而不是直接对整个文件算 FFT语音信号不是平稳信号全段一次性 FFT 会把不同时刻的频谱混在一起得到的是一坨没有时间分辨率的平均结果。降噪要做的是“每一小段时间里单独判断噪声长什么样”这依赖短时平稳假设在 20~40ms 量级的时间窗内语音的频谱特性近似不变。所以标准做法是对信号分帧。帧长取 25ms 时16kHz 采样率对应 400 个采样点帧移取 10ms 对应 160 点相邻帧有 60% 重叠。这个配置是语音处理里最常见的默认值既能保证频率分辨率够用又不会让帧间变化被抹得太平。加窗的目的有两个一是压制 FFT 的频谱泄漏二是保证重叠相加重建时不会产生跳变。汉明窗和汉宁窗都常用实验包里一般倾向汉明窗因为它的旁瓣衰减比矩形窗好不少做谱估计时噪声底更干净。分帧之后每一帧先乘窗函数再做 FFT得到的就是这一帧的短时频谱。后面所有降噪算法本质上都是在这个“帧-频点”二维矩阵里做增益调整而不是在时域波形上直接动手。2.2 三类降噪算法的数学模型与 MATLAB 函数对应实验包里有三套核心算法谱减法、维纳滤波、小波去噪。它们的假设和应用场景差别很大写实验报告时最容易混淆。谱减法是最直观的假设噪声是加性的、平稳的先用前几帧估算噪声功率谱再从带噪语音功率谱里减掉它剩下的就是语音功率谱。公式上写作|S|^2 |Y|^2 - α|N|^2α 叫过减因子用来补偿噪声估计偏差。它的优点是计算量小、实时性好缺点是处理非平稳噪声容易留下“音乐噪声”也就是听感上那种吱吱呀呀的残留音。维纳滤波的思路不同它根据信噪比估计一个频域增益G ξ/(1ξ)ξ 是先验信噪比。语音强的频段增益接近 1噪声强的频段增益压下去这种平滑过渡让音乐噪声明显更少但先验信噪比估计不准时语音会被削得发闷。小波去噪则从时频分解角度切入把信号分解到不同尺度对小波系数做阈值收缩噪声对应的小波系数小直接置零。它的优势是处理脉冲噪声和瞬态噪声时比谱减稳定劣势是阈值选择有玄学成分不同母小波和分解层数结果差异很大。算法核心假设适用场景核心 MATLAB 相关函数谱减噪声平稳、加性稳态噪声、实时处理fft、angle、abs、重叠相加维纳滤波需要先验 SNR 估计非平稳性适中的环境fft、迭代增益估计小波去噪语音与小波系数稀疏性瞬态噪声、脉冲噪声wdenoise、wthresh、wnoisest2.3 只修幅值不动相位从频域回时域的关键大多数降噪算法只修改幅度谱相位保留原带噪语音的相位。这是工程上的妥协相位对语音可懂度影响小于幅度但完全扔掉相位重构出来的声音有严重的金属感。所以标准流程是S |S_hat| * exp(i * angle(Y))带着原始相位做 ifft。这里最大的坑在重叠相加。分帧时每一帧是被窗函数截断的重建时必须把相邻帧的重叠部分按位置累加否则音量会忽大忽小。我一般会在重建时额外记录每一点的窗函数累加和最后做一次除法归一化这套逻辑在用ifft恢复每一帧后尤其重要。实际代码里idx (1 (i-1)*frameShift) : ((i-1)*frameShift frameLen)这个下标计算写错一位后面全塌。3. 跑通第一个谱减降噪 Demo加噪、分帧、谱减、重构的完整脚本3.1 读入语音并构造可控的带噪数据首先准备一段干净的 wav 文件推荐 16kHz 采样率、16bit、单声道这是语音处理的事实标准。用audioread读进来后先转成列向量然后叠加高斯白噪声通过调整噪声幅度控制信噪比 SNR。为了实验可复现随机噪声要固定种子否则每次运行结果都不一样报告里对不上。clear; rng(0); [x, fs] audioread(speech.wav); % 读入干净语音 x x(:); % 生成与语音等长的白噪声 numSamples length(x); snr_db 5; % 目标 SNR 5dB能明显听出噪声 cleanPower sum(x.^2) / numSamples; noisePower cleanPower / (10^(snr_db/10)); noise sqrt(noisePower) * randn(numSamples, 1); y x noise; % 带噪语音 audiowrite(noisy.wav, y, fs);逻辑说明先计算干净语音的功率再按照 SNR 公式SNR 10log10(P_signal/P_noise)反推噪声功率最后乘randn生成高斯白噪声。这里没有直接调用awgn函数因为手算能让学生看明白 SNR 的定义而不是把工具函数当黑匣子。参数说明snr_db 5是一个折中值0dB 时噪声几乎盖过语音10dB 时降噪效果不明显5dB 适合展示算法效果。rng(0)固定随机种子很关键实验重复性全靠它。3.2 谱减核心循环噪声估计、过减因子与谱底限制分帧、加窗、FFT 之后先拿前几帧做噪声功率谱估计。语音通常开头有 100~200ms 静音16kHz 下 10 帧约 160ms取前 10 帧足够。噪声谱估错会直接导致整段降噪翻车——如果前几帧恰好是清音估计出来的“噪声谱”会把语音一起减掉。frameLen floor(0.025 * fs); % 25ms 帧长16kHz 下 400点 frameShift floor(0.010 * fs); % 10ms 帧移 160点 numFrames floor((length(y) - frameLen) / frameShift) 1; win hamming(frameLen); % 分帧并计算每帧频谱 frames zeros(frameLen, numFrames); for i 1:numFrames idx (i-1)*frameShift 1 : (i-1)*frameShift frameLen; frames(:,i) y(idx) .* win; end Y fft(frames, frameLen, 1); % 每一列是一帧的复数频谱 % 用前 10 帧平均功率谱作为噪声谱 noiseFrames 10; noisePSD mean(abs(Y(:,1:noiseFrames)).^2, 2); % 谱减 alpha 1.5; % 过减因子 beta 0.1; % 谱底比例 S zeros(frameLen, numFrames); for i 1:numFrames P abs(Y(:,i)).^2; P_est max(P - alpha * noisePSD, beta * P); % 减去噪声谱并限制底 S(:,i) sqrt(P_est) .* exp(1i * angle(Y(:,i))); % 保留原始相位 end逻辑说明频谱矩阵每一列是一帧噪声谱从整段带噪语音的前 10 帧平均而来。谱减这一步里max(P - alpha*noisePSD, beta*P)是经典的过减法β 乘当前帧功率谱作为下限避免负功率导致相位翻转和难听的爆音。参数说明alpha 1.5是经验值偏高会让语音变干瘪偏低会留下更多噪声。beta 0.1表示噪声底最多压到原功率的 10%太小会出现音乐噪声太大则降噪不彻底。这两个参数是调听感最关键的旋钮。3.3 重叠相加重建波形用 spectrogram 和 SNR 验证重建的核心是重叠相加。由于分帧时帧移小于帧长每个采样点被多个帧覆盖直接累加会导致边缘能量偏大必须做窗函数归一化。recLen (numFrames - 1) * frameShift frameLen; x_rec zeros(recLen, 1); winAccum zeros(recLen, 1); for i 1:numFrames seg real(ifft(S(:,i))); idx (i-1)*frameShift 1 : (i-1)*frameShift frameLen; x_rec(idx) x_rec(idx) seg; winAccum(idx) winAccum(idx) win; end x_rec x_rec ./ max(winAccum, 1e-6); % 用窗累加和归一化 audiowrite(denoised.wav, x_rec, fs); % 计算降噪前后 SNR snr_before 10*log10(sum(x.^2) / sum((y - x).^2)); snr_after 10*log10(sum(x.^2) / sum((x_rec - x).^2)); fprintf(SNR before %.2f dB, after %.2f dB\n, snr_before, snr_after);逻辑说明winAccum记录每个采样点被多少窗函数能量覆盖最后用除法归一化这是重叠相加最常见也最稳的写法。max 里的1e-6防止除零。参数说明验证环节不要只盯着 SNR。SNR提升 5~8dB 是谱减的正常表现但更重要的判断标准是 spectrogram 里噪声底是否变平、音乐噪声是否明显。运行这段后建议立刻用sound(x_rec, fs)听一遍再用spectrogram(x_rec, win, frameLen-frameShift, frameLen, fs, yaxis)看谱图。4. 从谱减到维纳与小波同一条数据上的横向对比4.1 判决引导维纳滤波先验 SNR 估计决定一切维纳滤波效果好坏九成取决于先验信噪比 ξ 的估计质量。工程里最常用的是判决引导法用上一帧估计的语音谱和当前帧后验信噪比做一阶递归这样既平滑又不会过度滞后。增益公式写作G(k,l) ξ(k,l) / (1 ξ(k,l))每个频点单独计算。noisePSD mean(abs(Y(:,1:noiseFrames)).^2, 2); xi_prev max(eps, ones(frameLen,1)); % 上一帧先验SNR初始为1 alphaDD 0.98; % 判决引导平滑系数 S zeros(frameLen, numFrames); for i 1:numFrames P abs(Y(:,i)).^2; gamma P ./ noisePSD; % 后验SNR xi alphaDD * xi_prev (1-alphaDD) * max(gamma - 1, 0); G xi ./ (1 xi); % 维纳增益 S(:,i) G .* Y(:,i); % 直接作用于复数谱 xi_prev xi; end逻辑说明gamma P./noisePSD是后验信噪比max(gamma-1, 0)是对后验信噪比的保守估计避免负值。alphaDD 0.98让先验 SNR 的更新很平稳这是抑制音乐噪声的关键。与谱减对比维纳滤波没有用max做硬底限制增益是连续的 0~1 之间的值所以听感更干净。代价是如果alphaDD调太大语音起始段会响应变慢听起来像声音“软”了。A同学第一次跑这个实验时把 alphaDD 改成 0.995结果每个字开头都被削掉一半后来调回 0.98 才正常。4.2 小波去噪一行函数背后藏着三个参数小波去噪是这几套里代码量最小的MATLAB 里用wdenoise就能完成但参数含义比谱减更难直觉把握。母小波、分解层数、阈值规则三个参数任何一个选不好都会把语音细节抹成“水声”。x_rec_w wdenoise(y, 6, Wavelet, sym8, ... DenoisingMethod, Bayes, ... ThresholdRule, Soft, ... NoiseEstimate, LevelDependent);逻辑说明6是分解层数语音信号采样率 16kHz 时 6 层可以覆盖大约 125Hz 以上的频段低频部分保留语音基频。sym8是对称性较好的小波不会引入过多相位失真。Bayes表示用贝叶斯自适应阈值比固定阈值更适应不同噪声强度。参数说明分解层数超过 8 会把语音低频能量也当噪声滤掉低于 4 层则对高频噪声无计可施。Soft阈值比Hard阈值平滑但也会让清音变得更弱如果在做语音识别数据预处理优先用Soft。4.3 横向对比结果与选型建议在同一段带噪语音上跑三种方法结果差异非常直观。下面这个表是我在自己的测试样本上记录下来的典型表现不同设备录出来的语音结果会有浮动但趋势一致。算法输出 SNR 提升音乐噪声语音清晰度计算耗时谱减6~8dB明显中有机械感O(帧数*FFT长度)维纳滤波7~9dB较少较高略高于谱减小波去噪5~7dB几乎没有中高高频略暗较长选型时我的判断标准是做实时处理或批量大数据量音频先上谱减速度快且够稳做语音识别前端训练数据清洗优先维纳滤波因为它对语音失真更小遇到电磁脉冲、键盘敲击这类瞬态噪声用小波去噪反而更干净。如果你只是想交实验报告把三种方法结果和 spectrogram 对比图贴在同一页比单独跑一种深刻得多。5. 避坑清单谱减、窗函数和音频格式的五个常见问题5.1 加完噪后听起来跟原声没区别现象明明写了y x noise播放y时几乎听不到噪声降噪前后 SNR 都是 20dB 上下。原因噪声幅度设置不对。很多人直接把randn乘一个小常数导致实际 SNR 远高于预期或者干脆用awgn(x, 5)时没注意函数默认按整个序列功率计算如果你的语音里静音段特别长噪声会偏小。解决先算cleanPower再反推噪声功率像 3.1 节那样写。验证时打印snr_before必须落在目标值 ±1dB 范围内再继续。5.2 降噪后出现“咕嘟咕嘟”的音乐噪声现象谱减运行成功后背景噪声变成一阵一阵的颤音像开水壶在响语音反而不明显了。原因谱减后某些频点的功率被减到接近零这些频点在不同帧之间随机出现人耳听到的就是断续的“音乐噪声”。核心是过减因子 α 太大或者没有 β 限制。解决把alpha从 2.0 降到 1.2~1.5同时把beta调到 0.05~0.15给功率谱留一个底。如果还不行对P_est做一次时间域平滑P_smooth 0.8*P_est 0.2*P_prev能明显减少帧间抖动。5.3 降噪后开头有爆音第一帧尤其明显现象播放降噪结果时前几十毫秒先“咔嚓”一声后面才正常。原因重叠相加时帧索引计算错误或者窗归一化没做。最常见的是idx (i-1)*frameLen 1 : i*frameLen这是把帧移和帧长搞混了导致重建波形开头有跳变。解决idx (i-1)*frameShift 1 : (i-1)*frameShift frameLen并保存winAccum做除法归一化。这个错误几乎每个第一次手写 ISTFT 的人都会犯跑完先看x_rec开头 100 个点有没有突变。5.4 降噪后声音发闷像隔着被子说话现象SNR 提升很高但语音听不清楚高频细节全没了识别准确率反而下降。原因谱减法把高频弱语音误判成噪声或者维纳滤波在先验 SNR 估计不准时把高频增益压得太狠。语音能量集中在低频高频噪声又多降噪时很容易“宁可错杀”。解决加一阶预加重滤波器恢复高频x_pre filter([1 -0.97], 1, x_rec)0.97 这个系数在 16kHz 下常用或者对高频频点单独降低过减因子例如频率大于 4kHz 时alpha_eff 0.8 * alpha。5.5 spectrogram 图和听到的声音对不上现象画出的 spectrogram 时间轴长度和音频长度明显不符横轴频率范围也怪怪的比如 16kHz 采样率的语音只显示到 5kHz。原因调用spectrogram时没有统一传入采样率和窗口参数函数默认用了 256 点 FFT 和 1Hz 假定时钟横纵轴自然全错。解决与分帧窗口保持完全一致spectrogram(x, hamming(frameLen), frameLen-frameShift, frameLen, fs, yaxis)其中noverlap必须是frameLen - frameShift不能顺手填 50% 之类的比例。四五个参数全对上图才能还原你用的算法配置。6. 把降噪接到语音识别前端用端点检测和 MFCC 给结果打分6.1 端点检测先找到语音在哪里降噪效果好不好光看波形和 SNR 不够直观。更客观的方式是把降噪结果送进语音识别前端先做端点检测看语音段是否保留完整。用短时能量和过零率双门限法可以判断语音起止点有没有被降噪吃掉。frameEnergy sum(frames.^2, 1) / frameLen; % 每帧能量 th_high max(frameEnergy) * 0.35; % 高门限 th_low max(frameEnergy) * 0.15; % 低门限 isSpeech frameEnergy th_high; % 边缘扩展低门限把语音边界向两端补齐 for k 2:numFrames-1 if frameEnergy(k) th_high ~isSpeech(k-1) isSpeech(k-1) frameEnergy(k-1) th_low; end end逻辑说明高门限负责“确认这是语音”低门限负责“语音边界向外延伸”防止把词头和词尾的弱音截断。过零率可以再加一道判断用于区分清音和噪声但降噪后的语音用能量双门限基本够用。6.2 MFCC 特征对比降噪前后各算一组MFCC 是语音识别里最常用的特征。对降噪前后的音频分别提取 MFCC观察前几帧系数分布能直观看到降噪是否让特征更稳定。这里有 Audio Toolbox 的话直接用mfcc函数没有也可以按公式手动走 filterbank DCT 流程。[coeff_before, ~] mfcc(y, fs); % 带噪语音特征 [coeff_after, ~] mfcc(x_rec, fs); % 降噪语音特征 % 比较第一帧 MFCC 的距离 dist norm(coeff_before(1,:) - coeff_after(1,:)); fprintf(MFCC distance %.3f\n, dist);如果降噪真把噪声压住了降噪后语音的 MFCC 应该更接近干净语音的 MFCC同时带噪语音和干净语音的 MFCC 距离明显更大。这个指标比 SNR 更能反映“对识别是否友好”。从那以后我每次跑完降噪实验都会强制做一遍“音频 → 端点检测 → MFCC 距离”的验证而不再只盯着 SNR 数字因为听感和特征稳定性才是决定前端能不能用的关键。希望这套流程能帮你少走弯路下载后按这份教程跑一遍再对照 3.3 节的 spectrogram 参数去调你会比只按.m文件点运行的人更清楚每一步在干什么。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询