語音處理

語音處理（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)：$10 log_{\text{10}} (P/C) \,\!$

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

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

### 音樂訊號

低音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 檔，取樣頻率為fsHz

註：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_datan0:num_frame,fft_data0: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)

## 語音訊號的來源

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

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

## 語音訊號分類

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

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

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

- 母音：ㄚ ㄛ ㄜ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ 一 ㄨ ㄩ

母音｜ㄚ｜ㄛ｜ㄜ｜ㄝ｜ㄞ｜ㄟ｜ㄠ｜ㄡ

漢語拼音｜a｜o｜e｜e｜ai｜ei｜ao｜ou

通用拼音｜a｜o｜e｜e｜ai｜ei｜ao｜ou

母音｜ㄢ｜ㄣ｜ㄤ｜ㄥ｜ㄦ｜一｜ㄨ｜ㄩ

漢語拼音｜an｜en｜ang｜eng｜er｜i,y｜u,w｜yu,ju

通用拼音｜an｜en｜ang｜eng｜er｜i,y｜u,w｜yu,ju

- 子音：ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ

子音｜ㄅ｜ㄆ｜ㄇ｜ㄈ｜ㄉ｜ㄊ｜ㄋ

漢語拼音｜b｜p｜m｜f｜d｜t｜n

通用拼音｜b｜p｜m｜f｜d｜t｜n

子音｜ㄌ｜ㄍ｜ㄎ｜ㄏ｜ㄐ｜ㄑ｜ㄒ

漢語拼音｜l｜g｜k｜h｜j｜q｜x

通用拼音｜l｜g｜k｜h｜j｜q｜x

子音｜ㄓ｜ㄔ｜ㄕ｜ㄖ｜ㄗ｜ㄘ｜ㄙ

漢語拼音｜zh｜ch｜sh｜r｜z｜c｜s

通用拼音｜zh｜ch｜sh｜r｜z｜c｜s

## 共振峰與母音辨識

在語音頻譜中，母音常會出現數個明顯的頻譜峰值，稱為「共振峰」，通常以 $F_1$、$F_2$、$F_3$ 表示。不同母音的共振峰位置不同，因此可作為辨別母音的重要聲學線索。一般而言，第一共振峰 $F_1$，與舌位高低、口腔開合程度有關，第二共振峰 $F_2$，則常與舌位前後有關。

從工程觀點來看，共振峰可視為聲道濾波器在特定頻率附近產生的共振結果。當聲帶或氣流所產生的激發訊號通過聲道時，某些頻率成分會被加強，因而在頻譜上形成峰值。雙母音的頻譜則會隨時間改變，通常會由一個母音的共振峰型態逐漸移動到另一個母音的型態。

## 語音的架構

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

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

## 語音處理方法

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

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

## 發音模型與短時分析

從工程觀點來看，語音訊號可用簡化的發音模型描述。若以離散時間訊號表示，可寫成：

$$x[n]=e_{\mathrm{p}}[n]\ast g[n]\ast h[n]\ast r[n]$$

其中，$\ast$ 表示摺積(convolution)，$e_{\mathrm{p}}[n]$ 可視為激發訊號，$g[n]$ 表示聲帶模型，$h[n]$ 表示口腔或聲道模型，$r[n]$ 表示唇部輻射模型。

轉換到 Z 域後，可表示為：

$$X(z)=E_{\mathrm{p}}(z)G(z)H(z)R(z)$$

此模型可用來說明語音中音量、音高、子音與母音等特徵的形成。

由於語音訊號會隨時間變化，但在足夠短的時間內可近似為平穩訊號，因此語音處理常使用短時傅立葉轉換（Short-Time Fourier Transform, STFT）進行時頻分析：

$$G(t,f)=\int_{-\infty}^{\infty} w(t-\tau)g(\tau)e^{-j2\pi f\tau}\,d\tau$$

其中，$w(t-\tau)$ 為窗函數(window function)，用來強調時間 $t$ 附近的訊號。短時傅立葉轉換可形成頻譜圖，用於觀察語音能量在時間與頻率上的分布。

## 基本處理方法

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

$$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\}$$

在語音辨識、語音編碼與說話人辨識中，原始波形通常不會直接作為模型輸入，而是先轉換成能描述語音頻譜、聲道特性或時間變化的特徵。常見方法包括線性預測編碼、梅爾頻率倒頻譜係數與隱馬可夫模型等。如下：

- 線性預測編碼 (linear predictive coding, LPC) 是一種以線性模型描述語音訊號的方法。其基本假設是目前的語音樣本可以由前面若干個樣本的線性組合近似預測：$\hat{s}[n]=\sum_{k=1}^{p}a_k s[n-k]$，其中，為模型階數，為線性預測係數。預測誤差可寫成 $e[n]=s[n]-\hat{s}[n]$。LPC 可用少數參數描述語音訊號的頻譜包絡，因此常用於語音編碼、語音合成與語音分析。從發音模型的角度來看，LPC 也可視為對聲道濾波器的近似描述。
- 梅爾頻率倒頻譜係數 (Mel-frequency cepstral coefficients, MFCC) 是語音辨識與說話人辨識中常見的聲學特徵。MFCC 的設計考慮人耳對頻率的非線性感知，常使用梅爾尺度轉換： $m=2595\log_{10}(1+f/700)$ 計算流程通常包括分幀、加窗、傅立葉轉換、梅爾濾波器組、取對數與離散餘弦轉換。若第 $m$ 梅爾濾波器的能量為 $E_m$，則倒頻譜係數可表示為：$c_n=\sum_{m=1}^{M}\log(E_m)\cos\left[\frac{\pi n}{M}(m-\frac{1}{2})\right]$, MFCC 能有效表示短時間語音頻譜的包絡形狀，因此常被用於語音辨識、說話人辨識與語音分類等任務。
- 隱馬可夫模型 (hidden Markov model, HMM) 是傳統語音辨識中常用的統計模型。語音訊號具有明顯的時間序列特性，例如音素會依時間順序組成音節、詞與句子。HMM 可用隱藏狀態表示音素或語音單位的時間變化，並以觀測機率描述聲學特徵與狀態之間的關係。在語音辨識中，常將辨識問題表示為尋找最可能的詞序列： $\hat{W}=\arg\max_W P(W|X)$, 其中 $X$ 為觀測到的聲學特徵序列，$W$ 為可能的詞序列。利用貝氏定理，可進一步寫成：$\hat{W}=\arg\max_W P(X|W)P(W)$, 其中 $P(X|W)$ 可視為聲學模型，描述詞序列與聲學特徵之間的關係；$P(W)$ 則可視為語言模型，描述詞序列本身出現的機率。這種架構說明語音辨識不只依賴聲音訊號，也會結合語言與語意資訊。

## 語音處理的應用

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

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