wav文件头需逐字节解析:先校验“riff”4字节,再读取文件大小(总长减8),接着验证“wave”4字节;之后循环查找“fmt ”块(末尾有空格),从中提取采样率、位深、声道数等pcm参数;最后遍历块结构定位“data”块及音频数据长度,注意小端序和声道交错。

WAV文件头结构怎么读取和校验
WAV文件是RIFF容器格式,开头固定有12字节的RIFF头,必须严格按字节顺序解析,不能依赖结构体直接memcpy——因为编译器可能插入填充字节,导致字段错位。
正确做法是逐字段读取并手动赋值:
- 先读4字节校验
"RIFF",不匹配直接返回错误 - 接着读4字节文件总大小(
dwFileSize),注意这是整个文件长度减8,即file_size - 8 - 再读4字节格式标识,必须是
"WAVE" - 然后定位到
"fmt "子块:跳过当前块后,循环查找下一个4字节块ID,直到遇到"fmt "(注意末尾空格)
常见坑:Windows下用fread读二进制时忘记设rb模式,或用std::ifstream没调binary标志,导致换行符被误转,头数据全乱。
如何提取PCM采样参数(采样率、位深、声道数)
"fmt "块前4字节是子块大小(dwFmtSize),对标准PCM是16,但必须校验——有些录音设备会写18(含额外字段),这时要跳过扩展区再读"data"块。
关键字段在"fmt "块内偏移6开始:
-
wFormatTag:必须为1(WAVE_FORMAT_PCM),否则不是线性PCM -
wChannels:声道数,1或2最常见,但可能是6(5.1环绕) -
dwSamplesPerSec:采样率,常见44100/48000,但需按实际读取,别硬编码 -
wBitsPerSample:位深度,16最常见,但也可能是8、24、32;注意24位PCM在WAV中通常按3字节打包,不是int24类型 -
dwAvgBytesPerSec:可用来反推帧长,但建议用wChannels * wBitsPerSample / 8算每帧字节数更可靠
读取时务必用uint16_t/uint32_t等确定宽度的类型,避免short在不同平台大小不一致。
data块起始位置和音频数据长度怎么安全定位
WAV文件可能包含多个子块(如"LIST"、"cue "),不能假设"fmt "后紧跟"data"。必须从RIFF头起逐块遍历:
- 每个块结构:4字节ID + 4字节大小(
dwChunkSize)+dwChunkSize字节数据 - 读完
"fmt "块后,跳过其dwChunkSize字节,再读下一个4字节ID - 找到
"data"后,其后4字节就是音频数据长度(dwDataSize),不是文件剩余长度
容易踩的坑:dwDataSize是样本字节数,不是样本数;例如16位单声道,每样本2字节,真实样本数要除以2。另外,某些设备导出的WAV会在"data"前加"fact"块,必须跳过。
如何把原始字节转成可用的PCM样本数组
WAV的PCM数据是小端序(little-endian),且无符号8位(uint8_t)和有符号16/24/32位(int16_t/int32_t)混用,必须按wBitsPerSample分支处理:
- 8位:每个字节减128,转为
int16_t再归一化(-128~127 → -32768~32767) - 16位:直接
reinterpret_cast为int16_t*,但要注意内存对齐——若data指针地址%2≠0,需逐字节读取再合成 - 24位:每3字节一组,按小端拼成
int32_t,再右移8位(保留高24位符号) - 32位:直接转
int32_t*,但确认是PCM而非IEEE float(wFormatTag为3才是float)
最易忽略的是声道交错(interleaved):双声道数据是LRLRLR排列,不是LLRR。解交错必须按wChannels拆分,不能只看总长度。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











