WAV文件讀取


WAV是一種以RIFF為基礎的無壓縮音頻編碼格式,該格式以Header、Format Chunk及Data Chunk三部分構成。

本文簡要解析了各部分的構成要素,概述了如何使用C++對文件頭進行解析以及提取音頻數據。

WAVE format

上圖展示了WAV文件格式,包括每一field的大小與端序

  • ChunkID: 4字節大端序。文件從此處開始,對於WAV或AVI文件,其值總為“RIFF”。
  • ChunkSize: 4字節小端序。表示文件總字節數減8,減去的8字節表示ChunkID與ChunkSize本身所占字節數。
  • Format: 4字節大端序。對於WAV文件,其值總為“WAVE”

Format Chunk

  • Subchunk1ID: 4字節大端序。其值總為“fmt ”,表示Format Chunk從此處開始。
  • Subchunk1Size: 4字節小端序。表示Format Chunk的總字節數減8。
  • AudioFormat: 2字節小端序。對於WAV文件,其值總為1。
  • NumChannels: 2字節小端序。表示總聲道個數。
  • SampleRate: 4字節小端序。表示在每個通道上每秒包含多少幀。
  • ByteRate: 4字節小端序。大小等於SampleRate * BlockAlign,表示每秒共包含多少字節。
  • BlockAlign: 2字節小端序。大小等於NumChannels * BitsPerSample / 8, 表示每幀的多通道總字節數。
  • BitsPerSample: 2字節小端序。表示每幀包含多少比特。

Data Chunk

  • Subchunk2ID: 4字節大端序。其值總為“data”,表示Data Chunk從此處開始。
  • Subchunk2Size: 4字節小端序。表示data的總字節數。
  • data: 小端序。表示音頻波形的幀數據,各聲道按幀交叉排列。

使用C++解析WAV文件

文件頭結構

定義結構體WaveHeader來保存WAV文件頭,即Header、Format Chunk及Data Chunk的非data部分,此外在該結構體中添加了num_frame字段,用來保存文件總幀數,由於Header、Format Chunk與Data Chunk之間可能有其他說明信息,所以還添加了start_pos字段用來保存真正的data開始的位置。

typedef struct WaveHeader {
	char chunk_id[4] = { 0 };
	unsigned int chunk_size = 0;
	char format[4] = { 0 };
	char fmt_chunk_id[4] = { 0 };
	unsigned int fmt_chunk_size = 0;
	unsigned short audio_fomat = 0;
	unsigned short num_channels = 0;
	unsigned int sample_rate = 0;
	unsigned int byte_rate = 0;
	unsigned short block_align = 0;
	unsigned short bits_per_sample = 0;
	char data_chunk_id[4] = { 0 };
	unsigned int data_chunk_size = 0;
	int num_frame = 0;
	int start_pos = 0;
};

提取文件頭

/*
* fname: 文件路徑
* wh: 用來保存文件頭的結構體實例
*/
void getHead(string fname, WaveHeader &wh) {

	/*
	*由於事先並不知道文件大小,故定義足量大小的char數組覆蓋文件頭
	*之后可根據提取到的ChunkSize來定義提取音頻數據用的數組
	*/
	const int HEAD_LENGTH = 256 * 1024;//256kb
	char buf[HEAD_LENGTH];


	FILE *stream;
	freopen_s(&stream, fname.c_str(), "rb", stderr);
	fread(buf, 1, HEAD_LENGTH, stream);
	

	//記錄文件讀取位置
	int pos = 0;


	//尋找“RIFF”標記
	while (pos < HEAD_LENGTH) {
		if (buf[pos] == 'R'&&buf[pos + 1] == 'I'&&buf[pos + 2] == 'F'&buf[pos + 3] == 'F') {
			wh.chunk_id[0] = 'R';
			wh.chunk_id[1] = 'I';
			wh.chunk_id[2] = 'F';
			wh.chunk_id[3] = 'F';
			pos += 4;
			break;
		}
		++pos;
	}

	//讀取Header部分
	wh.chunk_size = *(int *)&buf[pos];
	pos += 4;
	wh.format[0] = buf[pos];
	wh.format[1] = buf[pos + 1];
	wh.format[2] = buf[pos + 2];
	wh.format[3] = buf[pos + 3];
	pos += 4;


	//尋找“fmt”標記
	while (pos < HEAD_LENGTH) {
		if (buf[pos] == 'f'&&buf[pos + 1] == 'm'&&buf[pos + 2] == 't') {
			wh.fmt_chunk_id[0] = 'f';
			wh.fmt_chunk_id[1] = 'm';
			wh.fmt_chunk_id[2] = 't';
			pos += 4;
			break;
		}
		++pos;
	}

	//讀取Format Chunk部分
	wh.fmt_chunk_size = *(int *)&buf[pos];
	pos += 4;
	wh.audio_fomat = *(short *)&buf[pos];
	pos += 2;
	wh.num_channels = *(short *)&buf[pos];
	pos += 2;
	wh.sample_rate = *(int *)&buf[pos];
	pos += 4;
	wh.byte_rate = *(int *)&buf[pos];
	pos += 4;
	wh.block_align = *(short *)&buf[pos];
	pos += 2;
	wh.bits_per_sample = *(short *)&buf[pos];
	pos += 2;


	//尋找“data”標記
	while (pos < HEAD_LENGTH) {
		if (buf[pos] == 'd'&&buf[pos + 1] == 'a'&&buf[pos + 2] == 't'&buf[pos + 3] == 'a') {
			wh.data_chunk_id[0] = 'd';
			wh.data_chunk_id[1] = 'a';
			wh.data_chunk_id[2] = 't';
			wh.data_chunk_id[3] = 'a';
			pos += 4;
			break;
		}
		++pos;
	}

	//讀取Data Chunk的非data部分
	wh.data_chunk_size = *(int *)&buf[pos];
	pos += 4;

	//記錄真正音頻數據的開始位置
	wh.start_pos = pos;

	//計算文件總幀數
	wh.num_frame = wh.data_chunk_size / (wh.num_channels*(wh.bits_per_sample / 8));
}

提取波形數據(data)

/*
* fname: 文件路徑
* wh: 對應的文件頭結構體實例
*/
void getData(string fname, WaveHeader &wh){

	//記錄文件讀取位置
	int pos = wh.start_pos;


	//為加快處理速度,根據ChunkSize將文件一次讀入內存
	FILE *stream;
	freopen_s(&stream, fname.c_str(), "rb", stderr);
	char* file_data = new char[wh.chunk_size + 8];
	fread(file_data, 1, wh.chunk_size + 8, stream);


	//以每幀2字節為例
	short left_data;
	short right_data;

	while(pos < wh.start_pos + wh.data_chunk_size){
		left_data = *(short*)&file_data[pos];
		//TODO: 處理左聲道數據
		pos += 2;

		right_data = *(short*)&file_data[pos];
		//TODO: 處理右聲道數據
		pos += 2;
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM