天天看點

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;
           

繼續閱讀