首頁(yè) > 評(píng)測(cè) > 音頻算法從這里開(kāi)始——【嵌入式音頻】第四章·G711編解碼

音頻算法從這里開(kāi)始——【嵌入式音頻】第四章·G711編解碼

嵌入式   音頻處理   G711   CODEC   ALaw   uLaw   
  • 作者:zhanzr
  • 來(lái)源:21ic
  • [導(dǎo)讀]
  • “EveryBoard Can Sing” 21ic打算攜手資(tu)深(ding)直男癌晚期工程師zhanzr21,來(lái)給大家講一講嵌入式系統(tǒng)與音頻處理的故事。 關(guān)于zhanzr21: 曾經(jīng)混跡于兩岸三地,摸爬滾打在前端后端,搞過(guò)學(xué)術(shù)上過(guò)班。現(xiàn)在創(chuàng)業(yè)中,歡迎各種撩

這一章開(kāi)始介紹一些算法相關(guān)的內(nèi)容.前面有幾章對(duì)播放強(qiáng)調(diào)較多,這期本來(lái)想寫(xiě)錄音方面的內(nèi)容.但是普通的麥克風(fēng)電路主要是模擬電路,獨(dú)立做一章來(lái)寫(xiě)覺(jué)得內(nèi)容有點(diǎn)少,所以會(huì)在其他章節(jié)穿插介紹.至于MEMS麥克風(fēng),因?yàn)樯婕暗絇DM解碼,可以在后面做獨(dú)立一章來(lái)寫(xiě).

本章內(nèi)容:

l G711協(xié)議

l ALaw與uLaw編解碼

l ALaw與uLaw生成與驗(yàn)證

l 在開(kāi)發(fā)板上實(shí)現(xiàn)G711編解碼

本章實(shí)踐:

l 【驗(yàn)證】1:ALaw算法分析驗(yàn)證

l 【驗(yàn)證】2:uLaw算法分析驗(yàn)證

l 【實(shí)驗(yàn)】1:Audacity操作轉(zhuǎn)音頻

l 【實(shí)驗(yàn)】2:Python制作ALaw和uLaw數(shù)據(jù)

l 【實(shí)驗(yàn)】3:SNR計(jì)算

l 【訓(xùn)練】:板子播放

工具:

l Python

l Excel

l Audacity

l 開(kāi)發(fā)板

l 音箱

點(diǎn)擊鏈接加入群【嵌入式音頻信號(hào)處理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks

嵌入式音頻專用資料代碼分享:https://pan.baidu.com/s/1dFh5pWd

本期活動(dòng)地址:[喜迎兒童節(jié)]嵌入式音頻活動(dòng)第四期

---

G711.jpg

前言

本章這里說(shuō)的算法,主要是指的編碼解碼的算法,就是所謂的CODEC(Encode/Decode).語(yǔ)音識(shí)別,語(yǔ)音合成,環(huán)境音效等技術(shù)屬于另外不同的算法,這些算法主要針對(duì)原始(raw)的音頻數(shù)據(jù)進(jìn)行.而在實(shí)際工程實(shí)踐中,編解碼算法是研究的最多的內(nèi)容.原因以前也說(shuō)過(guò),原始音頻內(nèi)容太多,對(duì)存儲(chǔ)傳輸?shù)膸捯筮^(guò)大,而音頻數(shù)據(jù)本身又有相當(dāng)大的冗余,故此這一方面的研究可以說(shuō)在個(gè)人電腦誕生之前就開(kāi)始了.早期的電信行業(yè)與計(jì)算機(jī)行業(yè)屬于不同的兩個(gè)行業(yè),電信行業(yè)資格要老些.電報(bào),電話,廣播這些技術(shù)在一次世界大戰(zhàn)之前就應(yīng)用很廣泛了.當(dāng)然真正跟現(xiàn)代電信業(yè)有直接關(guān)系的技術(shù)則是二戰(zhàn)之后才迅速發(fā)展的.

 

