网上有很多讲解Adpcm编解码的,但是就没有详细说明其是需要如何使用的。

这里就记录下是如何使用代码的,即是函数的参数需要填写什么,要注意的要点。

ADPCM(Adaptive Differential Pulse Code Modulation),是一种针对 16bits( 或8bits或者更高) 声音波形数据的一种有损压缩算法,它将声音流中每次采样的 16bit 数据以 4bit 存储,所以压缩比 1:4.

ADPCM数据存放形式

ADPCM的音频数据则是以块的形式保存,并且有固定的格式。每一个block包含header和data两部分。它在单声道下的定义如下:

typedef struct{ short  sample0;    //block中第一个采样值(未压缩) 2个字节char  index;     //上一个block最后一个index,第一个block的index=0;char  reserved;   //尚未使用的
}MonoBlockHeader // 块的头部

前2个字节的为未压缩的原始16bit数据,第3个字节为上一个块的index,第4个字节为保留位(没有使用)。ADPCM每个数据块的解压需要前三个字节的数据作为解压函数的dec_state结构体参数输入到解压函数中。

对于双声道,它的blockheader应该包含两个MonoBlockHeader其定义如下:

typedef struct
{MonoBlockHeader leftbher;MonoBlockHeader rightbher;
}StereoBlockHeader;

对于双通道来说,在解压缩时,其是分开处理的,所以必须有两个MonoBlockHeader。

那么经过adpcm编码之后,其音频数据是按照一块一块数据存放的,一块的数据,其结构是

struct ADPCMBlock
{short sample0; //原始PCM采样数据char index;char RESERVED;char sampledata[252];    //这个就是进行adpcm编码之后的音频数据
};

这里的整个数据块大小是256字节,但这不是规定一定要这个值的,可以由自己来定的。

很多文章只是给了编码和解码函数,没有完整的调用过程,这里面还是有点细节要注意,要注意每个块的index 和 sample0的填充。

adpcm的编解码文件

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" {
#endifstruct adpcm_state {short    valprev;    /* Previous output value */char    index;        /* Index into stepsize table */};//len 是采样点的个数,不是字节大小int adpcm_coder(short* indata, char* outdata, int len, struct adpcm_state* state);int adpcm_decoder(char* indata, short* outdata, int len, struct adpcm_state* state);#ifdef __cplusplus
}  /* extern "C" */
#endif#endif /* ADPCM_H*/

adpcm.c文件

/*
** 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, 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(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) & 0xf;// &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;/* 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;
}

使用编解码的方式

场景:现在有一pcm音频文件,需要把这个整段pcm音频进行adpcm编码,而每个adpcm块的大小是256字节。这里说的都是单通道的。双通道可能会有其他的不同。(主要是我做的是单通道的。。)

struct ADPCMBlock
{short sample0;char index;char RESERVED;char sampledata[252];
};

进行apdcm编码

那么需要从pcm文件中读取数据,那是应该读取多少呢,是一次性读取完还是分次读取呢。

是需要分次读取,因为需要把pcm数据刚好可以编码成一个个adpcm块(或者就一个adpcm块)。这里我们定下来adpcm块大小是256字节,一个块中的压缩后的adpcm音频数据是252字节,压缩比是1:4,所以原始pcm数据大小是252*4=1008.

而块头部还有2字节的原始pcm数据,所以需要每次从文件读取1008+2=1010字节来进行编码。

函数:int adpcm_coder(short* indata, char* outdata, int len, struct adpcm_state* state)

我们是需要直接保存首两个字节的原始pcm数据的,所以而在使用adpcm_coder函数时候,第一个参数那里就需要从第三个字节开始进行编码的。又因为是short*类型,所以是第一个参数写reinterpret_cast<short*>(&buf[2])。

第三个参数len不是进行编码的数据长度,是需要编码的采集点个数,因为每个采集点是short类型的,所以其个数是总长度/2,而又需要去掉首两个字节的原始pcm数据,所以其是(readdatasize - 2) / 2。

可以看出,这进行编解码的数据长度大小要一定是偶数的才行。

第四个参数是adpcm_state*类型state,该函数内部会更新参数state,其主要就是更新其成员valprev和index。

要是最后的要进行编码的数据编码后不够一个块大小,也要按照一个块大小编码,保存长度为1block。

int pcmToAdpcm(const char* PcmFileName,const char* outFileName)
{FILE* inFile = fopen(PcmFileName, "rb");if (!inFile) {return 0;}fseek(inFile, 0L, SEEK_END);  //到达文件的末尾int pcmSize = ftell(inFile);//返回给定流 stream 的当前文件位置,这样pcmSize就是文件的大小fseek(inFile, 0L, SEEK_SET);int totalblocks = 0;    //总的adpcm块的个数int curBlockNum = 0;    //已编码的adpcm块的个数int readUnitSize = 1010;    //每次从文件读取的字节大小,//为什么是1010呢,因为之前定了adpcm块是256字节的,一个块中的压缩后的adpcm音频数据是252字节,压缩比是1:4,所以原始pcm数据大小是252*4=1008.//而块头部还有2字节的原始pcm数据,所以需要每次从文件读取1008+2=1010字节来进行编码//计算出中的block的个数if (pcmSize % readUnitSize == 0)totalblocks = pcmSize / readUnitSize;elsetotalblocks = pcmSize / readUnitSize + 1;FILE* outFile = fopen(outFileName, "wb");char buf[1024];adpcm_state adpcmState;ADPCMBlock block;//下面的while循环内部的是adpcm编码操作的重点while (curBlockNum < totalblocks) {if (curBlockNum == 0)block.index = 0;elseblock.index = adpcmState.index;int readdatasize = fread(buf, 1, readUnitSize, inFile);   //从input_fd文件读取readUnitSize字节block.sample0 = (static_cast<short>(buf[1]) << 8) | buf[0];       //获取原始的2字节pcm数据block.RESERVED = 0;state.valprev = block.sample0;adpcm_coder(reinterpret_cast<short*>(&buf[2]), block.sampledata, (readdatasize - 2) / 2, &adpcmState);//convert the remain 504 sample points;fwrite(&block, 1, sizeof(block), outFile);++curBlockNum;}fclose(inFile);fclose(outFile);
}

进行adpcm解码

int adpcm_decoder(char* indata, short* outdata, int len, struct adpcm_state* state)。

第一个参数事adpcm块数据,前四个字节是头部,所以第一个参数写buf+4,从第五字节开始解码。

第二个参数是解码后的pcm数据。

第三个参数是adpcm块数据的长度,这个就真的是长度的。使用的时候需要减去头部4字节,所以使用方式是readdatasize - 4。

第四个参数是adpcm_state*类型state,该函数内部会更新参数state,其主要就是更新state.index。

其函数的返回值是解码后的pcm数据点的个数,即是有多少个short数据点。所以最终解码后的pcm数据长度是count*2。

int AdpcmToPcm(const char* adpcmFileName, const char* outFileName)
{FILE* inFile = fopen(adpcmFileName, "rb");if (!inFile) {std::cout << " open input file failed\n";return 0;}fseek(inFile, 0L, SEEK_END);  //到达文件的末尾int pcmSize = ftell(inFile);//返回给定流 stream 的当前文件位置,这样pcmSize就是文件的大小fseek(inFile, 0L, SEEK_SET);int totalblocks = 0;    //总的adpcm块的个数int curBlockNum = 0;    //已解码的adpcm块的个数int blockSize = sizeof(ADPCMBlock);//计算出中的block的个数if (pcmSize % readUnitSize == 0)totalblocks = pcmSize / blockSize;elsetotalblocks = pcmSize / blockSize + 1;FILE* outFile = fopen(outFileName, "wb");short pcmBuf[1024];char buf[1024];adpcm_state adpcmState;//下面的while循环内部的是adpcm编码操作的重点while (curBlockNum < totalblocks){if (blockcnt == 0) adpcmState.index = 0;else adpcmState.index = buf[2];memset(buf, 0, sizeof(buf));int readdatasize = fread(buf, 1, blockSize, inFile);//从文件读取adpcm块数据pcmBuf[0] = static_cast<short>(buf[1]) << 8 | buf[0];   //得到原始的2字节pcm数据state.valprev = pcmBuf[0];int count = adpcm_decoder(&buf[4], &pcmBuf[1], readdatasize - 4, &adpcmState);//返回值是采集点的个数fwrite(pcmBuf, 1,count * 2 , outFile);  //每个采集点是short类型的,总长度就是count*2++curBlockNum;}fclose(inFile);fclose(outFile);
}

ADPCM编解码的使用相关推荐

  1. adpcm编解码原理及其代码实现

    目录 1. 源代码 adpcm.h adpcm.c 2. adpcm编解码原理 1.adpcm编码原理 2.adpcm解码原理 注释说明 3. ADPCM数据存放形式 1. adpcm 数据块介绍 2 ...

  2. WAV系列之二:ADPCM编解码原理及代码实现

    参考自:<adpcm编解码原理及其代码实现> <ADPCM编码与解码学习笔记> <音频编码:ADPCM> 文章目录 1.PCM 1.1.采样 1.2.量化编码 2. ...

  3. 【语音编码】基于matlab ADPCM编解码(Matlab代码实现)

  4. 音频采样及编解码——LPCM 、ADPCM、G711、G726、AAC

    前言 ~~~~~~~       最近在查看hi3516a音频资料部分,遇到一些音频的专业术语,如LPCM .ADPCM.G711.G726等,故查询了一些资料,对这几个术语进行记录和总结. LPCM ...

  5. 音频采样及编解码——LPCM 、ADPCM、G711、G726

    前言 ~~~~~~~       最近再查看hi3516a音频资料部分,遇到一些音频的专业术语,如LPCM .ADPCM.G711.G726等,故查询了一些资料,对这几个术语进行记录和总结. LPCM ...

  6. 视音频编解码学习工程:FLV封装格式分析器

    ===================================================== 视音频编解码学习工程系列文章列表: 视音频编解码学习工程:H.264分析器 视音频编解码学习 ...

  7. 语音编码分类及编解码标准

    G.711类型:Audio 制定者:ITU-T 所需频宽:64Kbps 特性:算法复杂度小,音质一般 优点:算法复杂度低,压缩比小(CD音质>400kbps),编解码延时最短(相对其它技术) 缺 ...

  8. mplayer 所支持的音视频编解码

    这里我把mplayer 所支持的音视频编解码都罗列出来,方便大家查阅: ---------------------------------------------------------------- ...

  9. 我的Android进阶之旅------gt;Android中编解码学习笔记

    编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...

最新文章

  1. HDFS的shell和API操作
  2. java环境变量javac不能成功 win7_Java开发:Java环境搭建
  3. Matlab图像形态学处理—开操作和闭操作
  4. 【Java】7.1 与用户互动 7.2 系统相关
  5. POJ3889-Fractal Streets【分形,递归,分治】
  6. pat根据中序遍历和先序遍历_[leetcode/lintcode 题解] 前序遍历和中序遍历树构造二叉树...
  7. 无需手机NFC 如何使用微信小程序制作amiibo卡
  8. 华为ICT大赛2016模拟题
  9. 华为薪资等级结构表2020_华为技术等级1到22级解读,17级即可百万年薪加股权分红...
  10. 贱人工具箱使用技巧系列1——旋转复制
  11. android 天气类应用,一周天气预报!7款另类Android天气应用
  12. 关于国产数据库,不得不谈一下“数据库四小龙”
  13. 三大重组股上涨最具爆发力!
  14. 2010提升你幽默感的语句
  15. gSOAP 源码分析(二)
  16. RabbitMq 虚拟主机 virtual-host ,Springboot 中使用 RabbitMq 虚拟主机 virtual-host
  17. java学到能看懂代码_一篇文章能够看懂基础源代码之JAVA篇
  18. 代码审计工程师_36行代码完成5个审计师一周的工作
  19. 软件工程导论第3章习题答案
  20. 进击的java工程师

热门文章

  1. 嵌入式Linux系统libevent异步事件库的移植
  2. shell 函数 与 函数库
  3. Python 愤怒的小鸟代码实现(1):物理引擎pymunk使用
  4. kindeditor使用方法php,KindEditor
  5. ca i啊几次哦啊句iu家哦历史1
  6. PostgreSQL pg_dropcache修改(兼容pg13)
  7. 脉冲淀粉的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  8. 计算机科学优青,关于举办华南理工大学“海内外优秀青年学者论坛” 计算机科学与工程学院分论坛的通知...
  9. arch linux grub引导修复
  10. Oracle清空数据库中数据表数据的方法