如何读取字段顺序不固定的WAV文件头,解决固定结构体解析失效问题
WAV文件解析失败问题根因与修复方案
现有代码的核心问题
- 结构体内存对齐问题:C编译器默认会对结构体字段做对齐填充,直接
fread整个RiffWav结构体时,字段间的填充字节会导致读取到的数据和文件中实际的RIFF头布局错位。 - 类型长度歧义:代码中使用
long、unsigned long这类平台相关长度的类型,32位和64位系统下长度可能不一致,直接导致结构体大小、字段读取偏移错误。 - 子块顺序假设错误:符合RIFF规范的WAV文件在fmt块之后不一定直接接data块,可能插入LIST、INFO、PAD、ID3等可选元数据块,固定长度读头的逻辑无法适配这类文件。
- fmt块长度固定假设错误:
SubChunk1Size常见值为16(PCM格式),但带扩展信息的WAV(非PCM格式、多声道带通道掩码的WAV等)这个值可能为18、20、40等,固定结构体长度会导致后续字段读取偏移。 - 缓冲区申请错误:原有代码以音频帧数作为CHAR数组的申请长度,若采样位深大于8位,会出现缓冲区不足、读取越界的问题。
修复方案
1. 修正结构体定义
使用标准定长类型,同时取消结构体对齐:
#include <stdint.h> // 跨平台取消结构体对齐 #ifdef _MSC_VER #pragma pack(push, 1) #endif typedef struct { uint8_t ChunkID[4]; uint32_t ChunkSize; uint8_t Format[4]; uint8_t SubChunk1ID[4]; uint32_t SubChunk1Size; uint16_t AudioFormat; uint16_t NumChannels; uint32_t SampleRate; uint32_t BytesRate; uint16_t BlockAlign; uint16_t BitsPerSample; } __attribute__((packed)) RiffWavFmt; #ifdef _MSC_VER #pragma pack(pop) #endif
2. 动态遍历子块的读取逻辑
读完固定fmt字段后循环遍历后续子块,直到找到data块再读取音频数据:
FILE* WAVF = fopen(pParent->m_StrWavFileName, "rb"); if (!WAVF) { // 自行补充文件打开失败的错误处理 return; } RiffWavFmt fmtHdr = {0}; fread(&fmtHdr, 1, sizeof(RiffWavFmt), WAVF); // 跳过fmt块多余的扩展字节 if (fmtHdr.SubChunk1Size > 16) { fseek(WAVF, fmtHdr.SubChunk1Size - 16, SEEK_CUR); } // 循环查找data块 uint8_t subChunkId[4] = {0}; uint32_t subChunkSize = 0; while (1) { fread(subChunkId, 1, 4, WAVF); fread(&subChunkSize, 1, 4, WAVF); // 匹配到data块退出循环 if (subChunkId[0] == 'd' && subChunkId[1] == 'a' && subChunkId[2] == 't' && subChunkId[3] == 'a') { break; } // 非data块直接跳过 fseek(WAVF, subChunkSize, SEEK_CUR); } // 读取音频数据 LONGLONG frameCount = subChunkSize / (fmtHdr.NumChannels * (fmtHdr.BitsPerSample / 8)); uint8_t* audioBuf = new uint8_t[subChunkSize]; fread(audioBuf, 1, subChunkSize, WAVF); // 后续自行补充资源释放、文件关闭逻辑
内容的提问来源于stack exchange,提问作者user16395253
相关产品推荐
相关产品推荐