簡(jiǎn)介: ITU音頻標(biāo)準(zhǔn)G711

ITU(國(guó)際電信聯(lián)盟)是由國(guó)際電報(bào)聯(lián)盟發(fā)展而來(lái)的一個(gè)國(guó)際組織,1865年在法國(guó)巴黎創(chuàng)建. 1947年成為聯(lián)合國(guó)的一個(gè)下屬機(jī)構(gòu),總部設(shè)在日內(nèi)瓦.主要從事電信行業(yè)的各種技術(shù)的標(biāo)準(zhǔn)化,推進(jìn)等活動(dòng).大概跟ISO,IEC,EIA,IEEE這些機(jī)構(gòu)齊名.其中負(fù)責(zé)標(biāo)準(zhǔn)制定的機(jī)構(gòu)原稱做CCITT,1993改名為ITU-T.近幾年因?yàn)楹粲跫訌?qiáng)互聯(lián)網(wǎng)管制而跟各大壟斷公司,強(qiáng)權(quán)國(guó)家發(fā)生了一些爭(zhēng)吵.不過(guò)這跟本文沒(méi)有啥關(guān)系,只是說(shuō)到ITU,帶一筆交代一下.

關(guān)于音頻算法,ITU出了很多標(biāo)準(zhǔn),主要著重于語(yǔ)音領(lǐng)域,因?yàn)镮TU老本行就是研究電話語(yǔ)音傳輸?shù)?因?yàn)镮TU發(fā)布的語(yǔ)音算法標(biāo)準(zhǔn)很多,一一介紹就有點(diǎn)過(guò)于繁瑣.所以挑一兩個(gè)出來(lái)介紹,其余的標(biāo)準(zhǔn)感興趣的讀者可以自行研究.這里先給出一個(gè)概覽,ITU對(duì)于標(biāo)準(zhǔn)化的音頻算法,是按照帶寬來(lái)分類(lèi)的:

ITU音頻算法分類(lèi).jpg

圖 ITU音頻算法分類(lèi)

NB就是NarrowBand,即窄帶

WB就是WideBand,即寬帶

SWB就是SuperWideBand,即超寬帶

FB就是FullBand,即全帶寬

這些所謂的寬與窄都是約定俗成的,從采樣率,編解碼的算法等角度綜合衡量的.

ITU窄帶音頻算法標(biāo)準(zhǔn).jpg

圖 ITU窄帶音頻算法標(biāo)準(zhǔn)

ITU寬帶音頻算法標(biāo)準(zhǔn).jpg

圖 ITU寬帶音頻算法標(biāo)準(zhǔn)

ITU超寬帶音頻算法.jpg

圖 ITU超寬帶音頻算法

對(duì)于全帶寬,目前只有G.729. 某些標(biāo)準(zhǔn)算法有多個(gè)版本,分屬不同帶寬需求類(lèi)型. 這一大波算法中,G711屬于歷史最悠久的.本文主要介紹G711,計(jì)劃后面文章還寫(xiě)一個(gè)G726.其余算法暫時(shí)不打算專門(mén)分出章節(jié)來(lái)寫(xiě).

G711的內(nèi)容是將14bit或者13bit采樣的PCM音頻數(shù)據(jù)編碼成8bit的數(shù)據(jù)流,播放的時(shí)候?qū)⒋?bit的數(shù)據(jù)還原成14bit或者13bit進(jìn)行播放.不同于MPEG這種對(duì)整體或者一段音頻數(shù)據(jù)進(jìn)行考慮再進(jìn)行編解碼的做法,G711是波形編解碼算法,就是一個(gè)sample對(duì)應(yīng)一個(gè)編碼.所以壓縮比是固定的,為

8/14 = 57% (uLaw)或者

8/13 = 62% (aLaw).

因?yàn)橹饕糜谡Z(yǔ)音音頻,采樣率一般使用8KHz,這樣帶寬為8bit乘以8KHz = 64Kbps.這個(gè)64Kbps也常常被稱作"一路數(shù)字電話"作為非正式的帶寬計(jì)算單位.

