失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > PCM音频文件(.wav)压缩成ADPCM(.wav)

PCM音频文件(.wav)压缩成ADPCM(.wav)

时间:2023-10-29 07:25:04

相关推荐

PCM音频文件(.wav)压缩成ADPCM(.wav)

PCM音频文件压缩成adpcm格式的文件有多中方法(如使用ms ACM、sox等),本文主要介绍使用公开的算法(如下所示,如果需要可到网上搜一下:

=======================================

** 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.

=======================================)进行PCM到ADPCM转换的方法。

首先,要了解一下PCM格式和ADPCM格式的.wav文件的构成,可参考:

非PCM格式的文件会至少多加入一个“fact”块,它用来记录数据解压缩后的大小。(注意是数据而不是文件)这个“fact”块一般加在“data”块的前面。

1.PCM格式的wav文件的内部结构

2.WAVEFORMAT结构的认识

PCM和非PCM的主要区别是声音数据的组织不同,这些区别可以通过两者的WAVEFORMAT结构来区分。下面以PCM和IMA-ADPCM来进行对比:

WAVE的基本结构WAVEFORMATEX结构定义如下:

typedef struct

{

WORD wFormatag; //编码格式,包括WAVE_FORMAT_PCM,//WAVEFORMAT_ADPCM等

WORD nChannls; //声道数,单声道为1,双声道为2;

DWORD nSamplesPerSec;//采样频率;

DWORD nAvgBytesperSec;//每秒的数据量;

WORD nBlockAlign;//块对齐;

WORD wBitsPerSample;//WAVE文件的采样大小;

WORD sbSize; //PCM中忽略此值

}WAVEFORMATEX;

PCM的结构就是基本结构;

IMAADPCMWAVEFORMAT结构定义如下:

Typedef struct

{

WAVEFORMATEX wfmt;

WORD nSamplesPerBlock;

}IMAADPCMWAVEFORMAT;

IMA-ADPCM的wfmt->cbsize不能忽略,一般取值为2,表示此类型的WAVEFORMAT比一般的WAVEFORMAT多出2个字节。这两个字符也就是nSamplesPerBlock。

3.“fact”chunk的内部组织

在非PCM格式的文件中,一般会在WAVEFORMAT结构后面加入一个“fact”chunk,结构如下:

typedef struct{

char[4]; //“fact”字符串

DWORD chunksize;

DWORD datafactsize; //数据转换为PCM格式后的大小。

}factchunk;

datafactsize是这个chunk中最重要的数据,如果这是某种压缩格式的声音文件,那么从这里就可以知道他解压缩后的大小。对于解压时的计算会有很大的好处!

4.“data”chunk的内部组织

