You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:15:47