C++将音频PCM数据封装成wav文件的方法

 更新时间:2022年01月13日 13:31:36   作者:Alfred-N  
这篇文章主要为大家详细介绍了C++将音频PCM数据封装成wav文件的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言

使用声音设备采集的声音数据通常是PCM数据,直接写入文件是无法播放的,通常的做法是将其封装成wav格式,这样播放器就能够识别且播放了。本文将介绍如何将PCM封装成wav的方法。

一、如何实现?

首先需要构造wav头部,wav文件音频信息全部保存在头部,我们要做的就是在PCM数据的前面加入wav头,并且记录PCM的相关参数。

1.定义头结构

只定义PCM格式的wav文件头

//WAV头部结构-PCM格式
struct WavPCMFileHeader;

2.预留头部空间

创建文件时预留头部空间

FILE*f = fopen(fileName.c_str(), "wb+");
//预留头部位置
fseek(f, sizeof(WavPCMFileHeader), SEEK_SET);

3.写入PCM数据

写入数据,并记录数据总长度。

fwrite(data, 1, dataLength, f);
//录数据总长度
_totalDataLength += dataLength;

4.写入头部信息

关闭文件时,回到起始位置写入头部信息

//写入头部信息
fseek(f, 0, SEEK_SET);
WavPCMFileHeader h(_channels, _sampleRate, _bitsPerSample, _totalDataLength);
fwrite(&h, 1, sizeof(h), f);
fclose(f);

二、完整代码

WavWapper.h

#pragma once
#include<string>
namespace AC {
    class  WavWapper {
    public:
        WavWapper();
        ~WavWapper();
        /// <summary>
        /// 创建wav文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="channels">声道数</param>
        /// <param name="sampleRate">采样率,单位hz</param>
        /// <param name="bitsPerSample">位深</param>
        void CreateWavFile(const std::string &fileName, int channels, int  sampleRate, int  bitsPerSample);
        /// <summary>
        /// 写入PCM数据
        /// </summary>
        /// <param name="data">PCM数据</param>
        /// <param name="dataLength">数据长度</param>
        void WriteToFile(unsigned char* data, int dataLength);
        /// <summary>
        /// 关闭文件
        /// </summary>
        void CloseFile();
    private:
        void* _file=nullptr;
        uint32_t _totalDataLength=0;
    };
}

WavWapper.cpp

#include"WavWapper.h"
#include<stdio.h>
namespace AC {
    //WAV头部结构-PCM格式
    struct WavPCMFileHeader
    {
        struct RIFF {
            const    char rift[4] = { 'R','I', 'F', 'F' };
            uint32_t fileLength;
            const    char wave[4] = { 'W','A', 'V', 'E' };
        }riff;
        struct Format
        {
            const    char fmt[4] = { 'f','m', 't', ' ' };
            uint32_t blockSize = 16;
            uint16_t formatTag;
            uint16_t channels;
            uint32_t samplesPerSec;
            uint32_t avgBytesPerSec;
            uint16_t blockAlign;
            uint16_t  bitsPerSample;
        }format;
        struct  Data
        {
            const    char data[4] = { 'd','a', 't', 'a' };
            uint32_t dataLength;
        }data;
        WavPCMFileHeader() {}
        WavPCMFileHeader(int nCh, int  nSampleRate, int  bitsPerSample, int dataSize) {
            riff.fileLength = 36 + dataSize;
            format.formatTag = 1;
            format.channels = nCh;
            format.samplesPerSec = nSampleRate;
            format.avgBytesPerSec = nSampleRate * nCh * bitsPerSample / 8;
            format.blockAlign = nCh * bitsPerSample / 8;
            format.bitsPerSample = bitsPerSample;
            data.dataLength = dataSize;
        }
    };
    WavWapper::WavWapper()
    {
    }

