首頁 > 評測 > 【嵌入式音頻】第五章|差分編碼,ADPCM與G726
【嵌入式音頻】第五章|差分編碼,ADPCM與G726
- [導(dǎo)讀]
- “EveryBoard Can Sing” 21ic打算攜手資(tu)深(ding)直男癌晚期工程師zhanzr21,來給大家講一講嵌入式系統(tǒng)與音頻處理的故事。 關(guān)于zhanzr21: 曾經(jīng)混跡于兩岸三地,摸爬滾打在前端后端,搞過學(xué)術(shù)上過班。現(xiàn)在創(chuàng)業(yè)中,歡迎各種撩
前言
本章介紹另外一個廣泛使用的ITU音頻算法標準G726.與同G711一樣, G726也是由數(shù)字電話中的應(yīng)用而發(fā)展而來的語音壓縮算法.數(shù)字電話應(yīng)用中,G726經(jīng)常與G711同時使用以提高帶寬利用率.
點擊鏈接加入群【嵌入式音頻信號處理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks
嵌入式音頻專用資料代碼分享:https://pan.baidu.com/s/1dFh5pWd
本期活動地址:pls wt
幾個容易混淆的專業(yè)名詞
DM(Delta Modulation,也寫作Δ-modulation): 講的是一種調(diào)制編碼思路,即增量調(diào)制.比如如下數(shù)據(jù):
[1, 2, 3, 100, 0, 2000, 0, 60000]
如果以16 bit的PCM編碼傳輸,需要8x2=16個Bytes.如果以DM方式傳輸,最低只需要1個byte: 二進制(11110101).即僅僅傳輸每個sample與上次的變化趨勢.
DPCM(Differential pulse-code modulation):與DM基本同義,但是強調(diào)DM在音頻,信號處理方面的應(yīng)用.DPCM在音頻編碼上的應(yīng)用是1950年由Bell實驗室的C. Chapin Cutler發(fā)明的,當(dāng)然這個專利也早就過期了.
圖 當(dāng)年的專利申請書中的DPCM算法說明
LDM(Linear Delta-Modulation): 講的是最簡單的DM,也就是上述DM例子中的種類.可以說是DM的子集,如果僅僅提到DM,一般指的是LDM.
圖 清晰一點的DM編碼與解碼算法說明
CVSD/CVSDM(Continuously variable slope delta modulation):是LDM的一種發(fā)展,LDM的增量描述的步長固定,CVSD的步長可變.CVSD在軍用通信中應(yīng)用較為廣泛,因為它能提供可靠的通信質(zhì)量,但是對計算要求不那么高,很適合在惡劣的環(huán)境提供穩(wěn)定的輸出.
ADPCM(Adaptive differential pulse-code modulation): 是CVSD進一步發(fā)展,自適應(yīng)地計算步長.是本文的主角算法.
IMA(The Interactive Multimedia Association): 一個音頻處理的行業(yè)組織,1998年就停止活動了.IMA的最為人稱道的成就就是優(yōu)化與規(guī)范了ADPCM算法的實現(xiàn)標準.目前江湖上的ADPCM也被稱為IMA ADPCM,但是事實上除了IMA ADPCM,也沒有其他的廣泛實現(xiàn)的ADPCM規(guī)范.IMA的規(guī)范對于ITU的標準的最大改進就是將對數(shù)與浮點運算都優(yōu)化成了查表與定點版本.這樣一來對硬件的要求就大大降低了.
G726:ITU的ADPCM的標準,主要從數(shù)學(xué)角度對算法上進行定義.G726整合了G721與G723兩個舊標準的內(nèi)容.G721覆蓋的內(nèi)容為32kbps的ADPCM版本.G723覆蓋的是24kbps與40kbps的ADPCM版本.G726在整合G721與G723的基礎(chǔ)上又增加了一個16kbps的版本.其中以32kbps的版本使用的最廣泛,本文也主要介紹這個版本.看完G711文章的讀者應(yīng)該有映像,G711的兩種版本都是將13bit/14bit采樣的音頻壓縮到8bit.因為數(shù)字電話一般都是8KHz的采樣率,所以G711的帶寬需求為8K*8bit=64kbps.而G726的16kbps,24kbps,32kbps與40kbps分別將8bit的采樣(一般就是G711的輸出,也可以直接是16bit的原始sample)再壓縮成2bit,3bit,4bit與5bit.為了不繞口,本文若不另加說明后面講的都是32kbps也就是4bit一個sample的版本.
G727跟G726內(nèi)容基本一致,只是講的ADPCM在另外一種環(huán)境下的應(yīng)用.為了行文方便,下文如果不另外說明,G726與ADPCM可互相替換.
算法數(shù)學(xué)公式與實驗
為了更好說明ADPCM,也順便提一下LDM與CVSD.因為這三種算法是逐步發(fā)展的概念,且都有很廣泛的應(yīng)用.只是在實際應(yīng)用中,尤其是民用的音頻應(yīng)用領(lǐng)域,ADPCM是最具知名度的.
LDM
LDM的原理很容易理解,即是把原來每個sample(比如G711的輸出:8bit,或者直接是16bit的PCM數(shù)據(jù))以一個bit的增量來表示.比如考慮到以下的簡單遞增8bit PCM序列:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}
如果直接傳輸或者存儲該序列,則需要Lengh*sample_depth的數(shù)據(jù)量,這個特例中:
DataEntropy = 128 * 8bit = 128bit
也就是要傳輸128bit.但是仔細觀察了這個序列就可以得知,每個序列之間的變化只有一個LSB.所以可以將初始值設(shè)定為滿幅度的1/2,之后每次傳輸sample之間的增量:1.這樣立即得到了8:1的壓縮比.
只需傳輸以下數(shù)據(jù)序列(以byte流表示,第一個sample為初始值):
{ 00, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF}
在解碼的時候,遇到bit=0則減,遇到bit=1則增加即可基本完全還原原始數(shù)據(jù).
圖 遞增序列的LDM解碼波形與原始波形對比(紅為解碼結(jié)果,藍色為原始信號)
上面的例子是完全均等遞增的例子,那么其他數(shù)據(jù)怎么辦.考慮這樣一個方波序列:
{127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
還是以上面的算法進行編碼,結(jié)果為這樣:
{ FF, FF, 00, 00, FF, FF, 00, 00, FF, FF, 00, 00, FF, FF, 00, 00}
進行解碼之后,與原始波形相比是這樣:
圖 方波經(jīng)過LDM編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
不難看出來,LDM對快速變化的信號響應(yīng)不夠快.這是因為LDM使用1bit表示每兩個sample之間的變化,故此變化的步長也就固定了.步長若設(shè)定的太大則會失調(diào)震蕩,若設(shè)定的過小則無法響應(yīng)快速變化.所幸的是,音頻信號尤其是語音信號,變化幅度不可能有上述的方波那么夸張.
再來考慮一個正弦序列的實驗結(jié)果:
圖 1/4幅度,400Hz的正弦波經(jīng)過LDM編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
再來考慮一個直流序列的實驗結(jié)果:
圖 直流序列經(jīng)過LDM編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
結(jié)論是:LDM因為只有一個變化步長,所以能夠較好的編碼變化緩慢的信號,而對變化較大的信號則失真嚴重.
附上測試代碼:(網(wǎng)盤內(nèi)可直接打包下載)
# LDM 8bit Encode/Decode Test
# Author: zhanzr21 @ 21ic BBS
#
import math
import sys
list_8bit_pcm = []
STEP_SIZE = 1
TEST_LENGTH = 128
POS_AMP = 127
NEG_AMP = 0
FSample = 8000
TEST_SIGNAL_FREQ = 400
SAMPLE_N_PERIOD = (FSample//TEST_SIGNAL_FREQ)
INIT_VAL = int((POS_AMP/2))
GAIN=0.25
for i in range(TEST_LENGTH):
#Square
list_8bit_pcm.append((POS_AMP if (0==(i//16)%2) else NEG_AMP))
#Simple Ascend
# list_8bit_pcm.append(i)
#Sine
# tmpSample = (POS_AMP/2) + (POS_AMP/2)*math.sin(i*(2*math.pi)/SAMPLE_N_PERIOD)
# tmpSample = int(tmpSample * GAIN)
# list_8bit_pcm.append(tmpSample)
#DC
# tmpSample = 30
# list_8bit_pcm.append(tmpSample)
print()
print("The Original signal")
for v in list_8bit_pcm:
print( v, end=', ')
print()
print("Now Encode")
#test encode
byte_encode = 0
byte_reach = INIT_VAL
list_encode = []
for i in range(0,len(list_8bit_pcm)):
if(list_8bit_pcm[i]>byte_reach):
byte_encode = byte_encode | (1<<(i%8))
byte_reach = byte_reach + STEP_SIZE
else:
byte_reach = byte_reach - STEP_SIZE
#Update the reference
#byte_reach = list_8bit_pcm[i]
if(0==(i+1)%8):
#output
print('%02X' % (byte_encode), end=', ')
list_encode.append(byte_encode)
byte_encode = 0
print()
print("Now Decode")
#test decode
byte_encode = 0
byte_reach = INIT_VAL
list_decode = []
for i in range(0,len(list_encode)):
for j in range(8):
if(0!=(list_encode[i]&(1<
byte_reach = byte_reach + 1
list_decode.append(byte_reach)
else:
byte_reach = byte_reach - 1
list_decode.append(byte_reach)
#output
print('%u' % (byte_reach), end=', ')
CVSD
如上所述,此種算法是針對LDM的固定步長的改良.即遇到連續(xù)的1或者0則相應(yīng)的修改步長.一般情況下連續(xù)的門限為4或者3.還是針對上面的幾種波形做實驗.
圖 方波經(jīng)過CVSD編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
圖 遞增序列經(jīng)過CVSD編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
圖 正弦序列經(jīng)過CVSD編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
圖 直流序列經(jīng)過CVSD編解碼之后與原始波形對比(藍色為原始波形,紅色為解碼出來的波形)
不難看出來,CVSD與LDM相比起來對原始信號的還原度有所提高.當(dāng)然CVSD的調(diào)整門限,調(diào)整步長的速度都是可以設(shè)置的變量.因為CVSD并非本文主題,為了避免將話題扯的太遠,這里不深入展開.代碼在下面,感興趣的同學(xué)可以自己做做實驗.
CVSD測試代碼:
# CVSD 8bit Encode/Decode Test
# Author: zhanzr21 @ 21ic BBS
#
import math
import sys
TEST_LENGTH = 128
POS_AMP = 127
NEG_AMP = 0
FSample = 8000
TEST_SIGNAL_FREQ = 400
INIT_STEP_SIZE = 1
STEP_OF_STEP = 5
THRESOLD = 4
MAX_STEP_SIZE = int((POS_AMP/8))
MIN_STEP_SIZE = INIT_STEP_SIZE
SAMPLE_N_PERIOD = (FSample//TEST_SIGNAL_FREQ)
INIT_VAL = int((POS_AMP/2))
GAIN=0.25
list_8bit_pcm = []
for i in range(TEST_LENGTH):
#Square
# list_8bit_pcm.append((POS_AMP if (0==(i//16)%2) else NEG_AMP))
#Simple Ascend
# list_8bit_pcm.append(i)
#Sine
# tmpSample = (POS_AMP/2) + (POS_AMP/2)*math.sin(i*(2*math.pi)/SAMPLE_N_PERIOD)
# tmpSample = int(tmpSample * GAIN)
# list_8bit_pcm.append(tmpSample)
#DC
tmpSample = 30
list_8bit_pcm.append(tmpSample)
print()
print("The Original signal")
for v in list_8bit_pcm:
print( v, end=', ')
print()
print("Now Encode")
#test encode
STEP_SIZE = INIT_STEP_SIZE
byte_encode = 0
byte_reach = INIT_VAL
cont_1 = 0
cont_0 = 0
list_encode = []
for i in range(0,len(list_8bit_pcm)):
if(list_8bit_pcm[i]>byte_reach):
cont_1 = cont_1+1
cont_0 = 0
if(cont_1==THRESOLD):
#Increase the step and limit the step size
STEP_SIZE = STEP_SIZE+STEP_OF_STEP
STEP_SIZE = MAX_STEP_SIZE if(STEP_SIZE>MAX_STEP_SIZE) else STEP_SIZE
cont_1 = 0
byte_encode = byte_encode | (1<<(i%8))
byte_reach = byte_reach + STEP_SIZE
else:
cont_0 = cont_0+1
cont_1 = 0
if(cont_0==THRESOLD):
#Decrease the step and limit the step size
STEP_SIZE = STEP_SIZE-STEP_OF_STEP
STEP_SIZE = MIN_STEP_SIZE if(STEP_SIZE
cont_1 = 0
byte_reach = byte_reach - STEP_SIZE
if(0==(i+1)%8):
#output
print('%02X' % (byte_encode), end=', ')
list_encode.append(byte_encode)
byte_encode = 0
print()
print("Now Decode")
#test decode
byte_encode = 0
byte_reach = INIT_VAL
list_decode = []
STEP_SIZE = INIT_STEP_SIZE
cont_1 = 0
cont_0 = 0
for i in range(0,len(list_encode)):
for j in range(8):
mark = (0!=(list_encode[i]&(1<
if(mark):
cont_1 = cont_1+1
cont_0 = 0
if(cont_1==THRESOLD):
#Increase the step and limit the step size
STEP_SIZE = STEP_SIZE+STEP_OF_STEP
STEP_SIZE = MAX_STEP_SIZE if(STEP_SIZE>MAX_STEP_SIZE) else STEP_SIZE
cont_1 = 0
byte_reach = byte_reach + STEP_SIZE
else:
cont_0 = cont_0+1
cont_1 = 0
if(cont_0==THRESOLD):
#Decrease the step and limit the step size
STEP_SIZE = STEP_SIZE-STEP_OF_STEP
STEP_SIZE = MIN_STEP_SIZE if(STEP_SIZE
cont_1 = 0
byte_reach = byte_reach - STEP_SIZE
list_decode.append(byte_reach)
#output
print('%u' % (byte_reach), end=', ')
ADPCM
千呼萬喚始出來,我有點擔(dān)心沒有耐心的讀者已經(jīng)散了.不過ADPCM算法為上述LDM與CVSD的自然發(fā)展,這么寫我覺得更易理解.LDM使用固定步長,CVSD線性地調(diào)整步長,而ADPCM則是自適應(yīng)地調(diào)整步長.作為代價,G726所規(guī)定的最低速率是16kbps也就是2bit一個sample,對于8bit輸入是4:1的壓縮比,相對于LDM與CVSD的8:1已經(jīng)低了不少.而最常用的32kbps的版本則是4bit對應(yīng)一個sample,對于8bit的PCM來講是2:1的壓縮比.雖然減少了壓縮比,但是ADPCM的效果對于LDM與CVSD有很大提高.
稍稍介紹一下子ADPCM的算法框架.先看編碼:
圖 ADPCM編碼框圖
其中Si為輸入.預(yù)測的結(jié)果Sp與量化步長序號q保存為靜態(tài)變量,因為下次的編碼要用到.初始化的時候,Sp與q設(shè)定為0.t為最終的返回值(對于我們的32Kbps的版本,即4bit).輸入一個采樣Si后,計算預(yù)測結(jié)果與實際輸入的差值:d.對這個差值進行自適應(yīng)的量化得到返回值t.編碼器與解碼器都有內(nèi)部狀態(tài)變量.編碼器中其實也包含一個解碼器,這是為了節(jié)省需要傳輸?shù)臓顟B(tài)變量.上圖中用虛線包起來的就是內(nèi)嵌的解碼器部分.此解碼器使用返回值t已更新反向量化器,并得到一個逆量化差值:dq.dq會被加到預(yù)測值Sp上去以備下次迭代之用.
再看解碼:
圖 ADPCM解碼框圖
解碼的輸入為1個4bit的nibble(對于32kbps的版本來講)t,Sp與q同樣為靜態(tài)變量,每次迭代都會改變.初始的預(yù)測結(jié)果與步長指針都為0.dq增加到預(yù)測結(jié)果Sp上去,并輸出Sr.Sr也被更新為下一次迭代所需的Sp.
先看一個500個sample的編解碼效果:
圖 ADPCM效果圖(藍色為原始數(shù)據(jù),紅色為編碼解碼還原數(shù)據(jù),除了剛開始有點誤差,后面看不出明顯的誤差)
以下是ADPCM的測試代碼,這里讀入一段16bit的單通道音頻數(shù)據(jù),并進編碼解碼實驗.出于篇幅的關(guān)系,這里不做生成信號的實驗,但是感興趣的同學(xué)可以修改原始數(shù)據(jù)的那部分進行研究.ADPCM可以輸入8bit也可輸入16bit,考慮到16bit的音頻數(shù)據(jù)更加方便獲取與使用,這里使用16bit作為編碼的輸入與解碼的輸出.
# ADPCM 16bit Encode/Decode Test
# This is for algorithm test and study only, use Python audioop library if you
# need standard and tested quality
#
# Author: zhanzr21 @ 21ic BBS
#
import math
import sys
#Quantizer step size lookup table
StepSizeTable = [7,8,9,10,11,12,13,14,16,17,
19,21,23,25,28,31,34,37,41,45,
50,55,60,66,73,80,88,97,107,118,
130,143,157,173,190,209,230,253,279,307,
337,371,408,449,494,544,598,658,724,796,
876,963,1060,1166,1282,1411,1552,1707,1878,2066,
2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,
5894,6484,7132,7845,8630,9493,10442,11487,12635,13899,
15289,16818,18500,20350,22385,24623,27086,29794,32767]
#Table of index changes
IndexTable = [-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8]
#ADPCM_Encode.
#sample: a 16-bit PCM sample
#retval : a 4-bit ADPCM sample
index = 0
predsample = 0
def ADPCM_Encode(sample):
global index
global predsample
code=0
tmpstep=0
diff=0
diffq=0
step=0
step = StepSizeTable[index]
#compute diff and record sign and absolut value
diff = sample-predsample
if (diff < 0):
code=8
diff = -diff
#quantize the diff into ADPCM code
#inverse quantize the code into a predicted diff
tmpstep = step
diffq = (step >> 3)
if (diff >= tmpstep):
code = code | 0x04
diff = diff - tmpstep
diffq = diffq + step
tmpstep = tmpstep >> 1
if (diff >= tmpstep):
code = code | 0x02
diff = diff - tmpstep
diffq = diffq + (step >> 1)
tmpstep = tmpstep >> 1
if (diff >= tmpstep):
code = code | 0x01
diffq = diffq + (step >> 2)
#fixed predictor to get new predicted sample
if (code & 8):
predsample = predsample - diffq
else:
predsample = predsample + diffq
#check for overflow
if (predsample > 32767):
predsample = 32767
elif (predsample < -32768):
predsample = -32768
#find new stepsize index
index = index + IndexTable[code]
#check for overflow
if (index <0):
index = 0
elif (index > 88):
index = 88
#return new ADPCM code
return (code & 0x0f)
#ADPCM_Decode.
#code: a byte containing a 4-bit ADPCM sample.
#retval : 16-bit ADPCM sample
de_index = 0
de_predsample = 0
def ADPCM_Decode(code):
global de_index
global de_predsample
step=0
diffq=0
step = StepSizeTable[de_index]
#inverse code into diff
diffq = step>> 3
if (code&4):
diffq += step
if (code&2):
diffq += step>>1
if (code&1):
diffq += step>>2
# add diff to predicted sample
if (code&8):
de_predsample -= diffq
else:
de_predsample += diffq
# check for overflow
if (de_predsample > 32767):
de_predsample = 32767
elif (de_predsample < -32768):
de_predsample = -32768
# find new quantizer step size
de_index += IndexTable[code]
# check for overflow
if (de_index < 0):
de_index = 0
if (de_index > 88):
de_index = 88
# save predict sample and de_index for next iteration
# return new decoded sample
return (de_predsample)
list_16bit_pcm = bytes()
#Read From Sample File
fin = open('little_16bit8k.bin', mode='rb')
list_16bit_pcm=fin.read()
fin.close()
print()
print("ADPCM encode/decode Demo")
for i in range(len(list_16bit_pcm)//4):
#first sample
testSampleU16_0 = list_16bit_pcm[i*4] + list_16bit_pcm[1 + i*4] * 256
#unsigned to signed
if(testSampleU16_0>32767):
test_sample_0 = testSampleU16_0 - 65536
else:
test_sample_0 = testSampleU16_0
#second sample
testSampleU16_1 = list_16bit_pcm[2+i*4] + list_16bit_pcm[3 + i*4] * 256
#unsigned to signed
if(testSampleU16_1>32767):
test_sample_1 = testSampleU16_1 - 65536
else:
test_sample_1 = testSampleU16_1
# print( test_sample_0)
# print( test_sample_1)
#
#Now Encode
tmpU8_0 = ADPCM_Encode(test_sample_0)
tmpU8_1 = ADPCM_Encode(test_sample_1)
#print('%02X' % (tmpU8_1 | (tmpU8_0<<4)), end=' ')
#Now Decode
tmpDeS16_0 = ADPCM_Decode(tmpU8_0)
tmpDeS16_1 = ADPCM_Decode(tmpU8_1)
#print(tmpDeS16_0)
#print(tmpDeS16_1)
實踐制作ADPCM音頻數(shù)據(jù)
之前的文章都是提到使用Audacity來制作音頻數(shù)據(jù).但這集有點不同,因為Audacity本身支持的ADPCM版本為VOX ADPCM版本,與我們這里介紹的IMA ADPCM算法有點小小不同.
圖 上下兩曲線分別是Audacity分別VOX算法解碼VOX編碼與IMA編碼的ADPCM的結(jié)果,總體包絡(luò)一至,但幅度與過零點有些差別
當(dāng)然直接使用也沒有很大問題.導(dǎo)入導(dǎo)出時注意選擇格式:
圖 導(dǎo)入導(dǎo)出選擇格式,至于采樣率只是為了試聽與數(shù)據(jù)處理,不影響文件內(nèi)容
但為了更加精確評估研究,作者準備了一個工具腳本,使用Python的標準庫audioop來制作ADPCM編碼數(shù)據(jù).audioop使用的IMA ADPCM版本算法與我們實驗所用算法是一樣的.
將原始音頻制作成ADPCM的數(shù)據(jù)文件:
# ADPCM 16bit Encode Tool
# Using standlibary audioop to compress a 16bit PCM data file into 4bit ADPCM data file
#
# Author: zhanzr21 @ 21ic BBS
#
import audioop
INPUT_FILE = 'radio_dump_8k16bit_30s.bin'
ENCODE_FILE = 'audioop.adpcm'
#Read From data File
fin = open(INPUT_FILE, mode='rb')
raw_pcm=fin.read()
fin.close()
encode = audioop.lin2adpcm(raw_pcm, 2, None)
#Write to encode data file
fencode = open(ENCODE_FILE, mode='wb')
fencode.write(encode[0])
fencode.close()
如需改變文件,可直接修改代碼中定義文件名的位置.為了簡潔沒有做帶參數(shù)執(zhí)行的處理.
將ADPCM數(shù)據(jù)文件解碼成原始16bit的PCM音頻:
# ADPCM 16bit Decode Tool
# Using standlibary audioop to decode ADPCM data file into raw 16bit PCM data file
#
# Author: zhanzr21 @ 21ic BBS
#
import audioop
INPUT_FILE = 'audioop.adpcm'
DECODE_FILE = 'decode.bin'
#Read From data File
fin = open(INPUT_FILE, mode='rb')
raw_adpcm=fin.read()
fin.close()
decode = audioop.adpcm2lin(raw_adpcm, 2, None)
#Write to Decode data file
fdecode = open(DECODE_FILE, mode='wb')
fdecode.write(decode[0])
fdecode.close()
嵌入式系統(tǒng)播放實驗(STM32F769-Disco開發(fā)板為例)
這節(jié)實驗與上節(jié)基本相同,因為兩種算法相當(dāng)類似,都是固定壓縮比.所以流程也類似,只是我為了把實驗硬件搞得多樣化一點,這次用了STM32F769-Disco開發(fā)板子做實驗.實驗用的22050采樣率,16bit,單通道的音頻,這次使用久石讓的天空之城作為演示音頻.
首先把已有音頻轉(zhuǎn)成你想要的格式的raw數(shù)據(jù),再用上述所說的python腳本工具轉(zhuǎn)成ADPCM數(shù)據(jù),最后用STLink/或者JLink燒入芯片.這些步驟以前都講過多次,如有不明白可以翻翻以前的文章或者論壇發(fā)貼,群里直接提問.
圖 軟件流程
因為跟之前的播放差不多,所以這里也不詳解展開,如有疑問直接看共享文件夾中的代碼,或者論壇群內(nèi)提問都可以.
圖 F769播放ADPCM
總結(jié)
這節(jié)從基礎(chǔ)原理,編碼思想上對ADPCM進行了比較深入的分析.可能有讀者會覺得有點冗長,但是因為從LDM,CVSD著手,所里講解還是循序漸進的.這種思想不僅用在音頻編解碼,在其他很多領(lǐng)域都有應(yīng)用,所以大家學(xué)一下子是有用處的.多謝閱讀,如有任何建議與意見可以通過論壇/交流群聯(lián)系作者本人或者編輯.論壇活動見!
- 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!
網(wǎng)友評論
- 聯(lián)系人:巧克力娃娃
- 郵箱:board@21ic.com
- 我要投稿
-
歡迎入駐,開放投稿
-
人均百萬?英偉達中國員工收入曝光! 2024-08-29
-
《黑神話:悟空》玩家硬盤升級攻略:提升游戲體驗,暢享3A大作 2024-08-29
-
數(shù)睿數(shù)據(jù)參加《系統(tǒng)與軟件工程 低代碼開發(fā)平臺通用技術(shù)要求》國家標準編制 2024-08-29
- NRF52810藍牙數(shù)字耳機找人定制
預(yù)算:¥30005天前
- 125KW模塊式PCS軟硬件外包開發(fā)
預(yù)算:¥1100000015小時前
- 12V汽車啟動電源項目BMS設(shè)計
預(yù)算:¥50000023小時前
- 數(shù)據(jù)可視化軟件 開發(fā)
預(yù)算:¥5000023小時前
- PLC項目調(diào)試修改
預(yù)算:¥100001天前
- 起動電機控制器開發(fā)
預(yù)算:¥1100001天前