2016年4月11日

語音資料處理: Resampling

由於電話的語音資料,通常是以 8000 Hz 進行聲音取樣 (sampling) 的頻率,也就是每秒只取得 8000 個資料點,但如果是不同的聲音應用,可能會需要不同的取樣頻率,如果取樣的頻率越高,資料點越多,當然聲音的品質會越高,但如果要在不同的應用之間,讓語音資料能夠互相傳遞,就會面臨到 Resampling 的問題。

Audio 相關名詞的基本概念

通常從麥克風取到的語音資料,就是一連串的 byte array,要處理語音資料之前,要先對 Audio 有些基本的概念,關於 Audio 的相關概念,可以參考這個網頁 演算法筆記 - Audio)

sampling rate 取樣頻率:因為語音資料的數位化,電腦無法處理連續的資料,只能用大量的資料點,來趨近原始的連續函數,如果每一秒鐘取樣的資料點越多,就會越接近原始的連續函數,當然聲音的品質就越好。電腦的聲音檔案,通常採用 48000Hz 或 44100Hz 。手機與電話的聲音傳輸為 8000Hz 。

bit depth 位元深度:一個訊號用多少個位元紀錄。數值越高,音質越好。電腦的聲音檔案,通常採用 16 bits 或 24 bits。比較常見的是 16 bits 也就是兩個 bytes,剛好可對應到資料型別為 short。

channel 聲道:同時播放的聲音訊號總共幾條。比較常見的就是 mono 單聲道或是 stereo 雙聲道。當我們用 16 bits 進行聲音取樣時,如果是 mono,就是每 2個 bytes 一個取樣資料點,如果是 stereo,因為每一次取樣,會產生兩個資料點,所以取得的資料,就會是左聲道 2 bytes 加上右聲道 2 bytes,左右聲道交錯的 byte array。

在取樣理論中,原始的聲音音波如果是 x Hz,這時候取樣的頻率至少必須要是 2x Hz 兩倍以上,才能記錄原始震波的上下數值,換句話說,當 samping rate 為 48000Hz,最多只能記錄到 24000Hz 的聲音。

由於人類的聽覺範圍為 20Hz ~ 20000 Hz,但是一般的電話為了減少傳輸的資料量,只使用 8000Hz,也就是只能記錄到 4000Hz 以下的聲音,所以在電話裡面聽到的另外一方的聲音,通常會跟直接面對面聽到的聲音不一樣。

Endianness

由於每一個 sample 的聲音資料超過了一個 byte 的容量,在程式中就必須要用 2個 bytes 以上的容量來儲存資料,這時候,如果我們需要以 sample 點來進行一些運算處理,勢必要用某一個資料型別來將超過一個 byte 的資料讀取進來。

因為聲音資料是 byte array 的方式傳遞進來,但是當一個資料點要轉換成 byte array 時,就會有順序的問題,也就是 Endianess 的問題。

例如: 0x12345678,首先拆成 bytes:12 34 56 78
然後 byte array 的表示方式就有兩種
Little Endian: 最低位元組在後面 78 56 34 12
Big Endian: 最低位元組在最前面 12 34 56 78

Big-Endian 和 Little-Endian 並不是計算機工程師定義的名稱, 而是英文作家 Jonathan Swift 在將近 300 年前創造的名詞!這個名詞出現於 Swift 創作的著名小說 "Gulliver's Travels", 中文通常翻譯作《格利佛遊記》或是《小人國歷險記》。

格利佛意外抵達 Lilliput 的時候,該國正在內戰。內戰分成兩大派系:Big-Endian 和 Little-Endian。Big-Endian (保守派) 堅持要從雞蛋比較大的那一頭敲開蛋殼,而 Little-Endian (改革派) 堅持要從雞蛋比較小的那一頭敲開蛋殼。雞蛋比較大的那一頭叫做 big-end,因此支持大頭開蛋者就叫做 big-endian;同理,另一派就叫做 Little-endian。

ref: 用 C 語言窺探記憶體

以目前常見的CPU為例:

  • INTEL X86、DEC VAX 使用 Little-Endian 設計;
  • HP、IBM、MOTOROLA 68K 系列使用 Big-Endian 設計;
  • POWERPC 同時支援兩種格式,稱為 BI-ENDIAN。
  • 而 JVM 這個 "Virtual Machine" 是採用 Big-Endian

在聲音處理的程式中,會需要注意是用哪一種位元排序方式,因為不同的排序會造成完全不同的數值。

Resampling

因為我們需要在不同的取樣應用程式中,轉換語音資料,如果是從取樣頻率 16000 Hz 要轉換成取樣頻率 8000 Hz,最簡單的想法,就是把多餘的資料丟掉,把原本 16000 Hz 的 byte array 裡面的資料丟掉一半,就會變成 8000 Hz。

但是如果是要把 8000 Hz 轉換成 16000 Hz 的語音資料,要用什麼方法無中生有呢?這時候可以用另一個最簡單的方式,就是內插法 linear interpolation。

Interpolation 內插法 就像是以下這個函數圖形:

(x0, y0) 以及 (x1, y1) 分別是已知的兩個座標點,所以只要計算出這條線的斜率,利用 y=mx+c 這樣的方程式,我們可以產生出新的資料點,而且這個值是根據原本兩個鄰近的資料點產生出來的。

如果我們需要的新的資料點的 x 的位置,並不是剛好在 x0 及 x1 的中間,就像是 Linear interpolation 的圖形一樣,這時候我們必須要讓比較靠近 x 的 x0 有比較高的 weight 權重。

如果不是用線性函數,而是用高次方的函數,這就稱為 Polynomial interpolation

類似的問題,會在單聲道以及雙聲道的轉換發生,因為立體聲的音效,左右聲道的聲音資料不同,當立體聲雙聲道的語音資料要轉換成單聲道時,可以用內插法的方式進行資料合併,相反地,當單聲道轉換成雙聲道,理論上應該只要複製一份一樣的資料就好了。

濾波器 filter

濾波器是修改聲音的工具,例如刪除聲音的高頻部分,稱做 lowpass filter。

剛剛在取樣理論中有提到,原始的聲音音波如果是 x Hz,這時候取樣的頻率至少必須要是 2x Hz 兩倍以上,才能記錄原始震波的上下數值,換句話說,當 samping rate 為 48000Hz,最多只能記錄到 24000Hz 的聲音。

因此,當我們要把聲音訊號,由 sample rate 16000 Hz 降低為 8000 Hz 的時候,就會發生頻率超過 4000 Hz 的聲音資料,會收錄在降低取樣的聲音資料中,這時候就會發生高頻的噪音,因此我們必須在降低取樣頻率之前,就先將資料放入 low-pass filter 裡面,先將超過 4000Hz 頻率的資料濾除,然後再進行 resampling。

低通濾波器 Low-pass filter

References

How to convert between (most) audio formats in .NET