    WavWapper::~WavWapper()
    {
        CloseFile();
    }
    int _channels;
    int _sampleRate;
    int _bitsPerSample;
    void WavWapper::CreateWavFile(const std::string& fileName, int channels, int sampleRate, int bitsPerSample)
    {
        if (!_file)
        {
            _channels = channels;
            _sampleRate = sampleRate;
            _bitsPerSample = bitsPerSample;
            _totalDataLength = 0;
            _file = fopen(fileName.c_str(), "wb+");
            //预留头部位置
            fseek(static_cast<FILE*>(_file), sizeof(WavPCMFileHeader), SEEK_SET);
        }
    }
    void WavWapper::WriteToFile(unsigned char* data, int dataLength)
    {
        fwrite(data, 1, dataLength, static_cast<FILE*>(_file));
        _totalDataLength += dataLength;
    }
    void WavWapper::CloseFile()
    {
        if (_file)
        {
            if (_totalDataLength > 0)
            {
                //写入头部信息
                fseek(static_cast<FILE*>(_file), 0, SEEK_SET);
                WavPCMFileHeader h(_channels, _sampleRate, _bitsPerSample, _totalDataLength);
                fwrite(&h, 1, sizeof(h), static_cast<FILE*>(_file));
            }
            fclose(static_cast<FILE*>(_file));
            _file = nullptr;
        }
    }
}

三、使用示例

#include "WavWapper.h"
#include<Windows.h>
int main()
{    
    AC::WavWapper ww;
    //创建wav文件,确保pcm声音格式与参数一致
    ww.CreateWavFile("sound.wav",2, 44100, 16);
    while (flag)
    {
        //获取PCM数据
        //略
        //获取PCM数据-end
        //写入PCM数据
        ww.WriteToFile(data, dataLength);
    }
    //关闭文件
    ww.CloseFile();    
}

总结

以上就是今天要讲的内容,PCM封装成wav还是相对较简单的,只要了解wav头结构,然后自定义其头结构,然后再进行一定的测试,就可以实现这样一个功能。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C语言判定一棵二叉树是否为二叉搜索树的方法分析

    C语言判定一棵二叉树是否为二叉搜索树的方法分析

    这篇文章主要介绍了C语言判定一棵二叉树是否为二叉搜索树的方法,结合实例形式综合对比分析了C语言针对二叉搜索树判定的原理、算法、效率及相关实现技巧,需要的朋友可以参考下
    2018-08-08
  • C语言中堆空间的生成与释放详解

    C语言中堆空间的生成与释放详解

    以下是对C语言中堆空间的生成与释放进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-08-08
  • C语言进阶之字符串查找库函数详解

    C语言进阶之字符串查找库函数详解

    字符串是一种非常重要的数据类型,但是C语言不存在显式的字符串类型,C语言中的字符串都以字符串常量的形式出现或存储在字符数组中,下面这篇文章主要给大家介绍了关于C语言进阶之字符串查找库函数的相关资料,需要的朋友可以参考下
    2023-01-01
  • C语言约瑟夫环的实现

    C语言约瑟夫环的实现

    这篇文章主要介绍了C语言约瑟夫环的实现的相关资料,这里主要是利用数据数据结果中循环链表来实现,需要的朋友可以参考下
    2017-08-08
  • OpenCV图像处理基本操作详解

    OpenCV图像处理基本操作详解

    这篇文章主要为大家详细介绍了OpenCV图像处理基本操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-03-03
  • C++使用htslib库读入和写出bam文件的实例

    C++使用htslib库读入和写出bam文件的实例

    下面小编就为大家分享一篇C++使用htslib库读入和写出bam文件的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • 详解c++ libuv工作队列

    详解c++ libuv工作队列

    这篇文章主要介绍了c++ libuv工作队列的相关资料,帮助大家更好的理解和使用libuv,感兴趣的朋友可以了解下
    2021-02-02
  • C语言 详细讲解接续符和转义符的使用

    C语言 详细讲解接续符和转义符的使用

    接续符是用来告诉编译器行为的符号,那编译器遇到接续符是什么行为呢,就是去掉接续符,然后把下一行连接到现在这行上面,转义符是主要用于表示无回显字符,也用于表示常规字符,转义符必须放在单引号或者双引号里面
    2022-04-04
  • linux 匿名管道实例详解

    linux 匿名管道实例详解

    这篇文章主要介绍了linux 匿名管道实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • C++ LeetCode1781题解所有子字符串美丽值之和

    C++ LeetCode1781题解所有子字符串美丽值之和

    这篇文章主要为大家介绍了C++ LeetCode1781题解所有子字符串美丽值之和,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12

最新评论