Microsoft-ADPCM编码与解码原理与实例

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

DWORD nSamplesPerSec; /* sample rate */ DWORD nAvgBytesPerSec; /* for buffer estimation */ WORD nBlockAlign; /* block size of data */
Leabharlann Baidu
WORD wBitsPerSample; /* Number of bits per sample of mono data */ WORD cbSize; /* The count in bytes of the extra size */ } WAVEFORMATEX; 其中wFormatTag标明了文件的类型,这样我们就可以判断后面的数据 是以什么方式来存储和表示的了,比如, #define WAVE_FORMAT_PCM 0x0001 这样,WAVE_FORMAT_PCM 表示的就是普通的原始wav文件格式。再比如, #define WAVE_FORMAT_ADPCM 0x0002 我们就可以知道, wFormatTag的值为WAVE_FORMAT_ADPCM就是以ADPCM压缩的格 式了。具体的文件的类型有很多种,详细的定义可以在mmreg.h头文件 里找到。 WAVEFORMATEX是所有的format所共有的一个头部,不同格式会在 后面添加自己的相关的数据段,添加的段的字节数可以通过cbSize来得 到。 cbSize用来描述不同的格式后面添加的多余的字节数。比如 WAVE_FORMAT_ADPCM格式的format的这个值就是32,表明还有32 个字节在后面。 另外一些字段会在后面解释。
Microsoft ADPCM 编码解码算法
因为种种原因,最近需要把原始的wav文件压缩成ADPCM格式。但是 网上几乎搜不到相关的中文资料。花了相当长的时间,七拼八凑的从一 些文章中得到了些信息,终于搞定了它。为了方便遇到跟我一样麻烦的 人,我决定把它详细的写下来。 1. 关于DPCM DPCM是differential pulse code modulation的缩写,也就是差分脉冲编码 调制的意思。他的主要思想是通过已知的数据预测下一个数据,然后传 递预测值与实际值之间的差值。具体的细节可以在很多信号处理相关的 书上找到。 一般的DPCM编码器都是采用的线性预测。假设传递的数据是 X1,X2,...Xn,而下一个数据,Xn+1还是未知。可以通过前面的 X1,X2,...Xn的加权和来预测Xn+1,也就是 Xn+1 = ∑(Ai*Xi),其中i属于1...n 为了简化计算,大部分编码的实现只取前两项,也就是, Xn+1 = a*Xn + b*Xn-1, 现在,最主要的事情就是如何对a,b进行取值, 才能使得Xn+1的误差最小。 如果假设 x~i 是预测值,xi是实际值,那么,∑(x~i-xi)^2 最小的时候, a,b就是最优的。设 F=∑(X~i-Xi)^2,因为 X~i = a*X~i-1 + b*X~i-2,可以得 出,F是关于a,b的二元函数.也就是 F=f(a,b) 。可以分别对a和b求偏导数, 求出它的极值点。 f<sub>a</sub>(a,b) = 0 ; f<sub>b</sub>(a,b) = 0 ; 可以得到 a * ∑(Xi-1)^2 + b * ∑(Xi-1)*(Xi-2) = ∑Xi*Xi-1 a * ∑(Xi-1)*(Xi-2) + b * ∑(Xi-2)^2 = ∑Xi*Xi-2 如果设 alpha = ∑(Xi-1)^2 beta = ∑(Xi-1)*(Xi-2) gama = ∑(Xi-2)^2 m = ∑Xi*Xi-1 n = ∑Xi*Xi-2
typedef struct adpcmwaveformat_tag { WAVEFORMATEX wfxx; WORD wSamplesPerBlock; WORD wNumCoef; ADPCMCOEFSET aCoeff[wNumCoef]; } ADPCMWAVEFORMAT; 这下子就很清楚了,ADPCMCOEFSET里的两个正是我们的系数a和 b,ADPCMWAVEFORMAT扩展了WAVEFORMATEX的结构,添加了 一些列自有的信息,wSamplesPerBlock表明了每一个block里含有多少个 sample,而wNumCoef说明了后面的系数表的大小. 微软定义了一个7个的标准的系数表,当然,你也可以在后面添加自己的 系数,下面是他的定义: Coef Set Coef1 Coef2 0 256 0 1 512 -256 2 0 0
上面的式子就可以写成 a*alpha + b*beta = m a*beta + b*gama = n 算出alpha,beta,gama,m,n以后,a和b的值就可以计算出来了,实际上我们 只需要一个循环遍历前n个数就能把它们都求出来。 2. ADPCM的思想 如果直接使用DPCM进行编码的话,是得不到什么压缩的效率的。缘 故是,需要传输或保存的是预测值后的值与实际值之间的差值,这与原 来的数据占用同样的空间。 为了满足我们原始的压缩数据的动机,你可以对这些差值进行各种各 样的编码。因为,大部分情况下,差值都是像1,1,1,2,3,5,5,5之类的数。 可以对它们进行通常的游程编码或者huffman编码,运气好的话能够得 到很大的压缩比。 这样做会有一个很大的弊端。因为有些数据可能之间的联系会呈线性 或者某种连续函数的性质。但是大部分情况下,数据的分布还是有一定 的离散性的。当数据之间出现很大的跳跃的时候,这种方法就显得很苍 白无力了。 我们可以这么做,每次对得到的差值用一个随着差值大小变化的数来 除。这样就可以随着差值的变化,不断调整比例因子。这样出现较大的 跳跃时也能把我们要存储的差值限定在一个较小的范围之内。 如果你现在有些迷惑,没事,我们换种方式来说明一下。 假设差值是 diff,也就是 diff = X~i - Xi,那么,diff就有可能变动很 大,如果引入一个不断变化的因子iDelta,那么,diff' = diff / iDelta,而对 于iDelta,每当diff变大的时候,他就变大比较大,当diff变得比较小的 时候,他就相应的减小。这样,我们的diff'就能保持相对的稳定了。通 过iDelta的引入,可以使得我们的DPCM编码自动的适应数据间大幅度 的跳跃。这就是自适应脉冲编码调制,ADPCM的主要思想。 你现在可能会想,iDelta到底怎么变化,才能自动的匹配diff的变化? 一 种可行的方法就是,把它定义为diff的一个函数,这个函数根据不同的 diff的值的大小取不同大小的值。通常我们会做一个iDelta值的表,通 过 diff作为索引,这样,就可以根据不同的diff值,iDelta就可以作相应 的变化了。
fact chunk是一个值得注意的chunk,现在的wav文件,无论是否压缩, 都必须包含一个fact chunk,用来存储文件文件相关的信息。但是现在它 里面只定义了一个字段,用来指出文件里一个有多少个sample。 4. WAVE_FORMAT_ADPCM 的wav文件格式 再回到我们前面讨论的DPCM,对于预测后得到的差值diff,我们应该 怎么处理它才能的比较好的压缩比?比如我们的数据是3,3,4,7,9,2... 可 以注意到,如果预测做的比较好的话,得到的差值可能会很小,甚至为 0,假设我们的原始的音频数据时16位的,那么,如果仍然使用16位来 存储这些数据,肯定是一种浪费。很直观的,你会想到减少每一个diff 的存储空间,没错,这就是ADPCM格式压缩的wav文件采用的方法。 在压缩过的wav文件里,每一个diff使用4个bit来存储的,被称作一个 nibble。这样,我们将一个16bit的原始数据缩减到4bit,可以得到一个稳 定的4:1的压缩比。 因为我们采用的是预测编码,这就需要选择预测的系数a和b,我们在 前面已经详细的推导过了a和b的计算方法。现在,我们需要解决的是, 一个wav文件,我们是对整个文件计算来得出合理的a和b吗?显然,对 整个文件采用相同预测系数并不实际,首先是计算起来麻烦,再一个, 一个wav文件太长,如果对整个文件采用相同的a和b起不到什么效果, 对于局部的数据,偏差仍然会是很大,这就丧失了我们的初衷。 不妨这么考虑,我们将音频数据分成不同的块,分别对每一块求不同 的系数,来进行预测编码,一般声音都是连续的,在一个局部的小区域 里变化很小,所以a和b就能很准确的预测出每一个值了。 在apple下系统下,每一个块称作一个packet,以64个sample为固定的大 小。而在windows下被称作一个block,他的大小是可变的,所包含的 sample的个数由nSamplesPerBlock来指出,后面我们会看到. 在 WAVEFORMATEX结构里的nBlockAlign描述了每一个block所占的字节 数. 不同的采样率会有不同的大小,下面是blockAlign在不同采样率大小下 的值 nSamplesPerSec x Channels 8k 256 11k 256 22k 512 nBlockAlign
必须包含一个扩展了的format description,这通过一个WAVEFORMATEX 的结构来描述,但是以WAVE_FORMAT_PCM为格式的文件并不需要 这些额外的信息。 下面是WAVEFORMATEX的定义 typedef struct waveformat_extended_tag { WORD wFormatTag; WORD nChannels; /* format type */ /* number of channels (i.e. mono, stereo...) */
3. WAV文件的格式 IMA-ADPCM压缩的音频文件并没有一个统一的格式。我们现在只考 虑微软的自己的wav格式。apple公司的网站上有一篇写得很不错的 technical note, 可以看后面的链接地址。 wav文件是微软定义的一系列资源文件中的一个。这些文件通常是由一 系列的chunk组成。所有的文件都以RIFF标记开头,然后指出文件的大 小。接着表明类型,比如WAVE,MIDI等. 一个wav文件的结构大致如下 __________________________ | RIFF WAVE Chunk | | groupID = 'RIFF' | | riffType = 'WAVE' | | __________________ | | | Format Chunk | | | | ckID = 'fmt ' | | | |__________________| | | __________________ | | | Sound Data Chunk | | | | ckID = 'data' | | | |__________________| | |__________________________| 一般每一个chunk都有一个id和一个size来表明chunk的类型和大小,这 样就可以很容易的将chunk读出。id一般是四个字节,用ASCII 码的值来 标记,比如data chunk的id就是'data',而format chunk的id就是'fmt '.要注 意的是,size表明的大小是以字节为单位的,而且不包括id和size字段本 身所占的空间。 原始的wave文件一般只有两个chunk,也就是fmt和data,原则上,你可以添 加任何的其他chunk,用来添加不同的信息.这也是wav文件出现很多变种 的原因. windows下一般有两种格式的wav文件,一种是普通未压缩的原始数 据,另一种就是采用了ADPCM压缩了的。无论哪一种,你都可以使用 现存的播放器播放。 未压缩的存储格式比较简单,只需要一个format chunk来描述格式信 息,然后再用一个data chunk来存储数据。 现在的wave文件按规定都必须包括一个fact chunk,而且在fmt chunk里
44k
1024
这样,我们可以把压缩后的数据以block为单位存储在data chunk里.但是, 除了压缩的数据以外,我们同样还需要存储当前块的a和b系数的值. 下面是ADPCM格式的format chunk的具体定义 typedef struct adpcmcoef_tag { int iCoef1; int iCoef2; } ADPCMCOEFSET;
相关文档
最新文档