之前许多次说过,有空的时候写写博客,可是一直没有好好落实。由于工作的原因,经常会接触到一些音视频的编解码技术,而ADPCM就是我第一接触的音频编码技术。所以,本人的第一篇博客就是对ADPCM的编码与解码进行详解。本文对ADPCM编码与解码的描述,分为两章进行。
ADPCM(Adaptive Differential Pulse Code Modulation 差分脉冲编码调制)主要是针对连续的波形数据的, 保存的是相临波形的变化情况, 以达到描述整个波形的目的。本文的以IMA的ADPCM编码标准为例进行描述,IMA-ADPCM 是Intel公司首先开发的是一种主要针对16bit采样波形数据的有损压缩算法,压缩比为 4:1.它与通常的DVI-ADPCM是同一算法。 (对8bit数据压缩时是3.2:1,也有非标准的IMA-ADPCM压缩算法,可以达到5:1甚至更高的压缩比)4:1的压缩是目前使用最多的压缩方式。结尾附adpcm编解码的源代码adpcm.h与adpcm.c。
ADPCM编码本质是一种预测编码,那么它是怎么样进行预测的呢?预测编码利用相邻的音频数据在时间上的相关性,相邻采样点的音频数据具有相似的特点。因此,进过压缩后的数据并不是音频数据本身,而是该数据的预测值与实际值之差。偏差需要量化器进行量化,假如我们对于16bit的音频数据采用16bit的量化,那么偏差与实际的数据值占据的位数一样则无法达到压缩数据的目的,如果采用4bit的量化位数,其最大的量化步数只能是16,显然是不能满足使用要求,因此ADPCM应运而生,ADPCM是一种采用变步长的量化器的预测编码算法,它的本质是根据预测值与实际的偏差范围,在量化表格中选择出合适的量化值,使预测变化的幅度保持在4bit的范围内。ADPCM的核心公式如下,其中代表为量化后的值,
为量化步长,
代表经过量化后有效的偏差值,
加上本次的预测值做为下一次的运算的预测值:
整个ADPCM的编码过程分三步进行:
第一步为计算出当前实际值与预测值的偏差,
代表了当前数据的实际值,
为当前数的预测值。
为量化后的带符号的有效数据为4bit的数据,其最高位代表的数据的方向,bit3为1代表负数,代表-7~7的整型数据。
当小于0,
bit3被值1。
第二步为对进行量化,简易实现不考虑计算效率的情况下完全可以直接参考上面的公式,因为是在计算机平台进行了除法运算与小数运算,该作者很巧妙的把这些运算使用与或非来实现了,提高了运算的效率,有兴趣的读者可以看看代码,学习一下这种思路。我们细看一下公式,
,可以发现公式可以拆分为两部分实现,小数部分的量化被转换为了固定的
,因此节约了计算的成本。vpdiff就是对应这部分的值。
vpdiff = (step >> 3);
static int indexTable[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8,
};
static int stepsizeTable[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
};
未完。。。。。。
adpcm.h
/*
** adpcm.h - include file for adpcm coder.
**
** Version 1.0, 7-Jul-92.
*/
#ifndef ADPCM_H
#define ADPCM_H
#ifdef __cplusplus
extern "C" {
#endif
struct adpcm_state {
short valprev; /* Previous output value */
char index; /* Index into stepsize table */
};
int adpcm_coder(short *indata, unsigned char *outdata, int len, struct adpcm_state *state);
int adpcm_decoder(unsigned char *indata, short *outdata, int len, struct adpcm_state *state);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ADPCM_H*/
adpcm.c
/***********************************************************
Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
Netherlands.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
******************************************************************/
/*
* Cleaned up by Phil Frisbie for use in HawkVoice
*/
/*
** Intel/DVI ADPCM coder/decoder.
**
** The algorithm for this coder was taken from the IMA Compatability Project
** proceedings, Vol 2, Number 2; May 1992.
**
** Version 1.2, 18-Dec-92.
**
** Change log:
** - Fixed a stupid bug, where the delta was computed as
** stepsize*code/4 in stead of stepsize*(code+0.5)/4.
** - There was an off-by-one error causing it to pick
** an incorrect delta once in a blue moon.
** - The NODIVMUL define has been removed. Computations are now always done
** using shifts, adds and subtracts. It turned out that, because the standard
** is defined using shift/add/subtract, you needed bits of fixup code
** (because the div/mul simulation using shift/add/sub made some rounding
** errors that real div/mul don't make) and all together the resultant code
** ran slower than just using the shifts all the time.
** - Changed some of the variable names to be more meaningful.
*/
#include "adpcm.h"
/* Intel ADPCM step variation table */
static int indexTable[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8,
};
static int stepsizeTable[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
};
int adpcm_coder(short *indata, unsigned char *outdata, int len, struct adpcm_state *state)
{
int val; /* Current input sample value */
unsigned int delta; /* Current adpcm output value */
int diff; /* Difference between val and valprev */
int step; /* Stepsize */
int valpred; /* Predicted output value */
int vpdiff; /* Current change to valpred */
int index; /* Current step change index */
unsigned int outputbuffer = 0;/* place to keep previous 4-bit value */
int count = 0; /* the number of bytes encoded */
valpred = state->valprev;
index = (int)state->index;
step = stepsizeTable[index];
while (len > 0 ) {
/* Step 1 - compute difference with previous value */
val = *indata++;
diff = val - valpred;
if(diff < 0)
{
delta = 8;
diff = (-diff);
}
else
{
delta = 0;
}
/* Step 2 - Divide and clamp */
/* Note:
** This code *approximately* computes:
** delta = diff*4/step;
** vpdiff = (delta+0.5)*step/4;
** but in shift step bits are dropped. The net result of this is
** that even if you have fast mul/div hardware you cannot put it to
** good use since the fixup would be too expensive.
*/
vpdiff = (step >> 3);
if ( diff >= step ) {
delta |= 4;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 2;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 1;
vpdiff += step;
}
/* Phil Frisbie combined steps 3 and 4 */
/* Step 3 - Update previous value */
/* Step 4 - Clamp previous value to 16 bits */
if ( (delta&8) != 0 )
{
valpred -= vpdiff;
if ( valpred < -32768 )
valpred = -32768;
}
else
{
valpred += vpdiff;
if ( valpred > 32767 )
valpred = 32767;
}
/* Step 5 - Assemble value, update index and step values */
index += indexTable[delta];
if ( index < 0 ) index = 0;
else if ( index > 88 ) index = 88;
step = stepsizeTable[index];
/* Step 6 - Output value */
outputbuffer = (delta << 4);
/* Step 1 - compute difference with previous value */
val = *indata++;
diff = val - valpred;
if(diff < 0)
{
delta = 8;
diff = (-diff);
}
else
{
delta = 0;
}
/* Step 2 - Divide and clamp */
/* Note:
** This code *approximately* computes:
** delta = diff*4/step;
** vpdiff = (delta+0.5)*step/4;
** but in shift step bits are dropped. The net result of this is
** that even if you have fast mul/div hardware you cannot put it to
** good use since the fixup would be too expensive.
*/
vpdiff = (step >> 3);
if ( diff >= step ) {
delta |= 4;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 2;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 1;
vpdiff += step;
}
/* Phil Frisbie combined steps 3 and 4 */
/* Step 3 - Update previous value */
/* Step 4 - Clamp previous value to 16 bits */
if ( (delta&8) != 0 )
{
valpred -= vpdiff;
if ( valpred < -32768 )
valpred = -32768;
}
else
{
valpred += vpdiff;
if ( valpred > 32767 )
valpred = 32767;
}
/* Step 5 - Assemble value, update index and step values */
index += indexTable[delta];
if ( index < 0 ) index = 0;
else if ( index > 88 ) index = 88;
step = stepsizeTable[index];
/* Step 6 - Output value */
*outdata++ = (unsigned char)(delta | outputbuffer);
count++;
len -= 2;
}
state->valprev = (short)valpred;
state->index = (char)index;
return count;
}
int adpcm_decoder(unsigned char *indata, short *outdata, int len, struct adpcm_state *state)
{
unsigned int delta; /* Current adpcm output value */
int step; /* Stepsize */
int valpred; /* Predicted value */
int vpdiff; /* Current change to valpred */
int index; /* Current step change index */
unsigned int inputbuffer = 0;/* place to keep next 4-bit value */
int count = 0;
valpred = state->valprev;
index = (int)state->index;
step = stepsizeTable[index];
/* Loop unrolling by Phil Frisbie */
/* This assumes there are ALWAYS an even number of samples */
while ( len-- > 0 ) {
/* Step 1 - get the delta value */
inputbuffer = (unsigned int)*indata++;
delta = (inputbuffer >> 4);
/* Step 2 - Find new index value (for later) */
index += indexTable[delta];
if ( index < 0 ) index = 0;
else if ( index > 88 ) index = 88;
/* Phil Frisbie combined steps 3, 4, and 5 */
/* Step 3 - Separate sign and magnitude */
/* Step 4 - Compute difference and new predicted value */
/* Step 5 - clamp output value */
/*
** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
** in adpcm_coder.
*/
vpdiff = step >> 3;
if ( (delta & 4) != 0 ) vpdiff += step;
if ( (delta & 2) != 0 ) vpdiff += step>>1;
if ( (delta & 1) != 0 ) vpdiff += step>>2;
if ( (delta & 8) != 0 )
{
valpred -= vpdiff;
if ( valpred < -32768 )
valpred = -32768;
}
else
{
valpred += vpdiff;
if ( valpred > 32767 )
valpred = 32767;
}
/* Step 6 - Update step value */
step = stepsizeTable[index];
/* Step 7 - Output value */
*outdata++ = (short)valpred;
/* Step 1 - get the delta value */
delta = inputbuffer & 0xf;
/* Step 2 - Find new index value (for later) */
index += indexTable[delta];
if ( index < 0 ) index = 0;
else if ( index > 88 ) index = 88;
/* Phil Frisbie combined steps 3, 4, and 5 */
/* Step 3 - Separate sign and magnitude */
/* Step 4 - Compute difference and new predicted value */
/* Step 5 - clamp output value */
/*
** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
** in adpcm_coder.
*/
vpdiff = step >> 3;
if ( (delta & 4) != 0 ) vpdiff += step;
if ( (delta & 2) != 0 ) vpdiff += step>>1;
if ( (delta & 1) != 0 ) vpdiff += step>>2;
if ( (delta & 8) != 0 )
{
valpred -= vpdiff;
if ( valpred < -32768 )
valpred = -32768;
}
else
{
valpred += vpdiff;
if ( valpred > 32767 )
valpred = 32767;
}
/* Step 6 - Update step value */
step = stepsizeTable[index];
/* Step 7 - Output value */
*outdata++ = (short)valpred;
count += 2;
}
state->valprev = (short)valpred;
state->index = (char)index;
return count;
}