之所以取這些看起來(lái)比較低端的參數(shù),是因?yàn)樵缙谟布芰τ邢?且此技術(shù)當(dāng)初主要用于電話語(yǔ)音傳輸所致.G711是1972年制定的標(biāo)準(zhǔn),專利早就過(guò)期了,所以現(xiàn)在屬于Public Domain的內(nèi)容,任何人都能任意使用.

G711主要的兩種版本稱之為uLaw(u律,14bit版本)與ALaw(a律,13bit版本).

uLaw主要用于日本和北美地區(qū),除了日本和北美地區(qū)世界上基本上所有地方都用ALaw.從技術(shù)的角度來(lái)講, ALaw因?yàn)樘峁┝烁蟮膭?dòng)態(tài)范圍,同時(shí)對(duì)硬件要求高一點(diǎn).uLaw則對(duì)音量較低的信號(hào)提供更好的量化分辨率.一般來(lái)講,如果兩個(gè)國(guó)家的數(shù)字電話對(duì)接,如果有一方使用A-Law,另一方使用u-Law,那么就會(huì)采用A-Law.

有人會(huì)問(wèn),直接將14bit和13bit的PCM采樣進(jìn)行移位,丟掉低位得到8bit結(jié)果,豈不是更簡(jiǎn)單.但是音頻數(shù)據(jù)的特點(diǎn)不適合這樣的線性編碼,直接移位的結(jié)果會(huì)丟棄音頻的主要變化的部分(低位部分)從而導(dǎo)致直接移位的結(jié)果是音頻質(zhì)量大大降低.不管是ALaw還是uLaw,其核心思想是,音頻的大多數(shù)變化發(fā)生在整個(gè)動(dòng)態(tài)范圍的低部分.而且人耳對(duì)音頻幅度的感覺(jué)成對(duì)數(shù)關(guān)系.從這個(gè)角度來(lái)講,說(shuō)G711是前PC時(shí)代的MP3算法也有點(diǎn)道理.

下面分別介紹兩種版本算法的詳細(xì)情況.

 

A-Law詳解

A-Law的編碼公式如下:

A-Law的編碼公式.jpg

其中x為輸入,F(x)為編碼結(jié)果,A為壓縮參數(shù)一般取值為87.6 .

A-Law解碼公式,也就上述函數(shù)的反函數(shù)如下:

A-Law解碼公式,也就上述函數(shù)的反函數(shù).jpg

其中y為上述函數(shù)的結(jié)果編碼.

現(xiàn)在動(dòng)手來(lái)做一個(gè)編碼與解碼的程序來(lái)觀察一下子整個(gè)過(guò)程.Python 3腳本.

 

【驗(yàn)證】1:ALaw算法分析驗(yàn)證

# ALaw Encode Decode Test

# Author: zhanzr21 @ 21ic BBS

#

# If you just want to do G711 A-Law encode/decode, use the audioop package,

# This script is just for analysis and study of the algorithm itself.

#

import math

#Byte format test

LineData =[i for i in range(-4096,4096)]

#normalization

NormaData = [i/4096 for i in LineData]

A = 87.6

A_1 = (1/A)

f_test = open('g711_alaw_test.csv',mode='w')

for x in NormaData:

#Encode

