WAV系列之二:ADPCM编解码原理及代码实现-程序员宅基地

技术标签: 音频编解码  ADPCM  编解码  代码实现  PCM  

参考自:《adpcm编解码原理及其代码实现》
    《ADPCM编码与解码学习笔记》
    《音频编码:ADPCM》

1、PCM

PCM (Pulse Code Modulation),脉冲编码调制。

PCM是把声音从模拟信号转化为数字信号的技术,把一个时间连续取值连续的模拟信号变换成时间离散取值离散的数字信号,模拟信号转化为数字信号需要三个步骤:采样、量化、编码。

1.1、采样

采样用一个固定的频率对模拟信号进行提取样值。

常用采样率为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

1.2、量化编码

量化编码就是把采样得到的声音信号幅度转换成数字值。这个过程会产生失真,量化的精度越高失真越小。常见的量化位数为8bit,16bit,24bit。
clipboard

PCM约定俗成为无损编码,因为PCM代表了数字音频中最佳的保真水准,并不意味着PCM就能够确保信号绝对保真,PCM也只能做到最大程度的无限接近。

2、DPCM

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。

3、ADPCM

ADPCM (Adaptive Differential Pulse Code Modulation)、自适应差分脉冲编码调。

音频信号虽然是比较连续性的,有些差值比较小,有些差值比较大,如果差值比较大有可能用4bit表示不了,如果增大表示差值的位数(例如8bit\16bit)是可以解决这个问题,但就导致数据量变大,没起到压缩的目的,而且这种差值比较大的只是少数,大部分还是差值比较小的。

为了解决这个问题,前辈们就想出了 ADPCM,定义一个因子,用差值除以因子的值来表示两点之差,如果两点之间差值比较大,则因子也比较大。通过因子引入,可以使得DPCM编码自动适应差值比较大的数据。

ADPCM算法并没用固定标准,最经典的就是IMA ADPCM

4、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的编码过程分三步进行:

第一步为计算出当前实际值与预测值的偏差diffval 代表了当前数据的实际值,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);

4.1、adpcm编码原理

在这里插入图片描述
编码步骤:

  1. 求出输入的pcm数据与预测的pcm数据(第一次为上一个pcm数据)的差值diff;
  2. 通过差分量化器算出delta(通过index(首次编码index为0)求出step,通过diff和step求出delta)。delta即为编码后的数据;
  3. 通过逆量化器求出vpdiff(通过求出的delta和step算出vpdiff);
  4. 求出新的预测valpred,即上次预测的valpred+vpdiff;
  5. 通过预测器(归一化),求出当前输入pcm input的预测pcm值,为下一次计算用;
  6. 量化阶调整(通过delta查表及index,计算出新的index值)。为下次计算用;

4.2、adpcm解码原理

在这里插入图片描述
解码步骤(其实解码原理就是编码的第三到六步):

  1. 通过逆量化器求出vpdiff(通过存储的delta和index,求出step,算出vpdiff);
  2. 求出新的预测valpred,即上次预测的valpred+vpdiff;
  3. 通过预测器(归一化),求出当前输入pcm input的预测pcm值,为下一次计算用。预测的pcm值即为解码后的数据;
  4. 量化阶调整(通过delta查表及index,计算出新的index值)。为下次计算用;

注释说明:

  1. 通过编码和解码的原理我们可以看出其实第一次编码的时候已经进行了解码,即预测的pcm。
  2. 因为编码再解码后输出的数据已经被量化了。根据计算公式delta = diff*4/step; vpdiff = (delta+0.5)*step/4;考虑到都是整数运算,可以推导出:pcm数据经过编码再解码生成的预测pcm数据,如果预测pcm数据再次编码所得的数据与第一次编码所得的数据是相同的。故pcm数据经过一次编码有损后,不论后面经过几次解码再编码都是数据一样,音质不会再次损失。即相对于第一次编码后,以后数据不论多少次编解码,属于无损输出。

