wav数据格式详解

简述

WAV 文件是在 PC 机平台上很常见的、最经典的多媒体音频文件, 是 WaveForm 的简写,也称为波形文件,可直接存储声音波形,还原的波形曲线十分逼真。

WAV 的特点如下:真实记录自然声波形,基本无数据压缩,数据量大。

文件结构

WAV 文件遵循 RIFF 规则,其内容以区块(chunk)为最小单位进行存储。WAV文件一般由3个区块组成:RIFF chunk、Format chunk 和 Data chunk。

CHUNK

每个 CHUNK 的格式都是:

名称 字节数 说明
ID 4 标识
Size 4 该块的数据长度
Data Size 该块的数据

以下是存在的 CHUNK 类型:

数据块符号 数据块名称 可选否
RIFF chunk 文件头数据块
Format chunk 格式数据块
Data chunk 波形数据块
Fact chunk
Cue points chunk
Playlist chunk
Associated data list chunk

RIFF chunk

名称 字节数 端序 内容
ID 4 大端 RIFF (0x52494646)
Size 4 小端 fileSize - 8 (文件大小 - 8)
Type 4 大端 WAVE (0x57415645)

当 Type 是 WAVE 时,表示后面需要两个子块:Format chunkData chunk

Format chunk

名称 字节数 端序 内容
ID 4 大端 fmt (0x666D7420)
Size 4 小端 16
AudioFormat 2 小端 音频格式
NumChannels 2 小端 声道数 (1 or 2)
SampleRate 4 小端 采样率
ByteRate 4 小端 每秒数据字节数
BlockAlign 2 小端 数据块对齐
BitsPerSample 2 小端 采样位数 (8, 16 or 32)

需要注意的是,ByteRate 和 BlockAlign 是可以计算出来的,如果不满足公式可能导致读取文件错误:

ByteRate = SampleRate * NumChannels * BitsPerSample / 8

BlockAlign = NumChannels * BitsPerSample / 8

BitsPerSample 代表的是采样位数,即接下来的 Data chunk 中的每个波形数据有几个字节。

Data chunk

名称 字节数 端序 内容
ID 4 大端 data (0x64617461)
Size 4 小端 N
Data Size 小端 波形数据

需要注意的是,Size = ByteRate * seconds

如果声道数为 2,即多声道音频,在 Data 中每次采样依次排列,即例如 8bit 双声道的数据存放为:

采样1 采样2
声道1数据 (1 byte) 声道2数据 (1 byte) 声道1数据 (1 byte) 声道2数据 (1 byte)

音频处理

python3&wave

这里使用 Python3 对 wav 文件进行处理。

在 Python3 中自带一个 wave 模块支持对 wav 格式的音频数据进行处理,结合 numpy 可以自由操作所有 wav 的波形数据。

常用的操作有:

函数 作用
wave.open(filename, "r") 打开一个音频文件,返回 Wave_read 对象
Wave_read.close() 关闭一个 Wave_read 对象
Wave_read.getnchannels() 返回声道数量,1为单声道,2为立体声
Wave_read.getsampwidth() 返回采样位数
Wave_read.getframerate() 返回采样频率
Wave_read.getnframes() 返回音频总帧数
Wave_read.readframes(n) 读取 n 帧波形数据,如果参数为 -1 则读取所有

将波形数据转化为成 ndarray 的示例代码:

1
2
3
4
5
6
7
8
import wave
import numpy as np

f = wave.open("attachment.wav", "r")
data = f.readframes(-1)
data = np.frombuffer(data, dtype = [np.uint8, np.uint16, np.uint32][f.getsampwidth() // 2])
data.reshape(-1, f.getnchannels())
f.close()

python3&soundfile

但这里更推荐使用第三方库 soundfile,可以更轻松的将音频数据读取并转换为 ndarray,示例代码如下:

1
2
data, samplerate = sf.read("attachment.wav")
print(data)

其中,samplerate 是音频的采样率。

但默认读取类型是 float,如果觉得不好看,可以通过 dtype 参数控制输出的 numpy 数组的 dtype

1
2
data, samplerate = sf.read("attachment.wav", dtype = np.int16)
print(data)

同时如果是多声道的音频文件,它将转换出多个 ndarray 数组来保存不同声道的数据。