导读:本期聚焦于小伙伴创作的《c++如何将音频PCM数据保存为WAV文件_RIFF文件头构造》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《c++如何将音频PCM数据保存为WAV文件_RIFF文件头构造》有用,将其分享出去将是对创作者最好的鼓励。

WAV是Windows平台下常用的音频文件格式,本质上是在PCM原始音频数据前添加一段符合RIFF规范的头部信息,让播放器能够识别音频的采样率、位深、声道数等关键参数。要实现C++将PCM数据保存为WAV文件,核心是正确构造RIFF文件头,再将PCM数据追加到文件头部之后。

c++如何将音频PCM数据保存为WAV文件_RIFF文件头构造

WAV文件结构解析

WAV文件整体遵循RIFF(Resource Interchange File Format)规范,主要分为三个部分:

  • RIFF块:标识文件类型,包含文件总大小信息
  • fmt子块:存储音频格式相关的参数,比如声道数、采样率、位深等
  • data子块:存储实际的PCM音频数据

RIFF文件头字段说明

RIFF文件头的各个字段有固定的字节长度和含义,具体对应关系如下:

字段名字节数含义取值说明
ChunkID4块标识固定为"RIFF"
ChunkSize4文件总大小减8后续所有数据的总字节数
Format4文件格式固定为"WAVE"
Subchunk1ID4fmt块标识固定为"fmt "
Subchunk1Size4fmt块大小音频格式为PCM时固定为16
AudioFormat2音频格式PCM格式固定为1
NumChannels2声道数1为单声道,2为双声道
SampleRate4采样率常见值如44100、48000等
ByteRate4每秒字节数采样率*声道数*位深/8
BlockAlign2每样本块对齐大小声道数*位深/8
BitsPerSample2位深常见值如16、24等
Subchunk2ID4data块标识固定为"data"
Subchunk2Size4PCM数据大小PCM数据的总字节数

C++实现PCM转WAV的完整代码

下面给出完整的C++实现代码,包含RIFF文件头结构体定义、文件头构造、PCM数据写入等逻辑:

#include <iostream>
#include <fstream>
#include <cstring>

// RIFF文件头结构体,使用1字节对齐避免编译器自动填充字节
#pragma pack(push, 1)
struct WavHeader {
    // RIFF块
    char chunkID[4];       // "RIFF"
    uint32_t chunkSize;    // 文件总大小 - 8
    char format[4];        // "WAVE"
    // fmt子块
    char subchunk1ID[4];   // "fmt "
    uint32_t subchunk1Size;// 16(PCM格式固定值)
    uint16_t audioFormat;  // 1(PCM格式)
    uint16_t numChannels;  // 声道数
    uint32_t sampleRate;   // 采样率
    uint32_t byteRate;     // 每秒字节数
    uint16_t blockAlign;   // 每样本块对齐大小
    uint16_t bitsPerSample;// 位深
    // data子块
    char subchunk2ID[4];   // "data"
    uint32_t subchunk2Size;// PCM数据总字节数
};
#pragma pack(pop)

/**
 * 将PCM数据保存为WAV文件
 * @param pcmData PCM数据指针
 * @param pcmSize PCM数据总字节数
 * @param sampleRate 采样率
 * @param channels 声道数
 * @param bitsPerSample 位深
 * @param outputPath 输出WAV文件路径
 * @return 保存成功返回true,否则返回false
 */
bool savePcmToWav(const char* pcmData, uint32_t pcmSize, 
                  uint32_t sampleRate, uint16_t channels, 
                  uint16_t bitsPerSample, const char* outputPath) {
    // 构造WAV文件头
    WavHeader header;
    // 设置RIFF块信息
    memcpy(header.chunkID, "RIFF", 4);
    header.chunkSize = 36 + pcmSize; // 36是fmt和data块头部固定大小之和
    memcpy(header.format, "WAVE", 4);
    // 设置fmt子块信息
    memcpy(header.subchunk1ID, "fmt ", 4);
    header.subchunk1Size = 16;
    header.audioFormat = 1;
    header.numChannels = channels;
    header.sampleRate = sampleRate;
    header.byteRate = sampleRate * channels * bitsPerSample / 8;
    header.blockAlign = channels * bitsPerSample / 8;
    header.bitsPerSample = bitsPerSample;
    // 设置data子块信息
    memcpy(header.subchunk2ID, "data", 4);
    header.subchunk2Size = pcmSize;

    // 打开输出文件
    std::ofstream outFile(outputPath, std::ios::binary);
    if (!outFile.is_open()) {
        std::cerr << "无法打开输出文件: " << outputPath << std::endl;
        return false;
    }

    // 写入文件头
    outFile.write(reinterpret_cast<const char*>(&header), sizeof(WavHeader));
    // 写入PCM数据
    outFile.write(pcmData, pcmSize);
    outFile.close();
    return true;
}

int main() {
    // 构造一段示例PCM数据,模拟1秒的16位单声道44100采样率音频
    uint32_t sampleRate = 44100;
    uint16_t channels = 1;
    uint16_t bitsPerSample = 16;
    uint32_t pcmSize = sampleRate * channels * bitsPerSample / 8; // 1秒数据大小
    char* pcmData = new char[pcmSize];
    // 填充示例数据,这里用正弦波模拟音频数据
    for (uint32_t i = 0; i < pcmSize / 2; ++i) {
        // 生成440Hz的正弦波
        double value = 32767 * sin(2 * 3.1415926 * 440 * i / (double)sampleRate);
        int16_t sample = static_cast<int16_t>(value);
        memcpy(pcmData + i * 2, &sample, 2);
    }

    // 保存为WAV文件
    bool result = savePcmToWav(pcmData, pcmSize, sampleRate, channels, bitsPerSample, "output.wav");
    if (result) {
        std::cout << "WAV文件保存成功" << std::endl;
    } else {
        std::cout << "WAV文件保存失败" << std::endl;
    }

    delete[] pcmData;
    return 0;
}

注意事项

  • 结构体需要使用#pragma pack(push, 1)设置1字节对齐,避免编译器自动添加填充字节导致文件头格式错误
  • 所有多字节字段按照小端序存储,Windows平台下C++的默认字节序就是小端序,不需要额外转换,如果是跨平台开发需要注意字节序问题
  • ChunkSize字段的值是整个文件大小减去8,因为ChunkID和ChunkSize本身占用8字节,不包含在这部分大小的计算中
  • 如果PCM数据是从其他渠道获取的,需要确保PCM数据的格式参数和构造文件头时传入的参数一致,否则生成的WAV文件播放会出现杂音或者速度异常

C++PCMWAVRIFF文件头修改时间:2026-06-12 16:18:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。