1. 音頻簡介
經常見到這樣的描述: 44100HZ 16bit stereo 或者 22050HZ 8bit mono 等等.
44100HZ 16bit stereo: 每秒鍾有 44100 次采樣, 采樣數據用 16 位(2字節)記錄, 雙聲道(立體聲);
22050HZ 8bit mono: 每秒鍾有 22050 次采樣, 采樣數據用 8 位(1字節)記錄, 單聲道;
當然也可以有 16bit 的單聲道或 8bit 的立體聲, 等等。
采樣率是指:聲音信號在“模→數”轉換過程中單位時間內采樣的次數。采樣值是指每一次采樣周期內聲音模擬信號的積分值。
對於單聲道聲音文件,采樣數據為八位的短整數(short int 00H-FFH);
而對於雙聲道立體聲聲音文件,每次采樣數據為一個16位的整數(int),高八位(左聲道)和低八位(右聲道)分別代表兩個聲道。
人對頻率的識別范圍是 20HZ - 20000HZ, 如果每秒鍾能對聲音做 20000 個采樣, 回放時就足可以滿足人耳的需求. 所以 22050 的采樣頻率是常用的, 44100已是CD音質, 超過48000的采樣對人耳已經沒有意義。這和電影的每秒 24 幀圖片的道理差不多。
每個采樣數據記錄的是振幅, 采樣精度取決於儲存空間的大小:
1 字節(也就是8bit) 只能記錄 256 個數, 也就是只能將振幅划分成 256 個等級;
2 字節(也就是16bit) 可以細到 65536 個數, 這已是 CD 標准了;
4 字節(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.
如果是雙聲道(stereo), 采樣就是雙份的, 文件也差不多要大一倍.
這樣我們就可以根據一個 wav 文件的大小、采樣頻率和采樣大小估算出一個 wav 文件的播放長度。
譬如 "Windows XP 啟動.wav" 的文件長度是 424,644 字節, 它是 "22050HZ / 16bit / 立體聲" 格式(這可以從其 "屬性->摘要" 里看到),
那么它的每秒的傳輸速率(位速, 也叫比特率、取樣率)是 22050*16*2 = 705600(bit/s), 換算成字節單位就是 705600/8 = 88200(字節/秒),
播放時間:424644(總字節數) / 88200(每秒字節數) ≈ 4.8145578(秒)。
但是這還不夠精確, 包裝標准的 PCM 格式的 WAVE 文件(*.wav)中至少帶有 42 個字節的頭信息, 在計算播放時間時應該將其去掉,
所以就有:(424644-42) / (22050*16*2/8) ≈ 4.8140816(秒). 這樣就比較精確了.
關於聲音文件還有一個概念: "位速", 也有叫做比特率、取樣率, 譬如上面文件的位速是 705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思;
壓縮的音頻文件常常用位速來表示, 譬如達到 CD 音質的 MP3 是: 128kbps / 44100HZ.
2. wave文件格式
2.1 概述
WAVE文件是計算機領域最常用的數字化聲音文件格式之一,它是微軟專門為Windows系統定義的波形文件格式(Waveform Audio),由於其擴展名為"*.wav"。
WAVE是錄音時用的標准的WINDOWS文件格式,文件的擴展名為“WAV”,數據本身的格式為PCM或壓縮型。
WAV文件格式是一種由微軟和IBM聯合開發的用於音頻數字存儲的標准,它采用RIFF文件格式結構,非常接近於AIFF和IFF格式。符合 PIFF Resource Interchange File Format規范。所有的WAV都有一個文件頭,這個文件頭音頻流的編碼參數。
WAV對音頻流的編碼沒有硬性規定,除了PCM之外,還有幾乎所有支持ACM規范的編碼都可以為WAV的音頻流進行編碼。
多媒體應用中使用了多種數據,包括位圖、音頻數據、視頻數據以及外圍設備控制信息等。RIFF為存儲這些類型的數據提供了一種方法,RIFF文件所包含的數據類型由該文件的擴展名來標識,能以RIFF文件存儲的數據包括:
音頻視頻交錯格式數據(.AVI) 、波形格式數據(.WAV) 、位圖格式數據(.RDI) 、MIDI格式數據(.RMI) 、調色板格式(.PAL) 、多媒體電影(.RMN) 、動畫光標(.ANI) 、其它RIFF文件(.BND)。
wave文件有很多不同的壓縮格式,所以,正確而詳細地了解各種WAVE文件的內部結構是成功完成壓縮和解壓縮的基礎,也是生成特有音頻壓縮格式文件的前提。
最基本的WAVE文件是PCM(脈沖編碼調制)格式的,這種文件直接存儲采樣的聲音數據沒有經過任何的壓縮,是聲卡直接支持的數據格式,要讓聲卡正確播放其它被壓縮的聲音數據,就應該先把壓縮的數據解壓縮成PCM格式,然后再讓聲卡來播放。
2.2 Wave文件的內部結構
注:由於WAV格式源自Windows/Intel環境,因而采用Little-Endian字節順序進行存儲。
WAVE文件是以RIFF(Resource Interchange File Format, "資源交互文件格式")格式來組織內部結構的。
RIFF文件結構可以看作是樹狀結構,其基本構成是稱為"塊"(Chunk)的單元,最頂端是一個“RIFF”塊,下面的每個塊有“類型塊標識(可選)”、“標志符”、“數據大小”及“數據”等項所組成。塊的結構如表1所示:
名稱 |
Size |
備注 |
塊標志符 |
4 |
4個小寫字符(如 "fmt ", "fact", "data" 等) |
數據大小 |
4 |
DWORD類型,表示后接數據的大小(N Bytes) |
數據 |
N |
本塊中正式數據部分 |
表1:基本chunk的內部結構
上面說到的“類型塊標識”只在部分chunk中用到,如 "WAVE" chunk中,這時表示下面嵌套有別的chunk。
當使用了 "類型塊標識" 時,該chunk就沒有別的項(如塊標志符,數據大小等),它只作為文件讀取時的一個標識。先找到這個“類型塊標識”,再以它為起點讀取它下面嵌套的其它chunk。
每個文件最前端寫入的是RIFF塊,每個文件只有一個RIFF塊。從 Wave文件格式詳細說明 中可以看到這一點。
非PCM格式的文件會至少多加入一個 "fact" 塊,它用來記錄數據(注意是數據而不是文件)解壓縮后的大小。這個 "fact" 塊一般加在 "data" 塊的前面。
WAVE文件是由若干個Chunk組成的。按照在文件中的出現位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可選), Data Chunk。具體見下圖:
-------------------------------------------
| RIFF WAVE Chunk |
| ID = "RIFF" |
| RiffType = "WAVE" |
-------------------------------------------
| Format Chunk |
| ID = "fmt " |
-------------------------------------------
| Fact Chunk(optional) |
| ID = "fact" |
-------------------------------------------
| Data Chunk |
| ID = "data" |
-------------------------------------------
圖 Wav格式包含Chunk示例
Fact Chunk
=======================================
| |所占字節數| 具體內容 |
=======================================
| ID | 4Bytes | "fact" |
---------------------------------------
| Size | 4Bytes | 4 |
---------------------------------------
| data | 4Bytes |解壓后的音頻數據的大小(B)|
---------------------------------------
圖 Fact Chunk
2.3 Wave文件格式詳細說明
別名 字節數 類型 注釋
ckid 4 char "RIFF" 標志, 大寫
cksize 4 int32 文件長度。這個長度不包括"RIFF"標志 和
文件長度 本身所占字節, 下面的
子塊大小也是這樣。
fcc type 4 char "WAVE" 類型塊標識, 大寫。
ckid 4 char 表示"fmt" chunk的開始。此塊中包括文
件內部格式信息。小寫, 最后一個
字符是空格。
cksize 4 int32 文件內部格式信息數據的大小。
FormatTag 2 int16 音頻數據的編碼方式。1 表示是 PCM 編碼
Channels 2 int16 聲道數,單聲道為1,雙聲道為2
SamplesPerSec 4 int32 采樣率(每秒樣本數), 比如 44100 等
BytesPerSec 4 int32 音頻數據傳送速率, 單位是字節。其值為
采樣率×每次采樣大小。播放軟件
利用此值可以估計緩沖區的大小。
BlockAlign 2 int16 每次采樣的大小 = 采樣精度*聲道數/8(單
位是字節); 這也是字節對齊的最
小單位, 譬如 16bit 立體聲在這
里的值是 4 字節。播放軟件需要
一次處理多個該值大小的字節數
據,以便將其值用於緩沖區的調整。
BitsPerSample 2 int16 每個聲道的采樣精度; 譬如 16bit 在這
里的值就是16。如果有多個聲道,則
每個聲道的采樣精度大小都一樣的。
[cbsize] 2 int16 [可選]附加數據的大小。
[...] x
[ckid] 4 char "fact".
[cksize] 4 int32 "fact" chunk data size.
[fact data] 4 int32 解壓后的音頻數據的大小(Bytes).
ckid 4 char 表示 "data" chunk的開始。此塊中包含
音頻數據。小寫。
cksize 4 int32 音頻數據的長度
...... 文件聲音信息數據(真正聲音存儲部分)
[......] 其它 chunk
2.4 Windows平台上WAVEFORMAT結構的認識
PCM和非PCM的主要區別是聲音數據的組織不同,這些區別可以通過兩者的WAVEFORMAT結構來區分。
下面以PCM和IMA-ADPCM來進行對比。
WAVE的基本結構 WAVEFORMATEX 結構定義如下:
1 typedef struct
2 {
3 WORD wFormatag; //編碼格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
4 WORD nChannls; //聲道數,單聲道為1,雙聲道為2;
5
6 DWORD nSamplesPerSec; //采樣頻率;
7
8 DWORD nAvgBytesperSec; //每秒的數據量;
9
10 WORD nBlockAlign; //塊對齊;
11
12 WORD wBitsPerSample; //WAVE文件的采樣大小;
13
14 WORD cbSize; // The count in bytes of the size of extra
15 // information(after cbSize). PCM中忽略此值
16 } WAVEFORMATEX;
IMAADPCMWAVEFORMAT結構定義如下:
1 Typedef struct
2 {
3 WAVEFORMATEX wfmt;
4
5 WORD nSamplesPerBlock;
6
7 } IMAADPCMWAVEFORMAT;
IMA-ADPCM中的的wfmt->cbsize不能忽略,一般取值為2,表示此類型的WAVEFORMAT比一般的WAVEFORMAT多出2個字節。這兩個字符也就是nSamplesPerBlock。
"fact" chunk的內部組織
在非PCM格式的文件中,一般會在WAVEFORMAT結構后面加入一個 "fact" chunk, 結構如下:
1
2
3
4
5
6
7
8
9
|
typedef
struct
{
char
[4];
//“fact”字符串
DWORD
chunksize;
DWORD
datafactsize;
// 音頻數據轉換為PCM格式后的大小。
} factchunk;
|
datafactsize是這個chunk中最重要的數據,如果這是某種壓縮格式的聲音文件,那么從這里就可以知道他解壓縮后的大小。對於解壓時的計算會有很大的好處!
2.5 "data" chunk的內部組織
從 "data" chunk的第9個字節開始,存儲的就是聲音信息的數據了,(前八個字節存儲的是標志符 "data" 和后接數據大小size(DWORD)。這些數據可以是壓縮的,也可以是沒有壓縮的。
3. PCM數據格式
PCM(Pulse Code Modulation)也被稱為 脈碼編碼調制。PCM中的聲音數據沒有被壓縮,如果是單聲道的文件,采樣數據按時間的先后順序依次存入。(它的基本組織單位是BYTE(8bit)或WORD(16bit))
一般情況下,一幀PCM是由2048次采樣組成的( 參考 http://discussion.forum.nokia.com/forum/showthread.php?129458-請問PCM格式的音頻流,每次讀入或輸出的塊的大小是必須固定為4096B么&s=e79e9dd1707157281e3725a163844c49 )。
如果是雙聲道的文件,采樣數據按時間先后順序交叉地存入。如圖所示:
PCM的每個樣本值包含在一個整數i中,i的長度為容納指定樣本長度所需的最小字節數。
首先存儲低有效字節,表示樣本幅度的位放在i的高有效位上,剩下的位置為0,這樣8位和16位的PCM波形樣本的數據格式如下所示。
樣本大小 數據格式 最小值 最大值
8位PCM unsigned int 0 225
16位PCM int -32767 32767
參考資料:
[1]http://redsoft.ycool.com/post.2232742.html
[2]http://dev.firnow.com/course/3_program/hb/hbxl/20100803/518348.html
[3]http://hi.baidu.com/kindyb/blog/item/0a314f8859489c93a4c27297.html
[4]http://hi.baidu.com/kindyb/blog/item/353f4813df8799055aaf5397.html
[5]http://hi.baidu.com/kindyb/blog/item/2f31daa93f5ed4fb1e17a291.html
[6]http://hi.baidu.com/bigbigant/blog/item/7b91aa01e46dd4021d958317.html