wav音频格式解析

wav音频格式解析

本文参考:
WAVE文件格式解析 - Tocy - 博客园 (cnblogs.com)
Wave File Specifications (mcgill.ca)

简介

WAV文件是一种常见的音频文件格式,其结构遵循RIFF(Resource Interchange File Format)规范,这种规范来源于微软和IBM,我们可以在参考博客中找到相关规范文档。

WAV文件是由chunk组成的,这些chunk通常由ckID、cksize、chunk data组成。

文件结构

首先是很经典的结构图:
本图来源:csdn

这张图中有很多值得注意的地方:

  • 在最左边表明了当前的字段的编码属于小端模式还是大端模式。
  • 图中以不同的颜色区分了不同的chunk,而其中的subchunk表明了这些chunk之间的关系。
  • 每个chunk都有其承载的信息内容,这些信息的组织也有相关规范。

接下来分别分析其中的chunk

本文中分析hex文件的截图来源于HexEd.it — 基于浏览器的十六进制编辑器

RIFF

RIFF字段是每个WAV文件都有的部分

field Length Contents
ckid 4 “RIFF”
cksize 4 4+n
WAVEID 4 “WAVE”
WAVE chunks n 包括格式信息和数据块

这张表列出了RIFFchunk的结构,从中可知:

  • 其他的信息都在WAVE chunk中,即开始的图中的subchunk部分。
  • 同时,我们也知道这里的4+n指的是WAVEID的4字节和WAVE chunks的n字节,不包括ckid和cksize。
  • RIFF和WAVE属于固定的内容,是不会变的。

上图是对一个wav的文件放入16进制编辑器分析的截图,我们可以很容易看到转化为ASCII的RIFF和WAV
上图是对一个wav的文件放入16进制编辑器分析的截图,我们可以很容易看到转化为ASCII的RIFF和WAV字段。

fmt chunk

fmt块是一个可选的块,但是其中包含很多重要的信息,包括channels、sample pre seconds、bits per sample等,所以一般的文件都会有这个块,没有这部分信息很多的播放器会无法解析播放。

Field Length Contents
ckID 4 "fmt "
cksize 4 16/18/40
wFormatTag 2 Format code 对应不同的音频编码
nChannels 4 声道数
nSamplePreSecond 4 采样率
nAvgBytesPerSec 4 音频码率
nBlockAlign 2 音频数据块大小
wBitsPerSample 2 量化位数,比如8bit、16bit
cbSize 2 extension扩展块的大小(0 or 22)
wValidBitsPerSample 2 有效位数
dwChannelMask 4 声道掩码,比如左声道、右声道
SubFormat 16 GUID,数据格式码

该表格中表示了许多重要信息,我们要注意以下几点:

  • fmt 这个字段占4个字节,最后一个是空。
  • cksize是16是即为没有扩展部分,为18时,对应的cbsize为0,为40时,cbsize为22。
  • format code对应的音频编码为下表
Format Code PreProcessor Symbol Data
0x0001 WAVE_FORMAT_PCM PCM
0x0003 WAVE_FORMAT_IEEE_FLOAT IEEE float
0x0006 WAVE_FORMAT_ALAW 8-bit ITU-T G.711 A-law
0x0007 WAVE_FORMAT_MULAW 8-bit ITU-T G.711 µ-law
0xFFFE WAVE_FORMAT_EXTENSIBLE Determined by SubFormat

以下内容由AI翻译自第二篇参考文章,我对部分表达做了修改

PCM格式

格式块的第一部分用于描述PCM(脉冲编码调制)数据。

  • 对于PCM数据,文件头中的格式块fmt 声明了每个样本中的比特数/样本(wBitsPerSample)。最初的文档规定,每个样本的比特数应向上取整到下一个8比特的倍数。这个向上取整的值就是容器大小。这种信息是多余的,因为每个样本的容器大小(以字节为单位)也可以通过区块大小除以通道数(nBlockAlign / nChannels)来确定。
    • 这种冗余已经被用来定义新格式。例如,Cool Edit使用了一个格式,它声明了一个24比特的样本大小,以及一个从区块大小和通道数确定的4字节(32比特)的容器大小。通过这种组合,数据实际上是以32位IEEE浮点数存储的。然而,标准化(全量程223)与标准浮点格式不同。
  • PCM数据是二进制补码,除了1-8比特的分辨率,它们被表示为偏移二进制。

非PCM格式

扩展格式块用于非PCM数据。cbSize字段给出了扩展的大小。

  • 对于除PCM以外的所有格式,格式块必须有一个扩展部分。扩展可以是零长度的,但大小字段(值为0)必须存在。
  • 对于浮点数据,全量程是1。比特/样本通常为32或64。
  • 对于对数PCM格式(μ-law和A-law),比特/样本字段(wBitsPerSample)应设置为8比特。
  • 非PCM格式必须有一个fact块。

