LOG ENTRY / post
声道を物理で鳴らす — ブラウザで動く調音音声合成をつくる
声道を物理で鳴らす — ブラウザで動く調音音声合成をつくる
エンジン音、水槽の生態系、電子回路と作ってきて、次は人間の声にした。
方針はいつもと同じで、ニューラルネットは使わない。肺から口唇までを物理でシミュレートして、その結果として音が出てくるものを作る。単一HTMLファイル、スマホのブラウザで実時間。
→ ボイスラボ
日本語と英語のサンプルが40本入っています。年齢・性別・感情のスライダーを動かしながら聴くのが分かりやすいと思います。
結論から言うと、鳴った。ただしそこに至るまでに踏んだ地雷が面白かったので、そちらを中心に書く。
どういう構造か
音声生成は3段に分かれる。エンジン音でいえば「燃焼 → エキマニ → マフラー出口」とほぼ同じ構造をしている。
| 段 | 物理 |
|---|---|
| 音源(声門) | 声帯の自励振動+ベルヌーイ流れ |
| 伝播(声道) | 声門から口唇まで約17cmの管の中の音波伝播 |
| 放射(口唇・鼻孔) | 開口端からの放射 |
声帯が振動する仕組みが面白い。声門が開くとき収束形状、閉じるとき発散形状になることで、空気から組織へのエネルギー伝達が非対称になり、粘性減衰を上回って振動が持続する。要するにクラッチのスティック・スリップと同じ、負性抵抗による極限周期だ。方程式を立てて解けば、勝手に振動が立ち上がる。
実装は1次元のKelly-Lochbaum導波管。声道を断面積の違う円筒管の連なりとして表現し、各セクションの境界で波が反射・透過する。1962年にベル研で作られた、コンピュータが初めて歌を歌った(Daisy Bell)ときの方式そのものだ。
これに、
- 声帯:二質量モデル(自励振動、声道との双方向結合)
- 気管:1次元導波管、声門下共鳴
- 鼻腔:軟口蓋開度で結合する側枝
- 梨状窩:閉管の側枝
を足した。梨状窩というのは咽頭の左右にある窪みで、体積2.5cm³くらいの小さな空洞なのだが、これが4〜5kHz帯にスペクトルの谷を作る。実装コストは20セクションぶんしかないのに効果が大きい、費用対効果の高い投資だった。
母音をどう決めるか
声道の形をどう指定するか。Brad StoryのTubeTalkerという実装を参考にした。
A(x) = Ω(x) × exp(q₁φ₁(x) + q₂φ₂(x) + q₃φ₃(x)) × Γ(x)
Ω が話者固有の中立声道形状、φ が母音の整形モード、Γ が子音の狭窄関数。
ここで効いてくるのが、母音の整形モードは話者間でかなり似ていて、話者ごとに違うのは中立形状のほうという知見。つまり「発音」と「話者性」が分離できる。年齢スライダーは Ω だけを動かせばよく、音素の定義には一切触らなくていい。
モード係数は数値最適化で決めた。面積関数から連鎖行列で伝達関数を計算し、フォルマント周波数が文献値に合うように q を最適化する。一様管17cmの検証で誤差0.11%、母音のF1誤差は最大でも6%程度に収まった。
年齢と性別
一様にスケールしてはいけない、というのがこの部分の肝だった。
乳児は口腔と咽頭の比が約1.5:1で、1:1になるのは6〜8歳頃。だから子どもの声は「大人の声をピッチシフトしたもの」では絶対に再現できない。
さらに面白いのが思春期の男性で、F0は女性の50〜60%まで下がるのに、フォルマントは80〜90%までしか下がらない。声帯が伸びて厚くなること(F0を下げる)と、喉頭が下降して声道が伸びること(フォルマントを下げる)が独立に起きるからだ。モデル側で声帯寸法と声道長を別パラメータにしておけば、これは勝手に出る。
ここからバグの話
以下、実際に踏んだ地雷。どれも「物理の理解」ではなく「実装の詰め」の問題だった。
声道が2倍の長さだった
全部の母音のフォルマントが、綺麗に一律 −50% ずれていた。
導波管ではセクション長を dx = c/(2·fs) としていたのだが、前進波と後退波それぞれが1セクションあたり1サンプルの遅延を持つので、正しくは dx = c/fs。往復で2Nサンプルかかることを勘定に入れ忘れていた。一律50%という綺麗すぎるずれ方が手がかりになった。
損失フィルタが全フォルマントを消した
実際の声道は壁の粘弾性や粘性熱損失があるので、それを入れようとして各セクションに1極ローパスを掛けた。係数0.018。
これがカットオフ275Hz相当で、しかも46段カスケードする。フォルマントは影も形もなくなった。スペクトルを見ると、ただの -6dB/oct の滑らかな坂になっていて、共鳴が1本もない。
声帯が動かなかった
自励振動なので、方程式を解けば勝手に動き出すはず……が、ぴくりとも動かない。
原因は、駆動圧の式が P₁ = ΔP(1 − (a_min/a₁)²) になっていること。左右対称の平衡点では a_min = a₁ なので P₁ = 0、駆動力が厳密にゼロになる。不安定平衡点にぴったり座っていて動き出せない状態だった。
微小な初期非対称を入れたら、素直に110Hzで振動を始めた。
無声という状態が存在しなかった
これが一番厄介だった。
「さしすせそ」を鳴らすと「ぎぎょぎぎょぎょ」に聞こえる。/s/ が有声に聞こえるということは、無声化が効いていない。
声帯を外転させれば(左右に開けば)振動が止まるはず、と思って内転度パラメータを0まで下げても、声門は148Hzで振動し続けた。声門面積の写像を10倍に強めても止まらない。
真犯人は、質量の変位制限を ±3mm に置いていたことだった。生理的には0.6mm程度しか動かないのに、3mmも動けるようにしていたので、どんなに外転させても声帯が閉じてしまう。0.6mmに直したら、
| 内転度 | 1.0 | 0.7 | 0.5 | 0.35 | 0.2 | 0.0 |
|---|---|---|---|---|---|---|
| F0 | 180 | 136 | 128 | 96 | 停止 | 停止 |
と、内転度0.2以下で発振が止まる位相転移が出た。副産物として、声門流のスペクトル傾斜が −12dB/oct 前後で安定した。教科書どおりの値だ。波形が生理的な形に収まったことで、他の指標まで一緒に正しくなった。
摩擦音が「ほぼ雑音」だった
/s/ と /ʃ/ のスペクトル重心を測ったら、9890Hz と 10091Hz。ほぼ同じで、両方とも山が13kHz付近。要するに白色ノイズがそのまま出ていた。
原因は幾何だった。/s/ の狭窄位置を声道長の0.86の位置に置いていたのだが、これだと前部空洞が2.2cmになり、1/4波長共鳴が約4kHz。実際の /s/ は前部空洞が1〜1.5cmで、6〜9kHzに共鳴を持つ。位置を0.92に動かしたら、重心が6449Hz、/ʃ/ が4071Hz と正しい順序に分離した。
さらに狭窄面積も間違っていて、0.055cm²は狭すぎた。実際の /s/ の狭窄は0.1〜0.2cm²ある。「噴流」ではなく「ほぼ閉鎖」を作っていたことになる。
参照実装を読んだら自分の勘違いが分かった
行き詰まったので、Neil Thapenの Pink Trombone(MIT)のソースを読みに行った。同じ1次元導波管を使っていて、ブラウザで実際に鳴る唯一の参照実装だ。
読んで青ざめた。
- 出力に微分がない。 導波管の出力をそのまま出しているだけ。口唇放射の微分もハイパスも一切ない。
- 周波数依存の損失フィルタがない。
fade = 1.0。損失は声門端0.75と口唇端−0.85の反射係数だけ。 - 摩擦ノイズは事前にバンドパスで帯域制限してから声道に入れている。
- 狭窄が狭すぎても広すぎてもノイズが出ない窓が入っている。
私は「物理的に正しくしよう」として、1次元導波管では省略すべきものまで積み上げていた。放射の微分は声門源側と二重にかかっていたし、損失フィルタはそもそも不要だった。削るべきところで足していた。
この種の知識は論文には書いていない。「実際に鳴る実装」にしか凝縮されていない。数式は自分で書けても、音になる係数の範囲は先人が試行錯誤で見つけたものだ。もっと早く読みに行けばよかった。
現状
日本語と英語、それぞれ20本ずつのサンプルを鳴らせる。テキストはすべて書き下ろした自作で、音韻カバレッジを設計してある。年齢・性別・体格、感情・発話速度・強さ・明瞭度を全部スライダーで動かせる。
速度は実時間の5倍前後で生成できているので、スマホでも余裕がある。
面白いのは、速度を上げて明瞭度を下げると、ジェスチャが目標に届かずに自然に「もごもご」した発音になること。これは録音ベースの音声合成には真似できない部分で、調音モデルを使う意味がここにある。
正直なところ、ニューラルTTSの自然さには遠く及ばない。そこは競う土俵ではない。この作品の価値は、
- 声道の形が見えて、音がそこから出ていることが分かる
- スライダーが全部物理量(声門下圧、声帯剛性、狭窄位置)
- 年齢・性別が連続に変わり、その途中も物理的に妥当
という体験のほうにある。
現時点では了解度を稼ぐためにフォルマント共振器を少しだけ並列に混ぜていて、そこは純粋な物理モデルではない。まず人間の声に聞こえる状態を作ってから物理側を詰める、という順序を取った。混合率は0にもできるので、素の物理モデルの実力もそこで確認できる。
次
聴覚モデルを作ろうと思っている。ガンマトーンフィルタバンクで臨界帯域に分解して、内有毛細胞モデルを通し、変調スペクトルを見る。
理由は評価のためだ。今回、フォルマント周波数が数値上は合っているのに全く音声に聞こえない、という状態が何度もあった。オクターブ帯域のような粗い物差しでは、臨界帯域内でフォルマントが融合しているかどうかが見えない。耳の側のモデルがあれば、「母音対の距離が弁別閾を超えているか」を直接測れる。
作るものが増えていくが、まあそういうものだろう。