Wave文件格式主要是用来存储音频PCM数据的,其实也可以存储非PCM音频数据,这种情况我们就不考虑了。文件的扩展名为“.wav”,采用RIFF文件结构。
一、RIFF文件格式简介
1、RIFF文件是由一个一个的chunk组成的,并且chunk之间可以嵌套。使用小端存储。
2、chunk的基本结构如下:
struct chunk
{
char chunkId[4]; // 4个字符组成的chunk标识,少于4字符右补空格
U32 chunkSize; // 数据块的大小,字节
char data[chunkSize]
};
chunkId可以为"RIFF"、"LIST"、"fmt"、"data"...
3、当chunkId为"RIFF"或"LIST"(类型块)时,chunk的结构变形为下面这样:
struct chunk
{
char chunkId[4]; // 4字符组成的chunk标识,少于4字符右补空格
U32 chunkSize; // chunkType + data的数据大小,字节
char chunkType[4]; // chunk的类型,e.g. WAVE/AVI...
char data[chunkSize - 4]
};
二、Wave文件格式
1、Wave文件采用RIFF文件格式,当然也就遵循RIFF文件结构。总体来看Wave文件是由多个chunk嵌套组成的。
字段 | 长度(B) | 字段描述 | ||
chunk1 | chunkId | 4 | 第一个chunk的标识始终是"RIFF" | |
chunkSize | 4 | 该chunk的数据大小,包括chunkType | ||
chunkType | 4 | 对于Wave文件chunk的类型为"WAVE" | ||
data | chunk2 | chunkId | 4 | Wave文件的第二个chunk标识为"fmt" |
chunkSize | 4 | 该chunk的数据大小 | ||
data | wFormatTag | 2 | 音频数据格式,0x0001表示PCM数据 | |
nChannels | 2 | 声道数 | ||
nSamplesPerSec | 4 | 采样率,每秒采样次数 | ||
nAvgBytesPerSec | 4 | 每秒的音频数据大小(B),声道数*采样率*每个采样点的比特数/8 | ||
nBlockAlign | 2 | 每个时刻的音频数据块大小(B),声道数*每个采样点的比特数/8 | ||
wBitsPerSample | 2 | 每个采样点(的幅值)用多少比特编码(8 or 16) | ||
chunk3 | chunkId | 4 | Wave文件的第三个chunk的标识为"data" | |
chunkSize | 4 | 该chunk的数据大小 | ||
data | PCM数据 | chunkSize | PCM数据 |
从上表可以看出:
(1) Wave文件最外层是一个标识为"RIFF"的类型块chunk1
(2) 在chunk1的data部分嵌套了2个chunk,即chunk2和chunk3
(3) chunk2的标识为"fmt",在它的data部分存储音频的一些相关属性
(4) chunk3的标识为"data",在它的data部分存储具体的音频PCM数据
2、PCM数据存储格式
3、用二进制方式打开.wav文件 验证对Wave文件格式的理解
Windows 关机.wav
Wave测试文件我用的是C:\Windows\Media\Windows 关机.wav
二进制文本工具我用的是notepad++的Hex Editor插件,也可以用UltraEdit编辑器
为了方便描述,我把Wave文件的各个字段进行了编号1-14,依次对应上面表格中的各个字段
1: 0x 52 49 46 46,4字节,"RIFF"的ASCII码
对应chunk1的chunkId字段
2: 0x dc 95 02 00,4字节,由于RIFF文件格式采用小端存储,所以转换为人们熟悉的字节序(大端)为0x 00 02 95 dc,
再转换为十进制数为169436,表示chunk1的数据大小为169436字节。我们可以查看
Windows 关机.wav文件的大小为169444字节,由于chunkId和chunkSize已经占用8字节,
所以后面还有169436字节的数据。
对应chunk1的chunkSize字段
3: 0x 57 41 56 45,4字节,"WAVE"的ASCII码
对应chunk1的chunkType字段
4: 0x 66 6d 74 20,4字节,"fmt "的ASCII码,"fmt"不够4个字符,所以右边补空格。
对应chunk2的chunkId字段
5: 0x 10 00 00 00,4字节,同样是小端存储,转换为大端为0x 00 00 00 10即十进制的16,
表示chunk2的数据大小为16字节。从上面表格可知,wFormatTag、nChannels、
nSamplesPerSec、nAvgBytesPerSec、nBlockAlign、wBitsPerSample加起来刚好占16字节。
对应chunk2的chunkSize字段
6: 0x 01 00,2字节,转换为大端为0x 00 01,表示该Wave文件存储的是音频PCM数据。
对应chunk2的data部分的wFormatTag字段
7: 0x 02 00,2字节,转换为大端为0x 00 02即十进制的2,表示音频声道数为2即双声道。
对应chunk2的data部分的nChannels字段
8: 0x 44 ac 00 00,4字节,转换为大端为0x 00 00 ac 44即十进制的44100,表示音频采样率为44100Hz。
对应chunk2的data部分的nSamplesPerSec字段
9: 0x 10 b1 02 00,4字节,转换为大端为0x 00 02 b1 10即十进制的176400,
表示每秒的音频数据大小为176400B。用公式”声道数*采样率*每个采样点的比特数/8”
计算得到的刚好就是176400即2 * 44100 * 16 / 8 = 176400
对应chunk2的data部分的nAvgBytesPerSec
10: 0x 04 00,2字节,转换为大端为0x 00 04即十进制的4,表示每个时钟(有2个采样点)的音频数据有4B大小。
它的值等于”声道数*每个采样点的比特数/8”即2 * 16 / 8 = 4
对应chunk2的data部分的nBlockAlign字段
11: 0x 10 00,2字节,转换为大端为0x 00 10即十进制的16,表示每个采样值用16比特编码。
对应chunk2的data部分的wBitsPerSample字段
12: 0x 64 61 74 64,4字节,"data"的ASCII码
对应chunk3的chunkId字段
13: 0x b8 95 02 00,4字节,转换为大端为0x 00 02 95 b8即十进制的169400,
表示chunk3的data部分的数据大小即PCM音频数据的大小
对应chunk3的chunkSize字段
14: 音频PCM数据即chunk3的data