1. WAV格式
wav是微軟開發的一種音頻文件格式,注意,wav文件格式是無損音頻文件格式,相對於其他音頻格式文件數據是沒有經過壓縮的,通常文件也相對比較大些。、
支持多種音頻數字,取樣頻率和聲道,標准格式化的WAV文件和CD格式一樣,也是44.1K的取樣頻率,16位量化數字,因此在聲音文件質量和CD相差無幾! WAV打開工具是WINDOWS的媒體播放器。
通常使用三個參數來表示聲音,量化位數,取樣頻率和采樣點振幅。量化位數分為8位,16位,24位三種,聲道有單聲道和立體聲之分,單聲道振幅數據為n*1矩陣點,立體聲為n*2矩陣點,取樣頻率一般有11025Hz(11kHz) ,22050Hz(22kHz)和44100Hz(44kHz) 三種,不過盡管音質出色,但在壓縮后的文件體積過大!相對其他音頻格式而言是一個缺點,其 文件大小的計算方式為:WAV格式文件所占容量(B) = (取樣頻率 X量化位數X 聲道) X 時間 / 8 (字節= 8bit) 每一分鍾WAV格式的音頻文件的大小為10MB,其大小不隨音量大小及清晰度的變化而變化。
注:專業名詞(取樣頻率、量化位數、聲道)解釋:https://blog.csdn.net/eric88/article/details/17098603
-
采樣位數:也叫量化位數(單位:比特),是存儲每個采樣值所用的二進制位數。采樣值反應了聲音的波動狀態。采樣位數決定了量化精度。采樣位數越長,量化的精度就越高,還原的波形曲線越真實,產生的量化噪聲越小,回放的效果就越逼真。常用的量化位數有4、8、12、16、24。量化位數與聲卡的位數和編碼有關。
-
采樣頻率:采樣頻率是指錄音設備在一秒鍾內對聲音信號的采樣次數,采樣頻率越高聲音的還原就越真實越自然。越高所能描述的聲波頻率就越高。采樣率決定聲音頻率的范圍(相當於音調),可以用數字波形表示。以波形表示的頻率范圍通常被稱為帶寬。要正確理解音頻采樣可以分為采樣的位數和采樣的頻率。
- 聲道數: 使用的聲音通道的個數,也是采樣時所產生的聲音波形的個數。播放聲音時,單聲道的WAV一般使用一個喇叭發聲,立體聲的WAV可以使兩個喇叭發聲。記錄聲音時,單聲道,每次產生一個波形的數據,雙聲道,每次產生兩個波形的數據,所占的存儲空間增加一倍。
-
WAV格式大小:采樣率一般是44.1K,16bit采樣精度,存儲成WAV格式大小 = 44.1KHz(采樣率) X 16bit(采樣位數) X 2(雙聲道) X 播放時間
-
WAV格式是沒有壓縮無損的,MP3格式是按1:12壓縮保存的,所以MP3格式大小等於上式的1/12。
為什么還要用wav來做示例呢?這是因為WAV本質上是無壓縮的原始音頻文件,而且他的文件結構不算非常復雜,因此可以作為我們初學者的學習示例格式。
2. WAV的二進制格式解析
WAV文件格式分析:https://blog.csdn.net/zhihu008/article/details/7854529
-
大部分的多媒體文件都依循着一種結構來存放信息,這種結構稱為"資源互換文件格式"(Resources lnterchange File Format),簡稱RIFF。例如聲音的WAV文件
-
RIFF:可以看做是一種樹狀結構,其基本構成單位為chunk,猶如樹狀結構中的節點,每個chunk由 "辨別碼"、"數據大小"及"數據" 所組成。
2.1 WAV文件格式
- WAVE文件是非常簡單的一種RIFF文件,它的格式類型為"WAVE"。RIFF塊包含兩個子塊,這兩個子塊的ID分別是 "fmt" 和 "data",其中 "fmt" 子塊由結構PcmWaveFormat所組成,其子塊的大小就是sizeof(PcmWaveFormat),數據組成就是PcmWaveFormat結構中的數據。
圖三、WAVE文件結構
typedef struct WAV_RIFF { /* chunk "riff" */ char ChunkID[4]; /* "RIFF" */ /* sub-chunk-size */ uint32_t ChunkSize; /* 36 + Subchunk2Size */ /* sub-chunk-data */ char Format[4]; /* "WAVE" */ } RIFF_t; typedef struct WAV_FMT { /* sub-chunk "fmt" */ char Subchunk1ID[4]; /* "fmt " */ /* sub-chunk-size */ uint32_t Subchunk1Size; /* 16 for PCM */ /* sub-chunk-data */ uint16_t AudioFormat; /* PCM = 1*/ uint16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */ uint32_t SampleRate; /* 8000, 44100, etc. */ uint32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */ uint16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */ uint16_t BitsPerSample; /* 8bits, 16bits, etc. */ } FMT_t; typedef struct WAV_data { /* sub-chunk "data" */ char Subchunk2ID[4]; /* "data" */ /* sub-chunk-size */ uint32_t Subchunk2Size; /* data size */ /* sub-chunk-data */ // Data_block_t block; } Data_t; //typedef struct WAV_data_block { //} Data_block_t; typedef struct WAV_fotmat { RIFF_t riff; FMT_t fmt; Data_t data; } Wav;
- "data"子塊包含WAVE文件的數字化波形聲音數據,其存放格式依賴於"fmt"子塊中SubchunkID, SubchunkSize ,AudioFormat 成員指定的格式種類,在多聲道WAVE文件中,樣本是交替出現的。如16bit的單聲道WAVE文件和雙聲道WAVE文件的數據采樣格式分別如圖四所示:
圖四、WAVE文件數據采樣格式
2.2 C語言解析wav文件
wave.h
// // Created by douzi on 19-3-26. // #ifndef RESOLVE_WAV_WAVE_H #define RESOLVE_WAV_WAVE_H #include <cstdint> typedef struct WAV_RIFF { /* chunk "riff" */ char ChunkID[4]; /* "RIFF" */ /* sub-chunk-size */ uint32_t ChunkSize; /* 36 + Subchunk2Size */ /* sub-chunk-data */ char Format[4]; /* "WAVE" */ } RIFF_t; typedef struct WAV_FMT { /* sub-chunk "fmt" */ char Subchunk1ID[4]; /* "fmt " */ /* sub-chunk-size */ uint32_t Subchunk1Size; /* 16 for PCM */ /* sub-chunk-data */ uint16_t AudioFormat; /* PCM = 1*/ uint16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */ uint32_t SampleRate; /* 8000, 44100, etc. */ uint32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */ uint16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */ uint16_t BitsPerSample; /* 8bits, 16bits, etc. */ } FMT_t; typedef struct WAV_data { /* sub-chunk "data" */ char Subchunk2ID[4]; /* "data" */ /* sub-chunk-size */ uint32_t Subchunk2Size; /* data size */ /* sub-chunk-data */ // Data_block_t block; } Data_t; //typedef struct WAV_data_block { //} Data_block_t; typedef struct WAV_fotmat { RIFF_t riff; FMT_t fmt; Data_t data; } Wav; #endif //RESOLVE_WAV_WAVE_H
-
uint8_t: unsigned char
-
uint16_t:unsigned short int
-
uint32_t:unsigned int
-
uint64_t: unsigned long int
wave.cpp
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include "wave.h" int main() { FILE *fp = NULL; Wav wav; RIFF_t riff; FMT_t fmt; Data_t data; fp = fopen("/home/douzi/Douzi_qdreamer/resolve_wav/mbfio_mic3.wav", "rb"); if (!fp) { printf("can't open audio file\n"); exit(1); } fread(&wav, 1, sizeof(wav), fp); riff = wav.riff; fmt = wav.fmt; data = wav.data; /** * RIFF */ printf("ChunkID \t\t%c%c%c%c\n", riff.ChunkID[0], riff.ChunkID[1], riff.ChunkID[2], riff.ChunkID[3]); printf("ChunkSize \t\t%d\n", riff.ChunkSize); printf("Format \t\t\t%c%c%c%c\n", riff.Format[0], riff.Format[1], riff.Format[2], riff.Format[3]); printf("\n"); /** * fmt */ printf("Subchunk1ID \t%c%c%c%c\n", fmt.Subchunk1ID[0], fmt.Subchunk1ID[1], fmt.Subchunk1ID[2], fmt.Subchunk1ID[3]); printf("Subchunk1Size \t%d\n", fmt.Subchunk1Size); printf("AudioFormat \t%d\n", fmt.AudioFormat); printf("NumChannels \t%d\n", fmt.NumChannels); printf("SampleRate \t\t%d\n", fmt.SampleRate); printf("ByteRate \t\t%d\n", fmt.ByteRate); printf("BlockAlign \t\t%d\n", fmt.BlockAlign); printf("BitsPerSample \t%d\n", fmt.BitsPerSample); printf("\n"); /** * data */ printf("blockID \t\t%c%c%c%c\n", data.Subchunk2ID[0], data.Subchunk2ID[1], data.Subchunk2ID[2], data.Subchunk2ID[3]); printf("blockSize \t\t%d\n", data.Subchunk2Size); printf("\n"); // duration = Subchunk2Size / ByteRate printf("duration \t\t%d\n", data.Subchunk2Size / fmt.ByteRate); }
3. WAV文件語音數據的組織結構
參考:https://www.cnblogs.com/ranson7zop/p/7657874.html
WAV文件的聲音數據保存在數據塊中。塊標識符為“data”, 塊長度值為聲音數據的長度。
從數據塊的第9個字符開始是聲音波形采樣數據。每個樣本按采樣的時間先后順序寫入。樣本的字節數取決於采樣位數。對於多字節樣本, 低位字節數據 放在低地址單元,相鄰的高位字節數據放在高地址單元。多聲道樣本數據采用交替方式存儲。例如: 立體聲(雙聲道)采樣值的存儲順序為:
- 通道1第1采樣值, 通道2第1采樣值;通道1第2采樣值, 通道2第2采樣值;以此類推。基於PCM編碼的樣本數據排列方式。
(1)“52 49 46 46”這個是Ascii字符“RIFF”,這部分是固定格式,表明這是一個WAVE文件頭。
(2)“22 60 28 00”,這個是我這個WAV文件的數據大小,這個大小包括除了前面4個字節的所有字節,也就等於文件總字節數減去8。16進制的“22 60 28 00”對應是十進制的“2646050”。
(3)“57 41 56 45 66 6D 74 20”,也是Ascii字符“WAVEfmt”,這部分是固定格式。
以后是PCMWAVEFORMAT部分
(4)“12 00 00 00”,這是一個DWORD,對應數字18,這個對應定義中的PCMWAVEFORMAT部分的大小,可以看到后面的這個段內容正好是18個字節。一般情況下大小為16,此時最后附加信息沒有,上面這個文件多了兩個字節的附加信息。
(5)“01 00”,這是一個WORD,對應定義為編碼格式(WAVE_FORMAT_PCM格式一般用的是這個)。
(6)“01 00”,這是一個WORD,對應數字1,表示聲道數為1,是個單聲道Wav。
(7)“22 56 00 00”對應數字22050,代表的是采樣頻率22050,采樣率(每秒樣本數),表示每個通道的播放速度
(8)“44 AC 00 00”對應數字44100,代表的是每秒的數據量,波形音頻數據傳送速率,其值為:通道數×每秒樣本數×每樣本的數據位數/8(1*22050*16/8)。播放軟件利用此值可以估計緩沖區的大小。
(9)“02 00”對應數字是2,表示塊對齊的內容。數據塊的調整數(按字節算的),其值為通道數×每樣本的數據位值/8。播放軟件需要一次處理多個該值大小的字節數據,以便將其值用於緩沖區的調整。
(10)“10 00”數值為16,采樣大小為16Bits,每樣本的數據位數,表示每個聲道中各個樣本的數據位數。如果有多個聲道,對每個聲道而言,樣本大小都一樣。
(11)“00 00”此處為附加信息(可選),和(4)中的size對應。
(12)“66 61 73 74” Fact是可選字段,一般當wav文件由某些軟件轉化而成,則包含該項,“04 00 00 00”Fact字段的大小為4字節,“F8 2F 14 00”是fact數據。
(13)“64 61 74 61”,這個是Ascii字符“data”,標示頭結束,開始數據區域。
(14)“F0 5F 28 00”十六進制數是“0x285ff0”,對應十進制2646000,是數據區的開頭,以后數據總數,看一下前面正好可以看到,文件大小是2646050,從(2)到(13)包括(13)正好是2646050-2646000=50字節。
4. 總結
對WAV格式影響最大的參數是編碼格式。采用不同的編碼的WAV格式是不同的,PCM是最常見的編碼格式,其它的為壓縮編碼格式,一般很少使用,有的已經廢棄。隨着人們認識的進步可能還會有新的編碼格式出現。今后對WAV文件格式的更多的研究是壓縮編碼格式。