构建wav文件
这套用来构建一个wav文件,主要用接受传过来的音频数据并将其保存成音频文件,为了保证保证即使接受任意大小的音频数据也能构建出一个wav文件,所以在收到一个新的音频文件时,会构建一个音频数据并保存为wav文件,后面收到的会进行追加,这样即使发送方突然停止发送或突然停止保存或者当前程序崩溃,也会记录先前的数据音频数据不会丢失。
用于保存wav文件的类,他接受一个 WAV_INFO 结构体的数据,会一直保存。
WAV_INFO:
#pragma pack(1)
#define PLAYBACK_PCM 1 //1为PCM编码
#define PLAYBACK_BITSPER_SAMPLE 16 //数据位数
#define BUFF_SIZE 4096
#define FILE_PATH_SIZE 1024
typedef struct _WAV_INFO
{
wchar_t szFileName[1024];
long long iStartDeduceTime; //开始推演的推演时间戳
long long iEndDeduceTime; //推演结束的时间戳
unsigned short nChannleNumber; //通道数
unsigned long nSampleRate; //采样率
unsigned short nAudioFormat; //编码格式 1为PCM编码
unsigned short nBitsPerSample; //数据位数
int iBuffSize;
char szBuff[BUFF_SIZE];
_WAV_INFO ()
{
nChannleNumber = 0;
nSampleRate = 0;
nAudioFormat = PLAYBACK_PCM;
nBitsPerSample = PLAYBACK_BITSPER_SAMPLE;
iStartDeduceTime = 0;
iEndDeduceTime = 0;
bState = true;
memset(szBuff, 0, sizeof(BUFF_SIZE));
memset(szFileName, 0, sizeof(FILE_PATH_SIZE));
}
}WAV_INFO, *PWAV_INFO;
#pragma pack()
PlayBackWAVInfo.h文件:
#ifndef SOURCESPPLAYBACKINFO_H
#define SOURCESPPLAYBACKINFO_H
#include <map>
#include <QString>
#include <QFileInfo>
#include <QDataStream>
#include <QAudioFormat>
#include <qdebug.h>
#include <mutex>
#include <tuple>
#include <QDateTime>
#include "wavdeal.h"
#include <QReadWriteLock>
#define PLAYBACK_PCM 1 //1为PCM编码
#define PLAYBACK_BITSPER_SAMPLE 16 //数据位数
struct WavFileHeader
{
//RIFF头
char RiffName[4];
unsigned long nRiffLength; //存的是后面所有文件的大小; //存的是fmt保存的大小,包含这之后,data前面几个,共16个;
//数据类型标识符
char wavname[4];
//格式块中的块头
char fmtName[4];
unsigned long nFmtLength;
//格式块中的块数据
unsigned short nAudioFormat; //编码格式 1为PCM编码
unsigned short nChannleNumber; //通道数
unsigned long nSampleRate; //采样率
unsigned long nBytesPerSecond; //传输速率 = 采样率*通道数*样本数据位数/8
unsigned short nBytesPerSample; //数据块对齐单位 传输速率/采样率
unsigned short nBitsPerSample; //数据位数
//数据块
char chDATA[4];
unsigned long nDATALen; //数据的长度;
};
//读音频文件使用
struct WavFileHeader_OlD
{
//RIFF头
char RiffName[4];
unsigned long nRiffLength; //存的是后面所有文件的大小;
//数据类型标识符
char wavname[4];
//格式块中的块头
char fmtName[4];
unsigned long nFmtLength;
//格式块中的块数据
unsigned short nAudioFormat; //编码格式 1为PCM编码
unsigned short nChannleNumber; //通道数
unsigned long nSampleRate; //采样率
unsigned long nBytesPerSecond; //传输速率 = 采样率*通道数*样本数据位数/8
unsigned short nBytesPerSample; //数据块对齐单位 传输速率/采样率
unsigned short nBitsPerSample; //数据位数
};
struct extInfo{
//扩展信息
unsigned short nAppendMessage; //扩展域大小
char* AppendMessageData; //扩展信息域数据
};
struct FactInfo{
//Fact块,可选字段,一般当wav文件由某些软件转化而成,则包含块状和扩展信息
char factName[4];
unsigned long nFactLength;
char FactData[4];
//数据块中的块头
char DataName[4];
unsigned long nDataLength;
};
typedef struct _PLAYBACK_INFO
{
long long iStartTime; //记录开始时间戳(天文时间)
long long iCurTime; //当前写入的时间戳
long long iStartDeduceTime; //开始推演时间
unsigned short nChannleNumber; //通道数
unsigned long nSampleRate; //采样率
unsigned short nAudioFormat; //编码格式 1为PCM编码
unsigned short nBitsPerSample; //数据位数
QString strFileName; //文件名
QString strFilePath; //文件路径
_PLAYBACK_INFO()
{
iStartTime = 0;
iStartDeduceTime = 0;
}
} PLAYBACK_INFO, *PPLAYBACK_INFO;
class PlayBackWAVInfo
{
public:
PlayBackWAVInfo();
virtual ~PlayBackWAVInfo();
public:
//设置保存文件路径
void SetSaveFilePath(const QString &strFilePath);
//获取保存文件路径
QString GetSaveFilePath();
//保存
bool SavePlayBack(const WAV_INFO &wavInfo);
//关闭
bool ClosePlayBack(const Qstring &strName);
//读取音频数据,用于测试`
QByteArray ReadWavFile(const QString &strFilePath);
//关闭保存所有文件
void CloseAll();
private:
//创建音频文件
bool CleartWavFile(PPLAYBACK_INFO pPlayBackInfo);
//增加写入音频数据
bool WriteWavFile(PPLAYBACK_INFO pPlayBackInfo, QByteArray value);
//判断文件是否存在
bool isFileExist(const QString &strFilePath);
//初始化数据
void InitData(PPLAYBACK_INFO pPlayBackInfo, const WAV_INFO &wavInfo);
private:
//文件名称
std::map<QString, PPLAYBACK_INFO> m_mapPlayBackInfo;
//保存文件位置
QString m_strSaveMavFilePath;
//用于测试读取的音频
QByteArray m_musicDataArray;
//读写锁
QReadWriteLock m_readWriteLock;
};
#endif // SOURCESPPLAYBACKINFO_H
PlayBackWAVInfo.cpp文件:
#include "SourcesPlayBackInfo.h"
#include <QCoreApplication>
PlayBackWAVInfo::PlayBackWAVInfo()
{
m_mapPlayBackInfo.clear();
}
PlayBackWAVInfo::~PlayBackWAVInfo()
{
CloseAll();
}
//设置保存文件路径
void PlayBackWAVInfo::SetSaveFilePath(const QString &strFilePath)
{
m_strSaveMavFilePath = strFilePath;
}
QString PlayBackWAVInfo::GetSaveFilePath()
{
return m_strSaveMavFilePath;
}
bool PlayBackWAVInfo::SavePlayBack(const WAV_INFO &wavInfo)
{
PPLAYBACK_INFO pPlayBackInfo = nullptr;
QString strWavName = wavInfo.szFileName;
if (m_mapPlayBackInfo.count(strWavName) == 1)
{
//已经存在了直接写入
pPlayBackInfo = m_mapPlayBackInfo[strWavName];
}
else
{
//不存在创建一个
pPlayBackInfo = new PLAYBACK_INFO;
m_mapPlayBackInfo.insert(std::pair<QString, PPLAYBACK_INFO>(tuplePlayBack, pPlayBackInfo));
InitData(pPlayBackInfo, wavInfo);
CleartWavFile(pPlayBackInfo);
}
//写入文件
if (pPlayBackInfo != nullptr)
{
QByteArray value;
value.resize(wavInfo.iBuffSize);
memcpy(value.data(), wavInfo.szBuff, wavInfo.iBuffSize);
//写入数据
WriteWavFile(pPlayBackInfo, value);
//更新天文时间戳
QDateTime currentTime = QDateTime::currentDateTime();
pPlayBackInfo->iCurTime = currentTime.toMSecsSinceEpoch();
}
return true;
}
void PlayBackWAVInfo::InitData(PPLAYBACK_INFO pPlayBackInfo, const WAV_INFO &wavInfo)
{
pPlayBackInfo->iStartDeduceTime = wavInfo.iStartDeduceTime; //推演时间戳
pPlayBackInfo->nChannleNumber = wavInfo.nChannleNumber; //通道数
pPlayBackInfo->nSampleRate = wavInfo.nSampleRate; //采样率
pPlayBackInfo->nAudioFormat = wavInfo.nAudioFormat; //编码格式
pPlayBackInfo->nBitsPerSample = wavInfo.nBitsPerSample; //数据位数
pPlayBackInfo->strFileName = wavInfo.szFileName;
//更新天文时间戳
QDateTime currentTime = QDateTime::currentDateTime();
pPlayBackInfo->iStartTime = currentTime.toSecsSinceEpoch();
}
bool PlayBackWAVInfo::isFileExist(const QString &strFilePath)
{
QFileInfo fileInfo(strFilePath);
return fileInfo.isFile();
}
bool PlayBackWAVInfo::CleartWavFile(PPLAYBACK_INFO pPlayBackInfo)
{
//构建文件名
QString strFileName = QString(".wav").arg(pPlayBackInfo->strFileName);
QString strFilePath = m_strSaveMavFilePath + "\\" + strFileName;
pPlayBackInfo->strFilePath = strFilePath;
//打开文件
QFile pFile(strFilePath);
if (!pFile.open(QFile::ReadWrite))
{
return false;
}
//构建WAV文件
WavFileHeader wavFileInfo;
//RIFF标识头
wavFileInfo.RiffName[0] = 'R';
wavFileInfo.RiffName[1] = 'I';
wavFileInfo.RiffName[2] = 'F';
wavFileInfo.RiffName[3] = 'F';
//整个wav文件大小 - sizeof(RiffName + nRiffLength) 大小
wavFileInfo.nRiffLength = sizeof(WavFileHeader) - sizeof(unsigned long);
//wav 标识
wavFileInfo.wavname[0] = 'W';
wavFileInfo.wavname[1] = 'A';
wavFileInfo.wavname[2] = 'V';
wavFileInfo.wavname[3] = 'E';
//fmt
wavFileInfo.fmtName[0] = 'f';
wavFileInfo.fmtName[1] = 'm';
wavFileInfo.fmtName[2] = 't';
wavFileInfo.fmtName[3] = ' ';
wavFileInfo.nFmtLength = 0x0010; //一般情况下Size为16,如果为18则最后多了2个字节的附加信息
//格式块数据
wavFileInfo.nAudioFormat = pPlayBackInfo->nAudioFormat; //编码格式 1为PCM编码
wavFileInfo.nChannleNumber = pPlayBackInfo->nChannleNumber; //通道数
wavFileInfo.nSampleRate = pPlayBackInfo->nSampleRate; //采样率
wavFileInfo.nBitsPerSample = pPlayBackInfo->nBitsPerSample; //数据位数
//传输速率
wavFileInfo.nBytesPerSecond = (wavFileInfo.nSampleRate * wavFileInfo.nChannleNumber * PLAYBACK_BITSPER_SAMPLE) / 8;
//数据块对齐单位
wavFileInfo.nBytesPerSample = wavFileInfo.nBytesPerSecond / wavFileInfo.nSampleRate;
//数据块
wavFileInfo.chDATA[0] = 'd';
wavFileInfo.chDATA[1] = 'a';
wavFileInfo.chDATA[2] = 't';
wavFileInfo.chDATA[3] = 'a';
wavFileInfo.nDATALen = 0; //文件总长度
//保存到文件中
pFile.seek(0);
//将文件头保存到文件中
QByteArray datagram;
datagram.resize(sizeof(WavFileHeader));
memcpy(datagram.data(), &wavFileInfo, sizeof(WavFileHeader));
pFile.write(datagram);
//关闭文件
pFile.close();
return true;
}
bool PlayBackWAVInfo::WriteWavFile(PPLAYBACK_INFO pPlayBackInfo, QByteArray value)
{
//打开文件
QFile pFile(pPlayBackInfo->strFilePath);
if (!pFile.open(QFile::ReadWrite))
{
return false;
}
//获取文件总大小
unsigned long iFileSize = pFile.size();
WavFileHeader wavFileInfo;
//先读取结构体数据
pFile.seek(0); //设置文件位置
pFile.read((char*)(&wavFileInfo), sizeof(WavFileHeader));
//修改数据长度
wavFileInfo.nRiffLength += value.length();
wavFileInfo.nDATALen += value.length();
//覆盖数据
pFile.seek(0); //设置文件位置
QByteArray datagram;
datagram.resize(sizeof(WavFileHeader));
memcpy(datagram.data(), &wavFileInfo, sizeof(WavFileHeader));
pFile.write(datagram);
//写入数据
pFile.seek(pFile.size()); //设置到文件末尾
pFile.write(value);
//关闭文件
pFile.close();
}
//关闭
bool PlayBackWAVInfo::ClosePlayBack(const QString& strName)
{
if (m_mapPlayBackInfo.count(strName) == 1)
{
PPLAYBACK_INFO pPlayBack = m_mapPlayBackInfo[strName];
if (pPlayBack != nullptr)
{
//清空
m_mapPlayBackInfo.erase(m_mapPlayBackInfo.find(strName));
delete pPlayBack;
}
}
}
void PlayBackWAVInfo::CloseAll()
{
if (m_mapPlayBackInfo.empty())
{
return;
}
std::map<QString, PPLAYBACK_INFO>::iterator iter = m_mapPlayBackInfo.begin();
while (iter != m_mapPlayBackInfo.end())
{
PPLAYBACK_INFO pPlayBack = iter->second;
iter = m_mapPlayBackInfo.erase(iter);
delete pPlayBack;
}
}
//读取音频数据,用于测试
QByteArray PlayBackWAVInfo::ReadWavFile(const QString &strFilePath)
{
// QString fileName = QFileDialog::getOpenFileName(nullptr,"选择文件");
QString fileName = strFilePath; //"E://【阵元域数据】//核潜艇-柱晶级-SU05-110转-7叶.wav";
QAudioFormat format;
unsigned long totalLength = 0;
QByteArray soundDataArray;
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly))
{
qDebug()<<"文件打开失败";
}
//音频的所有数据
m_musicDataArray = file.readAll();
//获取wav数据内容
WavFileHeader_OlD wavFile;
memcpy(&wavFile, m_musicDataArray, sizeof (WavFileHeader));
if(wavFile.nAudioFormat == 1)
{
format.setCodec("audio/pcm");
qDebug()<<"PCM编码";
}
format.setSampleRate(wavFile.nSampleRate);
format.setChannelCount(wavFile.nChannleNumber);
format.setSampleSize(wavFile.nBitsPerSample);
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
// format.setSampleType(QAudioFormat::UnSignedInt);
int soundFs = wavFile.nSampleRate;
int extLength = 0;
if(wavFile.nFmtLength == 16)
{
qDebug()<<"没有扩展信息";
}
FactInfo factInfo;
memcpy(&factInfo,m_musicDataArray.mid(sizeof (WavFileHeader)+extLength),sizeof (FactInfo));
QString tempName = factInfo.factName;
tempName.resize(4);
qDebug()<<tempName;
qDebug()<<"数据长度:"<<factInfo.nFactLength;
totalLength = factInfo.nFactLength;
//截取音频文件
soundDataArray = m_musicDataArray.mid(sizeof (WavFileHeader) + extLength + sizeof (FactInfo));
qDebug()<<"音频文件数据长度:"<<soundDataArray.length();
return soundDataArray;
}
用法:
int main(int argc, char *argv[])
{
PlayBackWAVInfo PlayBackInfo;
QString strSavePath;
//设置保存文件路径
PlayBackInfo.SetSaveFilePath(strSavePath);
//保存音频数据,伪代码
while (true)
{
WAV_INFO wavInfo; //音频数据
PlayBackInfo.SavePlayBack(wavInfo);
}
return 0;
}