if(abs(x)

y = A*abs(x)/(1+math.log(A))

y = math.copysign(y, x)

elif((abs(x)<=1) and (abs(x)>=A_1)):

y = (1+math.log(A*abs(x)))/(1+math.log(A))

y = math.copysign(y, x)

else:

#Should not come here

print("ErrorEn %f", x)

yQ = (int(y*128))/128

res = 0

resQ = 0

#Decode

if(abs(y)<(1/(1+math.log(A)))):

res = abs(y)*(1+math.log(A))/A

res = math.copysign(res, y)

elif((abs(y)<=1) and ( abs(y)>=(1/(1+math.log(A))) ) ):

res = math.exp(abs(y)*(1+math.log(A))-1)/A

res = math.copysign(res, y)

else:

#Should not come here

print("ErrorDe %f", y)

#Decode(Quantization)

if(abs(yQ)<(1/(1+math.log(A)))):

resQ = abs(yQ)*(1+math.log(A))/A

resQ = math.copysign(resQ, yQ)

elif((abs(yQ)<=1) and ( abs(yQ)>=(1/(1+math.log(A))) ) ):

resQ = math.exp(abs(yQ)*(1+math.log(A))-1)/A

resQ = math.copysign(resQ, yQ)

else:

#Should not come here

print("ErrorDeQ %f", yQ)

#for debug

f_test.write('%f,%f,%f,%f,%fn' % (x, y, yQ, res, resQ))

f_test.close()

將結(jié)果畫(huà)成圖表以便觀察(Excel):

從-1至1的PCM數(shù)據(jù)的ALaw編解碼曲線.jpg

圖 從-1至1的PCM數(shù)據(jù)的ALaw編解碼曲線

其中:

Series1為原始13bit的PCM采樣數(shù)據(jù)[-4096,4096),歸一化到[-1,1)的范圍

Series2為連續(xù)編碼

Series3為量化后的編碼,可以看出在信號(hào)幅度高時(shí)誤差較大

Series4為連續(xù)解碼結(jié)果幾乎沒(méi)有失真

Series5為量化編碼的解碼結(jié)果,幅度大的時(shí)后誤差較大

可以看出ALaw對(duì)0附近的數(shù)據(jù)的編碼分辨率較高,而對(duì)于幅度較大的信號(hào)則分辨率較低.當(dāng)然,實(shí)際使用的的時(shí)候,不管什么語(yǔ)言都有現(xiàn)成的庫(kù)函數(shù)可供調(diào)用,這是個(gè)非常常見(jiàn)的算法.對(duì)于Python而言,使用audioop庫(kù)函數(shù)(https://docs.python.org/3/library/audioop.html)即可.對(duì)于C/C++,ITU給出了參考代碼,本文后面會(huì)進(jìn)一步說(shuō)明.

 

u-Law詳解

uLaw的編解碼公式分別如下:

uLaw的編碼公式.jpg

uLaw的解碼公式.jpg

其中u通常取值255.

還是動(dòng)手來(lái)做一個(gè)編碼與解碼的程序來(lái)觀察一下子整個(gè)過(guò)程.Python 3腳本.

【驗(yàn)證】2:uLaw算法分析驗(yàn)證

# uLaw Encode Decode Test

# Author: zhanzr21 @ 21ic BBS

#

# If you just want to do G711 u-Law encode/decode, use the audioop package,

# This script is just for analysis and study of the algorithm itself.

#

import math

#Byte format test

LineData =[i for i in range(-4096,4096)]

#normalization

NormaData = [i/4096 for i in LineData]

U = 255

U_1 = (1/U)

f_test = open('g711_ulaw_test.csv',mode='w')

for x in NormaData:

#Encode

y = math.log(1 + U*abs(x))/math.log(1 + U)

y = math.copysign(y, x)

yQ = (int(y*128))/128

res = 0

resQ = 0

#Decode

res = U_1*((1+U)**abs(y) - 1)

res = math.copysign(res, y)

#Decode(Quantization)

resQ = U_1*((1+U)**abs(yQ) - 1)

resQ = math.copysign(resQ, yQ)

#for debug

f_test.write('%f,%f,%f,%f,%fn' % (x, y, yQ, res, resQ))

f_test.close()

這個(gè)代碼比ALaw的代碼要簡(jiǎn)單一些,但是因?yàn)橛兄虚g結(jié)果的冪函數(shù)運(yùn)算,對(duì)硬件的要求要嚴(yán)格一些的.

uLaw的編解碼數(shù)據(jù)曲線.jpg

圖 uLaw的編解碼數(shù)據(jù)曲線

此圖與ALaw的類(lèi)似,只是因?yàn)樵疾蓸佣嗔?bit,故此數(shù)據(jù)多了一倍.本貼的共享目錄中有所有腳本與實(shí)驗(yàn)數(shù)據(jù),感興趣的同學(xué)可以下載到電腦上仔細(xì)品味一番.與ALaw相同,此處的代碼僅為研究?jī)?nèi)部信號(hào)過(guò)程,實(shí)際使用中有現(xiàn)成的庫(kù)函數(shù)可用(從標(biāo)準(zhǔn)符合度,優(yōu)化度等方面考慮).

 

