语言信号处理

**語音處理**（Speech processing），又稱**語音訊號處理**、**人聲處理**，其目的是希望做出想要的訊號，進一步做語音辨識，應用到手機介面甚至一般生活中，使人與電腦能進行溝通。

## 語音的相關常識

### 一般聲音檔格式

* 取樣頻率：22050Hz
* 單聲道或雙聲道
* 每筆資料用 8 個 bit 來表示
* 電腦中沒有經過任何壓縮的聲音檔：\*.wav

### 聲音的頻率

* 頻率範圍

人耳可以辨識頻率：20Hz ~ 20000Hz

說話：150~2000Hz

電話系統頻域：小於 3500Hz

電腦音效卡取樣頻率：44100Hz (最新技術可達 192K)(一般用 22050Hz、11025Hz 即可)

* 超音波 (ultrasound) 與次聲波 (infrasound)

超音波 (ultrasound)：> 20000Hz

次聲波 (infrasound)：< 20Hz

* 人對於頻率的分辨能力，是由於頻率的 **"比"** 決定

對人類而言，300Hz 和 400Hz 之間的差別，與 3000Hz 和 4000Hz 之間的差別是相同的

### 聲音的波長

* 波長較長：傳播距離較遠，但容易散射
* 波長較短：衰減較快，但傳播方向較接近直線

### 聲音的速度

* 聲音在空氣中傳播速度： 每秒 340 公尺 (15°C 時)
* 一般人，耳翼到鼓膜之間的距離： 2.7 公分。所以，人類對 3000Hz 左右頻率的聲音最敏感
* 每增加 1°C，聲音的速度增加 0.6 m/sec
* 聲音在水中的傳播速度是 1500 m/sec，在鋁棒中的傳播速度是 5000 m/sec

### 分貝

* 分貝 (dB)： $10log_{\text{10}}(P/C)\,\!$

其中 P 為音強 (正比於振福的平方)，C 為 0dB 時的音強

* 每增加 10dB，音強增加 10 倍，振幅增加 $10^{0.5}\,\!$ 倍；每增加 3dB ，音強增加 2 倍，振幅增加 $2^{0.5}\,\!$ 倍

### 音樂訊號

* 電子琴 Do 的頻率：

低音 Do: 131.32 Hz

中音 Do: 261.63 Hz

高音 Do: 523.26 Hz

更高音 Do: 1046.52 Hz

* 音樂每增加八度音，頻率變為 2 倍
* 每一音階有 12 個半音。增加一個半音，頻率增加 $2^{1/12}\,\!$ 倍 (1.0595 倍)
* 音樂通常會出現「和弦」(chord) 的現象。除了基頻 $f_{\text{0}}\,\!$ Hz 之外，也會出現 2 $f_{\text{0}}\,\!$ Hz, 3 $f_{\text{0}}\,\!$ Hz, 4 $f_{\text{0}}\,\!$ Hz , …… 的頻率

### 語音處理的工作

* 語音編碼 (Speech Coding)
* 語音合成 (Speech Synthesis)
* 語音增強 (Speech Enhancement)
* 語音辨認 (Speech Recognition)

音素→音節→詞→句→整段話

* 說話人辦認 (Speaker Recognition)
* 其他：語意、語言、情緒

### 子音和母音

* 母音： 依唇型而定。母音的能量大，頻率偏低，時間較長，出現在子音後或獨立出現
* 子音： 在口腔，鼻腔中某些部位將氣流暫時堵住後放開。子音的能量小，頻率偏高，時間較短，出現在母音前

### 語意學的角色

* 以「語意學」或「機率」來補足語音辨識的不足

例如：經過判定，一個聲音可能是

ㄅㄧ ㄖㄢ ㄆㄧ ㄖㄢ

ㄅㄧ ㄌㄢ ㄆㄧ ㄌㄢ

這個聲音是「必然」的機率比較大。

ㄅㄛ ㄅㄛ ㄆㄛ ㄆㄛ

可能是「伯伯」，也可能是「婆婆」，看上下文

* 當前主流的語音辨識技術：

梅爾頻率倒頻譜 (Mel-Frequency Cepstrum) + 語意分析 + 機器學習 (Machine Learning)

## 語音檔的處理 (Matlab)

### 讀取聲音檔

