上篇文章(基於MCRA-OMLSA的語音降噪(一):原理)講了基於MCRA-OMLSA降噪的原理,本篇講怎麼做軟件實現。軟件實現有多種方式。單純看降噪效果可用python,因為python有豐富的庫可用,可節省不少時間,把主要精力放在降噪效果提升上。如果要把算法用在產品上就得用其他語言。我們是芯片公司,且我們team偏底層,最常用的語言是C,所以我又用C實現了該算法。本文先講講在python下的實現,再講講在C下的實現。
一,python下的實現
Python有豐富的庫,音頻文件讀取的librosa/soundfile等,數學庫的numpy(裏面也包含了信號處理的fft等),連算指數積分的都有了(scipy.special.exp1)。算法原理搞清楚了後先畫軟件流程圖,如下圖:
根據流程圖並且基於現成的python庫很快就能把算法實現了。關鍵是調優,要有好的降噪效果。算法里參數較多,主要有αp、αs、αd等,其中有些有推薦的經驗值,有些需要自己tuning。參數tuning過程中降噪效果評估依舊用權威的PESQ。將乾淨語音和噪聲以指定的SNR(通常有0db/5db/10db/15db等)疊加後得到帶噪語音,用降噪算法對這個帶噪語音做降噪得到降噪後的語音。用PESQ工具分別將帶噪語音和降噪後的語音與原來的乾淨語音做比較,可以得到MOS分提高了多少。花了一些時間tuning後有了一個相對不錯的降噪效果,以疊加的是白噪聲為例,降噪後在各種SNR下的MOS分提升如下表:
二,C語言下的實現
C語言下的實現要用在產品中,算法的運算量(即 CPU load)是一個要重點考慮的因素。實現通常分兩個階段。第一階段是基於C中已有數學庫的浮點實現,在參數值一樣的情況下降噪效果要和python實現的保持一致。如果運算量較大或者處理器不支持浮點運算,需要進入第二階段。首先將用到的數學庫函數用自己寫的函數(函數里只有加減乘除等)代替,然後再將整個算法定點化,使運算量降下來。
1,第一階段
首先根據算法和流程圖定義結構體和API。這裡簡單把API列一下,如下圖:
從上圖看出,MCRA和OMLSA各有兩個API,相對簡潔,一個是初始化,一個是算法處理。實現時遇到的第一個問題是C語言數學庫里沒有提供指數積分函數,需要自己實現。在網上搜了下,書《常用算法程序集(C語言)第三版》的14.15節講了怎麼算指數積分,這裡簡單介紹下。令
又
其中γ為歐拉常數(γ = 0.577215664901532860606512)。
可以通過該書9.7節的勒讓德-高斯求積分法來求。對勒讓德-高斯求積分法感興趣的可以找相關資料來看,這裡就不詳細介紹了。把Ei(x)求出後再取反就是算法中所要的
的值。取幾個值比較這個實現與python里scipy.special.exp1的結果,如下表,可以看出精度還是挺高的。
算法代碼寫好後,還需要FFT相關的代碼才能調試,FFT相關的選用了CMSIS里浮點實現的代碼。調試時帶噪語音文件依舊用的是python實現調試的那個,這樣結果好對齊,方便比較各個環節的輸出,如FFT的輸出、噪聲估計的輸出等。調試時要一級一級的調,在誤差允許的範圍內,如果上一級的輸出一致而本級的輸出不一致,則問題就出在這一級里。在這個方法下很快就調試好了,在誤差允許的範圍內,每幀各個頻點降噪後的幅度譜與python里的保持一致。
2,第二階段
第二階段可分兩個子階段,一是把數學庫的函數用自己寫的函數(函數里只有加減乘除等運算)替代,二是把整個算法定點化。目前第一個子階段已完成,第二個子階段完成後有機會也會寫怎麼對算法做定點化的。
算法用到的數學庫函數主要是自然指數(exp())和一般指數(pow()),再加上做完FFT後是複數,需要用求平方根(sqrt())算得幅度譜,所以需要寫出這幾個庫函數對應的函數。對於一般的求指數而言,它可以轉換成求自然指數和自然對數,具體關係如下:,所以只要實現了自然指數和自然對數就可以了。這樣最終實現的函數是3個:自然指數,自然對數,求平方根。至於怎麼實現的這3個函數,細節較多,會在後面專門寫一篇文章講。實現完這些函數後與標準庫里的做比較,誤差在允許的範圍內即可。再把這些函數用在算法中,拿降噪後的幅度譜與python里的比較,誤差也在允許的範圍內。