首页 > 评测 > 【嵌入式音频】第五章|差分编码,ADPCM与G726

【嵌入式音频】第五章|差分编码,ADPCM与G726

差分编码   ADPCM   G726   嵌入式   音频   
  • 作者:zhanzr
  • 来源:21ic
  • [导读]
  • “EveryBoard Can Sing” 21ic打算携手资(tu)深(ding)直男癌晚期工程师zhanzr21,来给大家讲一讲嵌入式系统与音频处理的故事。 关于zhanzr21: 曾经混迹于两岸三地,摸爬滚打在前端后端,搞过学术上过班。现在创业中,欢迎各种撩

前言

本章介绍另外一个广泛使用的ITU音频算法标准G726.与同G711一样, G726也是由数字电话中的应用而发展而来的语音压缩算法.数字电话应用中,G726经常与G711同时使用以提高带宽利用率.

6t992m5wk76kn1.jpg

点击链接加入群【嵌入式音频信号处理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks

嵌入式音频专用资料代码分享:https://pan.baidu.com/s/1dFh5pWd

本期活动地址:pls wt

几个容易混淆的专业名词

DM(Delta Modulation,也写作Δ-modulation): 讲的是一种调制编码思路,即增量调制.比如如下数据:

[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基本同义,但是强调DM在音频,信号处理方面的应用.DPCM在音频编码上的应用是1950年由Bell实验室的C. Chapin Cutler发明的,当然这个专利也早就过期了.

Cutler_DPCM_patent.png

图 当年的专利申请书中的DPCM算法说明

LDM(Linear Delta-Modulation): 讲的是最简单的DM,也就是上述DM例子中的种类.可以说是DM的子集,如果仅仅提到DM,一般指的是LDM.

Delta1.svg.png

图 清晰一点的DM编码与解码算法说明

CVSD/CVSDM(Continuously variable slope delta modulation):是LDM的一种发展,LDM的增量描述的步长固定,CVSD的步长可变.CVSD在军用通信中应用较为广泛,因为它能提供可靠的通信质量,但是对计算要求不那么高,很适合在恶劣的环境提供稳定的输出.

ADPCM(Adaptive differential pulse-code modulation): 是CVSD进一步发展,自适应地计算步长.是本文的主角算法.

IMA(The Interactive Multimedia Association): 一个音频处理的行业组织,1998年就停止活动了.IMA的最为人称道的成就就是优化与规范了ADPCM算法的实现标准.目前江湖上的ADPCM也被称为IMA ADPCM,但是事实上除了IMA ADPCM,也没有其他的广泛实现的ADPCM规范.IMA的规范对于ITU的标准的最大改进就是将对数与浮点运算都优化成了查表与定点版本.这样一来对硬件的要求就大大降低了.

G726:ITU的ADPCM的标准,主要从数学角度对算法上进行定义.G726整合了G721与G723两个旧标准的内容.G721覆盖的内容为32kbps的ADPCM版本.G723覆盖的是24kbps与40kbps的ADPCM版本.G726在整合G721与G723的基础上又增加了一个16kbps的版本.其中以32kbps的版本使用的最广泛,本文也主要介绍这个版本.看完G711文章的读者应该有映像,G711的两种版本都是将13bit/14bit采样的音频压缩到8bit.因为数字电话一般都是8KHz的采样率,所以G711的带宽需求为8K*8bit=64kbps.而G726的16kbps,24kbps,32kbps与40kbps分别将8bit的采样(一般就是G711的输出,也可以直接是16bit的原始sample)再压缩成2bit,3bit,4bit与5bit.为了不绕口,本文若不另加说明后面讲的都是32kbps也就是4bit一个sample的版本.

G727跟G726内容基本一致,只是讲的ADPCM在另外一种环境下的应用.为了行文方便,下文如果不另外说明,G726与ADPCM可互相替换.

算法数学公式与实验

为了更好说明ADPCM,也顺便提一下LDM与CVSD.因为这三种算法是逐步发展的概念,且都有很广泛的应用.只是在实际应用中,尤其是民用的音频应用领域,ADPCM是最具知名度的.

LDM

LDM的原理很容易理解,即是把原来每个sample(比如G711的输出:8bit,或者直接是16bit的PCM数据)以一个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的数据量,这个特例中:

DataEntropy = 128 * 8bit = 128bit

也就是要传输128bit.但是仔细观察了这个序列就可以得知,每个序列之间的变化只有一个LSB.所以可以将初始值设定为满幅度的1/2,之后每次传输sample之间的增量:1.这样立即得到了8:1的压缩比.

只需传输以下数据序列(以byte流表示,第一个sample为初始值):

{ 00, 00, 00, 00, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF, FF}

在解码的时候,遇到bit=0则减,遇到bit=1则增加即可基本完全还原原始数据.

simple_series_ascend_codec_ldm.png

图 递增序列的LDM解码波形与原始波形对比(红为解码结果,蓝色为原始信号)

上面的例子是完全均等递增的例子,那么其他数据怎么办.考虑这样一个方波序列:

{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}

还是以上面的算法进行编码,结果为这样:

{ FF, FF, 00, 00, FF, FF, 00, 00, FF, FF, 00, 00, FF, FF, 00, 00}

进行解码之后,与原始波形相比是这样:

square_original_decode.png

图 方波经过LDM编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

不难看出来,LDM对快速变化的信号响应不够快.这是因为LDM使用1bit表示每两个sample之间的变化,故此变化的步长也就固定了.步长若设定的太大则会失调震荡,若设定的过小则无法响应快速变化.所幸的是,音频信号尤其是语音信号,变化幅度不可能有上述的方波那么夸张.

再来考虑一个正弦序列的实验结果:

simple_sine_codec_ldm.png

图 1/4幅度,400Hz的正弦波经过LDM编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

再来考虑一个直流序列的实验结果:

simple_dc_codec_ldm.png

图 直流序列经过LDM编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

结论是:LDM因为只有一个变化步长,所以能够较好的编码变化缓慢的信号,而对变化较大的信号则失真严重.

附上测试代码:(网盘内可直接打包下载)

# 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的固定步长的改良.即遇到连续的1或者0则相应的修改步长.一般情况下连续的门限为4或者3.还是针对上面的几种波形做实验.

square_codec_cvsd.png

图 方波经过CVSD编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

ascend_codec_cvsd.png

图 递增序列经过CVSD编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

sine_codec_cvsd.png

图 正弦序列经过CVSD编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

simple_dc_codec_ldm.png

图 直流序列经过CVSD编解码之后与原始波形对比(蓝色为原始波形,红色为解码出来的波形)

不难看出来,CVSD与LDM相比起来对原始信号的还原度有所提高.当然CVSD的调整门限,调整步长的速度都是可以设置的变量.因为CVSD并非本文主题,为了避免将话题扯的太远,这里不深入展开.代码在下面,感兴趣的同学可以自己做做实验.

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

千呼万唤始出来,我有点担心没有耐心的读者已经散了.不过ADPCM算法为上述LDM与CVSD的自然发展,这么写我觉得更易理解.LDM使用固定步长,CVSD线性地调整步长,而ADPCM则是自适应地调整步长.作为代价,G726所规定的最低速率是16kbps也就是2bit一个sample,对于8bit输入是4:1的压缩比,相对于LDM与CVSD的8:1已经低了不少.而最常用的32kbps的版本则是4bit对应一个sample,对于8bit的PCM来讲是2:1的压缩比.虽然减少了压缩比,但是ADPCM的效果对于LDM与CVSD有很大提高.

稍稍介绍一下子ADPCM的算法框架.先看编码:

encode_adpcm_block_algorithm.png

图 ADPCM编码框图

其中Si为输入.预测的结果Sp与量化步长序号q保存为静态变量,因为下次的编码要用到.初始化的时候,Sp与q设定为0.t为最终的返回值(对于我们的32Kbps的版本,即4bit).输入一个采样Si后,计算预测结果与实际输入的差值:d.对这个差值进行自适应的量化得到返回值t.编码器与解码器都有内部状态变量.编码器中其实也包含一个解码器,这是为了节省需要传输的状态变量.上图中用虚线包起来的就是内嵌的解码器部分.此解码器使用返回值t已更新反向量化器,并得到一个逆量化差值:dq.dq会被加到预测值Sp上去以备下次迭代之用.

再看解码:

decode_adpcm_block_algorithm.png

图 ADPCM解码框图

解码的输入为1个4bit的nibble(对于32kbps的版本来讲)t,Sp与q同样为静态变量,每次迭代都会改变.初始的预测结果与步长指针都为0.dq增加到预测结果Sp上去,并输出Sr.Sr也被更新为下一次迭代所需的Sp.

先看一个500个sample的编解码效果:

adpcm_codec_sample_effect.png

图 ADPCM效果图(蓝色为原始数据,红色为编码解码还原数据,除了刚开始有点误差,后面看不出明显的误差)

以下是ADPCM的测试代码,这里读入一段16bit的单通道音频数据,并进编码解码实验.出于篇幅的关系,这里不做生成信号的实验,但是感兴趣的同学可以修改原始数据的那部分进行研究.ADPCM可以输入8bit也可输入16bit,考虑到16bit的音频数据更加方便获取与使用,这里使用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音频数据

之前的文章都是提到使用Audacity来制作音频数据.但这集有点不同,因为Audacity本身支持的ADPCM版本为VOX ADPCM版本,与我们这里介绍的IMA ADPCM算法有点小小不同.

vox_ima_adpcm_diff.png

图 上下两曲线分别是Audacity分别VOX算法解码VOX编码与IMA编码的ADPCM的结果,总体包络一至,但幅度与过零点有些差别

当然直接使用也没有很大问题.导入导出时注意选择格式:

audacity_import_format.png

图 导入导出选择格式,至于采样率只是为了试听与数据处理,不影响文件内容

但为了更加精确评估研究,作者准备了一个工具脚本,使用Python的标准库audioop来制作ADPCM编码数据.audioop使用的IMA ADPCM版本算法与我们实验所用算法是一样的.

将原始音频制作成ADPCM的数据文件:

# 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()

如需改变文件,可直接修改代码中定义文件名的位置.为了简洁没有做带参数执行的处理.

将ADPCM数据文件解码成原始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()

嵌入式系统播放实验(STM32F769-Disco开发板为例)

这节实验与上节基本相同,因为两种算法相当类似,都是固定压缩比.所以流程也类似,只是我为了把实验硬件搞得多样化一点,这次用了STM32F769-Disco开发板子做实验.实验用的22050采样率,16bit,单通道的音频,这次使用久石让的天空之城作为演示音频.

首先把已有音频转成你想要的格式的raw数据,再用上述所说的python脚本工具转成ADPCM数据,最后用STLink/或者JLink烧入芯片.这些步骤以前都讲过多次,如有不明白可以翻翻以前的文章或者论坛发贴,群里直接提问.

adpcm_software_process.png

图 软件流程

因为跟之前的播放差不多,所以这里也不详解展开,如有疑问直接看共享文件夹中的代码,或者论坛群内提问都可以.

f769_playback_copy.jpg

图 F769播放ADPCM

总结

这节从基础原理,编码思想上对ADPCM进行了比较深入的分析.可能有读者会觉得有点冗长,但是因为从LDM,CVSD着手,所里讲解还是循序渐进的.这种思想不仅用在音频编解码,在其他很多领域都有应用,所以大家学一下子是有用处的.多谢阅读,如有任何建议与意见可以通过论坛/交流群联系作者本人或者编辑.论坛活动见!

  • 本文系21ic原创,未经许可禁止转载!

网友评论