ソースフィルタモデルで母音を音声合成

ソースフィルタモデルによる古典的な方法で母音 ”ieaou” の音声合成を試してみました。

ソースフィルタモデル

ソースフィルタモデルは参考文献 [1] の引用では以下のようなことです。

ソースフィルタモデルは音声を、声帯振動あるいは呼気流などによる音源を入力として、声道フィルタによる系を経た出力信号とみなす。

図は簡単な人の口の模式図ですが、声帯の振動を音源、声道による影響をフィルタとして考えるということです。このフィルタは声道フィルタと呼ばれています。

図:口の模式図
図:口の模式図

数式で説明すると、z変換された音声 Y は以下のように表せるということです。

$$
Y(z) = H(z) X(z)
$$

ここで、Xは声帯振動で得られた音源をz変換したもの、Hは声道フィルタの伝達関数です。

音声合成の手順

ソースフィルタモデルによる音声合成手順は以下です。

  1. 声帯振動を模擬した音源信号を作成
  2. 声道フィルタを作成
  3. 音源信号に声道フィルタを畳み込む

今回は参考文献 [1] を基に以下のブロック図のように音声を合成しました。

図:母音合成のブロック図
図:母音合成のブロック図

声帯振動は周波数F0の周期波形でモデル化します。また、3つのバンドパスフィルタ(BPF)を並列に置いたものを声道フィルタとします。

音源信号を作成

今回、声帯振動波形は下図のような周期T0のパルス列でモデル化します。

図:パルス列の声帯振動波形
図:パルス列の声帯振動波形

声帯振動波形の周期の逆数は基本周波数と呼ばれ、参考文献 [3] によると、人間が知覚する声の高さにだいたい対応しているそうです。そのため、イントネーションの解析などに利用される重要なパラメータとなります。

今回使用した各母音の基本周波数は参考文献 [2] に記載されている女性28人の平均値となっています。その値は以下の表のようになります。

表:使用した基本周波数
/i/ /e/ /a/ /o/ /u/
235 Hz 223 Hz 212 Hz 216 Hz 231 Hz

声道フィルタを作成

声道で共振がおこる周波数はフォルマント周波数と呼ばれており、音声の音韻らしさを形作る重要なパラメータとなっています。

声道フィルタはフォルマント周波数F1~F3を通す3つのバンドパスフィルタ(BPF)を並列に置いて作成します。

1つのバンドバスフィルタの伝達関数\( H_n\) は以下のようになります。

$$
H_n(z) = A_n \frac{\frac{\alpha^2+\omega_n^{2}}{\omega_n}z^{-1}}{1-2e^{-\alpha}\cos(\omega_n)z^{-1}+e^{-2\alpha}z^{-2}}
$$

