如何正确读取WAV文件数据 解决RiffWav头参数读取错误问题
问题核心错误点
首先你遇到的读取错误并非是WAV数据顺序不固定,核心问题出在你自己的实现逻辑上:
- 多字节字段读取逻辑完全错误:WAV所有多字节数值都是小端字节序存储,你直接把两个字节的原始数值相加的计算方式完全错误。比如声道数是16位小端字段,你拿到的25153就是直接相加的错误结果,正确计算应该是把高字节左移8位再和低字节做或运算。
- 硬编码WAV头长度为44字节:实际WAV文件格式允许在标准块之间插入LIST、INFO等自定义元数据块,SubChunk1的长度也不一定是16,固定偏移量的读取方式遇到非标准结构的WAV必然报错。
- 直接用结构体映射头存在内存对齐风险:不同编译器的结构体成员对齐规则不同,会导致结构体成员的实际内存偏移和WAV文件的存储偏移不一致,直接读取结构体大概率会出现字段错位。
正确的扫描实现方法
你需要的通用扫描逻辑不需要依赖固定偏移,只需要按RIFF格式的块规则遍历即可:
- 先验证前12字节的RIFF头标识,确认是合法WAV文件
- 循环遍历后续所有子块:每次先读取4字节子块ID,再读取4字节子块长度
- 遇到
fmt块(注意ID末尾带空格)时,读取其中的声道数、采样率、位深等核心参数 - 遇到
data块时记录数据长度,即可停止头扫描 - 其他无关子块直接跳过对应长度即可,不影响核心参数读取
参考代码片段
// 辅助函数:读取小端16位无符号整数 unsigned short read_le16(unsigned char* buf) { return (unsigned short)buf[0] | ((unsigned short)buf[1] << 8); } // 辅助函数:读取小端32位无符号整数 unsigned int read_le32(unsigned char* buf) { return (unsigned int)buf[0] | ((unsigned int)buf[1] << 8) | ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); } /** * 扫描WAV缓冲区获取核心参数 * 返回0表示解析成功,负值表示解析失败 */ int parse_wav_header(unsigned char* buf, int buf_len, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_size) { // 验证RIFF基础头 if (buf_len < 12 || memcmp(buf, "RIFF", 4) != 0 || memcmp(buf+8, "WAVE", 4) != 0) { return -1; } int offset = 12; int fmt_found = 0; while (offset + 8 <= buf_len) { char chunk_id[5] = {0}; memcpy(chunk_id, buf + offset, 4); unsigned int chunk_size = read_le32(buf + offset + 4); offset += 8; if (strcmp(chunk_id, "fmt ") == 0) { *channels = read_le16(buf + offset + 2); *sample_rate = read_le32(buf + offset + 4); *bits_per_sample = read_le16(buf + offset + 14); fmt_found = 1; } else if (strcmp(chunk_id, "data") == 0) { *data_size = chunk_size; return fmt_found ? 0 : -2; } // 跳过当前子块的内容 offset += chunk_size; } return -3; }
内容的提问来源于stack exchange,提问作者Omar
相关产品推荐
相关产品推荐