从“data”chunk的第9个字节开始,存储的就是声音信息的数据了,(前八个字节存储的是标志符“data”和后接数据大小size(DWORD)。这些数据可能是压缩的,也可能是没有压缩的。

PCM中的声音数据没有被压缩,如果是单声道的文件,采样数据按时间的先后顺序依次存入。(它的基本组织单位是BYTE(8bit)或WORD(16bit))如果是双声道的文件,采样数据按时间先后顺序交叉地存入。

IMA-ADPCM是压缩格式,它是从PCM的16位采样压缩成4位的。对于单声道的IMA-ADPCM来说,它是将PCM的数据按时间次序依次压缩并写入文件中的,每个byte中含两个采样,低四位对应第一个采样,高四位对应第二个采样。而对于双声道的IMA-ADPCM来说,它的存储相对就麻烦一些了,它是将PCM的左声道的前8个采样依次压缩并写入到一个DWORD中,然后写入“data”chunk里。紧接着是右声道的前8个采样。以此循环,当采样数不足8时(到数据尾端),应该把多出来的采样用0填充。

在IMA-ADPCM中,“data”chuck中的数据是以block形式来组织的,我把它叫做“段”,也就是说在进行压缩时,并不是依次把所有的数据进行压缩保存,而是分段进行的,这样有一个十分重要的好处:那就是在只需要文件中的某一段信息时,可以在解压缩时可以只解所需数据所在的段就行了,没有必要再从文件开始起一个一个地解压缩。这对于处理大文件将有相当的优势。同时,这样也可以保证声音效果。

Block一般是由block header (block头) 和 data 两者组成的。其中block header是一个结构,它在单声道下的定义如下:

Typedef struct

{

short sample0; //block中第一个采样值(未压缩)

BYTE index; //上一个block最后一个index,第一个block的index=0;

BYTE reserved; //尚未使用

}MonoBlockHeader;

有了blockheader的信息后,就可以不需要知道这个block前面和后面的数据而轻松地解出本block中的压缩数据。对于双声道,它的blockheader应该包含两个MonoBlockHeader其定义如下:

typedaf struct

{

MonoBlockHeader leftbher;

MonoBlockHeader rightbher;

}StereoBlockHeader;

在解压缩时,左右声道是分开处理的,所以必须有两个MonoBlockHeader;

以上内容参考/kindyb/archive//10/13/503024.aspx,对此表示感谢。

===================================================================

接下来,就是要实现从PCM到ADPCM的转换了。(以8KHz,16Bits,单声道为例)

定义结构:

#define ADPCM_BLOCKSIZE 252 //256 - 4;

typedef unsigned long ULONG;

typedef unsigned short USHORT;

typedef unsigned char BYTE;

typedef struct {

long chunkid;

long chunksize;

long wave_id;

} RIFFCHUNK;

typedef struct tagDATA

{

long ID;

unsigned long Size;

}DATA;

typedef struct

{

long chunkid;

unsigned long chunksize;

unsigned long timelength;

} FACTCHUNK;

typedef struct tagWaveFormat{

//long chunkSize;

short wFormatTag;

unsigned short wChannels;

unsigned long dwSamplesPerSec;

unsigned long awAvgBytesPerSec;

unsigned short wBlockAlign;

unsigned short wBitsPerSample;

unsigned short cbsize;

int headerSize ;

long dataLength ;

} WAVEFMT;

typedef struct

{

RIFFCHUNK riff;

long chunkid;

unsigned long chunksize;

USHORT wformattag; /* format type */

USHORT nchannels; /* number of channels (i.e. mono, stereo...) */

ULONG nsamplespersec; /* sample rate */

ULONG navgbytespersec; /* for buffer estimation */

USHORT nblockalign; /* block size of data */

USHORT wbitspersample; /* number of bits per sample of mono data */

USHORT cbsize; /* the count in bytes of the extra size */

USHORT wsamplesperblock;

FACTCHUNK fact;

DATA data;

}INTELADPCM_HEADER;

void adpcmEncoder()

{

FILE *inFile, *outFile;

inFile = fopen("c://input.wav", "rb");

if (!inFile)

{

printf("can't open the input file.");

return -2;

}

WAVEFMT waveFmt;

if(!checkWaveFileHeaderFormat(inFile, &waveFmt)) {

printf("input file's format is wrong.");

return -1;

}

outFile = fopen("c://output.wav", "wb+");

if (!outFile)

{

printf("can't open the output file.");

return -2;

}

setAdpcmHeader(outFile, waveFmt.dataLength);

intlen;

short inData[ADPCM_BLOCKSIZE * 2];

BYTE outData[ADPCM_BLOCKSIZE];

adpcm_state state;

state.index = 0;

state.valprev = 0;

long samples = waveFmt.dataLength;

while (samples > 0)

{

/* Read two frames worth of samples */

len = fread(inData, (size_t )1, sizeof (inData), inFile);

if (len <= 0)

{

perror("error reading input");

exit(1);

}

samples -= len;

if (len < sizeof (inData))

{

printf("memset. /n");

memset((char *)inData + len, 0, sizeof (inData) - len);

}

fwrite(&state, 4, 1, outFile);

len = adpcm_coder(inData, outData, len / 2, &state);

writeFile(outFile, outData, len);//此处高低位要互换

}

fclose(inFile);

fclose(outFile);

return 0;

}

void setAdpcmHeader( FILE *outFile, ULONG dataSize)

{

ULONG flen = 0;

INTELADPCM_HEADER adpcmhead;

adpcmhead.riff.chunkid = WAV_ID_RIFF;//4BYTE

adpcmhead.riff.wave_id = WAV_ID_WAVE;//4BYTE

adpcmhead.chunkid = WAV_ID_FMT;//4BYTE

adpcmhead.chunksize = 0x14;//2BYTE

adpcmhead.wformattag = 0x11;//2BYTE

adpcmhead.nchannels = 1;//2BYTE

adpcmhead.nsamplespersec = 8000;//4BYTE

adpcmhead.navgbytespersec = 4055;// = nsamplespersec / wsamplesperblock *nblockalign, 可调,我用4064效果更好

adpcmhead.nblockalign = 0x100;//2BYTE

adpcmhead.wbitspersample = 4;//2BYTE

adpcmhead.cbsize = 2;//2BYTE

adpcmhead.wsamplesperblock = 505;//为什么是这个数?没搞明白

adpcmhead.fact.chunkid = WAV_ID_FACT;

adpcmhead.fact.chunksize = 4;

adpcmhead.data.ID = WAV_ID_DATA;

int adpcmDataSize = (dataSize / (ADPCM_BLOCKSIZE * 4)) * (ADPCM_BLOCKSIZE + 4); //blockcount * 256

// 判断最后的一个block是否是1008(252*4)byte,如果不是,按实际大小设置

int lastBlockSize = dataSize % (ADPCM_BLOCKSIZE * 4);

if(lastBlockSize > 0)

{

adpcmDataSize += lastBlockSize / 4 + 4(sizeof(state));

}

adpcmhead.riff.chunksize = adpcmDataSize + sizeof(INTELADPCM_HEADER) - 8 ;

adpcmhead.fact.timelength = dataSize / 2;

adpcmhead.data.Size = adpcmDataSize;

fwrite( &adpcmhead, sizeof(adpcmhead), 1, outFile);

}

以上只是给出了部分关键的source,其他的按照以上的介绍完善以下即可。

如果觉得《PCM音频文件(.wav)压缩成ADPCM(.wav)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。