戰(zhàn):頻譜減法全流程源碼解析)
簡(jiǎn)介面向語音信號(hào)處理學(xué)習(xí)者的MATLAB語音降噪實(shí)驗(yàn)配套源碼包聚焦譜減法、維納濾波、自適應(yīng)濾波器LMS與小波分解等經(jīng)典方法適合高校學(xué)生、科研人員及人工智能方向開發(fā)者用于課程實(shí)驗(yàn)、算法對(duì)比或?qū)嶋H項(xiàng)目預(yù)研。壓縮包共35個(gè)文件以26個(gè).m源碼文件為主附4個(gè).wav語音樣本和5張.jpeg示意圖整體大小約409KB內(nèi)容按5.1自適應(yīng)濾波器、5.2譜減法、5.3維納濾波、5.4小波分解等章節(jié)組織每章均包含可運(yùn)行的主程序、功能函數(shù)與測(cè)試音頻。已有210人學(xué)習(xí)瀏覽便于在理解理論后對(duì)照代碼逐步復(fù)現(xiàn)各類降噪流程。通過運(yùn)行示例可觀察不同算法對(duì)含噪語音的處理效果并借助SNR計(jì)算與VAD等評(píng)估手段量化降噪性能從而掌握從分幀加窗、噪聲估計(jì)、譜修正到效果驗(yàn)證的完整鏈路為后續(xù)語音識(shí)別、語音增強(qiáng)及復(fù)雜場(chǎng)景下的信號(hào)處理打下基礎(chǔ)。1. 語音降噪不只是調(diào)參這套 MATLAB 源碼把全流程擺在了桌面上做語音識(shí)別、語音信號(hào)處理或者音頻相關(guān)項(xiàng)目的人遲早會(huì)遇到同一個(gè)尷尬局面算法論文看了不少一到自己手里錄音一加載就出現(xiàn)各種奇怪問題——底噪去不掉、說話聲發(fā)悶、甚至降完噪反而更糊。這套 MATLAB 語音降噪實(shí)驗(yàn)源碼包的實(shí)用之處在于它不是給你一堆抽象公式而是直接給了可運(yùn)行的頻譜減法實(shí)現(xiàn)配合分幀、加窗、FFT、逆變換的完整鏈路。你拿到手能立刻跑通一段帶噪語音看到降噪前后的波形對(duì)比、語譜圖變化再回頭調(diào)參數(shù)看效果整個(gè)過程比對(duì)著公式空想要踏實(shí)得多。適合剛接觸語音信號(hào)處理的本科生、轉(zhuǎn)做音頻算法的開發(fā)者也適合需要快速驗(yàn)證降噪思路的技術(shù)人員。語音降噪實(shí)驗(yàn)的關(guān)鍵不只是降噪本身而是你能否控制住每一步的中間結(jié)果。2. MATLAB 為什么適合做語音降噪實(shí)驗(yàn)選型理由與處理鏈路2.1 從語音識(shí)別到降噪MATLAB 的優(yōu)勢(shì)在哪很多人在做語音識(shí)別或語音信號(hào)處理時(shí)糾結(jié)要不要用 Python。Python 生態(tài)確實(shí)豐富但在這類實(shí)驗(yàn)教學(xué)場(chǎng)景里MATLAB 有一個(gè)無可替代的優(yōu)點(diǎn)從讀音頻文件到畫頻譜圖幾乎所有操作都是內(nèi)置函數(shù)不需要處理復(fù)雜的依賴關(guān)系。所謂“零環(huán)境折騰”對(duì)新手來說意味著把更多時(shí)間留給算法本身。另一個(gè)層面的優(yōu)勢(shì)是矩陣思維。語音信號(hào)本質(zhì)上是一個(gè)一維數(shù)組分幀之后變成二維矩陣傅里葉變換在 MATLAB 里就是一行fft。這種“數(shù)組進(jìn)來、矩陣出去”的思維方式特別適合教學(xué)演示用。你可以在命令行里隨時(shí)查看中間變量的尺寸和值這比在 Python 里反復(fù)調(diào)試包版本、檢查數(shù)據(jù)類型要順手得多。從工程角度看MATLAB 的音頻工具箱和信號(hào)處理工具箱提供了audioread、audiowrite、hann、spectrogram等實(shí)用函數(shù)剛好覆蓋語音降噪實(shí)驗(yàn)所需的全部環(huán)節(jié)。即使你沒有額外工具箱只用基礎(chǔ)函數(shù)也能完成整個(gè)流程。而且這套源碼包的代碼風(fēng)格比較規(guī)整注釋充分適合作為你后續(xù)寫自己算法的模板。2.2 一條完整的語音降噪處理鏈路分幀、加窗、FFT、ISTFT語音信號(hào)是非平穩(wěn)的但在一段極短的時(shí)間內(nèi)可以近似看作平穩(wěn)。這就是分幀的理論基礎(chǔ)。典型做法是把語音切成 2030 毫秒的幀幀與幀之間留 10 毫秒左右的重疊避免幀邊界處信息丟失。加窗的目的是減少頻譜泄漏。直接對(duì)一幀信號(hào)做 FFT相當(dāng)于隱式地加了一個(gè)矩形窗矩形窗的旁瓣很高會(huì)在頻譜上產(chǎn)生虛假的頻率成分。漢寧窗Hann是語音增強(qiáng)里最常用的窗函數(shù)主瓣寬度和旁瓣衰減之間比較平衡。FFT 之后每幀信號(hào)從時(shí)域變成頻域得到幅度譜和相位譜。頻譜減法Spectral Subtraction的核心假設(shè)是帶噪語音的幅度譜約等于干凈語音的幅度譜加上噪聲的幅度譜。既然噪聲幅度譜可以從靜音段估計(jì)出來那用帶噪幅度譜減去噪聲幅度譜再結(jié)合原始相位就能大致恢復(fù)出干凈語音。這里很多人會(huì)疑惑——“相位怎么辦”實(shí)際上人耳對(duì)相位失真相對(duì)不敏感直接用帶噪語音的相位是一個(gè)被廣泛接受的工程近似。逆變換那一步也有細(xì)節(jié)。由于幀與幀之間有重疊直接拼接會(huì)出現(xiàn)“臺(tái)階感”所以要用重疊相加OLA或重疊保留WOLA的方式重建時(shí)域信號(hào)。源碼包里用的是重疊相加配合窗函數(shù)做到“等重疊加”保證重建波形幅度不畸變。2.3 實(shí)驗(yàn)?zāi)_本的目錄結(jié)構(gòu)與運(yùn)行方式壓縮包解開之后你會(huì)看到以下幾個(gè)核心文件這里先給出一份文件清單以便對(duì)號(hào)入座文件/目錄作用spectrum_subtraction.m頻譜減法核心函數(shù)輸入帶噪語音輸出降噪語音denoise_demo.m主演示腳本讀取音頻、調(diào)用核心函數(shù)、繪制對(duì)比圖estimate_noise.m噪聲估計(jì)輔助函數(shù)支持前導(dǎo)靜音段估計(jì)法seg_snr.m分段信噪比計(jì)算函數(shù)用于定量評(píng)價(jià)降噪效果audio/測(cè)試音頻目錄包含干凈語音、合成帶噪語音樣本docs/實(shí)驗(yàn)指導(dǎo)文檔含參數(shù)說明和思考題運(yùn)行主腳本只需要在 MATLAB 里把當(dāng)前目錄切到源碼包根目錄然后執(zhí)行denoise_demo腳本會(huì)自動(dòng)讀取audio/目錄下的帶噪樣本調(diào)用spectrum_subtraction完成降噪并彈出三張圖帶噪語音波形、降噪語音波形、二者語譜圖對(duì)比。第一次跑通之后你再回頭逐段閱讀代碼按需修改參數(shù)就能看到效果變化。3. 頻譜減法原理與核心實(shí)現(xiàn)從公式到可運(yùn)行代碼3.1 頻譜減法的數(shù)學(xué)基礎(chǔ)人耳對(duì)相位的容忍頻譜減法的數(shù)學(xué)表達(dá)并不復(fù)雜。設(shè)帶噪語音為 (y(n))干凈語音為 (s(n))加性噪聲為 (d(n))則有[ y(n) s(n) d(n) ]對(duì)每一幀做 DFT 之后頻域表示近似為[ Y(k) S(k) D(k) ]頻譜減法的估計(jì)式通常寫成[ |\hat{S}(k)| \max(|Y(k)| - \alpha \cdot |\hat{D}(k)|, \ \beta \cdot |Y(k)|) ]其中 (\alpha) 是過減因子(\beta) 是譜下限系數(shù)。(|\hat{D}(k)|) 是噪聲幅度譜的估計(jì)值。相位直接用帶噪語音的相位 (\angle Y(k))于是增強(qiáng)后的頻譜為[ \hat{S}(k) |\hat{S}(k)| \cdot e^{j \angle Y(k)} ]為什么可以保留帶噪相位兩個(gè)原因一是人耳聽覺對(duì)相位不敏感二是準(zhǔn)確估計(jì)相位非常困難工程上普遍接受“幅度增強(qiáng)、相位保留”的策略。這個(gè)近似是理解整套代碼的關(guān)鍵也是后續(xù)調(diào)試時(shí)最先要記住的邊界條件。3.2 核心函數(shù)spectrum_subtraction.m逐段拆解以源碼包里的實(shí)現(xiàn)為例核心函數(shù)的大致結(jié)構(gòu)如下function [denoised, info] spectrum_subtraction(x, fs, params) % 頻譜減法定量語音降噪 % 輸入: x - 帶噪語音列向量; fs - 采樣率; params - 參數(shù)字典 % 輸出: denoised - 降噪后語音; info - 調(diào)試信息結(jié)構(gòu)體 if nargin 3 params struct(); end % ---- 默認(rèn)參數(shù)設(shè)置 ---- if ~isfield(params, framelen), params.framelen 25; end % 幀長(zhǎng) 25ms if ~isfield(params, hoplen), params.hoplen 10; end % 幀移 10ms if ~isfield(params, alpha), params.alpha 3.0; end % 過減因子 if ~isfield(params, beta), params.beta 0.01; end % 譜下限系數(shù) if ~isfield(params, noise_frames), params.noise_frames 10; end % 噪聲估計(jì)幀數(shù) % ---- 數(shù)據(jù)預(yù)處理 ---- x x(:); % 強(qiáng)制轉(zhuǎn)列向量 x x / max(abs(x) eps); % 歸一化防止后續(xù)幅度溢出 % ---- 幀參數(shù)換算 ---- nfft 2 ^ nextpow2(round(fs * params.framelen / 1000)); hop round(fs * params.hoplen / 1000); win hann(nfft, periodic); % 周期漢寧窗匹配頻譜分析 % ---- 分幀 ---- nframes floor((length(x) - nfft) / hop) 1; frames zeros(nfft, nframes); for i 1:nframes start_idx (i - 1) * hop 1; frames(:, i) x(start_idx : start_idx nfft - 1) .* win; end % ---- 頻域變換與幅度譜 ---- spec fft(frames, nfft, 1); mag abs(spec); phase angle(spec); % ---- 噪聲幅度譜估計(jì) ---- noise_mag mean(mag(:, 1:params.noise_frames), 2);這段代碼里最值得注意的是兩處一是hann(nfft, periodic)里的periodic選項(xiàng)它生成的窗函數(shù)首尾都為 0更適合頻譜分析和幀重建二是nextpow2的使用把幀長(zhǎng)補(bǔ)到 2 的整數(shù)次冪FFT 計(jì)算更快也方便后續(xù)與窗函數(shù)長(zhǎng)度對(duì)齊。歸一化那一步雖然簡(jiǎn)單但能避免后續(xù)譜減時(shí)數(shù)值溢出屬于實(shí)測(cè)里很容易踩的細(xì)節(jié)坑。繼續(xù)看譜減和重建部分% ---- 頻譜減法核心 ---- mag_enhanced max(mag - params.alpha * noise_mag, params.beta * mag); % ---- 結(jié)合帶噪相位逆變換回時(shí)域 ---- spec_enhanced mag_enhanced .* exp(1i * phase); frames_enhanced real(ifft(spec_enhanced, nfft, 1)); % ---- 重疊相加重建 ---- denoised zeros(length(x), 1); win_sum zeros(length(x), 1); for i 1:nframes start_idx (i - 1) * hop 1; denoised(start_idx : start_idx nfft - 1) ... denoised(start_idx : start_idx nfft - 1) frames_enhanced(:, i); win_sum(start_idx : start_idx nfft - 1) ... win_sum(start_idx : start_idx nfft - 1) win; end % 等重疊相加除以窗函數(shù)疊加和消除調(diào)制偽影 denoised denoised ./ max(win_sum, eps); % ---- 輸出調(diào)試信息 ---- info.nframes nframes; info.nfft nfft; info.hop hop; info.noise_mag noise_mag; end這里過減因子 (\alpha) 乘的是整個(gè)噪聲幅度譜估計(jì)而不是逐頻點(diǎn)變化屬于經(jīng)典頻譜減法的標(biāo)準(zhǔn)做法。(\beta) 的作用是給譜減結(jié)果一個(gè)下限防止某些頻點(diǎn)被減去過多導(dǎo)致音樂噪聲。重建部分最關(guān)鍵的是最后一步win_sum歸一化——因?yàn)榇昂瘮?shù)疊加后幅度不為 1直接重疊相加會(huì)產(chǎn)生明顯的周期性起伏俗稱“過車聲”。這是最容易被人忽略但影響很大的一步。3.3 主程序怎么調(diào)用文件讀取、參數(shù)傳遞與結(jié)果輸出核心函數(shù)寫好后主程序的作用就是拼接整個(gè)實(shí)驗(yàn)流程。denoise_demo.m的典型結(jié)構(gòu)如下%% 1. 讀取帶噪語音 [x, fs] audioread(audio/noisy_sample.wav); if size(x, 2) 1 x mean(x, 2); % 雙聲道轉(zhuǎn)單聲道 end %% 2. 設(shè)置降噪?yún)?shù) params struct(); params.framelen 25; % 幀長(zhǎng) 25ms params.hoplen 10; % 幀移 10ms params.alpha 3.0; % 過減因子 params.beta 0.01; % 譜下限 %% 3. 執(zhí)行降噪 [denoised, info] spectrum_subtraction(x, fs, params); %% 4. 保存結(jié)果 audiowrite(audio/denoised_output.wav, denoised, fs); %% 5. 畫圖對(duì)比 t (0:length(x)-1) / fs; figure; subplot(2,1,1); plot(t, x); title(帶噪語音波形); subplot(2,1,2); plot(t, denoised); title(降噪后語音波形);audioread讀進(jìn)來如果是雙聲道直接取均值混成單聲道是一種常見做法因?yàn)檎Z音降噪實(shí)驗(yàn)通常假設(shè)單聲道輸入。參數(shù)結(jié)構(gòu)體params的設(shè)計(jì)讓后續(xù)批量實(shí)驗(yàn)變得很方便——你可以循環(huán)修改alpha和beta跑多組實(shí)驗(yàn)不用改函數(shù)內(nèi)部代碼。運(yùn)行之后第一件事是聽降噪前后的音頻再對(duì)照波形圖看幅度變化。如果降噪后聽起來有“水聲”或“嘶嘶聲”十有八九是過減因子設(shè)太大需要回調(diào)。這段聽感判斷雖然沒有量化指標(biāo)但在參數(shù)調(diào)試階段是最快的反饋方式。4. 把帶噪語音跑通全流程分幀參數(shù)、窗函數(shù)與噪聲估計(jì)的匹配4.1 幀長(zhǎng)、幀移與窗函數(shù)怎么搭配分幀參數(shù)的選擇直接影響降噪效果也直接影響算法的實(shí)時(shí)性。實(shí)驗(yàn)中常見的坑是把幀長(zhǎng)設(shè)得過長(zhǎng)——比如 50 毫秒——這時(shí)語音在一幀內(nèi)已經(jīng)不平穩(wěn)了頻譜減出來的結(jié)果會(huì)顯得“渾濁”。反過來幀長(zhǎng)太短比如 10 毫秒頻率分辨率不足噪聲減不干凈。下面這張表是幾組常用參數(shù)組合可以直接套用場(chǎng)景幀長(zhǎng)幀移窗函數(shù)備注普通語音降噪25ms10ms漢寧窗最通用組合低信噪比 5dB30ms15ms漢寧窗幀長(zhǎng)加長(zhǎng)譜估計(jì)更穩(wěn)實(shí)時(shí)處理20ms10ms漢寧窗降低算法延遲但效果略差音樂噪聲抑制對(duì)比實(shí)驗(yàn)25ms10ms漢明窗與漢寧窗做對(duì)照觀察窗函數(shù)的選擇上源碼默認(rèn)漢寧窗。漢明窗Hamming的旁瓣更低但主瓣稍寬在噪聲估計(jì)和譜減場(chǎng)景里漢寧窗因?yàn)橹靼暾?、頻率分辨能力好通常比漢明窗更合適。你可以在實(shí)驗(yàn)里把自己實(shí)現(xiàn)里的hann換成hamming跑一遍比較語譜圖的差別這個(gè)練習(xí)比看十篇博客都直觀。4.2 噪聲估計(jì)的兩種方式前導(dǎo)靜音段與自適應(yīng)估計(jì)源碼包里estimate_noise.m提供了兩種噪聲估計(jì)方式。第一種是最經(jīng)典的前導(dǎo)靜音段估計(jì)法——假設(shè)語音文件開頭有一段只有噪聲沒有說話聲取前若干幀的平均幅度譜作為噪聲估計(jì)。這種方式簡(jiǎn)單可靠但有個(gè)明顯前提錄音頭部的確存在靜音段。第二種是自適應(yīng)噪聲估計(jì)常見做法是最小值統(tǒng)計(jì)法Minimum Statistics對(duì)當(dāng)前幀附近若干幀的幅度譜取最小值作為該時(shí)刻的噪聲估計(jì)。這個(gè)方法不需要靜音段先驗(yàn)對(duì)非平穩(wěn)噪聲更魯棒但計(jì)算量大參數(shù)更敏感。調(diào)用兩種估計(jì)方式的接口設(shè)計(jì)如下%% 方式1固定前導(dǎo)靜音段估計(jì) noise_mag1 estimate_noise(mag, fixed, noise_frames, 10); %% 方式2自適應(yīng)最小值統(tǒng)計(jì)估計(jì) noise_mag2 estimate_noise(mag, adaptive, smooth_factor, 0.7, min_frames, 5);實(shí)際使用中如果你處理的是教學(xué)錄音或者朗讀語料前導(dǎo)靜音段估計(jì)法已經(jīng)夠用。如果測(cè)試語音是從電影或電話錄音里截取的沒有干凈的靜音段那就必須走自適應(yīng)路線否則譜減之后的殘余噪聲會(huì)非常明顯。這種選型上的取舍在真實(shí)項(xiàng)目里比你調(diào)任何參數(shù)都重要。4.3 完整運(yùn)行示例標(biāo)準(zhǔn)帶噪樣本的處理前后對(duì)比源碼包的audio/目錄下預(yù)置了干凈語音和合成帶噪語音。你可以用一條命令把整個(gè)實(shí)驗(yàn)跑完% 讀取干凈語音 [clean, fs] audioread(audio/clean_speech.wav); % 合成帶噪語音疊加高斯白噪聲信噪比 10dB noise 0.05 * randn(size(clean)); noisy clean noise; % 執(zhí)行降噪 [denoised, ~] spectrum_subtraction(noisy, fs, params); % 播放對(duì)比 sound(clean, fs); pause(2); sound(noisy, fs); pause(2); sound(denoised, fs);用randn生成的高斯白噪聲與語音疊加是驗(yàn)證降噪算法最標(biāo)準(zhǔn)的方式因?yàn)榘自肼曨l譜平坦噪聲估計(jì)比較穩(wěn)定譜減效果容易觀察。等這一段跑通之后再換成實(shí)際錄音里的環(huán)境噪聲風(fēng)扇聲、鍵盤聲你會(huì)立刻發(fā)現(xiàn)效果變差——這不是算法不行而是噪聲的平穩(wěn)性變了。用一個(gè)非平穩(wěn)噪聲測(cè)試時(shí)你會(huì)發(fā)現(xiàn)前導(dǎo)靜音段估計(jì)法基本失效必須換自適應(yīng)策略。這種“從合成到真實(shí)”的測(cè)試路徑是語音增強(qiáng)實(shí)驗(yàn)里最有價(jià)值的步驟。5. 常見問題與避坑記錄頻譜減法在真實(shí)語音上的五個(gè)坑5.1 底噪不減反增問題出在噪聲估計(jì)段現(xiàn)象處理一段安靜環(huán)境下錄的語音降噪后背景底噪不僅沒小反而出現(xiàn)了持續(xù)的“隆隆”聲。原因噪聲估計(jì)幀選取得太少或者估計(jì)幀里混進(jìn)了語音起始的爆破音。當(dāng)噪聲幅度譜估計(jì)值偏大時(shí)譜減會(huì)過度削減某些頻段而其余頻段保留了原始底噪形成頻譜上的“空洞”聽感上反而覺得噪聲變大。我曾經(jīng)用從第 5 幀開始估計(jì)噪聲結(jié)果那幾幀恰好有唇齒音整個(gè)處理效果直接崩了。解決把noise_frames調(diào)大到 1520 幀并且檢查用于估計(jì)的幀里有沒有明顯的語音能量。更穩(wěn)妥的做法是先畫出前幾幀的波形或語譜圖確認(rèn)靜音段再設(shè)置參數(shù)。另一個(gè)辦法是把噪聲估計(jì)從固定前導(dǎo)段改為滑動(dòng)平均% 用前20幀的平均幅度譜做噪聲估計(jì) noise_mag mean(mag(:, 1:20), 2);5.2 降噪后出現(xiàn)音樂噪聲過減因子設(shè)大了現(xiàn)象降噪后的語音聽起來有一種“水聲”或“金屬聲”像背景里有人在彈電子琴。這就是典型的音樂噪聲Musical Noise。原因頻譜減法本質(zhì)上是非線性處理。過減因子 (\alpha) 太大時(shí)某些頻點(diǎn)的幅度被減到接近零而這些頻點(diǎn)在相鄰幀之間隨機(jī)出現(xiàn)和消失形成聽感上刺耳的孤立頻點(diǎn)。這幾乎是頻譜減法最著名的副作用所有做語音增強(qiáng)的人遲早會(huì)遇到。解決把 (\alpha) 從 3.0 回調(diào)到 1.52.5 之間同時(shí)把 (\beta) 從 0.01 稍微提高到 0.050.1給每個(gè)頻點(diǎn)一個(gè)下限避免幅度被減到零。音樂噪聲無法完全消除只能抑制到可接受范圍。這是頻譜減法這類方法的邊界理解這點(diǎn)比追求完美降噪更有價(jià)值。實(shí)測(cè)經(jīng)驗(yàn)是(\alpha) 在 2.0 附近、(\beta) 在 0.05 附近對(duì)大多數(shù)語料都算是個(gè)相對(duì)安全的折中點(diǎn)。5.3 重建語音有斷點(diǎn)分幀參數(shù)與 ISTFT 不匹配現(xiàn)象降噪后的語音波形看起來正常但聽感上聲音發(fā)“卡”尤其是輔音部分有丟失感像音頻文件被截?cái)嗔艘粯?。原因幀移長(zhǎng)度與窗函數(shù)長(zhǎng)度、疊加方式之間不匹配。常見錯(cuò)誤是分幀時(shí)frame_step設(shè)置為 10ms但逆變換重建時(shí)窗函數(shù)疊加后沒有做歸一化導(dǎo)致每幀邊界處出現(xiàn)幅度調(diào)制。另一個(gè)常見原因是幀長(zhǎng)不是 FFT 點(diǎn)數(shù)的整數(shù)倍導(dǎo)致ifft結(jié)果無法完美對(duì)齊。解決嚴(yán)格按下面對(duì)齊邏輯來。幀長(zhǎng)nfft必須是窗函數(shù)長(zhǎng)度幀移hop要小于幀長(zhǎng)重疊相加后用win_sum歸一化。這段代碼我在實(shí)際項(xiàng)目中用過很多次可以照抄nfft 512; % 幀長(zhǎng)對(duì)應(yīng)的 FFT 點(diǎn)數(shù) hop nfft / 4; % 75% 重疊 win hann(nfft, periodic); % 重疊相加后要除以窗疊加和 denoised denoised ./ max(win_sum, eps);5.4 保存文件失真音頻格式與數(shù)據(jù)類型沒對(duì)齊現(xiàn)象audiowrite保存出來的 wav 文件播放時(shí)聲音很小或帶有爆音。原因audiowrite默認(rèn)把浮點(diǎn)數(shù)據(jù)歸一化到 [-1, 1]如果降噪后的denoised數(shù)組里有超出這個(gè)范圍的值保存時(shí)會(huì)被截?cái)鄬?dǎo)致爆音。另外如果原始音頻是 16bit PCM 讀進(jìn)來的整數(shù)型MATLAB 的audioread會(huì)把它轉(zhuǎn)成 double 類型但數(shù)值范圍是 [-1, 1]如果你自己合成噪聲、疊加之后沒有重新歸一化很容易超出范圍。解決在保存之前強(qiáng)制做一次歸一化這是最保險(xiǎn)的通用處理% 保存前歸一化防止截?cái)?denoised denoised / max(abs(denoised) eps); audiowrite(audio/denoised_output.wav, denoised, fs);如果你需要保存成 16bit PCM可以用audiowrite(..., BitsPerSample, 16)指定位深但前提是數(shù)據(jù)值范圍在 [-1, 1] 內(nèi)。5.5 處理長(zhǎng)語音時(shí)內(nèi)存撐不住批處理要換思路現(xiàn)象處理一段 10 分鐘以上的長(zhǎng)錄音時(shí)MATLAB 占用內(nèi)存飆升甚至出現(xiàn)“內(nèi)存不足”的報(bào)錯(cuò)。原因spectrum_subtraction在函數(shù)內(nèi)部一次性讀入整段語音后分幀而語譜圖處理需要存儲(chǔ)所有幀的復(fù)數(shù)頻譜。以 16kHz 采樣、512 點(diǎn) FFT 為例10 分鐘錄音約有 19000 幀每幀 512 個(gè)復(fù)數(shù)點(diǎn)算上各類中間變量輕松超過 1GB 內(nèi)存。一次性處理整段音頻是這類實(shí)驗(yàn)里最容易忽略的資源問題。解決把長(zhǎng)音頻切片處理每段處理 5~10 秒處理完拼接回去。拼接時(shí)相鄰片段各保留 1 秒重疊用線性淡入淡出消除接縫。核心思路是算法本身不費(fèi)內(nèi)存費(fèi)內(nèi)存的是“一次性全裝進(jìn)來”。切片處理能把峰值內(nèi)存降一個(gè)數(shù)量級(jí)這也是工程實(shí)踐和教學(xué)實(shí)驗(yàn)的一個(gè)重要分界線。6. 驗(yàn)證降噪效果的三個(gè)硬指標(biāo)波形、語譜圖與信噪比提升6.1 用波形和語譜圖做肉眼判斷波形和語譜圖是驗(yàn)證降噪效果最直觀的工具。波形能看出幅度變化和端點(diǎn)位置是否被破壞語譜圖能看出噪聲頻段是否被抑制、語音的共振峰結(jié)構(gòu)是否保留。畫語譜圖的代碼們可以直接嵌進(jìn)你的實(shí)驗(yàn)?zāi)_本figure; subplot(2,1,1); spectrogram(noisy, hann(256), 128, 512, fs, yaxis); title(帶噪語音語譜圖); subplot(2,1,2); spectrogram(denoised, hann(256), 128, 512, fs, yaxis); title(降噪后語譜圖);判斷要點(diǎn)就兩個(gè)一是低頻段和高頻段的底色是否變淺二是語音頻段的亮色條紋是否保持連續(xù)。如果底色變淺但亮條紋也斷了說明過減太狠輔音被誤傷。好的降噪結(jié)果是“噪聲底色明顯變淺語音條紋基本不動(dòng)”。6.2 計(jì)算 segSNR逐幀信噪比才是靠譜的對(duì)比波形和語譜圖是定性判斷量化對(duì)比要靠分段信噪比segSNR。這個(gè)指標(biāo)是把語音切成若干短幀逐幀計(jì)算信噪比再取平均比全局信噪比更貼近人耳聽感。源碼包里的seg_snr.m可以直接調(diào)用snr_before seg_snr(clean, noisy, fs); snr_after seg_snr(clean, denoised, fs); fprintf(降噪前 segSNR: %.2f dB\n, snr_before); fprintf(降噪后 segSNR: %.2f dB\n, snr_after);如果你沒有干凈語音做參考還需要一個(gè)盲評(píng)估指標(biāo)常見做法是計(jì)算降噪前后的短時(shí)能量譜包絡(luò)方差方差越小說明殘留噪聲越平穩(wěn)。但教學(xué)實(shí)驗(yàn)里有干凈語音就直接用 segSNR不用繞彎路。一組值得記錄的數(shù)據(jù)是10dB 白噪聲輸入下經(jīng)典頻譜減法通常能把 segSNR 提升 3~6dB提升超過 8dB 時(shí)音樂噪聲往往已經(jīng)非常明顯。這個(gè)區(qū)間值可以作為你調(diào)試參數(shù)的標(biāo)尺。6.3 批處理與參數(shù)自動(dòng)尋優(yōu)腳本當(dāng)你需要系統(tǒng)性調(diào)參時(shí)手動(dòng)一次次改參數(shù)再聽效果會(huì)把人逼瘋。常見做法是寫一個(gè)簡(jiǎn)單循環(huán)讓腳本自己跑多組參數(shù)組合把所有結(jié)果記錄到一張表格里results table(); alpha_list [1.0, 1.5, 2.0, 2.5, 3.0]; beta_list [0.01, 0.03, 0.05, 0.1]; idx 1; for a alpha_list for b beta_list params.alpha a; params.beta b; denoised spectrum_subtraction(noisy, fs, params); snr seg_snr(clean, denoised, fs); results(idx).alpha a; results(idx).beta b; results(idx).snr snr; idx idx 1; end end % 按 snr 排序找出最優(yōu)組合 results struct2table(results); results sortrows(results, snr, descend); disp(results(1:5, :));不過我得提醒一句按照 segSNR 選出最優(yōu)參數(shù)并不一定等于聽感最好。這個(gè)實(shí)驗(yàn)里有個(gè)很有趣的現(xiàn)象——某組參數(shù)讓 segSNR 提升了 7dB聽感上卻滿是音樂噪聲反而不如提升 4dB 的一組參數(shù)舒服。從那以后我每次做語音增強(qiáng)實(shí)驗(yàn)都強(qiáng)制走一遍“參數(shù)掃描 聽感確認(rèn)”的雙重驗(yàn)證數(shù)學(xué)指標(biāo)只是方向標(biāo)耳朵才是最終裁判。這套源碼包的價(jià)值不在于給你標(biāo)準(zhǔn)答案而是讓你有能力把每個(gè)環(huán)節(jié)拆開看、對(duì)比看建立起自己的判斷標(biāo)準(zhǔn)。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取