使用声音设备采集的声音数据通常是PCM数据,直接写入文件是无法播放的,通常的做法是将其封装成wav格式,这样播放器就能够识别且播放了。本文将介绍如何将PCM封装成wav的方法。
首先需要构造wav头部,wav文件音频信息全部保存在头部,我们要做的就是在PCM数据的前面加入wav头,并且记录PCM的相关参数。
只定义PCM格式的wav文件头
//WAV头部结构-PCM格式
struct WavPCMFileHeader;
创建文件时预留头部空间
FILE*f = fopen(fileName.c_str(), "wb+");
//预留头部位置
fseek(f, sizeof(WavPCMFileHeader), SEEK_SET);
写入数据,并记录数据总长度。
fwrite(data, 1, dataLength, f);
//录数据总长度
_totalDataLength += dataLength;
关闭文件时,回到起始位置写入头部信息
//写入头部信息
fseek(f, 0, SEEK_SET);
WavPCMFileHeader h(_channels, _sampleRate, _bitsPerSample, _totalDataLength);
fwrite(&h, 1, sizeof(h), f);
fclose(f);
WavWapper.h
#pragma once
#include
namespace AC {
class WavWapper {
public:
WavWapper();
~WavWapper();
///
/// 创建wav文件
///
/// 文件名
/// 声道数
/// 采样率,单位hz
/// 位深
void CreateWavFile(const std::string &fileName, int channels, int sampleRate, int bitsPerSample);
///
/// 写入PCM数据
///
/// PCM数据
/// 数据长度
void WriteToFile(unsigned char* data, int dataLength);
///
/// 关闭文件
///
void CloseFile();
private:
void* _file=nullptr;
uint32_t _totalDataLength=0;
int _channels;
int _sampleRate;
int _bitsPerSample;
};
}
WavWapper.cpp
#include"WavWapper.h"
#include
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();
}
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
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头结构,然后自定义其头结构,然后再进行一定的测试,就可以实现这样一个功能。