参考自:《adpcm编解码原理及其代码实现》
《ADPCM编码与解码学习笔记》
《音频编码:ADPCM》
PCM (Pulse Code Modulation),脉冲编码调制。
PCM是把声音从模拟信号转化为数字信号的技术,把一个时间连续取值连续的模拟信号变换成时间离散取值离散的数字信号,模拟信号转化为数字信号需要三个步骤:采样、量化、编码。
采样用一个固定的频率对模拟信号进行提取样值。
常用采样率为8KHz,16kHz,22.05kHz,32kHz,44.1kHz,48kHz,192kHz。
人耳能够感觉到的最高频率为20kHz,要满足人耳的听觉要求,根据奈奎斯特采样定律则,需要每秒进行40k次采样,即40kHz。
8Khz的采样率就可以达到人的对话程度,通常电话的采样率为8kHz/16kHz。
常见的无线电广播采样率为22.05KHz,CD采样率为44.1kHz,DVD采样率为48kHz,Hi-Res音频采样率为192kHz
量化编码就是把采样得到的声音信号幅度转换成数字值。这个过程会产生失真,量化的精度越高失真越小。常见的量化位数为8bit,16bit,24bit。
PCM约定俗成为无损编码,因为PCM代表了数字音频中最佳的保真水准,并不意味着PCM就能够确保信号绝对保真,PCM也只能做到最大程度的无限接近。
DPCM(Differential Pulse Code Modulation),差分脉冲编码调。
PCM是不压缩的,通常数据量比较大,存储和通讯都必需付出比较大的代价,早期的通讯是不能传输那么大的数据量的,所以就要想办法把数据压缩一下,以减少带宽和存储的压力。
假设我们以8kHz的采样率,16bit量化编码,则1秒的数据量为8000 * 16 = 128000 bit 。一般音频信息都是比较连续的,不会突然很高或者突然很低,两点之间差值不会太大,所以这个差值只需要很少的几个位(比如4bit)即可表示。这样,我们只需要知道前一个点的值,又知道它与下一个点的差值,就可以计算得到下一个点了。这个差值就是所谓的Differential ,将PCM数据转成DPCM数据,数据量会小很多,如上面所说的用4bit的表示差值,则1秒的(8kHz采样率16bit量化编码) PCM数据转成DPCM则只需要大约32000bit , 压缩比大约4:1。
ADPCM (Adaptive Differential Pulse Code Modulation)、自适应差分脉冲编码调。
音频信号虽然是比较连续性的,有些差值比较小,有些差值比较大,如果差值比较大有可能用4bit表示不了,如果增大表示差值的位数(例如8bit\16bit)是可以解决这个问题,但就导致数据量变大,没起到压缩的目的,而且这种差值比较大的只是少数,大部分还是差值比较小的。
为了解决这个问题,前辈们就想出了 ADPCM,定义一个因子,用差值除以因子的值来表示两点之差,如果两点之间差值比较大,则因子也比较大。通过因子引入,可以使得DPCM编码自动适应差值比较大的数据。
ADPCM算法并没用固定标准,最经典的就是IMA 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的核心公式如下,其中 delta 代表为量化后的值,step 为量化步长,vpdiff 代表经过量化后有效的偏差值,vpdiff 加上本次的预测值做为下一次的运算的预测值:
整个ADPCM的编码过程分三步进行:
第一步为计算出当前实际值与预测值的偏差diff,val 代表了当前数据的实际值,valpred 为当前数的预测值。delta 为量化后的带符号的有效数据为4bit的数据,其最高位代表的数据的方向,bit3为1代表负数,代表-7~7的整型数据。
当 diff 小于0, delta bit3被置1。
第二步通过index(首次编码index为0)求出step,通过diff和step求出delta。
第三步为对 diff 进行量化,简易实现不考虑计算效率的情况下完全可以直接参考上面的公式,因为是在计算机平台进行了除法运算与小数运算,该作者很巧妙的把这些运算使用与或非来实现了,提高了运算的效率,有兴趣的读者可以看看代码,学习一下这种思路。我们细看一下公式,
可以发现公式可以拆分为两部分实现,小数部分的量化被转换为了固定的step/8,因此节约了计算的成本。vpdiff 就是对应这部分的值。
vpdiff = (step >> 3);
注释说明:
delta = diff*4/step; vpdiff = (delta+0.5)*step/4;
考虑到都是整数运算,可以推导出:pcm数据经过编码再解码生成的预测pcm数据,如果预测pcm数据再次编码所得的数据与第一次编码所得的数据是相同的。故pcm数据经过一次编码有损后,不论后面经过几次解码再编码都是数据一样,音质不会再次损失。即相对于第一次编码后,以后数据不论多少次编解码,属于无损输出。adpcm.h
#ifndef ADPCM_H
#define ADPCM_H
struct adpcm_state
{
int valprev;
int index;
};
extern void adpcm_coder(short *indata, signed char *outdata, int len, struct adpcm_state *state);
extern void adpcm_decoder(signed char *indata, short *outdata, int len, struct adpcm_state *state);
#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.
******************************************************************/
/*
** 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"
#include /*DBG*/
#ifndef __STDC__
#define signed
#endif
/* 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
};
void adpcm_coder(short *indata, signed char *outdata, int len, struct adpcm_state *state)
{
short *inp; /* Input buffer pointer */
signed char *outp; /* output buffer pointer */
int val; /* Current input sample value */
int sign; /* Current adpcm sign bit */
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 */
int outputbuffer; /* place to keep previous 4-bit value */
int bufferstep; /* toggle between outputbuffer/output */
outp = (signed char *)outdata;
inp = indata;
valpred = state->valprev;
index = state->index;
step = stepsizeTable[index];
bufferstep = 1;
for ( ; len > 0 ; len-- ) {
val = *inp++;
/* Step 1 - compute difference with previous value */
diff = val - valpred;
sign = (diff < 0) ? 8 : 0;
if ( sign ) diff = (-diff);
/* 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.
*/
delta = 0;
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;
}
/* Step 3 - Update previous value */
if ( sign )
valpred -= vpdiff;
else
valpred += vpdiff;
/* Step 4 - Clamp previous value to 16 bits */
if ( valpred > 32767 )
valpred = 32767;
else if ( valpred < -32768 )
valpred = -32768;
/* Step 5 - Assemble value, update index and step values */
delta |= sign;
index += indexTable[delta];
if ( index < 0 ) index = 0;
if ( index > 88 ) index = 88;
step = stepsizeTable[index];
/* Step 6 - Output value
if ( bufferstep ) {
outputbuffer = (delta << 4) & 0xf0;
} else {
*outp++ = (delta & 0x0f) | outputbuffer;
}*/
if ( bufferstep ) {
outputbuffer = delta & 0x0f;
} else {
*outp++ = ((delta << 4) & 0xf0) | outputbuffer;
}
bufferstep = !bufferstep;
}
/* Output last step, if needed */
if ( !bufferstep )
*outp++ = outputbuffer;
state->valprev = valpred;
state->index = index;
}
void adpcm_decoder(signed char *indata, short *outdata, int len, struct adpcm_state *state)
{
signed char *inp; /* Input buffer pointer */
short *outp; /* output buffer pointer */
int sign; /* Current adpcm sign bit */
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 */
int inputbuffer; /* place to keep next 4-bit value */
int bufferstep; /* toggle between inputbuffer/input */
outp = outdata;
inp = (signed char *)indata;
valpred = state->valprev;
index = state->index;
step = stepsizeTable[index];
bufferstep = 0;
for ( ; len > 0 ; len-- ) {
/* Step 1 - get the delta value */
if ( !bufferstep ) {
inputbuffer = *inp++;
delta = inputbuffer & 0xf;
} else {
delta = (inputbuffer >> 4) & 0xf;
}
bufferstep = !bufferstep;
/* Step 2 - Find new index value (for later) */
index += indexTable[delta];
if ( index < 0 ) index = 0;
if ( index > 88 ) index = 88;
/* Step 3 - Separate sign and magnitude */
sign = delta & 8;
delta = delta & 7;
/* Step 4 - Compute difference and new predicted value */
/*
** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
** in adpcm_coder.
*/
vpdiff = step >> 3;
if ( delta & 4 ) vpdiff += step;
if ( delta & 2 ) vpdiff += step>>1;
if ( delta & 1 ) vpdiff += step>>2;
if ( sign )
valpred -= vpdiff;
else
valpred += vpdiff;
/* Step 5 - clamp output value */
if ( valpred > 32767 )
valpred = 32767;
else if ( valpred < -32768 )
valpred = -32768;
/* Step 6 - Update step value */
step = stepsizeTable[index];
/* Step 7 - Output value */
*outp++ = valpred;
}
state->valprev = valpred;
state->index = index;
}
本部分为adpcm数据存放说明,属于细节部分,很多代码解码出来有噪音就是因为本部分细节不对,所以需要仔细阅读。
adpcm数据是一个block一个block存放的,block由block header (block头) 和data 两者组成的。其中block header是一个结构体,它在单声道下的定义如下:
Typedef struct
{
short sample0; //block中第一个采样值(未压缩)
BYTE index; //上一个block最后一个index,第一个block的index=0;
BYTE reserved; //尚未使用
}MonoBlockHeader;
对于双声道,它的blockheader应该包含两个MonoBlockHeader其定义如下:
typedaf struct
{
MonoBlockHeader leftbher;
MonoBlockHeader rightbher;
}StereoBlockHeader;
在解压缩时,左右声道是分开处理的,所以必须有两个MonoBlockHeader;
有了blockheader的信息后,就可以不需要知道这个block前面数据而轻松地解出本block中的压缩数据。故adpcm解码只与本block有关,与其他block无关,可以只单个解任何一个block数据。
block的大小是固定的,可以自定义,每个block含的采样数nsamples计算如下:
//
#define BLKSIZE 1024
block = BLKSIZE * channels;
//block = BLKSIZE;//ffmpeg
nsamples = (block - 4 * channels) * 8 / (4 * channels) + 1;
例如audition软件就是采用上面的,单通路block为1024bytes,2041个samples,双通路block为2048,也是含有2041个sample。
而ffmpeg采用block =1024bytes,即不论单双通路都为1024bytes,通过公式可以算出单双通路的samples数分别为2041和1017;
byte 0 byte 1 | byte 2 byte 3 | byte 4 byte 5 | byte 6 byte 7 | byte 8 byte 9 | … |
---|---|---|---|---|---|
sample0 | sample1 | sample2 | sample3 | sample4 | … |
单通路压缩为adpcm数据为 4bytes block head + raw data:
byte 0 byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | byte 7 | byte 8 | byte 9 | … |
---|---|---|---|---|---|---|---|---|---|
sample0 | index | reserved | data0 | data1 | data2 | data3 | data4 | data5 | … |
其中sample1编码后存data0低4位,sample2编码后存data0高四位...
byte 0 byte 1 | byte 2 byte 3 | byte 4 byte 5 | byte 6 byte 7 | byte 8 byte 9 | … |
---|---|---|---|---|---|
sampleL0 | sampleR0 | sampleL1 | sampleR1 | sampleL2 | … |
双通路压缩为adpcm数据为 4bytes block L head + 4bytes block R head + 4bytes raw L data + 4bytes raw R data…:
adpcm双通路block head:
byte 0 byte 1 | byte 2 | byte 3 | byte 4 byte 5 | byte 6 | byte 7 |
---|---|---|---|---|---|
sample0L | indexL | reservedL | sample0R | indexR | reservedR |
接着双通路raw压缩数据4byte L, 4byte R …:
byte8 | byte9 | byte10 | byte11 | byte12 | byte13 | byte14 | byte15 | byte16 | byte17 | byte18 | … |
---|---|---|---|---|---|---|---|---|---|---|---|
data0L | data1L | data2L | data3L | data0R | data1R | data2R | data3R | data4L | data5L | data6L | … |
注意:需要特别留意双声道的处理和当数据不够1 block时的处理方式。