嵌入式里有時候也會和音頻打交道,比如最近特別火的智能音箱產品,離不開前端的音頻信號採集、降噪,中間的語音識別(ASR)、自然語言處理(NLP),以及後端的文語合成(TTS)、音頻播放。音頻信號採集是處理聲音的第一步,要採集音頻就離不開PCM編碼,音頻採集完成自然需要保存,waveform格式(.wa... ...
大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是PCM編碼及Waveform音頻文件格式。
嵌入式里有時候也會和音頻打交道,比如最近特別火的智能音箱產品,離不開前端的音頻信號採集、降噪,中間的語音識別(ASR)、自然語言處理(NLP),以及後端的文語合成(TTS)、音頻播放。音頻信號採集是處理聲音的第一步,要採集音頻就離不開PCM編碼,音頻採集完成自然需要保存,waveform格式(.wav)是一種最經典的音頻文件格式。今天痞子衡就給大家詳細介紹PCM編碼以及waveform文件格式。
一、聲音基礎
眾所周知,聲音是由物體振動產生的聲波,聲音通過介質(空氣或固體、液體)傳播並能被人聽覺器官所感知。發音物體情況(材料,距離,振動強度等)不同,產生的聲音也不同。為了區分不同的聲音,我們主要用如下三個參數來描述聲音的特征:
- 音量:人主觀上感覺到的聲音大小(也叫響度),由“振幅”(amplitude)和人離聲源的距離決定。
- 音調:聲音的高低(高音、低音),由“頻率”(frequency)決定,頻率越高音調越高。
- 音色:音色是一種抽象的東西,波形決定了聲音的音色。聲音因不同發聲物體材料而具有不同特性,波形是把這個抽象特性直觀的表現出來。典型的音色波形有方波,鋸齒波,正弦波,脈衝波等。
三大參數里除了音色沒有度量單位外(可以認為音色是聲音的UID,每種音色都是獨一無二的),音量和音調均有度量單位,這意味著音量和音調是可調整的,也是聲音之間可對比的特征參數。
1.1 音量單位-分貝(dB)
聲波是一種機械波(壓力波)。聲波(空氣質點)的連續振動,使空氣分子不斷交替的壓縮和鬆弛,使大氣壓迅速產生起伏,這種氣壓的起伏部分,就稱為聲壓。聲壓的振幅表示質點離開平衡位置的距離,反映從波形波峰到波谷的壓力變化,以及波所攜帶的能量的多少。
聲壓值雖然可以反映音量大小,但人們日常生活中遇到的聲音,若以聲壓值表示,變化範圍非常大(達到六個數量級以上),並且人體聽覺對聲信號強弱刺激反應不是線形的,而是成對數比例關係。因此音量並不是聲壓值來計量,而是用分貝來計量,首先來看分貝計算的標準公式:
NdB = 10 * log10 (Pi / Po)
上述公式中Po為基準聲壓值,NdB即是聲壓信號Pi對基準聲壓Po的分貝值。從公式可以看出分貝是指兩個相同類型物理量(Pi、Po)的比較結果,它是無量綱的。分貝又稱為被量度量Pi的"級",它代表被量度量比基準量高出多少"級"。下麵列舉常見分貝值:
分貝值 | 人耳感覺 |
---|---|
1dB | 剛能聽到的聲音 |
1 - 15dB | 感覺非常安靜 |
20 - 40dB | 耳語音,冰箱的嗡嗡聲 |
40 - 60dB | 室內正常交談的聲音 |
60 - 70dB | 走在鬧市區的感覺,有點吵 |
70 - 85dB | 汽車穿梭在馬路上,85dB是保護聽力的一般要求 |
85 - 100dB | 摩托車啟動,裝修電鑽 |
100 - 150dB | 飛機起飛、燃放煙花爆竹 |
1.2 音調單位-頻率(Hz)
頻率是每秒經過一給定點的聲波周期數量,其單位是Hz,1KHz表示每秒經過一給定點的聲波有1000個周期。根據頻率範圍,我們將聲波分為如下三種:
聲波類別 | 頻率範圍 | 特性與應用 |
---|---|---|
次聲波 | 低於20Hz | 部分動物(狗、大象)能發出/感知,常用於自然災害監測 |
可聞聲 | 20Hz ~ 20KHz | 人的聽覺感知範圍 |
超聲波 | 高於20KHz | 部分動物(狗、蝙蝠)能發出/感知,常用於深海探測(聲吶)、醫學檢查(B超) |
關於聲波頻率特別要提的是,聲波可以被分解為不同頻率不同強度正弦波的疊加,這種變換(或分解)的過程,稱為傅立葉變換(Fourier Transform)。
二、PCM編碼原理
聲波是一種在時間上和振幅上均連續的模擬量,在嵌入式里要想研究聲波,首先需要將聲波轉換成一連串電壓變化的模擬電信號,麥克風器件就是一種採集聲波信號並將其轉換成模擬電壓信號輸出的裝置。
有了聲波的模擬電壓信號,下一步需要將模擬信號數字化,即將模擬信號經過模數轉換器(A/D)後變成數字信號,說白了就是將聲音數字化。最常見的聲音數字化方式就是脈衝編碼調製PCM(Pulse Code Modulation),PCM是70年代末發展起來的技術,最早應用於由飛利浦和索尼公司共同推出的CD上,下圖給出了PCM編碼全過程:
從上圖中我們可以看到PCM編碼主要有三個過程:採樣、量化、編碼,在這過程中主要有4個參數用於評價PCM:聲道數、採樣率、量化位數、編碼方式。痞子衡會在下麵逐一介紹PCM編碼過程時穿插介紹這4個參數:
2.1 採樣
所謂採樣,即按一定的採樣頻率將模擬信號變成時間軸上離散的抽樣信號的過程。原則上採樣頻率越高,聲音的質量也就越好,聲音的還原也就越真實。
採樣率即每秒從模擬信號中提取並組成離散信號的採樣個數,用赫茲(Hz)來表示。說到採樣率有一個不得不提的著名定律,即香農(Shannon)/奈奎斯特(Nyquist)採樣定律,該定律表明採樣頻率必須大於或等於所傳輸的模擬信號的最高頻率的2倍,才能不失真地恢復模擬信號。
最常說的“無損音頻”,一般都是指傳統CD格式中的44.1kHz/16bit的文件格式,而之所以稱為無損壓縮,是因為其包含了20Hz-22.05kHz這個完全覆蓋人耳可聞範圍的聲音頻率而得名。
關於聲道數,其實非常好理解,就是採集聲音的通道數,我們知道有單聲道(mono),立體聲(雙聲道stereo)、杜比7.1環繞聲,其實就是聲音採集的通道數有差別,聲道越多,越能體現聲音的空間立體效果。
2.2 量化
前面採樣得到的抽樣信號雖然是時間軸上離散的信號,但仍然是模擬信號,其採樣值在一定的取值範圍內,可有無限多個值,必須採用“四捨五入”的方法把樣值分級“取整”,使一定取值範圍內的樣值由無限多個值變為有限個值,這一過程稱為量化。
量化位數指的是描述數字信號所使用的位數。如麥克風采集的電壓範圍為0-3.3V,8bit的量化精度為3.3V/256,16bit的量化精度為3.3V/65536。
2.3 編碼
量化後的抽樣信號就轉化為按抽樣時序排列的一串十進位數字碼流,即十進位數字信號。簡單高效的數據系統是二進位碼系統,因此,應將十進位數字碼變換成二進位編碼,這種把量化的抽樣信號變換成給定字長(量化位數)的二進位碼流的過程稱為編碼。
編碼方式種類非常多,其對比可見 Comparison of audio coding formats,PCM音頻格式編碼常見有四種:PCM(Linear PCM)、ADPCM(Adaptive differential PCM)、 A-law(A律13折線碼)、μ-law(μ律15折線碼),最簡單的當然是下圖所示的LPCM(示例為4bit),這是一種均勻量化編碼,廣泛用於 Audio CD, AES3, WAV, AIFF, AU, M2TS, VOB中。
除LPCM外,A-law和μ-law是兩種不得不提的非均勻量化編碼,這兩種非均勻量化編碼是為了提高小信號的信噪比,其基本思想是在量化之前先讓信號經過一次處理,對大信號進行壓縮而對小信號進行較大的放大,這一處理過程通常也稱為“壓縮量化”。壓縮量化的實質是“壓大補小”,使小信號在整個動態範圍內的信噪比基本一致。下麵是這兩種編碼與LPCM的 對比圖。
三、Waveform文件格式解析
前面講的PCM編碼後的聲音數據是需要保存的,WAVE文件常常用來保存PCM編碼數據。WAVE文件是微軟公司(Microsoft)開發的一種聲音文件格式,用於保存Windows平臺的音頻信息資源,被Windows平臺及其應用程式所廣泛支持,WAVE文件預設打開工具是WINDOWS的媒體播放器。
3.1 RIFF文件格式標準
WAVE文件是以微軟RIFF格式為標準的,RIFF全稱為資源互換文件格式(Resources Interchange File Format),是Windows下大部分多媒體文件遵循的一種文件結構。RIFF文件所包含的數據類型由該文件的擴展名來標識,能以RIFF格式存儲的數據有很多:音頻視頻交錯格式數據(.AVI)、波形格式數據(.WAV)、點陣圖數據格式(.RDI)、MIDI格式數據(.RMI)、調色板格式(.PAL)、多媒體電影(.RMN)、動畫游標(.ANI)等。
如下代碼所示的CK結構體是RIFF文件的基本單元,該基本單元也稱 Chunk。其中ckID用於標識塊中所包含的數據類型,其取值可有'RIFF'、'LIST'、'fmt '、'data'等;ckSize表示存儲在ckData域中的數據長度(不包含ckID和ckSize的大小);ckData存儲數據,數據以位元組為單位存放,如果數據長度為奇數,則最後添加一個空位元組。
由於RIFF文件結構最初是由Microsoft和IBM為PC機所定義,RIFF文件是按照小端little-endian位元組順序寫入的。
typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef DWORD FOURCC; // Four-character code
typedef struct {
FOURCC ckID; // The unique chunk identifier
DWORD ckSize; // The size of field <ckData>
BYTE ckData[ckSize]; // The actual data of the chunk
} CK;
Chunk是可以嵌套的,但是只有ckID為'RIFF'或者'LIST'的Chunk才能包含其他的Chunk。標誌為'RIFF'的Chunk是比較特殊的,每一個RIFF文件首先存放的必須是一個'RIFF' Chunk,並且只能有這一個標誌為'RIFF'的Chunk。
更多RIFF的知識詳見這個網站鏈接 RIFF (Resource Interchange File Format),鏈接里收集了很多介紹RIFF的資源。
3.2 WAVE文件結構
WAVE是Microsoft開發的一種音頻文件格式,它符合上面提到的RIFF文件格式標準,可以看作是RIFF文件的一個具體實例。既然WAVE符合RIFF規範,其基本的組成單元也是Chunk。一個 WAVE文件 通常有三個Chunk以及一個可選Chunk,其在文件中的排列方式依次是:RIFF Chunk,Format Chunk,Fact Chunk(附加塊,可選),Data Chunk,如下圖所示:
根據上面的WAVE文件結構圖,可以定義如下44bytes的wave_head_t用來描述WAVE文件的頭。如果你曾經接觸過Windows的音頻介面API,你會發現wave_fmt_t中的部分結構與標準MSDN里的 WAVEFORMAT 是一致的。
typedef char int8_t; //有符號8位整數
typedef short int16_t; //有符號16位整數
typedef int int32_t; //有符號32位整數
struct _wave_tag
{
int8_t riff[4]; //"RIFF",資源交換文件標誌
int32_t filesize; //文件大小(從下個地址開始到文件尾的總位元組數)
int8_t wave[4]; //"WAVE",文件標誌
} wave_tag_t;
struct _wave_format
{
int8_t fmt[4]; //"fmt ",波形格式標誌
int32_t chunksize; //文件內部Chunk信息大小
int16_t wFormatTag; //音頻數據編碼方式
int16_t wChanles; //聲道數
int32_t nSamplesPerSec; //採樣率
int32_t nAvgBytesPerSec; //波形數據傳輸速率(每秒平均位元組數)
int16_t nBlockAlign; //數據的調整數(按位元組計算)
int16_t wBitsPerSample; //樣本數據位數
} wave_fmt_t;
struct _wave_data
{
int8_t data[4]; //"data",數據標誌符
int32_t datasize; //採樣數據總長度
} wave_dat_t;
struct _wave_head
{
wave_tag_t waveTag;
wave_fmt_t waveFmt;
wave_dat_t waveDat;
} wave_head_t;
wave_head_t結構體內除了wFormatTag成員之外,其他都可以根據字面上的意思來理解。關於wFormatTag的具體定義,可見Windows SDK里的 mmreg.h文件,下麵列舉了幾個最常見Format的Tag值定義:
當WAVE文件的頭被解析成功後,下一步便是獲取WAVE文件里的聲音源數據,我們知道聲音文件有單聲道和多聲道之分,對於單聲道文件很好理解,聲音數據就是按序排放,而如果是立體聲(2聲道)文件,那麼左右聲道的聲音數據到底是怎麼排放的呢?下麵以一個示例立體聲文件數據(僅分析前72bytes)進行解釋:
offset(h)
00000000: 52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20
00000010: 10 00 00 00 01 00 02 00 22 56 00 00 88 58 01 00
00000020: 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00
00000030: 24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6
00000040: 3c f2 24 f2 11 ce 1a 0d
下圖是這個72bytes數據解析圖,從圖中可以看到,左右聲道的聲音數據是按塊(nBlockAlign指定)交替排放的。
更多WAVE的知識詳見這兩個網站鏈接 WAVE Audio File Format 和 Audio File Format Specifications,鏈接里收集了很多介紹WAVE的資源。
3.3 WAVE文件實例分析
WAVE文件格式我們都瞭解透徹了,下麵我們嘗試分析一個經典的WAVE文件:"Windows XP 啟動.wav",這個文件可以說是最知名的WAVE文件了,痞子衡特別喜歡這段music,讓我們直接用二進位編輯器HxD打開它:
按wave_head_t解析WAVE頭可知,這段wave是44.1kHz/16bit雙聲道線性PCM碼音頻,實際音頻數據總長度為1361076bytes(1361076(datasize)/176400(nAvgBytesPerSec)=7.7158秒),最後再用Adobe Audition(原Cool Edit)打開查看其波形圖:
至此,PCM編碼及Waveform音頻文件格式痞子衡便介紹完畢了,掌聲在哪裡~~~