音频数据通常需要占用大量的储存空间,或者在传输时占用大量的信道带宽。可以通过特定的压缩算法压缩这些音频数据,从而减少在储存、传输时的音频数据量。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