wav檔案格式分析(程式碼 C++ )
我們先隨便找一個wav檔案,檢視其屬性,就能得到下面的結果。
上面主要注意檔案大小,聲音長度與位元率。
檔案佔用空間就不用關心了,如果有人想知道為什麼檔案佔用空間比檔案大小要大,我在這裡也解釋一下。這和檔案在硬碟中的組織方式有關係,這裡的硬碟分割槽是以最小4096Byte為單位的,我檔案的大小是1325044Byte,那麼1325044/4096=323.49,為了能把檔案對齊的放到硬碟中,所以佔用的空間就要是324*4096=1327104Byte了,所以佔用的空間就是這麼多了。你也可以建立一個只寫一個字母的txt檔案試試,檔案大小雖然為1Byte,不過佔用空間也為4096Byte。
上面說的當然和wav檔案沒什麼關係,下面就正式說wav檔案的問題。
用ultraedit開啟就是下面這個樣子:
用表格說明一下檔案的格式:
起始地址 |
佔用空間 |
本地址數字的含義 |
00H |
4byte |
RIFF,資源交換檔案標誌。 |
04H |
4byte |
從下一個地址開始到檔案尾的總位元組數。高位位元組在後面,這裡就是001437ECH,換成十進位制是1325036byte,算上這之前的8byte就正好1325044byte了。 |
08H |
4byte |
WAVE,代表wav檔案格式。 |
0CH |
4byte |
FMT ,波形格式標誌 |
10H |
4byte |
00000010H,16PCM,我的理解是用16bit的資料表示一個量化結果。 |
14H |
2byte |
為1時表示線性PCM編碼,大於1時表示有壓縮的編碼。這裡是0001H。 |
16H |
2byte |
1為單聲道,2為雙聲道,這裡是0001H。 |
18H |
4byte |
取樣頻率,這裡是00002B11H,也就是11025Hz。 |
1CH |
4byte |
Byte率=取樣頻率*音訊通道數*每次取樣得到的樣本位數/8,00005622H,也就是22050Byte/s=11025*1*16/2。 |
20H |
2byte |
塊對齊=通道數*每次取樣得到的樣本位數/8,0002H,也就是2=1*16/8。 |
22H |
2byte |
樣本資料位數,0010H即16,一個量化樣本佔2byte。 |
24H |
4byte |
data,一個標誌而已。 |
28H |
4byte |
Wav檔案實際音訊資料所佔的大小,這裡是001437C8H即1325000,再加上2CH就正好是1325044,整個檔案的大小。 |
2CH |
不定 |
量化資料。 |
注意屬性中的位元率是176kbps,而1CH中為22050Byte/s,換算一下就會發現22050*8/1024並不等於176,而是等於172,這裡我想可能是通訊中的1K並不等於1024而是等於1000的原因(通訊原理書中好像有),如果按22050*8/1000這樣算,就正好等於176了。其實位元率也可以這樣算,總位元組除以時長得到每秒位元組率,再乘以8除以1000就得到位元率了,即(1325000/60)*8/1000=176kbps。
最後是量化資料的表示。
看資料結尾的表示吧,我這音訊最初那一段都是0,不好解釋。
Ultraedit中的表示:
Matlab中的表示:
上面的matlab是662500個數,正好也是11325000的一半。可以看出資料是有正有負的浮點數,為正負雙向PCM量化編碼,得到的十六進位制位的最高位是一個符號位,為0表示正數,為1表示負數,正好表示-32768~32767。而且十六進位制的資料正數要除以32767,負數要除以32768才能得到結果,如果是單向PCM就要除以65535了。
比如最後一位十六進位制為2710H,在Matlab中為0.3052,而2710H的十進位制10000除以32767正好近似為0.3052。
再比如matlab中662472這個數為-0.0206,考慮到44位的偏差,在ultraedit中為1437BAH位上的FD5DH,表示為十進位制-675除以32768正好近似為-0.0206。
當然,上面只是針對matlab和ultraedit中的資料進行具體的分析,真正程式設計處理時還是要根據情況有所不同的。量化位數的不同,32bit或是8bit處理又不同了。符號的表示也可能不同,原碼補碼反碼等等的知識可能也要用到。
這裡我只把基本的資料提取出來了,沒有進行下一步處理,資料提取出來,後面怎麼應用就看具體情況了。
#include <iostream> #include <fstream> using namespace std; struct wav_struct { unsigned long file_size; //檔案大小 unsigned short channel; //通道數 unsigned long frequency; //取樣頻率 unsigned long Bps; //Byte率 unsigned short sample_num_bit; //一個樣本的位數 unsigned long data_size; //資料大小 unsigned char *data; //音訊資料 ,這裡要定義什麼就看樣本位數了,我這裡只是單純的複製資料 }; int main(int argc,char **argv) { fstream fs; wav_struct WAV; fs.open("B:\\output.wav",ios::binary|ios::in); // fs.seekg(0x04); //從檔案資料中獲取檔案大小 // fs.read((char*)&WAV.file_size,sizeof(WAV.file_size)); // WAV.file_size+=8; fs.seekg(0,ios::end); //用c++常用方法獲得檔案大小 WAV.file_size=fs.tellg(); fs.seekg(0x14); fs.read((char*)&WAV.channel,sizeof(WAV.channel)); fs.seekg(0x18); fs.read((char*)&WAV.frequency,sizeof(WAV.frequency)); fs.seekg(0x1c); fs.read((char*)&WAV.Bps,sizeof(WAV.Bps)); fs.seekg(0x22); fs.read((char*)&WAV.sample_num_bit,sizeof(WAV.sample_num_bit)); fs.seekg(0x28); fs.read((char*)&WAV.data_size,sizeof(WAV.data_size)); WAV.data=new unsigned char[WAV.data_size]; fs.seekg(0x2c); fs.read((char *)WAV.data,sizeof(char)*WAV.data_size); cout<<"檔案大小為 :"<<WAV.file_size<<endl; cout<<"音訊通道數 :"<<WAV.channel<<endl; cout<<"取樣頻率 :"<<WAV.frequency<<endl; cout<<"Byte率 :"<<WAV.Bps<<endl; cout<<"樣本位數 :"<<WAV.sample_num_bit<<endl; cout<<"音訊資料大小:"<<WAV.data_size<<endl; cout<<"最後20個數據:"<<endl; for (unsigned long i=WAV.data_size-20;i<WAV.data_size;i++) { printf("%x ",WAV.data[i]); } fs.close(); delete[] WAV.data; system("pause"); }
執行結果: