我们最终的目的是在ZN-X开发板上实现:
对IMA-ADPCM编码的音频数据进行解码,最后通过DAC播放出来(使用ZN-X开发板上的TLC5615 10位DAC)。
这里所详细介绍的关于WAV的数据格式和结构,以及RIFF数据头的一些内容,在我们的录音实验中也是很有用的。
音频数据(ADPCM等编码)+RIFF数据头=可在播放器/电脑上播放的WAV文件
要对ADPCM解码,不光要了解解码算法,还要知道ADPCM音频数据的数据格式。也就说是,我们要知道在数据中,哪些是用于描述信息的,哪些是真正的数据,数据又是如何被存放的。所以我们有必要对ADPCM音频数据格式进行介绍,这里结合WAV文件进行介绍(大部分的WAV音频文件,都是采用ADPCM编码的,而且WAV音频应用很广,具有很强的代表意义!)
WAV文件,总体来说,是由两部分组成的:RIFF数据头与音频数据块。(RIFF数据头,主要是描述音频的一些信息和属性,比如采样率、采样点位数、采用的编码算法、总共多少个音频数据块等等;音频数据块,就是原始音频数据经过ADPCM编码后的数据,其中还会掺杂少量的控制信息,这些信息对于解码是很有用的,所以我们要对它们进行准确的提取和分析。)
WAV文件的变种太多,主要的区别就在于音频数据的压缩编码算法的不同。编码算法主要依世界各地区或应用领域的不同而不同,比如欧洲使用a-law算法、美国日本使用u-law算法;在嵌入式播放器中广泛使用IMA-ADPCM算法、在电话通信中使用GSM算法。VS1003输出的音频格式为IMA-ADPCM,它最简单,适合嵌入式设备这种处理和计算能力比较低的平台。(不光是VS1003,现在很多的WAV音频文件,都来源于录音笔等嵌入式或便携式设备,所以大多使用IMA-ADPCM编码方式。其实,像GSM、a-law等编码我们平时遇到的比较少!)所以,这里我们专门针对于IMA-ADPCM编码的WAV来进行研究和实验。
不同编码的WAV文件的RIFF数据头有一定的差异,但基本上差不多(主要是一些数据结构上的差异,但是关于音频的一些必须的、一定要有的描述信息是必然包含其中的,比如采样位率、编码算法、音频数据块的数量、音频数据的开始位置等等!RIFF数据头主要是告诉我们如何将声音还原出来!)
VS1003不光可以解码MP3,也可以解码WAV。振南尝试过,将WAV文件的RIFF数据头去掉,而把纯ADPCM数据传给VS1003,此时,VS1003是无法播放出声音的!因为没有RIFF数据头中的音频描述信息,VS1003根本就无法知道该用何种算法去解码、又该用多少采样率对播放声音!!
“RIFF”是RIFF标识:RIFF,全称是资源交换文件格式,它是一大类文件,不光只是WAV,像AVI等视频文件也是RIFF。它是由于微软所制定的。
文件总数据量-8:除了RIFF标识以及此字段的4个字节本身,其它剩下的总共数据量。
WAVE:WAV文件标识,说明后面的数据是WAV音频数据。
fmt :数据格式描述块的标识,后面紧紧跟随的就是关于音频数据的一些描述信息。
fmt信息数据长度:后面的一定长度的数据是用于描述音频数据基本信息的,如采样率、总共的音频数据块数量等等。
编码算法:0x0011代码IMA-ADPCM,其它编码有各自指定的代码,解码程序依此调用相应的解码算法。
通道数:1或2,左右声道
采样率:8KHz、16KHz、22KHz等等。
每秒数据流字节数:每秒播放需要的数据量。
音频数据块大小:编码后的音频数据是按块来存放的,这里定义了每一个数据块的大小,以便我们准确的读取数据进行解码
编码位数:IMA-ADPCM大多编码位数为4位。(把原来通过16位采样的声音信号点,通过ADPCM压缩为4位即可表达的数值)
附加字节:普通的WAV文件的音频数据描述信息到这里就结束了,但是IMA-ADPCM编码的WAV则比较特殊,它是有附加字节的,附加字节有2个,也就是后面的“每块采样数”
每块采样数:是指在每一个音频数据块中,包含多少个采样点。如果音频数据块是256的话,那么它所包含的采样点的个数是(256-4)*2+1=505!!(这到底是怎么算出来的,后面振南会讲!!)要知道每一个音频数据中有多少个采样点,这对于我们的声音解码还原,最终把它播放出来,是极为重要的!
后面有一个”data”标识,后面是数据区总长度,后面紧接着就是真正的经过ADPCM编码的音频数据了。也就是一个个的很多个依次存放的数据块!
IMA-ADPCM编码的音频数据,通常一个数据块是256个字节。这256个字节并不都是音频数据:它前4个字节是特殊的定义,后面的252个字节是音频数据(252个字节,每个字节包含两个4位,每个4位表达的都是当前采样点与上一个采样点之间的差值!!看过前面,ADPCM原理的人应该知道,这个差值并不是直接的差值,而是实际差值除以一个适当的因子而得到的,以便把可能较大的差值转为可由4个位来表达的数值!!)
所以,后面的252个字节,一共可以表达252*2=504个采样点!!
但是我们想:既然ADPCM存储的是每两点之间的差值,那我起码要知道第一个点是采样值是多少吧!?没错,这个第一个点的采样值是不能被编码的,而只是是原始的音频采样值,ADPCM默认采用16位采样,也就是两个字节,所以一个音频数据块的最前面的两个字节,用于表达第一个采样点的值!!而后,根据后面的差值,逐一可以去计算得到各个采样点的值!但是它又涉及到一个除数因子的选取问题,这些因子在IMA-ADPCM算法中是已经被定义好的(IMA-ADPCM已经标准化,它的算法和相关因子也是给定的),这个因子的选取是由前面的音频信号的变化程度通过预测技术来决定的,所以我们需要知道在上一个音频数据块被解码之后,最后所使用的除数因子是哪个!好,这上一个因子的索引值(也就是因子数组的下标)就存在音频数据块的第3个字节中!!最后只剩下第4个字节,这个字节是用于以后的功能扩展的,暂时未用!!
后面的504个采样点,再加上前面的第一个采样点,所以一个数据块中一共有505个采样点!!
如果采样率为8kHz的话,也就是1秒钟采样8000个音频信号点,那么核算下来,IMA-ADPCM编码的每一个数据块,可以播放大约63毫秒!播放一秒钟的声音,大约需要60个音频数据块!!
在上面的RIFF数据头的结构定义中,我们应该注意到一点:在音频信息的描述中,有一大堆的0。它们是属于”fact”信息块的(“fact”信息块其实并不是必须的,在很多WAV文件中并没有这部分,很多都是”fmt ”后面就直接是”data”了!那这个”fact”到底有什么用?它为什么有这么多0?)
这需要我们了解一些关于存储器,尤其是以块为单位进行读写的存储器,比如FlashROM、硬盘、SD卡、CF卡等等。它们每一次读写的最小单位就是一个扇区,一个扇区通常是512,或512的整数倍。这样,如果我们按512的整数倍去读写数据,效果是最高的!我们不希望去处理一些不足512字节的零散数据。(比如我们要向512字节的扇区中写入20个字节,我们需要先把整个扇区的数据先读到内存,然后把20个字节放到数据中,然后写回到扇区中!这个读-插入-写的操作效率是很低!)所以,我们希望每一次写入数据,都是从扇区的0位置开始写的,并用写用的数据量是512字节。
WAV的RIFF数据头有用的部分也就只有几十个字节而已,如果我们直接在RIFF数据头的那几十个字节(真正有用的是”fmt ”信息块)后面直接紧跟着写入ADPCM编码数据,就会导致它必然会跨越扇区!它将涉及到多次的“读出-插入-写入”操作。这样效率是很低的,甚至可能导致在限定的时间内根本都来不及将音频数据写入到存储器(如果采样率比较高,存储器又比较慢的话!)。
所以,引入了”fact”这个数据区,它可以携带一些辅助性的有用的数据,有益于解码器更方便准确的去解码音频数据,比如它里面记录的“音频采样数据块总数量”,它可以用于计算音频文件声音播放的总时间长度,但其实它也可以通过其它RIFF数据头中的参数来计算,所以”fact”并不是必须的!我们将”fact”数据区的数据总长设置为456(这456个字节的目的就是为了把RIFF数据头凑成512字节的整扇区,以方便后面的音频数据都可以以整扇区来写入)。这样加上”fact”标识、以及“数据总长字段”占用的8个字节,一共是464字节;加上前面RIFF数据头的40个字节,一共是504个字节;再加上最后的”data”标识,以及其后的4个字节(数据区总长度),一共是512字节。这就让RIFF数据头凑够了一个扇区,后面的音频数据就都可以按扇区写入了。(每一个ADPCM音频数据块数据量为256字节,也就每两个数据块写入到一个扇区中)。这样,在作录音数据向存储器写入的过程中,效果和速度将要“杠杠”的!这也是为什么众多嵌入式录音设备,比如录音笔、DV等,喜欢使用IMA-ADPCM编码的原因了。