服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++将音频PCM数据封装成wav文件的方法

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

2022-08-20 12:19Alfred-N C/C++

这篇文章主要为大家详细介绍了C++将音频PCM数据封装成wav文件的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言

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

一、如何实现?

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

1.定义头结构

只定义PCM格式的wav文件头

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

2.预留头部空间

创建文件时预留头部空间

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

3.写入PCM数据

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

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

4.写入头部信息

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

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

二、完整代码

WavWapper.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#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;
        }
    }
}

三、使用示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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头结构,然后自定义其头结构,然后再进行一定的测试,就可以实现这样一个功能。

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

原文链接:https://blog.csdn.net/u013113678/article/details/122268109

延伸 · 阅读

精彩推荐
  • C/C++C指针原理教程之垃圾回收-内存泄露

    C指针原理教程之垃圾回收-内存泄露

    C语言没有运行时库,无法自动压缩使用中的内存,缩小堆栈所需内存空间。若只申请内存,没有释放,势必造成系统内存不断减少、丢失。长时间的运行,...

    myhaspl11182021-07-21
  • C/C++在C/C++项目中合理使用宏详解

    在C/C++项目中合理使用宏详解

    这篇文章主要介绍了在C/C++项目中合理使用宏详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    Duco5192021-10-13
  • C/C++VC中使用GDI+的配置方法概述

    VC中使用GDI+的配置方法概述

    这篇文章主要介绍了VC中使用GDI+的配置方法,需要的朋友可以参考下...

    C语言程序设计7032021-01-22
  • C/C++深入分析Visual C++进行串口通信编程的详解

    深入分析Visual C++进行串口通信编程的详解

    本篇文章是对Visual C++进行串口通信编程进行了详细的分析介绍,需要的朋友参考下...

    C++教程网3582020-12-02
  • C/C++C语言实现单词助手功能

    C语言实现单词助手功能

    这篇文章主要为大家详细介绍了C语言实现单词小助手,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    ぃAnn11112021-08-04
  • C/C++C语言数据结构之迷宫问题

    C语言数据结构之迷宫问题

    这篇文章主要为大家详细介绍了C语言数据结构之迷宫问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    lear_scu9922021-07-25
  • C/C++C语言指针必备基础全面覆盖

    C语言指针必备基础全面覆盖

    数据对象是指存储在内存中的一个指定数据类型的数值或字符串,它们都有一个自己的地址,指针是保存这个地址的变量,本篇文章带你掌握C语言指针的用...

    波风张三10272022-02-15
  • C/C++vc中使用SendMessage自定义消息函数

    vc中使用SendMessage自定义消息函数

    这篇文章主要介绍了vc中使用SendMessage自定义消息函数的相关资料,需要的朋友可以参考下...

    C语言教程网7522021-02-28