* 電腦中，沒有經過壓縮的聲音檔都是 \*.wav 的型態，有經過壓縮的聲音檔是 \*.mp3 的型態
* 讀取： audioread (2015 版本以後的 Matlab，**wavread** 將改為 **audioread**)
* 例： [**x**, **fs**] = audioread('C:\WINDOWS\Media\ringin.wav');

可以將 ringin.wav 以數字向量 **x** 來呈現。**fs**: 取樣頻率 (sampling frequency)

這個例子當中 size (**x**) = 122868 2 (2 指的是雙聲道 (**Stereo**，俗稱立體聲))，**fs** = 22050

* .wav 檔中所讀取的資料，值都在 -1 和 +1 之間
* 畫出聲音的波型

time = [0:size (x,1)-1]/fs; (x 是用 audioread 所讀出的向量)

subplot(2,1,1); **plot(time, x(:,1))**; xlim([time(1),time(end)])

subplot(2,1,2); **plot(time, x(:,2))**; xlim([time(1),time(end)])

### 繪出頻譜

X = fft(x(:,1));

X=X.';

N=length(X); N1=round(N/2);

dt=1/fs;

X1=[X(N1+1:N),X(1:N1)]\*dt; (shifting for spectrum)

f=[[N1:N-1]-N,0:N1-1]/N\*fs; (valid f)

plot(f, abs(X1));

### 聲音的播放

* sound(**x**): 將 **x** 以 8192Hz 的頻率播放
* sound(**x**, **fs**): 將 **x** 以 **fs** Hz 的頻率播放

註：**x** 必須是 1 個 column (或 2 個 columns)，且 **x** 的值應該介於 -1 和 +1 之間

* soundsc(**x**, **fs**): 自動把 **x** 的值調到 -1 和 +1 之間再播放

### 製作.wav 檔：audiowrite

audiowrite(filename, x, fs)

將數據 **x** 變成一個 \*.wav 檔，取樣頻率為 **fs**Hz

註：**x** 必須是 1 個 column (或 2 個 columns)，且 **x** 的值應該介於 -1 和 +1

### 錄音的方式

#### 基本指令說明

以下這三個指令要並用才可以錄音

* recorder = audiorecorder (Fs, nb, nch); (提供錄音相關的參數)

Fs: sampling frequency

nb: using nb bits to record each data

nch: number of channels (1 or 2)

* recordblocking (recorder, Sec); (錄音的指令)

recorder: the parameters obtained by the command **audiorecorder**

Sec: the time length for recording

* audioarray = getaudiodata(recorder);

(將錄音的結果，變成 audioarray 這個 column vector，如果是雙聲道，則 audioarray 是兩個 column vectors)

#### 範例

Sec = 3; (錄音的時間為三秒)

Fs = 8000; (sampling frequency 為 8000 Hz)

recorder = audiorecorder(Fs, 16, 1);

recordblocking(recorder, Sec);

audioarray = getaudiodata (recorder); (錄音結果為 audioarray，是一個 column vector (如果是雙聲道，則是兩個 column vectors))

sound (audioarray, Fs); (播放錄音的結果)

t = [0:length(audioarray)-1]./Fs;

plot (t, audioarray); (將錄音的結果用圖畫出來)

xlabel('sec','FontSize',16);

audiowrite ('test.wav', audioarray, Fs) (將錄音的結果存成 \*.wav 檔)

 

## 語音檔的處理 (Python)

### 安裝模組

* pip install numpy
* pip install scipy
* pip install matplotlib # plot
* pip install pipwin
* pipwin install simpleaudio # vocal files
* pipwin install pyaudio

### 讀音訊檔

要先 import 相關模組: import wave

#### 讀取音檔

wavefile = wave.open('C:/WINDOWS/Media/Alarm01.wav', 'rb')

獲得音檔取樣頻率和音訊長度:

fs =wavefile.getframerate() # sampling frequency

num\_frame = wavefile.getnframes() # length of the vocal signal

>>> fs

22050

>>> num\_frame

122868

#### 讀取波形與數據

要先 import 相關模組: import numpy as np

* str\_data = wavefile.readframes(num\_frame)
* wave\_data = np.frombuffer (str\_data, dtype=np.int16) # 轉成整數型態
* wave\_data = wave\_data / max(abs(wave\_data)) # normalization
* n\_channel = 2
* wave\_data = np.reshape (wave\_data, (num\_frame, n\_channel)) # 若為雙聲道音檔需要做 reshape

