音频数据通常需要占用大量的储存空间,或者在传输时占用大量的信道带宽。可以通过特定的压缩算法压缩这些音频数据,从而减少在储存、传输时的音频数据量。ADPCM就是这样一种针对音频的压缩算法。
1. ADPCM概述
ADPCM(adaptive differential pulse code modulation),自适应差分脉冲编码调制,是一种固定码长的音频编解码器。通常,两个连续的音频样本之间具有较高的相关性,可以用先前的样本值去估算当前样本的预测值,使实际样本值与预测值之间的差值达到最小。相对于直接表达PCM值,这意味着可以使用较少的比特来表达这种差值。而ADPCM就是通过编码当前样本与预测结果之间的差异,减少音频之间的冗余信息,实现音频的压缩。
ADPCM是一种有损压缩算法,根据不同的音频质量、压缩比需求,ADPCM对音频样本之间的差值可以量化成4级(2比特)、8级(3比特)、16级(4比特)或者32级(5比特)。目前有很多ADPCM算法的实现,不同的算法区别于不同的量化等级以及预测模式,如用于电话系统的ITU-T G.726就是基于ADPCM算法。总的来说,ADPCM是基于时间域波形的编码,相对基于频域的音频编解码器来说,其算法复杂度要简单的多,是一种简单、有效、低成本的音频编解码器方案。
有一种广泛使用的ADPCM算法IMA-ADPCM,这个编解码器可以应用于不同的计算机平台,微软开发的WAV声音文件格式就支持IMA-ADPCM的编码。它是一个四位量化算法,由Interactive Multimedia Association (IMA)开发。通过查表定点预测替换掉复杂的浮点数学运算,极大地降低了算法的复杂度。因此,IMA-ADPCM的主要优点在于它的简单性,适用于各种音频信号,支持任意的音频采样率,较高的压缩率(1:4),并且具有良好的音频质量。
2. IMA-ADPCM编码
IMA-ADPCM编码把一个16位PCM采样值压缩成一个4位ADPCM编码值,压缩比为1:4。编码的实现如下:
/* Quantizer step size lookup table */
static const uint16_tStepSizeTable[89]={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 */
static const int8_tIndexTable[16]={0xff,0xff,0xff,0xff,2,4,6,8,0xff,0xff,0xff,0xff,2,4,6,8};
/**
* @brief ADPCM_Encode.
* @param sample: a 16-bit PCM sample
* @retval : a 4-bit ADPCM sample
*/
uint8_t ADPCM_Encode(int32_t sample)
{
static int16_t index = 0;
static int32_t predsample = 0;
uint8_t code=0;
uint16_t tmpstep=0;
int32_t diff=0;
int32_t diffq=0;
uint16_t step=0;
step = StepSizeTable[index];
/* 2. compute diff and record sign and absolut value */
diff = sample-predsample;
if (diff < 0)
{
code=8;
diff = -diff;
}
/* 3. quantize the diff into ADPCM code */
/* 4. inverse quantize the code into a predicted diff */
tmpstep = step;
diffq = (step >> 3);
if (diff >= tmpstep)
{
code |= 0x04;
diff -= tmpstep;
diffq += step;
}
tmpstep = tmpstep >> 1;
if (diff >= tmpstep)
{
code |= 0x02;
diff -= tmpstep;
diffq+=(step >> 1);
}
tmpstep = tmpstep >> 1;
if (diff >= tmpstep)
{
code |=0x01;
diffq+=(step >> 2);
}
/* 5. fixed predictor to get new predicted sample*/
if (code & 8)
{
predsample -= diffq;
}
else
{
predsample += diffq;
}
/* check for overflow*/
if (predsample > 32767)
{
predsample = 32767;
}
else if (predsample < -32768)
{
predsample = -32768;
}
/* 6. find new stepsize index */
index += IndexTable[code];
/* check for overflow*/
if (index <0)
{
index = 0;
}
else if (index > 88)
{
index = 88;
}
/* 8. return new ADPCM code*/
return (code & 0x0f);
}
3. IMA-ADPCM解码
IMA-ADPCM解码把一个4位ADPCM编码值解压成一个16位PCM值,解码的实现如下:
/**
* @brief ADPCM_Decode.
* @param code: a byte containing a 4-bit ADPCM sample.
* @retval : 16-bit ADPCM sample
*/
int16_t ADPCM_Decode(uint8_t code)
{
static int16_t index = 0;
static int32_t predsample = 0;
uint16_t step=0;
int32_t diffq=0;
step = StepSizeTable[index];
/* 2. inverse code into diff */
diffq = step>> 3;
if (code&4)
{
diffq += step;
}
if (code&2)
{
diffq += step>>1;
}
if (code&1)
{
diffq += step>>2;
}
/* 3. add diff to predicted sample*/
if (code&8)
{
predsample -= diffq;
}
else
{
predsample += diffq;
}
/* check for overflow*/
if (predsample > 32767)
{
predsample = 32767;
}
else if (predsample < -32768)
{
predsample = -32768;
}
/* 4. find new quantizer step size */
index += IndexTable [code];
/* check for overflow*/
if (index < 0)
{
index = 0;
}
if (index > 88)
{
index = 88;
}
/* 5. save predict sample and index for next iteration */
/* done! static variables */
/* 6. return new speech sample*/
return ((int16_t)predsample);
}
4. 应用例程
main函数例程实现从数字麦克风获取音频缓存并ADPCM编码这一帧缓存,形成编码帧,再经过ADPCM解码,解码帧输出到音频输出缓存,放出声音,实现声音的回放。
通常音频的编解码都是以固定长度的音频数据为帧单位,ADPCM编码一帧的函数实现如下:
void Adpcm_FrameEncode(const int16_t*PCMBuffer, void *EncodeBuffer, int32_t Len)
{
int32_ti;
uint8_tHighBits;
uint8_tCode;
uint8_t*pCode;
HighBits= 0;
pCode= (uint8_t *)EncodeBuffer;
for(i=0; i<Len; i++) {
Code= ADPCM_Encode(PCMBuffer[i]);
if(HighBits) {
*pCode|= Code << 4;
pCode++;
HighBits= 0;
}else {
*pCode= Code;
HighBits= 1;
}
}
}
ADPCM解码一帧的函数实现如下:
void Adpcm_FrameDecode(int16_t*PCMBuffer, const void *EncodeBuffer, int32_t Len)
{
int32_ti;
uint8_tHighBits;
uint8_tCode;
constuint8_t *pCode;
HighBits= 0;
pCode= EncodeBuffer;
for(i=0; i<Len; i++) {
if(HighBits) {
Code= *pCode >> 4;
pCode++;
HighBits= 0;
}else {
Code= *pCode & 0xf;
HighBits= 1;
}
PCMBuffer[i]= ADPCM_Decode(Code);
}
}
main函数中声音先编码,再解码回放的实现如下:
while (1) {
if (DmicState.Event) {
Adpcm_FrameEncode((int16_t*)DmicState.Buffer[DmicState.ReadIndex],
EncodeBuffer, AUDIO_FRAME_SIZE);
if(DmicState.ReadIndex >= AUDIO_NUM_BUFFERS-1) {
DmicState.ReadIndex= 0;
}else {
DmicState.ReadIndex++;
}
Adpcm_FrameDecode(PCMBuffer,EncodeBuffer, AUDIO_FRAME_SIZE);
for(i=0; i<AUDIO_FRAME_SIZE; i++) {
I2SState.TxBuffer[I2SState.TxWriteIndex][i]= PCMBuffer[i];
}
if(I2SState.TxWriteIndex >= AUDIO_NUM_BUFFERS-1) {
I2SState.TxWriteIndex= 0;
}else {
I2SState.TxWriteIndex++;
}
DmicState.Event= 0;
}
}
5. 附录
附件为ADPCM音频压缩的MDK工程,相应的文档。
源码:http://pan.baidu.com/s/1eS0BUQe