在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件的读写操作,只使用标准C++库函数,不依赖于其他的库。
WAV文件结构
WAV是符合RIFF标准的多媒体文件,其文件结构可以如下:
WAV 文件结构 |
---|
RIFF块 |
WAVE FOURCC |
fmt 块 |
fact 块(可选) |
data块(包含PCM数据) |
首先是一个RIFF块,有块标识RIFF,指明该文件是符合RIFF标准的文件;接着是一个FourCC,WAVE,该文件为WAV文件;fmt块包含了音频的一些属性:采样率、码率、声道等;fact 块是一个可选块,不是PCM数据格式的需要该块;最后data块,则包含了音频的PCM数据。实际上,可以将一个WAV文件看着由两部分组成:文件头和PCM数据,则WAV文件头各字段的意义如下:
本文实现的是一个能够读取PCM数据格式的单声道或者双声道的WAV文件,是没有fact块以及扩展块。
结构体定义
通过上面的介绍发现,WAV的头文件所包含的内容有两种:RIFF文件格式标准中需要的数据和关于音频格式的信息。对于RIFF文件格式所需的信息,声明结构体如下:
// The basic chunk of RIFF file format
struct Base_chunk{
FOURCC fcc; // FourCC id
uint32_t cb_size; // 数据域的大小
Base_chunk(FOURCC fourcc)
: fcc(fourcc)
{
cb_size = 0;
}
};
chunk是RIFF文件的基本单元,首先一个4字节的标识FOURCC,用来指出该块的类型;cb_size
则是改块数据域中数据的大小。
文件头中另一个信息则是音频的格式信息,实际上是frm chunk的数据域信息,其声明如下:
// Format chunk data field
struct Wave_format{
uint16_t format_tag; // WAVE的数据格式,PCM数据该值为1
uint16_t channels; // 声道数
uint32_t sample_per_sec; // 采样率
uint32_t bytes_per_sec; // 码率,channels * sample_per_sec * bits_per_sample / 8
uint16_t block_align; // 音频数据块,每次采样处理的数据大小,channels * bits_per_sample / 8
uint16_t bits_per_sample; // 量化位数,8、16、32等
uint16_t ex_size; // 扩展块的大小,附加块的大小
Wave_format()
{
format_tag = 1; // PCM format data
ex_size = 0; <