#### 畫出音訊波形圖

要先 import 相關模組: import matplotlib.pyplot as plt

* time = np.arange(0, num\_frame)\*1/fs
* plt.plot(time, wave\_data)
* plt.show()

### 畫出頻譜

要先 import 相關模組: from scipy.fftpack import fft

* fft\_data = abs (fft (wave\_data [:,1]))/fs # only choose the 1st channel # 注意要乘上 1/fs
* n0=int(np.ceil(num\_frame/2))
* fft\_data1=np.concatenate ([fft\_data [n0:num\_frame],fft\_data [0:n0]]) # 將頻譜後面一半移到前面
* freq=np.concatenate ([range (n0-num\_frame,0),range (0,n0)])\*fs/num\_frame # 頻率軸跟著調整
* plt.plot(freq,fft\_data1)
* plt.xlim (-1000,1000) # 限制頻率的顯示範圍
* plt.show()

### 播放聲音

要先 import 相關模組: import simpleaudio as sa

* n\_bytes =2 # using two bytes to record a data
* wave\_data = (2\*\*15-1)\* wave\_data # change the range to -215 ~ 215
* wave\_data = wave\_data.astype(np.int16)
* play\_obj = sa.play\_buffer(wave\_data, n\_channel, n\_bytes, fs)
* play\_obj.wait\_done()

### 製作音檔

* f = wave.open('testing.wav', 'wb')
* f.setnchannels (2) # 設定聲道數
* f.setsampwidth (2) # 每個 samples 有幾個位元組
* f.setframerate (fs) # 設定取樣頻率
* f.writeframes(wave\_data.tobytes())
* f.close()

### 錄音

要先 import 相關模組: import pyaudio

import pyaudio

pa=pyaudio.PyAudio()

fs = 44100

chunk = 1024

stream = pa.open(format=pyaudio.paInt16, channels=1,rate=fs, input=True, frames\_per\_buffer=chunk)

vocal=[]

count=0

## 時頻分析結果分析

### Matlab

#### 畫出時頻分析結果

可採行兩種方式：

* 使用 mesh 指令畫出立體圖 (但結果不一定清楚，且執行時間較久)
* 將 amplitude 變為 gray-level，用顯示灰階圖的方法將結果表現出來

假設 y 是時頻分析計算的結果

image (abs (y)/max (max (abs (y)))\*C) % C 是一個常數，可以選 C=400

或 image (t, f, abs (y)/max (max (abs (y)))\*C)

colormap (gray (256)) % 變成 gray-level 的圖

set (gca,'Ydir','normal') % 若沒這一行，y-axis 的方向是倒過來的

set (gca,'Fontsize',12) % 改變橫縱軸數值的 font sizes

xlabel('Time (Sec)','Fontsize',12) % x-axis

ylabel('Frequency (Hz)','Fontsize',12) % y-axis

title('STFT of x(t)','Fontsize',12) % title

#### 計算程式執行時間的指令

tic (這指令如同按下碼錶)

toc (show 出碼錶按下後已經執行了多少時間)

註：通常程式執行第一次時，由於要做程式的編譯，所得出的執行時間會比較長。程式執行第二次以後所得出的執行時間，是較為正確的結果

### Python

#### 事前安裝模組

* pip install numpy
* pip install matplotlib

#### 畫出時頻分析結果

假設 y 為時頻分析結果 (應為二維的矩陣數列)，將 y 以灰階方式畫出來

import numpy as np

import matplotlib.pyplot as plt

C = 400

y = np.abs(y) / np.max(np.abs(y)) \* C

plt.imshow (y, cmap='gray', origin='lower') # 加上 origin='lower' 避免上下相反

plt.xlabel('Time (Sec)')

plt.ylabel('Frequency (Hz)')

plt.show()

若要加上座標軸數值 (在 plt.show () 之前加上以下程式碼)

x\_label = ['0', '10', '20', '30'] # 橫軸座標值

y\_label = ['-5', '0', '5'] # 縱軸座標值

