ADPCM 音频压缩算法

       ADPCM(Adaptive Differential Pulse Code Modulation),是一种针对 16bits( 或8bits或者更高) 声音波形数据的一种有损压缩算法,它将声音流中每次采样的 16bit 数据以 4bit 存储,所以压缩比 1:4. 而且压缩/解压缩算法非常简单,是一种低空间消耗,高质量高效率声音获得的好途径。保存声音的数据文件后缀名为 .AUD 的大多用ADPCM 压缩。
  ADPCM 主要是针对连续的波形数据的,保存的是波形的变化情况,以达到描述整个波形的目的。

  8bits采样的声音人耳是可以勉强接受的,而 16bit 采样的声音可以算是高音质了。ADPCM 算法却可以将每次采样得到的 16bit 数据压缩到 4bit。需要注意的是,如果要压缩/解压缩得是立体声信号,采样时,声音信号是放在一起的,需要将两个声道分别处理。

以上文字摘自http://blog.csdn.net/lxc1014/article/details/9316381

自己写该算法时参考了上述博客中的内容。但后来才知道STM32官方也有该算法的文档和程序,于是自己基于上面两份资料写了关于该算法的代码。当然还有很多不懂的,不对处望博友指正。我用的是STM32的ADC、DAC,它是12Bit采样,所以我稍微将传统的ADPCM给改了下。

typedef __packed struct
{
int16_t valprev;

int8_t index;
}_s_ADPCM;

int8_t index_adjust[16] = {-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8};
uint16_t step_table[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
};

void ADPCM_Encoder(_s_ADPCM *state,int16_t* pInBuf,uint8_t *pOutBuf,uint8_t Len)

{
uint8_t i=0,j=0;
int16_t Index=0;
uint8_t code=0;
int32_t data=0,predata = 0 ,delta=0,prediff=0;
uint16_t step=0,tmpstep=0;
predata = (int32_t)state->valprev;

Index = (int16_t)state->index;
for(i=0;i{
data=(int32_t)*(pInBuf+i);
step = step_table[Index];

delta = data - predata;
if(delta<0)
{
delta=-delta;
code=8;
}
else
{
code=0;
}

#if 1
tmpstep = step;
prediff = (step >>3);
if(delta >= tmpstep)
{
code |= 0x04;
delta -= tmpstep;
prediff += step;
}

tmpstep = tmpstep>>1;

if(delta >= tmpstep)

{
code |= 0x02;
delta -= tmpstep;
prediff += (step >>1);
}
tmpstep = tmpstep>>1;
if(delta >= tmpstep)
{
code |= 0x01;
prediff += (step>>2);
}
#else
code = (delta<<2)/(step);
if(code >7) 
code = 7;

prediff = (delta * step)>>2;

#endif

if(code & 8)
{
predata -= prediff;
}
else
{
predata += prediff;
}

if(predata > 2047)
{
predata =2047;
}
else if(predata < -2048)
{
predata =-2048;
}

Index+=index_adjust[code];
if(Index < 0)
Index=0;
else if(Index >59)
Index=59;

if(i&0x01)
{
*(pOutBuf+j) |=(uint8_t)((code&0x0f)<<4);
j++;
}
else
{
*(pOutBuf+j) = (uint8_t)(code&0x0f);
}
}
state->valprev = (int16_t)predata;
state->index =(int8_t)Index;
}

void ADPCM_Decoder(uint8_t *pInBuf,int16_t *pOutBuf,uint8_t Len,_s_ADPCM *state)
{
uint8_t i=0,j=0,tmp_data=0,inCode=0;
uint16_t step=0;
int16_t Index=0;
int32_t predsample=0,diffq=0;

predsample = (int32_t)state->valprev;
Index = (int16_t)state->index;

for(i=0;i<(Len<<1);i++)
{
tmp_data=pInBuf[i>>1];
if(i&0x01)
inCode=(tmp_data&0xf0)>>4;
else
inCode=tmp_data&0x0f;
step = step_table[Index];

diffq = step>>3;
if(inCode & 0x04)
diffq += step;
if(inCode & 0x02)
diffq += (step>>1);
if(inCode & 0x01)
diffq += (step>>2);

if(inCode & 8)
predsample-= diffq;
else
predsample+=diffq;
if(predsample > 2047)
predsample =2047;
else if (predsample <-2048)
predsample = -2048;

Index+= index_adjust[inCode];
if(Index < 0)
Index=0;
if(Index >59)
Index = 59;

pOutBuf[j++] = (int16_t)predsample;
}
state->valprev = (int16_t)predsample;
state->index = (int8_t)Index;
}

对上述代码进行简单说明下:

编码时将16Bit压缩成4Bit,输出时是8Bit的数据,先接到的数据放在8Bit的低4位,第二次接到的数据放在高4位。

其中在编码函数中采用了条件编译,现在采用的即为算法介绍中编码方式。算法参考文档:http://www.mp3-tech.org/programmer/docs/adpcm.pdf

(当然也是STM32 ADPCM编码采用的方式)。另一个条件选择是一种粗略算法,我感觉没有前面一种编码效果好(我只验证了第一种编码方式,第二种没有试验)。

解码时就要将压缩后的数据给分开来。即假如编码数量为32个16Bit的数据,经过压缩可得到16个8Bit的数据,再经过解压缩可得到32个16Bit数据。编解码局限性在于编码数量我只考虑了偶数情况,奇数情况我还没考虑。

当然上述算法还有待优化~~时间允许的话,一定仔细研究下,从而优化自己的代码。

你可能感兴趣的:(STM32)