天天看点

PCM数据保存为WAV文件

因工程需要,需要截取一段PCM数据将其保存为WAV文件。网上代码很多,这里仅记录一下,方便自己以后查阅。

关于WAV文件格式不多余赘述这里直接上一张图:

图片来源:https://www.cnblogs.com/danju/p/3716194.html

PCM数据保存为WAV文件

下面附代码:

结构体声明文件:

#ifndef _CWAVEDEF_H
#define _CWAVEDEF_H
#include <MMSystem.h>

//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 <iostream>
#include <fstream>
#include <string>
#include <memory>

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;
           

继续阅读