plt.xticks(np.arange(0, x\_max, step=int(x\_max/(len(x\_label)-1)), x\_label)

plt.yticks(np.arange(0, y\_max, step=int(y\_max/(len(y\_label)-1)), y\_label)

 

## 語音訊號的來源

人聲是由於聲帶震動，而產生聲音。當運動肌肉擠壓，使肺臟中的空氣通過聲帶時，空氣流動使得聲帶做周期性的震動，又再一次震動了空氣，接著，帶著動能的空氣離開氣管到達口腔或鼻腔，在腔室中震動，最後離開在嘴唇傳到人耳變成聲音。

若調整口腔中舌頭的位置，會產生不同種類的聲音，如果舌頭沒有做太多的動作，空氣只有在口腔中共振，接著直接流出嘴唇，會產生母音，若提起舌頭，使口鼻腔相通，則會出現鼻音。

## 語音訊號分類

從中文發音的觀點來說，聲音仍可分為子音與母音，母音和子音可以用兩種方式區分：

* 發聲方式：一般而言，母音跟嘴唇形狀有關，而且不與鼻腔共振。相對而言，在發出子音時，就會運用到鼻腔配合發聲。
* 頻譜分析：從頻譜上觀察可以發現子音的訊號頻率較高，持續時間較短，且會在母音之前出現。而母音的頻率較低，持續時間較長，在子音後或獨立出現，另外，母音的能量也會比子音大。

下面列出中文注音符號中的母音、子音及其拼音。

* 母音：ㄚ ㄛ ㄜ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ 一 ㄨ ㄩ
* 子音：ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ

## 語音的架構

要分析語音訊號前，必須先了解其架構，語音的要素從小到大分別是：音素→音節→詞彙→句子→整段話。

音素是聲音的最小單位，例如「呵」這個字的音素，就是「ㄏ」和「ㄜ」，但是音素和注音符號並不相等，例如「鷗」雖然只有「ㄡ」這個母音，但是由於是雙母音，所以會把他拆成兩個音素。音節在中文而言，就是只一個字，例如：「天天開心」就有四個音節。詞彙是文字組成的有意義片段，各種不同的詞彙集結成句子，最後變成整段話，這就是語音的架構。

## 語音處理方法

用麥克風或其他裝置收到的類音聲音訊號，經由類比數位轉換裝置，將資料數據化進行處理，最後再經過數位類比轉換裝置輸出。因此，我們在處理時是針對數位訊號，語音訊號是一種離散時間訊號。其訊號處理流程如下：

1. 收取並取樣訊號：利用麥克風或各種收音裝置，收取類比語音訊號，再用 ADC 裝置 (如類比數位轉換卡) 把類比訊號變成數位訊號，接著根據奈奎斯特理論作取樣，若不符合理論則會造成訊號失真。
2. 量化及編碼：由於電腦中的記憶都是 0 和 1，因此要將所收到的數據用一段適合的 0 跟 1 去儲存，這個動作就稱為量化，所使用的 0 與 1 越多，所需的記憶體越多。接著利用編碼器將數值以波形呈現，因此雖然是數位訊號，但是在電腦中所見到的是類比。
3. 訊號標準化：將語音訊號標準化，使其數值都落在同一個範圍。
4. 音框選擇：由於語音訊號是一段很長的訊號，因此會針對想要處理的部分取音框。
5. 端點偵測：端點偵測的目的是使訊號處理的範圍更精確，只要設定一個音量閾值，若訊號小於閾值，則將其視為沒訊號，但是若雜訊過高，則會產生誤差。
6. 去雜訊：由於雜訊多集中在高頻的部分，因此利用簡單的高頻濾波器，就可以去掉部分雜訊。

## 基本處理方法

語音訊號是屬於離散時間系統，因此會用離散時間的傅立葉轉換去做處理，除此之外，摺積、窗函數都是一定會使用到的處理方法。

* 離散時間傅立葉轉換：

　
: $x[n]={\frac {1}{2\pi }}\int \limits _{-\pi }^{\pi }X(\omega )e^{-j\omega n}\,d\omega .$

　
: $X(\omega )=\sum _{k=0}^{N-1}x[n]e^{-j\omega n}$

* 摺積：

兩訊號做 convolution 等於，兩訊號先做傅立葉轉換，相乘後再做反傅立葉轉換，藉此可以更快速的處理訊號。

　
: ${\mathcal {F}}\{h*x\}={\mathcal {F}}\{h\}\cdot {\mathcal {F}}\{x\}$

## 語音處理的應用

語音處理主要有兩個目的：

* 減少訊號雜訊，做出想要的訊號模組。
* 進行語音辨識，使人可以利用語言與電腦溝通。

 

## 參見

* 語音識別

## 參考文獻