因工程需要,需要截取一段PCM数据将其保存为WAV文件。网上代码很多,这里仅记录一下,方便自己以后查阅。
关于WAV文件格式不多余赘述这里直接上一张图:
图片来源:https://www.cnblogs.com/danju/p/3716194.html
下面附代码:
结构体声明文件:
#ifndef _CWAVEDEF_H
#define _CWAVEDEF_H
#include
//RIFF头的宏定义缩写,便于后面的比较 低字节-》高字节读取时比较使用
#define ID_RIFF mmioFOURCC('R', 'I', 'F', 'F')
#define ID_WAVE mmioFOURCC('W', 'A', 'V', 'E')
#define ID_DATA mmioFOURCC('d', 'a', 't', 'a')
#define ID_FMT mmioFOURCC('f', 'm', 't', '\x20')
#define ID_FACT mmioFOURCC('f', 'a', 'c', 't')
//WAVE文件一般有四种块,它们依次是:RIFF块、格式块、附加块(可选),数据块
struct RIFFFORMAT{//长度12
unsigned char Ckid[4]; //RIFF标识
unsigned long FileSize; //文件大小
unsigned char FccType[4]; //WAVE标志
};
//WAV格式块
struct WAVE_FORMAT
{
unsigned char Ckid[4]; //fmt
unsigned long CkLen; //块大小 16
unsigned short wFormatTag;//音频格式一般为WAVE_FORMAT_PCM
unsigned short nChannels;//采样声道数
unsigned long nSamplesPerSec;//采样率
unsigned long nAvgBytesPerSec;//每秒字节数 采样率*采样精度/8(字节位数)
unsigned short nBlockAlign;//块大小 采样字节*声道数
unsigned short wBitsPerSample;//采样精度 采样精度/8 = 采样字节
//unsigned short cbSize; //预留字节 一般为0扩展域有的没有
};
//wave数据块
struct WAVE_DATA{
unsigned char Wdid[4]; //data 标志
unsigned long WdSize; //块大小
//unsigned char* Wdbuf; //数据指针 有符号
};
#endif
保存文件:
void CycQueue::Save() //保存文件
{
time_t t;
struct tm * lt;
time (&t);//获取Unix时间戳。
lt = localtime (&t);//转为时间结构
char buf[30];
sprintf (buf,"../%4d%02d%02d%02d%02d%02d.wav",lt->tm_year+1900, lt->tm_mon+1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
fw = fopen(buf,"wb+");
Addhead();
return;
}
void CycQueue::Addhead()
{
RIFFFORMAT head;//RIFF块
memcpy(head.Ckid,"RIFF",4);
head.FileSize = m_writeindex+36;
memcpy(head.FccType,"WAVE",4);
fwrite(&head,1,sizeof(RIFFFORMAT),fw); //写入文件
WAVE_FORMAT fmt;//格式块
memcpy(fmt.Ckid,"fmt ",4);
fmt.CkLen = 16; //块大小 16
fmt.wFormatTag = WAVE_FORMAT_PCM;//音频格式一般为WAVE_FORMAT_PCM
fmt.nChannels = 1; //采样声道数
fmt.nSamplesPerSec = 44100; //采样率
fmt.nAvgBytesPerSec = 88200; //每秒字节数 采样率*采样精度/8(字节位数)
fmt.nBlockAlign = 2; //块大小 采样字节*声道数
fmt.wBitsPerSample = 16; //采样深度 (采样精度/8 = 采样字节)
//fmt.cbSize = 0;
fwrite(&fmt,1,sizeof(WAVE_FORMAT),fw); //写入文件
WAVE_DATA data; //数据块
memcpy(data.Wdid,"data",4);
data.WdSize = m_writeindex;
fwrite(&data,1,sizeof(WAVE_DATA),fw); //写入文件
fwrite(m_Audio,1,m_writeindex,fw);
fclose(fw); //关闭文件,清零
fw = NULL;
return;
}
注:
特别注意格式块中的保留字节,也就是注释掉的
//unsigned short cbSize; //预留字节 一般为0扩展域有的没有
这部分一般为wav文件的扩展格式中才包含,还有事实块等。简单直接的wav文件
不需要管。
部分振动数据的转化
#include
#include
#include
#include
////RIFF头的宏定义缩写,便于后面的比较 低字节-》高字节
//#define ID_RIFF mmioFOURCC('R', 'I', 'F', 'F')
//#define ID_WAVE mmioFOURCC('W', 'A', 'V', 'E')
//#define ID_DATA mmioFOURCC('d', 'a', 't', 'a')
//#define ID_FMT mmioFOURCC('f', 'm', 't', '\x20')
//#define ID_FACT mmioFOURCC('f', 'a', 'c', 't')
//WAVE文件一般有四种块,它们依次是:RIFF块、格式块、附加块(可选),数据块
struct RIFFFORMAT{//长度12
unsigned char Ckid[4]; //RIFF标识
unsigned long FileSize; //文件大小
unsigned char FccType[4]; //WAVE标志
};
//WAV音频头
struct WAVE_FORMAT
{
unsigned char Ckid[4]; //fmt
unsigned long CkLen; //块大小 16
unsigned short wFormatTag; //音频格式一般为WAVE_FORMAT_PCM
unsigned short nChannels; //采样声道数
unsigned long nSamplesPerSec; //采样率
unsigned long nAvgBytesPerSec; //每秒字节数 采样率*采样精度/8(字节位数)
unsigned short nBlockAlign; //块大小 采样字节*声道数
unsigned short wBitsPerSample; //采样精度 采样精度/8 = 采样字节
//unsigned short cbSize; //预留字节 一般为0扩展域
};
//wave数据块
struct WAVE_DATA{
unsigned char Wdid[4]; //data 标志
unsigned long WdSize; //块大小
//unsigned char* Wdbuf; //数据指针 有符号
};
using namespace std;
int main()
{
//读取振动数据
FILE* fp = fopen("../123.txt", "rb+");
if (!fp)return 0;
fseek(fp, 0L, SEEK_END);
long size = ftell(fp) - 34;//获取文件数据大小
unsigned char*data = new unsigned char[size];
memset(data, 0, size);
//读取采样率
fseek(fp, 22L, SEEK_SET);//高在前 低在后
fread(data, 8, 1, fp);
//采样率
int sample = ((data[0] << 24) & 0xff000000) | ((data[1] << 16) & 0xff0000)| ((data[2] << 8) & 0xff00) | (data[3] & 0xff);
//时长
int duration =(data[4] << 24 & 0xff000000) | (data[5] << 16 & 0xff0000) | (data[6] << 8 & 0xff00) | (data[7] & 0xff);
//跳转到数据区
fseek(fp, 34L, SEEK_SET);//高在前 低在后
fread(data, size, 1, fp);
//关闭文件
fclose(fp);
//========开始WAV=======//
FILE*fw = fopen("../test.wav", "wb+");
RIFFFORMAT head;//RIFF块
memcpy(head.Ckid, "RIFF", 4);
head.FileSize = size + 36;
memcpy(head.FccType, "WAVE", 4);
fwrite(&head, sizeof(RIFFFORMAT),1, fw); //写入head
WAVE_FORMAT fmt;//格式块
memcpy(fmt.Ckid, "fmt ", 4);
fmt.CkLen = 16; //块大小 16
fmt.wFormatTag = 1; //音频格式一般为WAVE_FORMAT_PCM
fmt.nChannels = 1; //采样声道数
fmt.nSamplesPerSec = sample; //采样率
fmt.nAvgBytesPerSec = sample*4; //88200每秒字节数 采样率*采样精度(指的bit位数)/8(字节位数)
fmt.nBlockAlign = 4; //块大小 = 采样字节*声道数
fmt.wBitsPerSample = 32; //采样深度 = 采样字节*位数 (采样精度/8 = 采样字节)
//fmt.cbSize = 0;
fwrite(&fmt, sizeof(WAVE_FORMAT),1, fw); //写入fmt
WAVE_DATA wdata; //数据块
memcpy(wdata.Wdid, "data", 4);
wdata.WdSize = size;
fwrite(&wdata, sizeof(WAVE_DATA), 1, fw); //写入data
fflush(fw);
for (int i = (size-1); i>-1; i--)
{
fwrite((void*)(&data[i]), 1, 1, fw);//pcm数据部分为低字节在前高字节在后
}
fclose(fw); //关闭文件,清零
delete data;
return 0;