WAV文件的二進制格式解析


在這裏插入圖片描述
根據網絡上的各種資料可以得知WAVE文件本質上就是一種RIFF格式,它可以抽象成一顆樹(數據結構的一種)來看。

ss

我們看到這張圖上面,從上到下分別對應着二進制數據在文件中相對於起始位置的偏移量。每一個格子對應一個字段,field size表示每個字段所佔據的大小,根據這個大小以及當前的偏移量,我們也可以計算出下一個字段的起始地址(偏移量)。

一個chunk結構其實就是三個部分,第一部分標識符用於說明這個chunk是存什麼內容,第二部分是說明這個chunk的內容有多大,用於讓程序知道如果要找到下一個chunk該把地址偏移多少去讀取,第三部分則是實際內容

頂級chunk: 說明這個chunk是存什麼內容

子chunk

第一個子chunk

Subchunk1ID 在WAV文件中恆定爲fmt,表示該subchunk的內容爲該WAV音頻文件的一些元數據,即WAV音頻的一些格式信息

AudioFormat這個字段一般爲1,表示這個WAV音頻爲PCM編碼

NumChannels則是該WAV音頻文件的聲道數量。1爲單聲道,2爲雙聲道。

SampleRate 則爲採樣率

ByteRate 爲數據傳輸速率。其值爲:通道數*每秒數據位數*每樣本的數據位數/8

BlockAlign 則是每個block的平均大小,它等於NumChannels * BitsPerSample/8,至於block是什麼,以及它的計算公式是怎麼得來的需要來看看另一個Subchunk。BitsPerSample則爲每秒採樣比特,有的地方稱它爲量化精度或者PCM位寬。

另一個子chunk

Subchunk2ID 是在WAV文件中恆定爲data,即WAV音頻文件的實際音頻數據,裏面存儲的是音頻的採樣數據。但是我們的音頻如果是雙聲道,那麼實際上某一個採樣時刻採樣的數據是由左聲道和右聲道共同組成的。而這個共同組成的採樣我們把他成爲block。前面有講到BlockAlign = NumChannels * BitsPerSample / 8,這個現在就很好理解了,至於爲什麼末尾要除以8,這是因爲計算機中是以8個二進制數表示一個字節,所以要除以8來求出字節數。

音頻的持續長度,我們可以通過Subchunk2Size除以ByteRate,也就是實際音頻data的chunk總長度除以每秒字節數得到持續多少秒。

實例:用C語言解析wav文件

#include <stdio.h>
#include <stdint.h> /*struct類型裏面我用的是uint32_t等類型,而不是傳統的int,short等等
                    這些類型是由stdint.h頭文件提供*/
#include <stdlib.h>

typedef struct wave_tag //聲明結構體的相關參數 
 
{
 
char ChunkID[4]; // "RIFF"標誌
 
unsigned int ChunkSize; // 文件長度(WAVE文件的大小, 不含前8個字節)
 
char Format[4]; // "WAVE"標誌
 
char SubChunk1ID[4]; // "fmt "標誌
 
unsigned long int SubChunk1Size; // 過渡字節(不定)
 
unsigned short int AudioFormat; // 格式類別(10H爲PCM格式的聲音數據)
 
unsigned short int NumChannels; // 通道數(單聲道爲1, 雙聲道爲2)
 
unsigned short int SampleRate; // 採樣率(每秒樣本數), 表示每個通道的播放速度
 
unsigned int ByteRate; // 波形音頻數據傳輸速率, 其值爲:通道數*每秒數據位數*每樣本的數據位數/8
 
unsigned short int BlockAlign; // 每個樣點的Byte數(按字節算), 其值爲:通道數*每樣本的數據位數/8
 
unsigned short int BitsPerSample; // 每個樣點的數據位數, 表示每個聲道中各個樣本的數據位數.
 
char SubChunk2ID[4]; // 數據標記"data"
 
unsigned long int SubChunk2Size; // 語音數據的長度
 
} WAVE;
 
int main(int argc, char *argv[])
 
{
 
FILE *fp;                      //定義指針型文件 
 
WAVE wav;                     //調用結構體wav 
 
fp=fopen("E:\\test_wave\\2.wav","rb"); /*以二進制方式打開並讀取(rb)wav文件,
                                        通常r是正常方式讀取文件 */ 
 
fread(&wav, sizeof(WAVE), 1, fp);     //讀取文件信息 

//打印輸出wave文件頭的相關信息 
printf("ChunkID=\t%c%c%c%c \n",wav.ChunkID[0],wav.ChunkID[1],wav.ChunkID[2],wav.ChunkID[3]); //輸出"RIFF"標誌
 
printf("ChunkSize=\t%d\n",wav.ChunkSize); //輸出文件長度,即WAVE文件的大小
 
printf("Format=\t\t%c%c%c%c\n",wav.Format[0],wav.Format[1],wav.Format[2],wav.Format[3]);//輸出"WAVE"標誌 
 
printf("SubChunk1ID=\t%c%c%c%c\n",wav.SubChunk1ID[0],wav.SubChunk1ID[1],wav.SubChunk1ID[2],wav.SubChunk1ID[3]);// 輸出"fmt "標誌
 
printf("SubChunk1Size=\t%ld\n",wav.SubChunk1Size);// 輸出過渡字節(不定)
 
printf("AudioFormat=\t%x\n",wav.AudioFormat); // 輸出格式類別(10H爲PCM格式的聲音數據)
 
printf("NumChannels=\t%d\n",wav.NumChannels);//輸出通道數 
 
printf("SampleRate=\t%d\n",wav.SampleRate);//輸出採樣率 
 
printf("ByteRate=\t%d\n",wav.ByteRate);// 輸出波形音頻數據傳輸速率
 
printf("BlockAlign=\t%d\n",wav.BlockAlign);//輸出每個樣點的Byte數
 
printf("BitsPerSample=\t%d\n",wav.BitsPerSample);// 輸出每個樣點的數據位數
 
printf("SubChunk2ID=\t%c%c%c%c \n",wav.SubChunk2ID[0],wav.SubChunk2ID[1],wav.SubChunk2ID[2],wav.SubChunk2ID[3]);//輸出data標誌 
 
printf("SubChunk2Size=\t%ld\n",wav.SubChunk2Size);//輸出數據的長度 
 
return 0;
 
}

運行結果:

image-20200110113424163.png

通過WinHex驗證結果

WinHex 是一款以通用的 16 進制編輯器爲核心,專門用來對付計算機取證數據恢復、低級數據處理、以及 IT 安全性、各種日常緊急情況的高級工具: 用來檢查和修復各種文件、恢復刪除文件、硬盤損壞、數碼相機卡損壞造成的數據丟失等。可進行 2 進制、16 進制 ASCII, Intel 16 進制, 和 Motorola S 轉換。
image-20200110113930082.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章