摘 要: 基于ARM體系構(gòu)架和嵌入式Linux操作系統(tǒng)構(gòu)建了算法平臺(tái)的硬件和軟件結(jié)構(gòu),并在該平臺(tái)移植了G.729語(yǔ)音編解碼算法。通過(guò)對(duì)軟件優(yōu)化設(shè)計(jì)以及采用基于ARM指令集的算法優(yōu)化策略,對(duì)G.729編解碼器進(jìn)行優(yōu)化,提高了系統(tǒng)運(yùn)行速度。
關(guān)鍵詞: ARM;嵌入式Linux;G.729;語(yǔ)音處理
隨著互聯(lián)網(wǎng)的發(fā)展,基于網(wǎng)絡(luò)的多媒體通信越來(lái)越引起人們的關(guān)注。多媒體通信的基礎(chǔ)是語(yǔ)音通信,為此國(guó)際電信聯(lián)盟電信組(ITU-T)創(chuàng)立了G.711、G.723、G.729等多個(gè)語(yǔ)音編碼的標(biāo)準(zhǔn),其中G.729以其較低的編解碼復(fù)雜度、較高的語(yǔ)音質(zhì)量和很低的編解碼延時(shí)獲得了人們的青睞。G.729采用的是共軛結(jié)構(gòu)的代數(shù)碼激勵(lì)線性預(yù)測(cè)算法CS-ACELP(Conjugate Structure Algebraic Code Excited Linear Prediction),這是一種基于CELP編碼模型的算法。由于G.729編碼器能夠?qū)崿F(xiàn)高語(yǔ)音質(zhì)量(MOS分4.1)和低算法延時(shí),所以被廣泛應(yīng)用于IP電話、移動(dòng)通信、多媒體網(wǎng)絡(luò)等領(lǐng)域。
1 系統(tǒng)介紹
本文所研究的嵌入式語(yǔ)音終端要求能夠?qū)崿F(xiàn)點(diǎn)對(duì)點(diǎn)的實(shí)時(shí)語(yǔ)音通信,同時(shí)具有占用系統(tǒng)資源少、功耗低等特點(diǎn)。為了滿足設(shè)計(jì)要求,系統(tǒng)在基于ARM9內(nèi)核硬件體系構(gòu)架及嵌入式Linux操作系統(tǒng)平臺(tái)上移植了G.729編解碼算法。應(yīng)用Linux操作系統(tǒng)多進(jìn)程與多線程編程相結(jié)合的方法以及管道通信方式來(lái)提高系統(tǒng)運(yùn)行效率。通過(guò)Linux多進(jìn)程編程機(jī)制實(shí)現(xiàn)編碼模塊和解碼模塊的同步運(yùn)行,在編解碼模塊內(nèi)采用Linux多線程編程機(jī)制實(shí)現(xiàn)了關(guān)鍵數(shù)據(jù)緩沖區(qū)的保護(hù)和共享、調(diào)度網(wǎng)絡(luò)傳輸、音頻數(shù)據(jù)處理等功能的系統(tǒng)資源分配。
2 系統(tǒng)硬件構(gòu)架
本系統(tǒng)采用Mini2440開(kāi)發(fā)板作為算法平臺(tái), Mini2440是一款基于ARM9內(nèi)核的開(kāi)發(fā)板,它采用Samsung S3C2440作為微處理器,并采用專業(yè)穩(wěn)定的CPU內(nèi)核電源芯片和復(fù)位芯片來(lái)保證系統(tǒng)運(yùn)行時(shí)的穩(wěn)定性,最高主頻達(dá)到533 MHz。系統(tǒng)的硬件平臺(tái)結(jié)構(gòu)如圖1所示。模擬音頻信號(hào)通過(guò)音頻接口UDA1341轉(zhuǎn)換成數(shù)字音頻信號(hào)后采用DMA方式交由S3C2440微處理器編碼,處理后的數(shù)據(jù)通過(guò)網(wǎng)絡(luò)接口與遠(yuǎn)端嵌入式語(yǔ)音終端進(jìn)行語(yǔ)音交互,也可以與計(jì)算機(jī)網(wǎng)絡(luò)的語(yǔ)音終端進(jìn)行語(yǔ)音交互。系統(tǒng)使用觸摸屏為人機(jī)交互介質(zhì),基于qtopia軟件編寫(xiě)用戶使用界面,控制軟件終端的運(yùn)行。
3 系統(tǒng)軟件設(shè)計(jì)
Mini2440開(kāi)發(fā)板已經(jīng)移植了基于Linux-2.6.32內(nèi)核的嵌入式Linux操作系統(tǒng),Linux操作系統(tǒng)能夠方便地構(gòu)建交互性好、運(yùn)行穩(wěn)定、可擴(kuò)展性強(qiáng)的專用通信設(shè)備。系統(tǒng)軟件設(shè)計(jì)主要分成三部分:(1)對(duì)源代碼進(jìn)行去串行化操作,以便適應(yīng)于遠(yuǎn)程網(wǎng)絡(luò)傳輸;(2)是對(duì)算法進(jìn)行優(yōu)化,提高運(yùn)行效率;(3)進(jìn)行系統(tǒng)軟件設(shè)計(jì)以及優(yōu)化,這是最重要的一步。系統(tǒng)軟件設(shè)計(jì)采用服務(wù)器—客戶端的模式,應(yīng)用Linux多進(jìn)程與多線程相結(jié)合的編程方法實(shí)現(xiàn)。軟件主要由兩個(gè)進(jìn)程組成,即采樣—編碼—發(fā)送進(jìn)程和接收—解碼—播放進(jìn)程,系統(tǒng)通過(guò)這兩個(gè)進(jìn)程實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)實(shí)時(shí)語(yǔ)音通信。在Linux環(huán)境下的項(xiàng)目開(kāi)發(fā)中,一般使用GNU make工具來(lái)維護(hù)程序模塊關(guān)系和生成可執(zhí)行文件,對(duì)G.729算法進(jìn)行移植。由于采用多線程的編程方法,所以在Makefile文件中需要加入對(duì)線程庫(kù)的支持。
3.1 源代碼去串行化
G.729采用的是共軛結(jié)構(gòu)的代數(shù)碼激勵(lì)線性預(yù)測(cè)算法,這是一種基于CELP編碼模型的算法。編碼器對(duì)10 ms長(zhǎng)的語(yǔ)音幀進(jìn)行處理,逐幀地提取CELP模型參數(shù)(LP濾波器系數(shù)、自適應(yīng)碼本和固定碼本索引和增益),然后對(duì)這些參數(shù)進(jìn)行編碼和傳輸。在解碼器端,這些參數(shù)用來(lái)恢復(fù)激勵(lì)信號(hào)和合成濾波器參數(shù),重建語(yǔ)音是使激勵(lì)信號(hào)通過(guò)線性預(yù)測(cè)(LP)濾波器濾波后得到的。
ITU-T發(fā)布的G.729語(yǔ)音編碼器的源代碼在編碼完畢后進(jìn)行了“串行化”的操作,使得經(jīng)過(guò)G.729算法處理后的數(shù)據(jù)流文件看起來(lái)比處理前的語(yǔ)音數(shù)據(jù)更大,這對(duì)語(yǔ)音信號(hào)遠(yuǎn)程網(wǎng)絡(luò)傳輸并沒(méi)有任何作用,因此不能直接應(yīng)用于工程項(xiàng)目。ITU-T為了在標(biāo)準(zhǔn)化方針中進(jìn)行丟幀隱藏測(cè)試,對(duì)語(yǔ)音編解碼器參考軟件的碼流格式一般要求為ITU-T G.192中規(guī)定的格式,即用16位的0x007F表示一個(gè)比特“0”,用0x0081表示一個(gè)比特“1”,每個(gè)幀頭會(huì)有同步字和包的長(zhǎng)度。對(duì)于同步字,0x6B20表示該幀為壞幀,0x6B21表示該幀為好幀,這導(dǎo)致了編碼后數(shù)據(jù)的增大。
解決上述問(wèn)題的方法是去掉串行化代碼。在BITS.C文件中定義了4個(gè)函數(shù):
static void bit2byte(Word16 para,int bitlen,unsigned char*bits,int bitpos);
static Word16 byte2bit(int bitlen,unsigned char *bits,int bitpos);
void prm2bits_ld8k(int prm[],INT16 bits[]);
void bits2prm_ld8k(INT16 bits[],int prm[]);
這個(gè)文件就是編碼后流文件大小沒(méi)有變化的關(guān)鍵所在,對(duì)這4個(gè)函數(shù)進(jìn)行改寫(xiě)就可以得到壓縮后的流文件,這樣標(biāo)準(zhǔn)的ITU-T的G.729語(yǔ)音編碼器和解碼器就基本可以達(dá)到1:16的編碼效率。
3.2 程序設(shè)計(jì)方案
系統(tǒng)軟件設(shè)計(jì)采用服務(wù)器/客戶端的模式,結(jié)合Linux多線程的編程方法,使整個(gè)系統(tǒng)能夠流暢地實(shí)現(xiàn)多用戶的接入和切換。在系統(tǒng)中使用IP地址作為服務(wù)器的編號(hào),用來(lái)區(qū)分來(lái)自于不同用戶的呼叫,軟件啟動(dòng)后以服務(wù)器的形式在內(nèi)存中運(yùn)行,等待用戶輸入或者遠(yuǎn)程用戶的接入,通信結(jié)束后重新返回此處等待。軟件主要由兩個(gè)進(jìn)程組成,在進(jìn)程之間采用Linux管道技術(shù)實(shí)現(xiàn)通信和數(shù)據(jù)傳遞,在線程之間采用互斥鎖的機(jī)制對(duì)關(guān)鍵數(shù)據(jù)進(jìn)行保護(hù)和實(shí)現(xiàn)線程之間的同步,以保證系統(tǒng)的平穩(wěn)、流暢運(yùn)行。系統(tǒng)軟件流程如圖2所示。在主進(jìn)程采樣—編碼—發(fā)送進(jìn)程中創(chuàng)建接收—解碼—播放子進(jìn)程,然后交由Linux操作系統(tǒng)進(jìn)行調(diào)度。在兩個(gè)進(jìn)程交互執(zhí)行的過(guò)程中,采用Linux管道通信的機(jī)制進(jìn)行數(shù)據(jù)傳遞,同時(shí)使用Linux信號(hào)(signal)實(shí)現(xiàn)進(jìn)行間的同步。如在語(yǔ)音通信中子進(jìn)程接收—解碼—播放進(jìn)程與遠(yuǎn)端語(yǔ)音終端網(wǎng)絡(luò)連接好以后將會(huì)發(fā)送一個(gè)同步信號(hào)給正在等待的主進(jìn)程。
………
int client_ready = 0;
………
//進(jìn)程間同步
client_fd=accept(server_fd,(struct sockaddr*)&client_addr, &sin_size);
printf("accept a new client\n");
client_ready = 1;
mysigval.sival_int=real;
sigqueue(server_pid,SIGTERM,mysigval);
………
使用多線程編程方法實(shí)現(xiàn)進(jìn)程中不同任務(wù)的同步運(yùn)行,在主進(jìn)程采樣—編碼—發(fā)送進(jìn)程中,首先對(duì)編碼器進(jìn)行初始化,然后創(chuàng)建采樣和網(wǎng)絡(luò)發(fā)送線程。
………
//創(chuàng)建子線程
if(pthread_create(&thread_s,NULL,speechSample,NULL)) {
printf("pthread_create failed.\n");
exit(1);
}
if(pthread_create(&thread_s,NULL,streamServer,NULL)) {
printf("pthread_create failed.\n");
exit(1);
}
………
為了降低網(wǎng)絡(luò)發(fā)送線程的運(yùn)行次數(shù),在程序運(yùn)行過(guò)程中對(duì)每20幀編碼后的數(shù)據(jù)進(jìn)行tcp包封裝后發(fā)送,這樣既提高了軟件執(zhí)行效率,又降低了程序的復(fù)雜度。由編碼主線程維護(hù)兩個(gè)緩沖區(qū)buf1和buf2,buf1緩沖區(qū)存放等待發(fā)送的20幀數(shù)據(jù),buf2緩沖區(qū)存放編碼線程正在對(duì)其進(jìn)行操作的后20幀數(shù)據(jù),使用流水線方式,循環(huán)地把buf2的數(shù)據(jù)拷貝至buf1。在數(shù)據(jù)拷貝時(shí)采用線程互斥鎖對(duì)關(guān)鍵數(shù)據(jù)進(jìn)行保護(hù),對(duì)互斥鎖靜態(tài)賦值后使得在同一時(shí)刻僅有一個(gè)線程對(duì)這一部分關(guān)鍵數(shù)據(jù)進(jìn)行操作。這樣避免了線程間競(jìng)爭(zhēng)導(dǎo)致數(shù)據(jù)丟失,同時(shí)定義了data_move_ready旗語(yǔ)可以實(shí)現(xiàn)線程之間同步。
………
data_move_ready=0;
………
//線程間同步
pthread_mutex_unlock(&mutex);
data_move_ready=1;
memmove(buf1,buf2,buf_count);
pthread_mutex_unlock(&mutex);
………
經(jīng)過(guò)以上設(shè)計(jì),使得網(wǎng)絡(luò)數(shù)據(jù)的采集傳輸和編解碼算法之間的依賴性降低到最低,保證了數(shù)據(jù)在系統(tǒng)中的流暢性。軟件運(yùn)行過(guò)程中,子進(jìn)程接收—解碼—播放進(jìn)程的執(zhí)行機(jī)制與主進(jìn)程采樣—編碼—發(fā)送進(jìn)程的執(zhí)行機(jī)制一樣,是主進(jìn)程的一個(gè)逆過(guò)程。
4 編解碼器的優(yōu)化
G.729算法雖然有8 Kb/s的編碼速率,是編碼速率和合成語(yǔ)音質(zhì)量綜合效率最優(yōu)的壓縮算法之一,但是直接在ARM上運(yùn)行的效率卻相當(dāng)?shù)?,因此需要?duì)算法進(jìn)行優(yōu)化,提高編解碼效率。編解碼器的優(yōu)化主要采用代碼優(yōu)化的方法,針對(duì)算法運(yùn)算強(qiáng)度最大環(huán)節(jié)或函數(shù),應(yīng)用ARM指令集將該環(huán)節(jié)或函數(shù)重載,以實(shí)現(xiàn)對(duì)算法的優(yōu)化。其次采用匯編語(yǔ)言的目的就是盡量避免ARM處理器流水線上的沖突,充分利用流水線的功能。
根據(jù)語(yǔ)音編碼的特點(diǎn),編解碼的函數(shù)都是由一些基本的加減乘除的簡(jiǎn)單函數(shù)組織而成,G.729編碼器的運(yùn)算工作主要集中在LSP矢量量化、自適應(yīng)碼本搜索和固定碼本搜索環(huán)節(jié)。通過(guò)對(duì)算法具體分析可知,算法運(yùn)算量主要集中在L_mac()、L_mult()、L_add()、Sature()及L_sub()這幾個(gè)函數(shù)。對(duì)這些函數(shù)進(jìn)行優(yōu)化時(shí)可以將其定義為內(nèi)聯(lián)函數(shù),當(dāng)該類函數(shù)被調(diào)用時(shí),編譯器自動(dòng)在目標(biāo)代碼段展開(kāi)該類函數(shù),省去頻繁調(diào)用函數(shù)的開(kāi)銷。G.729算法中包含大量的char和short類型變量,而32位定點(diǎn)ARM編譯器在每次存儲(chǔ)char和short類型變量時(shí)需要額外操作,如果將char和short類型局部變量改為int和unsigned int類型則會(huì)大大降低算法的運(yùn)算量。大量的if語(yǔ)句判斷增加了系統(tǒng)中跳轉(zhuǎn)指令,影響了流水線的流暢性,所以盡量減少跳轉(zhuǎn)指令的使用,通過(guò)填入其他非相關(guān)指令實(shí)現(xiàn)合理利用流水線的目的。
本文提出采用Linux多進(jìn)程與多線程相結(jié)合的設(shè)計(jì)方案,并根據(jù)ARM處理器的特點(diǎn),進(jìn)行了系統(tǒng)性能的優(yōu)化。系統(tǒng)延時(shí)為一幀數(shù)據(jù)處理時(shí)間和20幀數(shù)據(jù)Tcp封裝時(shí)間,即10 ms+20×10 ms=210 ms,在算法處理過(guò)程中沒(méi)有數(shù)據(jù)堆積,語(yǔ)音處理結(jié)果完全達(dá)到了預(yù)期效果。
參考文獻(xiàn)
[1] 姚天任.數(shù)字語(yǔ)音處理[M].武漢:華中科技大學(xué)出版社,2007.
[2] ITU-T. Recommendation G. 729-Anneb B A silence scheme for G. 729 optimized for terminals conforming to Recommendation V7.0[S]. Geneva: ITU, 1994.
[3] WU Chienhsuan, Huang Chinyu, Chang Junru. Applation-specific RISC achitecture for ITU-T G.729 decoding processing[M]. IEEE, 2006.
[4] 杜春雷.ARM體系結(jié)構(gòu)與編程[M].北京:清華大學(xué)出版社,2003.
[5] 吳海濤.G.729語(yǔ)音編碼算法實(shí)現(xiàn)方法研究及DSP實(shí)現(xiàn)[J].哈爾濱理工大學(xué)學(xué)報(bào),2005(6).