C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++将音频PCM数据封装成wav文件

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

作者: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头结构,然后自定义其头结构,然后再进行一定的测试,就可以实现这样一个功能。

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

您可能感兴趣的文章:
阅读全文