在電腦上制作/驗(yàn)證G711數(shù)據(jù)

兩種方式,一種使用工具制作,一種使用程序來(lái)編碼.實(shí)際工作當(dāng)中當(dāng)然使用工具制作,用程序來(lái)編碼是為了驗(yàn)證我們對(duì)算法的理解.工具還是老熟人:Audacity.

【實(shí)驗(yàn)】1:Audacity操作轉(zhuǎn)音頻

打開(kāi)一個(gè)2ch/16bit的音頻文件.

原始音頻文件.jpg

圖 原始16bit/2ch/11.025K的音頻文件

為了更清楚理解,我們把它處理成單通道8K,16bit的數(shù)據(jù),具體之前的章節(jié)講過(guò),也不贅述.結(jié)果是這樣的.

單通道結(jié)果.jpg

點(diǎn)File/Export Audio在格式中選擇ALaw/uLaw分別導(dǎo)出兩個(gè)文件出來(lái).

1.jpg

這樣,制作就完成了,我們可以先重新打開(kāi)工具,導(dǎo)入這兩個(gè)文件看看波形試聽(tīng)一下子以驗(yàn)證上述操作無(wú)誤.

2.jpg

圖 導(dǎo)入的時(shí)候選格式

3.jpg

圖 從上到下分別是ALaw還原,uLaw還原,原始16bit的PCM波形,肉眼看不出差別來(lái)的.后面我們會(huì)用程序來(lái)計(jì)算SNR.

至于文件大小,原始文件是1100248 bytes, 兩個(gè)G711編碼的文件都是550124 bytes.壓縮比看起來(lái)是2:1,但是如同前文所言,ALaw僅僅使用了13bit的數(shù)據(jù),uLaw使用了14bit的數(shù)據(jù),都是取的數(shù)據(jù)的MSB開(kāi)始的部分.所以壓縮比其實(shí)沒(méi)有這么大的.

現(xiàn)在開(kāi)始用程序來(lái)對(duì)這個(gè)原始文件分別進(jìn)行ALaw/uLaw的編碼/解碼.為了更好的兼容性,直接使用python的標(biāo)準(zhǔn)庫(kù)函數(shù)audioop,ALaw/uLaw的代碼分別如下:

【實(shí)驗(yàn)】2:Python制作ALaw和uLaw數(shù)據(jù)

ALaw測(cè)試代碼:

# ALaw Encode Test using the audioop package

# Author: zhanzr21 @ 21ic BBS

#

import math

import sys

import audioop

DEFAULT_INPUT = 'test_8k16bitmono.bin'

DEFAULT_OUTPUT = 'test_8k16bitmono.alaw'

DEFAULT_BACK = 'test_8k16bitmono.aback'

raw_name = ''

alaw_name = ''

back_name = ''

def alaw_encode(in_name, out_name):

fin = open(in_name, mode='rb')

fout = open(out_name, mode='wb')

raw_data = fin.read()

fin.close()

test_ba = audioop.lin2alaw(raw_data, 2)

fout.write(test_ba)

fout.close()

return

def alaw_decode(in_name, out_name):

fin = open(in_name, mode='rb')

fout = open(out_name, mode='wb')

raw_data = fin.read()

fin.close()

test_ba = audioop.alaw2lin(raw_data, 2)

fout.write(test_ba)

