C++如何将.wav文件subchunk2的音频数据存储到数组
WAV音频数据读取存入数组实现方案
你现有代码存在几处会导致读取失败的关键问题,以下是修正后的完整实现和说明。
现有代码核心错误
- 结构体未做内存对齐:C/C++默认会对结构体成员做字节对齐,你定义的头结构体内存布局和WAV标准规定的44字节头不匹配,直接
fread会导致所有头字段读错位 - 字段含义理解错误:
subchunk2_size存储的是音频数据块的总字节数,不是样本总数。16位PCM音频单个样本占2字节,实际总样本数需要按位深换算 - 读取逻辑不完整:现有循环仅把数据读到临时缓冲区,没有写入你申请的目标数组,也没有做音频格式常规的整数到浮点的归一化转换
- 缺少格式校验:没有校验文件合法性和编码格式,遇到非PCM、非16位的WAV文件会直接读错
修正后实现代码
首先修正WAV头结构体定义,强制1字节对齐保证内存布局和标准一致:
// 强制1字节对齐,消除编译器自动插入的填充字节 #pragma pack(push, 1) typedef struct WavHeader { char chunk_id[4]; int chunk_size; char format[4]; char subchunk1_id[4]; int subchunk1_size; short audio_format; short num_channels; int sample_rate; int byte_rate; short block_align; short bits_per_sample; char subchunk2_id[4]; int subchunk2_size; } WavHeader; #pragma pack(pop)
完整读取逻辑:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; int main() { const int FRAME_SIZE = 512; FILE* infile = fopen("E:/fCWT-main/MATLAB/1s_speech.wav", "rb"); if (!infile) { cout << "打开WAV文件失败" << endl; return -1; } WavHeader meta; fread(&meta, 1, sizeof(WavHeader), infile); // 基础合法性校验 if (memcmp(meta.chunk_id, "RIFF", 4) != 0 || memcmp(meta.format, "WAVE", 4) != 0) { cout << "输入不是合法WAV文件" << endl; fclose(infile); return -1; } if (meta.audio_format != 1) { cout << "仅支持PCM编码WAV,其他编码请先转码" << endl; fclose(infile); return -1; } if (meta.bits_per_sample != 16) { cout << "示例代码适配16位位深,其他位深请修改样本转换逻辑" << endl; fclose(infile); return -1; } // 计算实际样本参数 int total_audio_bytes = meta.subchunk2_size; int bytes_per_sample = meta.bits_per_sample / 8; int total_samples = total_audio_bytes / bytes_per_sample; // 申请浮点数组存储音频,归一化到[-1,1]区间(音频处理通用输入格式) float* audio_data = (float*)malloc(sizeof(float) * total_samples); if (!audio_data) { cout << "内存申请失败" << endl; fclose(infile); return -1; } short buff16[FRAME_SIZE]; int read_total_bytes = 0; int write_pos = 0; int frame_count = 0; while (read_total_bytes < total_audio_bytes) { // 计算本次需要读取的字节数,避免最后一帧多读 int to_read = min((int)(FRAME_SIZE * sizeof(short)), total_audio_bytes - read_total_bytes); int nb = fread(buff16, 1, to_read, infile); if (nb == 0) break; int read_sample_count = nb / sizeof(short); // 16位整数转归一化浮点存入目标数组 for (int i = 0; i < read_sample_count; i++) { audio_data[write_pos + i] = buff16[i] / 32768.0f; } write_pos += read_sample_count; read_total_bytes += nb; frame_count++; } cout << "读取完成:" << endl; cout << "采样率:" << meta.sample_rate << "Hz" << endl; cout << "声道数:" << meta.num_channels << endl; cout << "总样本数:" << total_samples << endl; cout << "按" << FRAME_SIZE << "采样/帧划分,总帧数:" << frame_count << endl; // 后续直接使用audio_data数组做音频处理即可 // 自定义处理逻辑写在这里 // 资源释放 free(audio_data); fclose(infile); return 0; }
补充说明
- 双声道及多声道音频读取后,样本按声道顺序交替存储(如双声道为左、右、左、右...),按需做声道分离即可
- 若需要处理8位、24位、32位位深的音频,只需修改样本读取和转换的对应逻辑,头读取和整体流程不变
- 不要使用
while(!feof(infile))做循环判断,该标志只有在读取操作触碰到文件尾后才会置位,会导致最后多读一次无效数据
内容的提问来源于stack exchange,提问作者AMISHI VIJAY
相关产品推荐
相关产品推荐

