1. 程式人生 > >Speex手冊(五)——Speex編解碼器API(2)和語音處理API(1)

Speex手冊(五)——Speex編解碼器API(2)和語音處理API(1)

5.4  模式查詢

        Speex模式查詢類似於speex_encoder_ctl和speex_decoder_ctl函式呼叫。因為模式是隻讀的,所以僅能獲取一個特別模式的資訊。函式呼叫如下:

                void speex_mode_query(Speexmode *mode, int request, void *ptr);

        其中request可允許的值有(除非特別宣告,這些值通過指標ptr返回):

                SPEEX_MODE_FRAME_SIZE  獲取模式的幀大小(樣本數)

                SPEEX_SUBMODE_BITRATE

 通過ptr獲取子模式位元率(整數,單位bps)

5.5  封包和帶內訊號

        有時期望每個資料包(或其他基本儲存單元)不止包含一幀,此時可在呼叫speex_bits_write寫資料流之前呼叫speex_encode N次來實現。若每個包的幀數不是由帶外機制決定,則可以包含一個終止碼。終止碼用5位對程式碼15(十進位制)編碼,如表9.2所示。注意對於1.0.2版本,呼叫speex_bits_write自動插入終止碼來填充最後一位元組,這樣沒有任何其他費用並保證包裡沒有更多幀時Speex能準確檢測到。

        可以傳送帶內資訊到另一端。這些資訊之前加入一個包含4位資訊型別程式碼的模式14的“偽幀”。表5.1列出了可能的程式碼,包含他們的意義和資訊大小。大多數資訊是要傳送到另一端編碼器或解碼器的請求,可以自由接受或無視這些請求。預設的,無視所有帶內資訊。

                

                                                         表1   帶內訊號程式碼

        最後,應用可以用程式碼13定義常用帶內資訊,資訊位元組大小用5位編碼,解碼器若不知如何解碼則可以跳過它。

6  語音處理API(libspeexdsp)

        對於1.2beta3版本,Speex中的非編解碼部分單獨分離成libspeexdsp庫,包括前處理器,聲學回聲消除器,抖動緩衝器和重取樣器。在UNIX環境下,可通過在編譯器命令列中新增-lspeexdsp -lm連結到你的程式中。正如libspeex,libspeexdsp呼叫時可重入的,但不是執行緒安全的,這意味著可以在多執行緒中使用,但多執行緒中對同一狀態的呼叫必須由互斥鎖保護。

6.1 前處理器

        呼叫Speex前處理器之前,首先需要標頭檔案

                #include <speex/speex_preprocess.h>

        然後,建立前處理器狀態:

                SpeexPreprocessState *preprocess_state = speex_preprocess_state_init(frame_size,sampling_rate);

推薦frame_size的大小同編碼器中的一樣,為20ms。

        對於每一輸入幀,需要呼叫:

                speex_preprocess_run(preprocess_state, audio_frame);

其中audio_frame可為輸入或輸出。若不需要輸出處理後的資料,可呼叫以下函式替代:

                speex_preprocess_eatimate_update(preprocess_state, audio_frame);

這一函式會更新前處理器內部狀態變數而無需計算輸出音訊,節省CPU週期。

        前處理器引數可以改變,通過呼叫:

                speex_preprocess_ctl(preprocess_state, request, ptr);

呼叫方法同編碼器和譯碼器中的一樣,選項在6.1.1中列出。

        最後,銷燬前處理器狀態:

                speex_preprocess_state_destroy(preprocess_state);

6.1.1  前處理器選項

        同編解碼器一樣,預處理選項可通過函式控制。可用選項有:

                SPEEX_PREPROCESS_SET_DENOISE  開啟(1)或關閉(2)降噪(spx_int32_t型)

                SPEEX_PREPROCESS_GET_DENOISE  獲取降噪狀態(spx_int32_t型)

                SPEEX_PREPROCESS_SET_AGC  開啟(1)或關閉(2)自動增益控制(AGC)(spx_int32_t型)

                SPEEX_PREPROCESS_GET_AGC  獲取AGC狀態(spx_int32_t型)

                SPEEX_PREPROCESS_SET_VAD  開啟(1)或關閉(2)聲音活動檢測(VAD)(spx_int32_t型)

                SPEEX_PREPRECESS_GET_VAD  獲取VAD狀態(spx_int32_t型)

                SPEEX_PREPROCESS_SET_AGC_LEVEL

                SPEEX_PREPROCESS_GET_AGC_LEVEL

                SPEEX_PREPROCESS_SET_DEREVERB  開啟(1)或關閉(2)混響消除(spx_int32_t型)

                SPEEX_PREPROCESS_GET_DEREVERB  獲取混響消除狀態(spx_int32_t型)

                SPEEX_PREPROCESS_SET_DEREVERB_LEVEL  暫時不可用

                SPEEX_PREPROCESS_GET_DEREVERB_LEVEL  暫時不可用

                SPEEX_PREPROCESS_SET_DEREVERB_DECAY  暫時不可用

                SPEEX_PREPROCESS_GET_DEREVERB_DECAY  暫時不可用

                SPEEX_PREPROCESS_SET_PROB_START

                SPEEX_PREPROCESS_GET_PROB_START

                SPEEX_PREPROCESS_SET_PROB_CONTINUE

                SPEEX_PREPROCESS_GET_PROB_CONTINUE

                SPEEX_PREPROCESS_SET_NOISE_SUPPRESS  設定最大噪聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_GET_NOISE_SUPPRESS  獲取最大噪聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_SUPPRESS  設定最大殘餘回聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_GET_ECHO_SUPPRESS  獲取最帶殘餘回聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE  近端活動時設定最大殘餘回聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE  近端活動時獲取最大殘餘回聲衰減,單位dB(負spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_STATE  為殘餘回聲一直設定相關回聲消除器(若沒有殘餘回聲消除,為指標或NULL)

                SPEEX_PREPROCESS_GET_ECHO_STATE  獲取相關回聲消除器(指標)

6.2  回聲消除

        Speex庫現在包含一個可用的聲學回聲消除演算法。使用回聲消除器之前,需要包含標頭檔案:

                #include <speex/speex_echo.h>

        然後,建立回聲消除器狀態:

                SpeexEchoState *echo_state = speex_echo_state_int(frame_size. filter_length);

其中frame_size是你一次想處理的資料大小(樣本數),filter_length是你想使用的回聲消除濾波器長度(樣本數,也叫尾長)。推薦使用20ms幀長(或等於編解碼器幀長),確保容易執行FFT(2的指數大小最佳)。推薦尾長近似為房間混響時間的三分之一,例如,在小房間中,混響時間是300ms,則尾長最好為100ms(取樣率8000Hz對應800樣本數)。

        建立回聲消除器狀態後,處理音訊:

                speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame);