fout.close()

return

if __name__ == "__main__":

if(2 <= len(sys.argv)):

raw_name = sys.argv[1]

else:

raw_name = DEFAULT_INPUT

if(3 <= len(sys.argv)):

alaw_name = sys.argv[2]

else:

alaw_name = DEFAULT_OUTPUT

if(4 <= len(sys.argv)):

back_name = sys.argv[3]

else:

back_name = DEFAULT_BACK

alaw_encode(raw_name, alaw_name)

#for verification

alaw_decode(alaw_name, back_name)

#for compare with Audacity

alaw_decode('test_8k16bitmono_ref.alaw', 'test_8k16bitmono_ref.aback')

uLaw測(cè)試代碼:

# uLaw Encode Test using the audioop package

# Author: zhanzr21 @ 21ic BBS

#

import math

import sys

import audioop

DEFAULT_INPUT = 'test_8k16bitmono.bin'

DEFAULT_OUTPUT = 'test_8k16bitmono.ulaw'

DEFAULT_BACK = 'test_8k16bitmono.uback'

raw_name = ''

ulaw_name = ''

back_name = ''

def ulaw_encode(in_name, out_name):

fin = open(in_name, mode='rb')

fout = open(out_name, mode='wb')

raw_data = fin.read()

fin.close()

test_ba = audioop.lin2ulaw(raw_data, 2)

fout.write(test_ba)

fout.close()

return

def ulaw_decode(in_name, out_name):

fin = open(in_name, mode='rb')

fout = open(out_name, mode='wb')

raw_data = fin.read()

fin.close()

test_ba = audioop.ulaw2lin(raw_data, 2)

fout.write(test_ba)

fout.close()

return

if __name__ == "__main__":

if(2 <= len(sys.argv)):

raw_name = sys.argv[1]

else:

raw_name = DEFAULT_INPUT

if(3 <= len(sys.argv)):

ulaw_name = sys.argv[2]

else:

ulaw_name = DEFAULT_OUTPUT

if(4 <= len(sys.argv)):

back_name = sys.argv[3]

else:

back_name = DEFAULT_BACK

ulaw_encode(raw_name, ulaw_name)

#for verification

ulaw_decode(ulaw_name, back_name)

#for compare with Audacity

ulaw_decode('test_8k16bitmono_ref.ulaw', 'test_8k16bitmono_ref.uback')

【實(shí)驗(yàn)】3:SNR計(jì)算

SNR計(jì)算代碼:

#SNR Test for 16bit PCM data

#Author: zhanzr21 @ 21ic BBS

import math

import sys

import audioop

EFAULT_INPUT_A = 'test_8k16bitmono.aback'

EFAULT_INPUT_U = 'test_8k16bitmono.Uback'

DEFAULT_REF = 'test_8k16bitmono.bin'

inA_name = ''

inU_name = ''

ref_name = ''

def SNR_S16(input16, ref16):

EnergySignal = 0.0

EnergyError = 0.0

SNR = 0.0

temp = 0