可扩展格式

WAVE_FORMAT_EXTENSIBLE格式代码表明格式块有一个扩展。扩展有一个字段,它声明了每个样本的有效比特数(wValidBitsPerSample)。另一个字段(dwChannelMask)包含位,这些位指示从通道到扬声器位置的映射。最后一个字段(SubFormat)是一个16字节的全局唯一标识符(GUID)。

  • 使用WAVE_FORMAT_EXTENSIBLE格式时,原始的比特/样本字段(wBitsPerSample)必须与容器大小匹配(8 * nBlockAlign / nChannels)。这意味着wBitsPerSample必须是8的倍数。现在通过wValidBitsPerSample指定容器大小内的减少精度。
  • 有效比特数(wValidBitsPerSample)仅用于信息。数据在容器大小的精度中被正确表示。有效比特数可以是1到容器大小的比特中的任何值。
  • 扬声器位置掩码使用18位,每位对应一个扬声器位置(例如,前置左或顶部后右),以指示通道到扬声器的映射。这个字段是信息性的。全零字段表明通道按照顺序映射到输出:第一个通道到第一个输出,第二个通道到第二个输出,等等。
  • GUID的前两个字节形成指定数据格式代码的子代码,例如 WAVE_FORMAT_PCM。其余的14个字节包含一个固定字符串,\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71

当出现以下情况时,应使用WAVE_FORMAT_EXTENSIBLE格式:

  • PCM数据的比特/样本超过16比特。
  • 通道数超过2。
  • 实际的比特/样本数不等于容器大小。
  • 需要指定从通道到扬声器的映射。

fact chunk

fact是一个可选块,用于记录一些事实信息。所有非pcm编码都必须有fact chunk。Fact块对于所有新的WAVE格式都是必需的,但是对于标准的WAVE_FORMAT_PCM文件则不是必需的。
其基本结构如下:

Field Length Contents
ckID 4 “fact”
cksize 4 块大小:最小为4
dwSampleLength 4 音频数据的样本大小,以字节为单位

data chunk

data chunk中包含音频的数据。
其结构如下:

Field Length Contents
ckID 4 “data”
cksize 4 数据大小 n
sampled data n 数据
pad byte 0 or 1 如果n是奇数,进行填充

一般来说,常见的音频编码是PCM。在这里简单介绍PCM格式:PCM(Pulse Code Modulation)是一种模拟信号的数字化方法,ADC(Analog to Digital Converter)芯片是实现这一方法的器件。 PCM编码就是这个方法中的数字音频编码方式。PCM编码是最原始的音频编码,其他编码都是在它基础上再次编码和压缩的。

以下是pcm编码的格式:
图片来源:csdn

其他chunk

  • JUNK是用于对齐的填充段。
  • bext是由一些广播软件生成的可选段,用于存储元数据信息,如音频文件的标题、艺术家、版权信息等。
  • LIST 是一个特殊的块,用于包含其他子块的列表。标识符为"LIST",后面跟着一个子块类型的标识符,如"INFO"(用于存储元数据信息)。
  • INFO ChunkLIST块的一个子块,用于存储元数据信息,如音频文件的标题、艺术家、版权信息等。标识符为"INFO"。
  • Cue Point这是一个可选的chunk,它包含了关于音频数据的标记信息,如标记点的位置、名称等。这些信息通常用于在音频播放器中添加书签或跳转到特定位置。
  • slnt 通常用于存储音频文件的缩略图或预览图像,它通常出现在音频文件的元数据中,而不是音频数据本身。
  • 其他的块在参考博客的链接文档中有详细介绍

例子

在我的两篇参考博客中都有例子,可以直接参考对照。另附一个博客中的例子,有图和直接的16进制数据对照:WAVE 文件格式分析 - 唐风思琪 - 博客园 (cnblogs.com)

总结

  • 如果我们想要实现一个wav文件的播放器,那么我们需要关心的chunk包括RIFFfmt data,其他的块中的内容不是必须的。
  • 这些块的结构是有规律的:4个字节的标识符以及紧接着的4个字节的chunk大小。这一规律使得我们尽管不完全知道wav的chunk种类也能找到我们需要的信息:保留自己需要的块,跳过不认识的块。
  • fmt 中包含了解析data的重要参数,我们要比较了解这一部分内容。本文也花了不少篇幅在这部分,例子中的大部分也是在分析fmt chunk。
  • 在我查找资料的过程中,我发现了这个项目:jazzlost/WavParser: Wave Formate File Parser (github.com)。这个项目用c++实现了wav的解析,可以直接运行,可以作为参考学习。

wav音频格式解析
http://hhhheying.github.io/2024/04/19/wav音频格式解析/
作者
hy
发布于
2024年4月19日
许可协议