其中input_frame是麥克風採集到的音訊資料(近端),echo_frame是揚聲器中播放的音訊(遠端,待消除),output_frame是回聲消除後的資料。

        需要重點考慮的是input_frame和echo_frame之間的關係,在任何時候,近端中存在的回聲必須傳送到echo_frame作為參考訊號。換言之,回聲消除器不能消除沒有參考訊號的回聲。另一方面,輸入訊號和參考回聲訊號的延時必須足夠小,否則回聲消除器效果不好甚至無效(譯者注:具體參考NLMS演算法,這是聲學回聲消除的一個關鍵問題,就是近端和遠端的同步問題,因為不同步演算法收斂慢甚至不收斂。微軟的回聲消除效果很好,但也對系統版本和硬體有要求,因為我們設定的取樣率跟硬體實際取樣率還是有一點差別的,具體參見微軟相關論文)。在完全同步時,程式碼可以像這樣:

                write_to_soundcard(echo_frame, frame_size);

                read_from_soundcard(input_frame, frame_size);

                speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame);

        若想更好的回聲消除效果,可在前處理器後設置回聲消除器(見6.1),即初始化時呼叫:

                speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);

        對於1.2-beta2版本,可用另一種方法代替speex_echo_cancellation()函式。當錄音和播放需要同步處理時(如不同執行緒或使用poll()或select()系統呼叫),很難確定input_frame對應哪一echo_frame(譯者注:即同步問題)。作為替代,回放執行緒可簡單呼叫:

                speex_echo_playback(echo_state,echo_frame);

每次播放一幀,就呼叫錄音執行緒處理錄音的一幀:

                speex_echo_capture(echo_state, input_frame, output_frame);

在內部,speex_echo_playback()函式簡單的將播放幀放在一個buffer中,然後在呼叫speex_echo_cancel()時由speex_can_capture()呼叫buffer中資料。使用這種API呼叫方式的一個副作用是回放幀需要延遲兩幀,這是音效卡引起的正常延遲。若回放和錄音已經完全同步,speex_echo_cancellation()函式能更好地控制回聲消除效果。

        最後,回聲消除狀態需要銷燬:

                speex_echo_state_destroy(echo_state);

也可以重置回聲消除器,這樣就可以重新利用回聲消除器而無需再建立一個新的:

                speex_echo_state_reset(echo_state);

6.2.1  故障查詢

        回聲消除器正常工作可能存在幾個問題。其中之一是程式碼中存在bug(或效果是次優的),其他需要考慮的問題有:

                1)當換一塊音效卡來錄音和播放時,原來的回聲消除器無效,除非兩塊音效卡在同樣的時鐘脈衝源上有相同的取樣時鐘“鎖定”,否則,它們的時鐘總會有一些偏移,這將使得回聲消除器不收斂(譯者注:這就是軟體設定的取樣率與硬體實際的取樣率有些許差別的問題)。

                2)錄音和回放之間的延時必須儘可能小。任何訊號都是先回放(遠端),然後被近端取樣錄音,過多的延時意味著部分濾波器長度的浪費。最壞情況下,延時比濾波器長度還大,此時不能消除任何回聲。

                3)回聲尾長(濾波器長度)並不是越大越好。實際上,尾長越長,濾波器收斂越慢。當然,尾長太短也不能消除回聲,但更常見的問題是許多人設定了一個很長的尾長然後奇怪為什麼沒有消除回聲。

                4)非線性失真不能被回聲消除所使用的線性自適應濾波器建模,因此不能消除非線性失真。可使用好的音訊裝置並且避免飽和與削波。

        具體可參考Alexey Frunze的《Echo Cancellation Demystified》(http://www.embeddedstar.com/articles/2003/7/article20030720-1.html),解釋了回聲消除的基本原理。具體演算法在不同文章中的描述不同,但回聲消除的通用方法就是自適應濾波器。

        在1.2beta2版本後,原始碼中包含了一個新的echo_diagnostic.m工具。第一步是在建立是定義DUMP_ECHO_CANCEL_DATA,這將會使回聲消除自動儲存近端、遠端和處理後的訊號到檔案中(aec_rec.sw、sec_play.sw和aec_out.sw),這正是AEC的輸入和輸出。使用這一工具時,需要開啟倍頻程:

                echo_diagnostic('aec_rec.sw', 'aec_play', 'aec_diagnostoc', 1024);

值1024是濾波器長度,可以改變。將會輸出一些有用的資訊,回聲消除音訊將儲存到aec_diagnostic.sw。如果輸出無效(回聲沒有消除),可能是播放或錄音的問題。