本文将详细分析不同格式的wav
文件。
简介
wav
即wave
(下文统称为wav
), 该文件格式是由微软开发的用于音频数字存储的标准,可应用于Windows
、Linux
和MacOS
等多种操作系统,文件扩展名为.wav
,是waveform
的简写。
wav文件结构
在Windows
下,大部分多媒体文件都遵循RIFF
(Resource Interchange File Format
,资源互换文件格式)格式来存储数据。
RIFF
文件的基本存储单位称为块(chunk
),一个遵循RIFF
格式的文件由若干个chunk
组成,每个chunk
又由块标识、块长度和块数据三部分构成,其基本结构见表1:
Field | Size(bytes) | Type |
---|---|---|
块标识 | 4 | S8 * |
块长度 | 4 | U32 |
块数据 | S8 * |
**表1** `chunk`基本结构
- 块标识:用于标识块中的数据,由
4
个ASCII
字符组成,如不满4
个字符则在右边以空格充填,如:RIFF
、LIST
、fmt
和data
等; - 块长度:用于标识块数据域中的数据长度,块标识域和块长度域不包括在其中,所以一个
chunk
的实际长度为该值加8
; - 块数据:存储块数据,若数据长度为奇数,则在最后添加一个
NULL
;
特别需要注意的是,RIFF
格式规定只有块标识为RIFF
或LIST
的块可以含有子块(subChunk
),而其它块只可以包含数据。块标识为RIFF
或LIST
的块的结构与表1大致相同,只不过其块数据域分为两部分:
type
:由4
个ASCII
字符组成,代表RIFF
文件的类型,如WAVE
和AVI
,或者代表LIST
块的类型,如avi
文件中的列表hdrl
和movi
;data
:实际的块内容,包含若干subChunk
;
wav
文件是非常简单的一种RIFF
文件,其本身就是由一个块标识为RIFF
的chunk
和多个subChunk
组成的一个chunk
,其组成如下:
RIFF chunk
:主块,必选,块标识为RIFF
,说明这是一个RIFF
文件,RIFF
文件的第一个块标识必须是RIFF
;Format chunk
:子块,必选,块标识为fmt
,用于存储文件的一些参数信息,比如采样率、通道数和编码格式等;Fact chunk
:子块,可选,块标识为fact
,基于压缩编码的wav
文件必须含有fact
块;List chunk
:子块,可选,块标识为LIST
,用于记录文件版权和创建时间信息;Data chunk
:子块,必选,块标识为data
,用于存储音频数据;
目前业界标准的wav
文件仅由RIFF chunk
、Format chunk
和Data chunk
组成:
wav文件头格式
wav
文件从数据类型上看,主要由文件头和数据体两部分组成:
- 文件头:由
RIFF chunk
、Format chunk
、List chunk
和Fact chunk
等组成,用于存储一些文件信息; - 数据体:由
Data chunk
组成,用于存储音频数据;
标准格式
标准的wav
文件头仅由RIFF chunk
和Format chunk
组成,长度为44
个字节,格式见表2:
file offset(bytes) | Field name | Field size(bytes) | type | endian | description |
---|---|---|---|---|---|
0 | Chunk ID | 4 | S8 * | big | “RIFF”,表明为RIFF文件 |
4 | Chunk Size | 4 | U32 | little | 除了RIFF及自己之外,整个文件的长度,即文件总字节数减去8字节 |
8 | Format | 4 | S8 * | big | “WAVE”,表明为wav格式 |
12 | Subchunk1 ID | 4 | S8 * | big | “fmt “ |
16 | Subchunk1 Size | 4 | U32 | little | 表示fmt数据块即subchunk1除了Subchunk1 ID和Subchunk1 Size之后剩下的长度,一般为16, 大于16表示存在扩展区域,可选值为16、18、20、40等 |
20 | AudioFormat | 2 | U16 | little | 编码格式,即压缩格式,0x01表示pcm格式,无压缩,参见表3 |
22 | NumChannels | 2 | U16 | little | 音频通道数,单声道为1,立体声或双声道为2 |
24 | SampleRate | 4 | U32 | little | 采样频率,每个通道单位时间采样次数,可选值为16000kHz和44100kHz等 |
28 | ByteRate | 4 | U32 | little | 数据传输速率,可用此估算缓冲区长度,ByteRate = SampleRate * NumChannels * (BitsPerSample / 8) |
32 | BlockAlign | 2 | U16 | little | 采样一次的字节数,即一帧的字节数,表示块对齐的内容(数据块的调整数),播放软件一次处理多少个该长度的字节数据,以便将其用于缓冲区的调整,BlockAlign = NumChannels * (BitsPerSample / 8) |
34 | BitsPerSample | 2 | U16 | little | 采样位宽,即每个采样点的bit数,可选值8、16或32等 |
36 | Subchunk2 ID | 4 | S8 * | big | “data” |
40 | Subchunk2 Size | 4 | U32 | little | 音频数据的总长度,即文件总字节数减去wav文件头的长度 |
44 | Data | little | 音频数据 |
**表2** 标准的`wav`文件头格式
通过wav
文件头信息,我们可以计算出音频时长:
1 | 音频时长(s) = Subchunk2 Size / ByteRate |
压缩编码格式
wav
文件几乎支持所有ACM
规范的编码格式,其信息存储在wav
文件头偏移20
、21
两个字节中,常见的压缩编码格式见表3:
格式代码 | 格式名称 | Format chunk 长度 | 是否有Fact chunk |
---|---|---|---|
0 (0x0000) | unknown | unknown | unknown |
1 (0x0001) | PCM/uncompressed | 16 | 无 |
2 (0x0002) | Microsoft ADPCM | 18 | 有 |
3(0x0003) | IEEE float | 18 | 有 |
6 (0x0006) | ITU G.711 a-law | 18 | 有 |
7 (0x0007) | ITU G.711 µ-law | 18 | 有 |
17 (0x0011) | IMA ADPCM | unknown | unknown |
20 (0x0016) | ITU G.723 ADPCM (Yamaha) | unknown | unknown |
49 (0x0031) | GSM 6.10 | 20 | 有 |
64 (0x0040) | ITU G.721 ADPCM | unknown | 有 |
80 (0x0050) | MPEG | unknown | unknown |
65,534 (0xFFFE) | 扩展格式标识 | 40 | unknown |
65,536 (0xFFFF) | Experimental | unknown | unknown |
**表3** 常见的压缩编码格式
扩展格式
当然,也不是所有的wav
文件头都是44
个字节的,比如通过FFmpge
编码而来的wav
文件头通常大于44
个字节,目前比较常见的wav
文件头长度有44字节、46字节、58字节和98字节。
Format chunk扩展
当wav
文件采用非PCM
编码即压缩格式时,会扩展Format chunk
,在其之后扩充了一个数据结构,见表4:
file offset(bytes) | Field name | Field size(bytes) | type | description |
---|---|---|---|---|
24 | extand size | 2 | U16 | 除其自身外的扩展区域长度 |
26 | extand area | 包含扩展的格式信息,其长度取决于压缩编码类型。当某种编码格式(如ITU G.711 a-law)使扩展区的长度为0时,该字段还必须保留,只是长度字段的数值为0。 |
**表4** 标准Format chunk扩展格式
由此可以得出,如果
Subchunk1 Size
等于0x10(16)
,表示不包含Format chunk
扩展,wav
文件头长度为44
字节;如果大于0x10(16)
,则包含Format chunk
扩展,扩展长度的最小值为18(16+2)
,此时wav
文件头大于44
字节。
当编码格式代码为0xFFFE
时,Format chunk
扩展长度为24
字节,格式见表5。
file offset(bytes) | Field name | Field size(bytes) | type | description |
---|---|---|---|---|
24 | 扩展区长度 | 2 | U16 | 值为22 |
26 | 有效采样位数 | 2 | U16 | 最大值为 每个采样字节数 * 8 |
28 | 扬声器位置 | 4 | U32 | 声道号与扬声器位置映射的二进制掩码 |
32 | 编码格式 | 2 | U16 | 真正的编码格式代码 |
34 | 14 | 值为{\x00, \x00, \x00, \x00, \x10, \x00, \x80, \x00, \x00, \xAA, \x00, \x38, \x9B, \x71} |
**表5** 编码为0xFFFE的Format chunk扩展格式
Fact chunk
采用压缩编码(修订版Rev.3
以后出现的编码格式)的wav
文件必定有含有Fact chunk
,其结构符合标准chunk
结构,参见表1。
Fact chunk
的块标识符为”fact
“,块长度至少为4
个字节,目前其只有一个块数据内容,即每个声道采样总数,或采样帧总数。该值等于Subchunk2 Size / BlockAlign
。
值得注意的是,在实测中发现,将压缩编码格式文件转换成PCM
编码格式后,原Fact chunk
仍然存在。
wav文件动态解析库
wav
文件格式是一种极其简单的文件格式,如果对其结构足够熟悉,完全可以通过代码正确读写,从而免去引入一些复杂的中间库,降低复杂度,提高工作效率。
这里提供了一个wav文件动态解析库,欢迎使用。