WAV文件是微软公司开发的一种声音文件格式,大部分的WAV文件中的音频数据是直接通过PCM(Pulse Code Modulation)脉冲编码调制得到的样本数据,由于未被压缩或压缩比很小所以其质量较高,但同时数据量也很大。在数字领域,存储音频文件的最直接方式就是在满足奈奎斯特抽样定理的条件下,将模拟的音频文件进行抽样量化,将得到的一系列样本值以二进制形式存储在文件中,这就是PCM脉冲编码调制的大致步骤。尽管大部分的WAV文件存储的都是未经压缩的,但它也支持一些音频压缩方法。
那么如何将模拟的音频变成存储在电脑中的二进制数据呢?首先是选定抽样频率,对模拟音频进行抽样使得其在时间域是离散的,再将抽样值进行量化。WAV文件对每个抽样值的量化比特数没有具体规定,但固定在存储时每个样本值由整数个字节来表示,因此每个样本值的比特数均应该为8bit的整数倍如8bit、16bit、24bit、32bit等。当某个样本值的比特数不是8bit的整数倍时,则需要将其左移并再低位填充0使其变成8bit的整数倍。例如某个抽样量化后的样本值为1011000101,则存储时需要将其补0为10110001 01000000,再采用小端序(先存低字节,再存高字节,默认存储方式)的方式进行存储,最终该样值被存为B1 40。一个需要注意的小细节是在 WAV文件中,8bit的是unsigned 类型的无符号数据,但16bit或更高的量化级的都是signed 类型的有符号数。
将模拟音频转化为二进制的数据后,要想将其存储在电脑中需要考虑其数据组织的形式,这样在后续对文件进行操作时会更加方便。WAV文件是一种基于RIFF规范的音频文件格式。RIFF(Resource Interchange File Format)是一种自包含的文件组织格式,它的基本单位是Chunk,一个基于RIFF规范的文件是由若干个Chunk组成的。
Chunk:
1.基本格式:
struct chunk
{
u32 chunk_id; /* 块标志 */
u32 chunk_size; /* 块大小*/
u8 data[chunk_size]; /* 块内容*/
};
chunk_id是一个4字节的ASCII标识符,用来标识该Chunk中的数据,不足四字节的再末尾用空位占位。可以有:'RIFF','LIST','fmt ','data','WAV ','AVI '等,采用小端序。
chunk_size也是四字节,存储的是data中的数据长度(以字节为单位),不包括chunk_id和chunk_size本身所占的内存单元。
data[chunk_size]:该Chunk中的数据,必须为偶数个字节,若长度为奇数则在最后添一个空字节。
2.各种子类:
Chunk有多种子类,其中两种类型为“RIFF”和“LIST”的两种Chunk可以包含其他块,其他的Chunk则只能有数据。
“RIFF”/“LIST”子类格式如下:
struct chunk
{
u32 chunk_ id; /* 块标志 */
u32 chunk_size; /* 块大小 ,由于此时的data部分分为type和restdata两部分,所以chunk_size应该为type和restdata数据长度之和*/
u32 type; /* 类型 */
u8 restdata[chunk_size-4] /* data中除了type4个字节后的数据,可以有包含的其他Chunk的数据 */
};
WAV文件格式:
一个WAV文件就是一个RIFF文件。其组成方式为:一个RIFF Chunk,其type为“WAVE”,其restdata部分包含了两个Chunk分别为“fmt” Chunk和“data” Chunk。
其详细的结构如下:
ChunkID:四字节,“RIFF”(ASCII字符);
ChunkSize:四字节,表示该WAV文件除去ChunkID和ChunkSize之外的所有数据长度的总和,等于Format长度+SubChunk1长度(绿色部分)+Subchunk2长度(橙色部分),即4+(4+4+Subchunk Size)+(4+4+Subchunk2 Size)=4+24+(8+Subchunk2 Size)=36+Subchunk2 Size,由于只能用4字节来表示文件的大小,因此WAV文件限制在4GB内(有的程序限制其大小为2GB);
Format:四字节,,表示该RIFF文件的类型,此处为:“WAVE”(ASCII字符);
Subchunk1ID:四字节,“fmt ”(注意末尾有一个空格占位,因为一个Chunk的ID必须为四位的ASCII字符);
Subchunk1Size:四字节,“fmt”chunk除去Subchunk1ID和Subchunk1Size之外的所有数据长度之和,为2+2+4+4+2+2=16;
AudioFormat:两字节,表示音频数据的编码方式,PCM=1,若不为1则表示其他的数据编码方式,其值所代表的详细编码方式可以见文章末尾;
NumChannels:两字节,表示声道数,Mono = 1,Stereo = 2等
SampleRate:四字节,采样率,常见的采样率有8000,44100等(单位为Hz);
ByteRate:四字节,表示一秒钟内的音频信号数据需要多少个字节来存储,为SampleRate* NumChannels * BitsPerSample/8
BlockAlign:两字节,表示每一时刻的音频信号的抽样值需要几个字节来表示,为NumChannels* BitsPerSample/8
BitsPerSample:两字节,表示量化比特数,8bits = 8, 16 bits = 16等;
(若该音频文件不是PCM 编码,则其fmt chunk后还会有一些其他的参数)
Subchunk2 ID:四字节,“data”(ASCII字符);
Subchunk2 Size:四字节,data部分数据的长度,若为奇数则在末尾添加空字节使得长度为偶数;
Data:Subchunk2Size个字节,实际的声音数据。
下面是举例说明一个WAV文件中的数据(只列举了文件的前72字节):
其颜色与上面的结构图相对应。由图可知该WAV文件是双声道、16bit量化的,因此某一时刻的样本值由四字节来表示。文件的音频信号样值若为多声道,其值是交叉排列的,例如双声道为:左声道样值、右声道样值、左声道样值、右声道样值……如此排列,多声道同理。
由Audio Format这个参数的值,可以得知WAV文件中音频数据的编码方式,下列是目前WAV文件支持的编码方式:
Code Description
0 (0x0000) Unknown
1 (0x0001) PCM/uncompressed
2 (0x0002) Microsoft ADPCM
6 (0x0006) ITU G.711 a-law
7 (0x0007) ITU G.711 µ-law
17 (0x0011) IMA ADPCM
20 (0x0016) ITU G.723 ADPCM (Yamaha)
49 (0x0031) GSM 6.10
64 (0x0040) ITU G.721 ADPCM
80 (0x0050) MPEG
65,536 (0xFFFF) Experimental
本文参考网址:
http://www.codeguru.com/cpp/g-m/multimedia/audio/article.php/c8935/PCM-Audio-and-Wave-Files.htm
http://soundfile.sapp.org/doc/WaveFormat/