嵌入式学习小组
直播中

何壮梦

7年用户 292经验值
私信 关注

关于ADPCM压缩算法流程介绍

关于ADPCM压缩算法流程介绍

回帖(1)

杨文田

2021-6-3 17:23:16
  ADPCM(Adaptive Differential Pulse Code Modulation),是针对16bits(或8bits)声音数据数据的一种有损压缩算法,声音流中每次采样的16bit数据以4bit 存储所以,压缩比 1:4。另外压缩/解压缩非常简单,所以是一种低消耗,高效率声音获得的好方法。保存声音的数据文件后缀名为.AUD的大多数ADPCM压缩算法。ADPCM
  主要是针对连续的。
  8bits的声音人耳是可以勉强了解的,因为它的编码和解码的过程很简单,列在我们身边,相信大家能够了解。ADPCM算法可以将每次采样得到的16bit数据压缩到4bit。需要注意的是,要压缩/解压缩得到声音信号,时,信号是放在一起的,需要将两个声道分别处理。
  ADPCM过程压缩
  首先我们认为声音信号都是从零开始的,那么需要初始化两个变量
  INT索引= 0,prev_sample = 0;
  下面的循环将包括处理声音数据流,注意其中的getnextsample()应该得到一个16bit的数据,而outputdata()可以计算出来的数据保存起来,程序中的step_table[],index_adjust[]附在后面:
  int index=0,prev_sample:=0;
  while (还有数据要处理)
  {
  cur_sample=getnextsample(); // 得到当前的采样数据
  delta=cur_sample-prev_sample; // 计算出和上一个的增量
  if (delta《0) delta=-delta,***=8; // 取绝对值
  else *** = 0 ; // *** 保存是符号位
  code = 4*delta / step_table[index]; // 根据 steptable[] 得到一个 0-7 的值
  if (code》7) code=7; //它描述了声音强度的变化量
  指数 += index_adjust[code] ; //根据声音强度调整下一步取steptable的顺序
  if (index《0) index=0; // 容易下一个得到更合适的变化量的描述
  else if (index》88) index=88;
  prev_sample=cur_sample;
  输出(代码| ***);// 加上符号位保存起来
  }
  ADPCM解压缩过程
  接压缩实际是压缩的一个逆过程,同样的,其中的 getnextcode() 应该得到一个编码,,而 outputsample() 可以将解码出来的声音信号保存起来。这种代码同样使用了同一个的 setp_table[] 和 index_adjust () 附在后面:
  int index=0,cur_sample=0;
  while (还有数据要处理)
  {
  code=getnextcode(); // 得到下一个数据
  if ((code & 8) != 0) ***=1 else ***=0;
  代码&=7; // 将代码分离为数据和符号
  delta = (step_table[index]*code)/4+step_table[index]/8; //一个加的珍珠是为了减少负
  if (***==1) delta=-delta;
  cur_sample+=delta; // 计算出当前的数据
  》32767) output_sample(32767);
  否则 if (cur_sample《-32768) output_sample(-32768);
  否则 output_sample(cur_sample);
  index+=index_adjust[代码];
  如果(指数《0)指数=0;
  如果(指数》88)指数=88;
  }
  附 ADPCM压缩算法实现
  /******************************************** *****************
  版权所有 1992 年,荷兰阿姆斯特丹 Stichting Mathematisch Centrum
  。
  保留所有权利特此授予出于任何目的免费
  使用、复制、修改和分发本软件及其
  文档的许可,
  前提是所有副本中
  均出现上述版权声明,并且该版权声明和本许可声明均出现在
  支持文档,以及 Stichting Mathematisch 的名字
  未经事先明确的书面许可,不得将 Centrum 或 CWI 用于与软件分发有关的广告或宣传。
  STICHTING MATHEMATISCH CENTRUM 不
  承担与本软件有关的所有保证,包括对适销性和
  适用性的所有默示保证,在
  任何情况下,STICHTING MATHEMATISCH CENTRUM 均不对任何第三方或第三方
  使用利润,无论是在
  合同,疏忽诉讼或其他民事行为,所产生OUT
  与使用或连接或性能本软件所引发的。
  ****************************************************** ****************/
  /*
  ** Intel/DVI ADPCM 编码器/解码器。
  **
  ** 该编码器的算法取自 IMA Compatability Project
  **会议录,第 2 卷,第 2 期;1992 年 5 月。
  **
  ** 1.2 版,92 年 12 月 18 日。
  **
  ** 更改日志:
  ** - 修正了一个愚蠢的错误,其中增量计算为
  ** stepsize*code/4 而不是 stepsize*(code+0.5)/4。
  ** - 有一个逐一错误导致它
  在蓝色月亮中选择** 错误的增量。
  ** - 已删除 NODIVMUL 定义。现在总是
  使用移位、加法和减法来完成计算。事实证明,因为标准
  ** 是使用 shift/add/subtract 定义的,您需要一些修复代码
  **(因为使用 shift/add/sub 的 div/mul 模拟产生了一些
  真正的 div/mul 不会产生的舍入** 错误),并且所有结果代码
  ** 运行速度比一直使用 shift 慢。
  ** - 更改了一些变量名称以使其更有意义。
  */
  #include “adpcm.h”
  #include /*DBG*/
  #ifndef __STDC__
  #define signed
  #endif
  /* Intel ADPCM 阶跃变化表 */
  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
  , 3,4,4,4, 4 544, 598, 658, 724, 796,
  876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
  2272, 2499, 4, 4, 5, 30, 4, 5, 30, 4, 30, 30
  5894,6484,7132,7845,8630,9493,10442,11487,12635,13899,
  15289,16818,18500,20350,22385,24623,27086,29794,32767
  };
  void
  adpcm_coder(indata, outdata, len, state)
  短输入数据[];
  字符输出数据[];
  内里;
  struct adpcm_state *state;
  {
  短 *输入; /* 输入缓冲区指针 */ 有
  符号字符 *outp; /* 输出缓冲区指针 */
  内部值;/* 当前输入样本值 */
  int sign; /* 当前 adpcm 符号位 */
  int delta; /* 当前 adpcm 输出值 */
  int diff; /* val 和 valprev 的区别 */
  int step; /* 步长 */
  int valpred; /* 预测输出值 */
  int vpdiff; /* 当前对 valpred 的更改 */
  int index; /* 当前步长变化索引 */
  int outputbuffer; /* 保留前 4 位值的位置 */
  int bufferstep; /* 在输出缓冲区/输出之间切换 */
  outp = (signed char *)outdata;
  inp = 数据;
  valpred = 状态-》 valprev;
  索引 = 状态-》 索引;
  step = stepsizeTable[index];
  缓冲步 = 1;
  for ( ; len 》 0 ; len-- ) {
  val = * inp ++;
  /* 第 1 步 - 计算与先前值的
  差异*/ diff = val - valpred;
  符号 = (差异 《 0) ? 8 : 0;
  如果(符号)差异=(-差异);
  /* 第 2 步 - 划分和钳位 */
  /* 注意:
  ** 此代码 *近似* 计算:
  ** delta = diff*4/step;
  ** vpdiff = (delta+0.5)*step/4;
  ** 但在移位步骤中位被丢弃。这样做的最终结果是
  ** 即使你有快速的 mul/div 硬件,你也不能
  很好地利用它,因为修复太昂贵了。
  */
  增量 = 0;
  vpdiff = (步骤》》 3);
  如果(差异》 = 步){
  delta = 4;
  差异 -= 步骤;
  vpdiff += 步;
  }
  步 》》= 1;
  if ( diff 》= step ) {
  delta |= 2;
  差异 -= 步骤;
  vpdiff += 步;
  }
  步 》》= 1;
  if ( diff 》= step ) {
  delta |= 1;
  vpdiff += 步;
  }
  /* 第 3 步 - 更新前一个值 */
  if ( sign )
  valpred -= vpdiff;
  否则
  valpred += vpdiff;
  /* 第 4 步 - 将前一个值限制为 16 位 */
  if ( valpred 》 32767 )
  valpred = 32767;
  否则如果 ( valpred 《 -32768 )
  valpred = -32768;
  /* 第 5 步 - 组合值,更新索引和步长值 */
  delta |= sign;
  index += indexTable[delta];
  如果(索引 《 0)索引 = 0;
  如果(指数》 88)指数= 88;
  step = stepsizeTable[index];
  /* 第 6 步 - 输出值 */
  if ( bufferstep ) {
  outputbuffer = (delta 《《 4) & 0xf0;
  } else {
  *outp++ = (delta & 0x0f) | 输出缓冲区;
  }
  bufferstep = !bufferstep;
  }
  /* 输出最后一步,如果需要 */
  if ( !bufferstep )
  *outp++ = outputbuffer;
  状态-》 valprev = valpred;
  状态-》索引=索引;
  }
  void
  adpcm_decoder(indata, outdata, len, state)
  char indata[];
  短输出数据[];
  内里;
  struct adpcm_state *state;
  {
  签名字符 *inp; /* 输入缓冲区指针 */
  short *outp; /* 输出缓冲区指针 */
  int 符号;/* 当前 adpcm 符号位 */
  int delta; /* 当前 adpcm 输出值 */
  int step; /* 步长 */
  int valpred; /* 预测值 */
  int vpdiff; /* 当前对 valpred 的更改 */
  int index; /* 当前步长变化索引 */
  int inputbuffer; /* 放置下一个 4 位值 */
  int bufferstep; /* 在输入
  缓冲区/输入之间切换 */ outp = outdata;
  inp = (signed char *)indata;
  valpred = 状态-》 valprev;
  索引 = 状态-》 索引;
  step = stepsizeTable[index];
  缓冲步 = 0;
  for ( ; len 》 0 ; len-- ) {
  /* Step 1 - 获取增量值 */
  if ( bufferstep ) {
  delta = inputbuffer & 0xf;
  } else {
  inputbuffer = *inp++;
  delta = (inputbuffer 》》 4) & 0xf;
  }
  bufferstep = !bufferstep;
  /* 第 2 步 - 查找新的索引值(稍后)*/
  index += indexTable[delta];
  如果(索引 《 0)索引 = 0;
  如果(指数》 88)指数= 88;
  /* 第 3 步 - 分离符号和幅度 */
  sign = delta & 8;
  delta = delta & 7;
  /* 第 4 步 - 计算差异和新预测值 */
  /*
  ** 计算 ‘vpdiff = (delta+0.5)*step/4’,但请参阅 adpcm_coder 中的注释
  **。
  */
  vpdiff = 步骤 》》 3;
  if (delta & 4) vpdiff += step;
  if ( delta & 2 ) vpdiff += step》》1;
  if ( delta & 1 ) vpdiff += step》》2;
  如果(符号)
  valpred -= vpdiff;
  否则
  valpred += vpdiff;
  /* 第 5 步 - 钳制输出值 */
  if ( valpred 》 32767 )
  valpred = 32767;
  否则如果 ( valpred 《 -32768 )
  valpred = -32768;
  /* 第 6 步 - 更新步长值 */
  step = stepsizeTable[index];
  /* 第 7 步 - 输出值 */
  *outp++ = valpred;
  状态-
  》 valprev = valpred;
  状态-》索引=索引;
  }。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分