for i in range(len(input16)//2):

#input value

u16_sample = input16[i*2] + input16[1 + i*2] * 256

#unsigned to signed

if(u16_sample>32767):

test_sample = u16_sample - 65536

else:

test_sample = u16_sample

#normalization

test = test_sample / 32768

#Ref value

u16_sample = ref16[i*2] + ref16[1 + i*2] * 256

#unsigned to signed

if(u16_sample>32767):

test_sample = u16_sample - 65536

else:

test_sample = u16_sample

#normalization

ref = test_sample / 32768

EnergySignal = EnergySignal + ref * ref

EnergyError = EnergyError + (ref - test) * (ref - test)

try:

SNR = 10 * math.log10 (EnergySignal / EnergyError);

except ZeroDivisionError:

SNR = math.inf

return (SNR)

def SNR_S16_FILE(inF, refF):

fin = open(inF, mode='rb')

ba_in = fin.read()

fin.close()

fref = open(refF, mode='rb')

ba_ref = fref.read()

fref.close()

if(len(ba_in) != len(ba_ref)):

print("Size Not Equal %s,%s" % inF, refF)

return 0

else:

return SNR_S16(ba_in, ba_ref)

if __name__ == "__main__":

if(2 <= len(sys.argv)):

inA_name = sys.argv[1]

else:

inA_name = EFAULT_INPUT_A

if(3 <= len(sys.argv)):

inU_name = sys.argv[2]

else:

inU_name = EFAULT_INPUT_U

if(4 <= len(sys.argv)):

ref_name = sys.argv[3]

else:

ref_name = DEFAULT_REF

print('Ref SNR:%f' % SNR_S16_FILE(ref_name, ref_name))

print('alaw SNR:%f' % SNR_S16_FILE(inA_name, ref_name))

print('ulaw SNR:%f' % SNR_S16_FILE(inU_name, ref_name))

#for comparign with Audacity result

print('Audicatiy alaw SNR:%f' % SNR_S16_FILE('test_8k16bitmono_ref.aback', ref_name))

 

print('Audicatiy ulaw SNR:%f' % SNR_S16_FILE('test_8k16bitmono_ref.uback', ref_name))

分別運(yùn)行以上三個(gè)腳本,就能得到ALaw編碼,uLaw編碼文件,以及同library解碼的文件,另外為了參考,把Audacity的編碼文件也使用audioop進(jìn)行了解碼.

SNR計(jì)算的結(jié)果:

Ref SNR:inf

alaw SNR:37.544108

ulaw SNR:37.150272

Audicatiy alaw SNR:37.523572

 

Audicatiy ulaw SNR:37.166987

可以看出audioop編碼的SNR要比Audacity的效果有小小差別,也有可能是兩者對(duì)原始數(shù)據(jù)的量化取舍方法不同導(dǎo)致.另外ALaw在我們這個(gè)例子中取得SNR稍稍好于uLaw的結(jié)果.這是因?yàn)檫@段音頻的本身特點(diǎn)決定的.

 

在板子上播放G711數(shù)據(jù)——【訓(xùn)練】:板子播放

(以GD32F105開(kāi)發(fā)板為例)

C語(yǔ)言解碼G711,已經(jīng)有非常多的例子,優(yōu)化也很成熟了.這里參考已有的SpanDSP工程中的代碼給出解碼代碼片段.其余代碼因?yàn)楦暗奈恼轮貜?fù)較多請(qǐng)參考共享文件夾中的工程.總體來(lái)說(shuō),把原理搞懂之后,G711是非常簡(jiǎn)單的算法,一個(gè)sample對(duì)應(yīng)一個(gè)sample.可以說(shuō)G711是常用的算法中,最簡(jiǎn)單的音頻編解碼算法.

C語(yǔ)言解碼ALaw/uLaw代碼:

#define ULAW_BIAS 0x84 /* Bias for linear code. */

#define ALAW_AMI_MASK 0x55

static __inline int16_t alaw_to_linear(uint8_t alaw) {

int i;

int seg;

alaw ^= ALAW_AMI_MASK;

i = ((alaw & 0x0F) << 4);

seg = (((int) alaw & 0x70) >> 4);

if (seg)

i = (i + 0x108) << (seg - 1);

else

i += 8;

return (int16_t)((alaw & 0x80) ? i : -i);

}

/*! brief Decode an u-law sample to a linear value.

param ulaw The u-law sample to decode.

return The linear value.

*/

static __inline int16_t ulaw_to_linear(uint8_t ulaw) {

int t;

/* Complement to obtain normal u-law value. */

ulaw = ~ulaw;

/*

* Extract and bias the quantization bits. Then

* shift up by the segment number and subtract out the bias.

*/

t = (((ulaw & 0x0F) << 3) + ULAW_BIAS) << (((int) ulaw & 0x70) >> 4);

return (int16_t)((ulaw & 0x80) ? (ULAW_BIAS - t) : (t - ULAW_BIAS));

}