4.3、源代码

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 <stdio.h> /*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
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/houxiaoni01/article/details/104702570

智能推荐

Linux查看登录用户日志_怎么记录linux设备 发声的登录和登出-程序员宅基地

文章浏览阅读8.6k次。一、Linux记录用户登录信息文件1  /var/run/utmp----记录当前正在登录系统的用户信息;2  /var/log/wtmp----记录当前正在登录和历史登录系统的用户信息;3  /var/log/btmp:记录失败的登录尝试信息。二、命令用法1.命令last,lastb---show a listing of la_怎么记录linux设备 发声的登录和登出

第四章笔记:遍历--算法学中的万能钥匙-程序员宅基地

文章浏览阅读167次。摘要:1. 简介 2. 公园迷宫漫步 3. 无线迷宫与最短(不加权)路径问题 4. 强连通分量1. 简介在计算机科学裡,树的遍历(也称为树的搜索)是圖的遍歷的一种,指的是按照某种规则,不重复地访问某种樹的所有节点的过程。具体的访问操作可能是检查节点的值、更新节点的值等。不同的遍历方式,其访问节点的顺序是不一样的。两种著名的基本遍历策略:深度优先搜索(DFS) 和 广度优先搜索(B...

【案例分享】使用ActiveReports报表工具,在.NET MVC模式下动态创建报表_activereports.net 实现查询报表功能-程序员宅基地

文章浏览阅读591次。提起报表,大家会觉得即熟悉又陌生,好像常常在工作中使用,又似乎无法准确描述报表。今天我们来一起了解一下什么是报表,报表的结构、构成元素,以及为什么需要报表。什么是报表简单的说:报表就是通过表格、图表等形式来动态显示数据,并为使用者提供浏览、打印、导出和分析的功能,可以用公式表示为:报表 = 多样的布局 + 动态的数据 + 丰富的输出报表通常包含以下组成部分:报表首页:在报表的开..._activereports.net 实现查询报表功能

Ubuntu18.04 + GNOME xrdp + Docker + GUI_docker xrdp ubuntu-程序员宅基地

文章浏览阅读6.6k次。最近实验室需要用Cadence,这个软件的安装非常麻烦,每一次配置都要几个小时,因此打算把Cadence装进Docker。但是Cadence运行时需要GUI,要对Docker进行一些配置。我们实验室的服务器运行的是Ubuntu18.04,默认桌面GNOME,Cadence装进Centos的Docker。安装Ubuntu18.04服务器上安装Ubuntu18.04的教程非常多,在此不赘述了安装..._docker xrdp ubuntu

iOS AVFoundation实现相机功能_ios avcapturestillimageoutput 兼容性 ios17 崩溃-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏2次。首先导入头文件#import 导入头文件后创建几个相机必须实现的对象 /** * AVCaptureSession对象来执行输入设备和输出设备之间的数据传递 */ @property (nonatomic, strong) AVCaptureSession* session; /** * 输入设备 */_ios avcapturestillimageoutput 兼容性 ios17 崩溃

Oracle动态性能视图--v$sysstat_oracle v$sysstat视图-程序员宅基地

文章浏览阅读982次。按照OracleDocument中的描述,v$sysstat存储自数据库实例运行那刻起就开始累计全实例(instance-wide)的资源使用情况。 类似于v$sesstat,该视图存储下列的统计信息:1>.事件发生次数的统计(如:user commits)2>._oracle v$sysstat视图

随便推点

Vue router报错:NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated"}的解决方法_navigationduplicated {_name: 'navigationduplicated-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏9次。我最近做SPA项目开发动态树的时候一直遇到以下错误:当我点击文章管理需要跳转路径时一直报NavigationDuplicated {_name: “NavigationDuplicated”, name: “NavigationDuplicated”}这个错误但是当我点击文章管理后,路径跳转却是成功的<template> <div> 文章管理页面 <..._navigationduplicated {_name: 'navigationduplicated', name: 'navigationduplic

Webrtc回声消除模式(Aecm)屏蔽舒适噪音(CNG)_webrtc aecm 杂音-程序员宅基地

文章浏览阅读3.9k次。版本VoiceEngine 4.1.0舒适噪音生成(comfort noise generator,CNG)是一个在通话过程中出现短暂静音时用来为电话通信产生背景噪声的程序。#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS)static const EcModes kDefaultEcMode = kEcAecm;#elsestati..._webrtc aecm 杂音

医学成像原理与图像处理一:概论_医学成像与图像处理技术知识点总结-程序员宅基地

文章浏览阅读6.3k次,点赞9次,收藏19次。医学成像原理与图像处理一:概论引言:本系列博客为医学成像原理与图像处理重要笔记,由于是手写,在此通过扫描录入以图片的形式和电子版增补内容将其进行组织和共享。前半部分内容为图像处理基础内容,包括图像的灰度级处理、空间域滤波、频率域滤波、图像增强和分割等;后半部分内容为医学影象技术,包括常规胶片X光机、CR、DR、CT、DSA等X射线摄影技术、超声成像技术、磁共振成像(MRI)技术等。本篇主要内容是概论。_医学成像与图像处理技术知识点总结

notepad++ v8.5.3 安装插件,安装失败怎么处理?下载进度为0怎么处理?_nodepa++-程序员宅基地

文章浏览阅读591次,点赞13次,收藏10次。notepad++ v8.5.3 安装插件,下载进度为0_nodepa++

hive某个字段中包括\n(和换行符冲突)_hive sql \n-程序员宅基地

文章浏览阅读2.1w次。用spark执行SQL保存到Hive中: hiveContext.sql(&quot;insert overwrite table test select * from aaa&quot;)执行完成,没报错,但是核对结果的时候,发现有几笔数据超出指定范围(实际只包含100/200)最终排查到是ret_pay_remark 字段包含换行符,解决方案:执行SQL中把特殊字符替换掉regexp_replace(..._hive sql \n

印象笔记05:如何打造更美的印象笔记超级笔记_好的印象笔记怎么做的-程序员宅基地

文章浏览阅读520次,点赞10次,收藏8次。印象笔记05:如何打造更美的印象笔记超级笔记本文介绍印象笔记的具体使用,如何打造更美更实用的笔记。首先想要笔记更加好看和实用,我认为要使用超级笔记。所谓超级笔记就是具有很多便捷功能的笔记。_好的印象笔记怎么做的

推荐文章

热门文章

相关标签