ADPCM音频压缩 象棋小子 1048272975 音频数据通常需要占用大量的储存空间,或者在传输时占用大量的信道带宽。可以通过特定的压缩算法压缩这些音频数据,从而减少在储存、传输时的音频数据量。ADPCM就是这样一种针对音频的压缩算法。 1. ADPCM概述ADPCM(adap tive 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}; /** * @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工程,相应的文档。
|