$$
\newcommand{\sb}[1]{_{#1}}
\begin{align}
\omega\sb{n} &=2\pi F\sb{n}/f\sb{s} \\[5pt] \alpha &= \pi f\sb{bn}/f\sb{s} \\[5pt] A\sb{n} &= 10^{L\sb{n}/20}
\end{align}
$$

ここで、\(L_n\) は第 n フォルマントの振幅 [dB]、\(f_{bn}\) は第 n フォルマントのバンド幅 [Hz]、\( F_n\) は第 n フォルマント周波数 [Hz]、\(f_s\) はサンプリング周波数です。

今回使用した各母音のフォルマント周波数、フォルマントの振幅についても参考文献 [2] に記載されている女性28人の平均値となっています。その値を以下の表に示します。

表:使用したフォルマント周波数
/i/ /e/ /a/ /o/ /u/
F1 310 Hz 610 Hz 850 Hz 590 Hz 370 Hz
F2 2790 Hz 2330 Hz 1220 Hz 920 Hz 950 Hz
F3 3310 Hz 2990 Hz 2810 Hz 2710 Hz 2670 Hz
表:使用したフォルマント振幅
/i/ /e/ /a/ /o/ /u/
F1 -4 dB -2 dB -1 dB 0 dB -3 dB
F2 -24 dB -17 dB -5 dB -7 dB -19 dB
F3 -28 dB -27 dB -28 dB -34 dB -43 dB

また、使用したフォルマントのバンド幅については参考文献 [2] に記載されていた各母音の全平均値となっています。その値を以下の表に示します。

表:使用したフォルマントバンド幅
F1 F2 F3
49.7 Hz 64.0 Hz 115.2 Hz

音韻/a/のときの声道フィルタ(並列に3つ並んだBPF)の周波数特性を下図に示します。

図:/a/の声道フィルタの周波数特性
図:/a/の声道フィルタの周波数特性

プログラム

ソースフィルタモデルによって ”ieaou” を合成するプログラムは以下です。参考文献 [1] に掲載されているプログラムを基に作成しています。

from math import sin, cos, exp
import soundfile as sf
import numpy as np
import scipy.signal as sg

fs = 48000        # サンプリング周波数
mora_per_sec = 1  # 話す速度(モーラ/秒)
gain = 0.5        # 音の大きさ  

sec = (1/mora_per_sec)*5  # 音源の長さ
T   = 1.0/fs           # サンプリング周期
nsample = int(fs*sec)  # データ数

# フォルマント周波数 i,e,a,o,u
F = [[235,   223,  212,  216,  231], # F0
     [310,   610,  850,  590,  370], # F1
     [2790, 2330, 1220,  920,  950], # F2
     [3310, 2990, 2810, 2710, 2670]] # F3

# フォルマント振幅 [dB] i,e,a,o,u
L = [[  0,   0,   0,   0,   0],
     [ -4,  -2,  -1,   0,  -3],  # F1
     [-24, -17,  -5,  -7, -19],  # F2
     [-28, -24, -28, -34, -43]]  # F3

F = np.array(F, dtype=np.float64)
L = np.array(L, dtype=np.float64)
L = L + 12  # 振幅特性が小さすぎたので、大きくする
L = 10**(L/20.0)  # log->linear

band  = np.array([0, 50.0, 64.0, 115.0]) # 帯域幅
alpha = band*np.pi*T

sample_per_mora=int(fs/mora_per_sec) # 1モーラのサンプル数
x = np.zeros(sample_per_mora)
y = np.zeros(nsample, dtype=np.float64)

for i in range(5):
    T0 = int(fs/F[0,i])  # 基本周期(サンプル数)

    # 声帯振動を作成
    x[:] = 0
    x[0::T0] = gain

    # 音韻の始まりと終わり
    start = sample_per_mora*i
    end   = sample_per_mora*(i+1)

    # フォルマント周波数の共振フィルタをかける
    for k in range(1,4):
        w  = 2.0*np.pi*F[k,i]*T
        b = np.array([0.0, 0.0, 0.0])
        a = np.array([1.0, 0.0, 0.0])
        b[1] = ((alpha[k]**2+w**2)/w)*sin(w)*exp(-alpha[k])
        b = b * L[k,i]
        a[1] = -2*exp(-alpha[k])*cos(w)
        a[2] = exp(-2.0*alpha[k])           
        y[start:end] = y[start:end] + sg.lfilter(b, a, x)

sf.write("ieaou.wav", y, fs, subtype='PCM_16')

6~8行目:サンプリング周波数、1秒あたりに話すモーラ数、声帯振動波形のパルスの大きさを設定しています。

28行目:合成信号が小さくなりすぎたので、フィルタの振幅特性を12dB(4倍)大きくしています。

29行目:記載している振幅値が log 表記なので、linear に変換しています。

40~43行目:パルス列の声帯振動波形を作成します。

49~58行目:3つのBPFを作成して、58行目でBPFを畳み込んだものを足し合わせています。

60行目:合成した音声をwavで出力しています。

結果

パルス列でモデル化した声帯振動信号と声道フィルタを畳み込んで母音合成した信号は以下です。

声帯振動信号

合成音声(声道フィルタを畳み込んだ信号)

たしかに"ieaou" に聞こえるようになってますね。

おわりに

ソースフィルタモデルを使って、母音 "ieaou" を音声合成しました。フィルタを通すだけで人工音が音声のようになるのは不思議な感じがしますね。

参考文献
[1] 小坂直敏,”サウンドエフェクトのプログラミング Cによる音の加工と音源合成”,株式会社オーム社,2012.
[2] 電子情報通信学会,”聴覚と音声”,pp.350-371,コロナ社,1973.
[3] 森勢将雅,”音声分析合成”,コロナ社,2018.