NXP MCU 技术论坛
直播中

刘天

13年用户 132经验值
擅长:微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件 微处理器/微控制 数字及可编程逻辑 模拟与电源 基础元器件
私信 关注
[经验]

【NXP LPC54110试用体验】ADPCM音频压缩

ADPCM音频压缩
象棋小子          1048272975
音频数据通常需要占用大量的储存空间,或者在传输时占用大量的信道带宽。可以通过特定的压缩算法压缩这些音频数据,从而减少在储存、传输时的音频数据量。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
              Code= ADPCM_Encode(PCMBuffer);
              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
              if(HighBits) {
                     Code= *pCode >> 4;
                     pCode++;
                     HighBits= 0;
              }else {
                     Code= *pCode & 0xf;
                     HighBits= 1;
              }
              PCMBuffer= 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
              I2SState.TxBuffer[I2SState.TxWriteIndex]= PCMBuffer;
       }
       if(I2SState.TxWriteIndex >= AUDIO_NUM_BUFFERS-1) {
              I2SState.TxWriteIndex= 0;
       }else {
              I2SState.TxWriteIndex++;
       }
       DmicState.Event= 0;
}
}
5. 附录
附件为ADPCM音频压缩的MDK工程,相应的文档。

回帖(1)

杨治平

2019-8-21 15:02:18
很好的经验,宝贵的代码。赞!
但不知 LPC54110 这IC怎么回事,未查到?
举报

更多回帖

×
20
完善资料,
赚取积分