相比較而言,ITU官方給出的代碼使用浮點(diǎn)計(jì)算的思路,代碼顯得臃腫一點(diǎn).

ITU的ALaw解碼參考代碼:

void alaw_expand(lseg, logbuf, linbuf)

long lseg;

short *linbuf;

short *logbuf;

{

short ix, mant, iexp;

long n;

for (n = 0; n < lseg; n++)

{

ix = logbuf[n] ^ (0x0055); /* re-toggle toggled bits */

ix &= (0x007F); /* remove sign bit */

iexp = ix >> 4; /* extract exponent */

mant = ix & (0x000F); /* now get mantissa */

if (iexp > 0)

mant = mant + 16; /* add leading '1', if exponent > 0 */

mant = (mant << 4) + (0x0008); /* now mantissa left justified and */

/* 1/2 quantization step added */

if (iexp > 1) /* now left shift according exponent */

mant = mant << (iexp - 1);

linbuf[n] = logbuf[n] > 127 /* invert, if negative sample */

? mant

: -mant;

}

}

程序流程:

程序流程圖.jpg

圖 軟件流程圖

F105播放ALaw/uLaw的圖片:

TIM截圖20170525001624.jpg

圖 F105播放G711音頻

關(guān)于GD32F105開(kāi)發(fā)板:

此開(kāi)發(fā)板是作者專門(mén)為嵌入式音頻處理學(xué)習(xí)所制作的,或許不久之后即可開(kāi)開(kāi)放領(lǐng)取,請(qǐng)大家期待!

 

G711的擴(kuò)展

截止本文寫(xiě)作時(shí),G711標(biāo)準(zhǔn)有兩個(gè)擴(kuò)展:G711.0與G711.1.G711.0也稱作G711 LLC,也就是無(wú)損壓縮擴(kuò)展.因?yàn)榻?jīng)過(guò)了進(jìn)一步的壓縮,帶寬又可降低一半左右.G711.1則是對(duì)原G711的64Kbps的擴(kuò)展.可以選擇80,96Kbps的帶寬來(lái)提高傳輸?shù)馁|(zhì)量.因?yàn)槟壳皫捹Y源的發(fā)展較快,可以預(yù)見(jiàn)G711.1的采用會(huì)有比較快的進(jìn)展.因?yàn)槠?本文不對(duì)這兩個(gè)擴(kuò)展作展開(kāi)討論,感興趣的同學(xué)可以參考ITU官方文檔以了解更多細(xì)節(jié).

 

總結(jié)

G711是最簡(jiǎn)單的音頻編解碼算法,但是應(yīng)用非常廣泛.從數(shù)字電話到網(wǎng)絡(luò)語(yǔ)音會(huì)議,都能見(jiàn)到他的影子.原因在于其標(biāo)準(zhǔn)化的歷史悠久,另外因?yàn)槠渌惴ê?jiǎn)單,能在幾乎所有處理器上輕易實(shí)現(xiàn).了解了G711,可以說(shuō)對(duì)數(shù)字電話入了門(mén),也算是懷了一把舊.美中不足的是,如果不算上還沒(méi)有廣泛應(yīng)用的G711.0的壓縮擴(kuò)展,G711的壓縮比還是比較寒酸的.壓縮比更加高的算法將在后面的章節(jié)陸續(xù)登場(chǎng),敬請(qǐng)期待!

點(diǎn)擊鏈接加入群【嵌入式音頻信號(hào)處理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks

嵌入式音頻專用資料代碼分享:https://pan.baidu.com/s/1dFh5pWd

本期活動(dòng)地址:-pls wt

  • 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!

網(wǎng)友評(píng)論

  • 聯(lián)系人:巧克力娃娃
  • 郵箱:board@21ic.com
  • 我要投稿
  • 歡迎入駐,開(kāi)放投稿

熱門(mén)標(biāo)簽
項(xiàng)目外包 more+