WAVE绘制频谱图(二)——WAVE文件解析提取PCM数据

为了不太多依赖于平台环境,音频数据提取部分尽量都用c++实现。

话不多说,直接还是上代码,代码比较详细不再赘述。

WAVE格式头结构体:

//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 long Ckid;	//RIFF标识
	unsigned long FileSize; //文件大小
	unsigned long FccType; //WAVE标志
};

//WAV音频头
struct WAVE_FORMAT
{
	unsigned long Ckid; //fmt
	unsigned long CkSize; //块大小
	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 long Wdid; //data 标志
	unsigned long WdSize; //块大小
	unsigned char* Wdbuf; //数据指针 有符号
};

读取PCM文件:


	char* m_path = "../test.wav";
	//音频头文件信息
	RIFFFORMAT head;//RIFF头
	WAVE_FORMAT fmt;//格式块
	WAVE_DATA data;//数据块
    
        fstream fs;	
	fs.open(m_path, std::ios::binary | std::ios::in); 
	fs.read((char*)&head, sizeof(RIFFFORMAT));
	
	if(head.Ckid != ID_RIFF||head.FccType!=ID_WAVE) //不是wave格式
	{
		fs.close();
		return;
	}

	fs.read((char*)&fmt, 24);	//读取格式块
	if(fmt.Ckid != ID_fmt||fmt.wFormatTag != WAVE_FORMAT_PCM)//只处理pcm 数据
	{
	    fs.close();
		return;
	}
	if(fmt.CkSize == 18) //说明含有保留字节
	{
		fs.read((char*)&(fmt.cbSize), 2);
	}else
	{
		fmt.cbSize = 0;	 //不含有则置位0
	}

	//以下标志位有的wave包含fact 有的不包含 需要区分
	unsigned long Sign;	  
        fs.read((char*)&Sign, 4);

	if(Sign == ID_fact)	 //如果有fact块则跳过
	{
		fs.seekp(4,ios::cur);//从当前位置跳过4个字节
		fs.read((char*)&Sign, 4);//再读取
	}

	if(Sign == ID_data)//读取到data数据块
	{
		data.Wdid = Sign;
		fs.read((char*)&(data.WdSize), 4);//数据块大小
		data.Wdbuf = new unsigned char[data.WdSize];
		fs.read((char*)data.Wdbuf, data.WdSize);//数据读取
		fs.close();	  //读取完成关闭文件
	}

上述代码部分设计RIFF头比较,才用了Windows中的 mmioFOURCC()包含于  文件中,当然也可以自己实现

实现过程如下:

#define MAKEFOURCC(ch0, ch1, ch2, ch3)                              \
                ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) |   \
                ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))

一个简单的位操作,过程不复杂。

如上操作就已经把PCM数据取出来存放在data.wdbuf中了。

你可能感兴趣的:(音视频)