棒読み音声合成エンジンの作り方
はじめに
ずんだボイスで棒読み音声を合成するiPhoneアプリ「ギコチナイド」を作成しました。無料のアプリとして公開中ですので、ぜひ試してみてください。
本記事で解説する内容
アプリには自作の音声合成エンジンが搭載されています。といっても、アイデアを理解してしまえば誰でも作れるほど素朴なものです。この記事では素朴んあ棒読み音声合成エンジンの作り方を解説します。
ただし、実装の詳細については説明しません。またライセンスの都合上、音声合成に必要なボイスデータは掲載しません。
対象読者
対象読者は音声合成に興味のある方です。記事を読み進めるための前提知識は不要です。
音声合成の方式
音声合成には大きく2種類の方式があります。
- テキストを音響特徴量とよばれる中間形式に変換する。その後、音響モデルを用いて特徴量から音声波形を生成する。
- 事前に音声の断片を用意する。テキストに応じて、それらの断片を連結することで、所望の音声を得る。
ゆっくりボイスでおなじみSofTalkやリアルな音声を合成できるVOICEVOXは方式1で実装されています。今回の記事では解説しませんので、興味の湧いた方は各自で学んでみてください。
それでは方式2について説明します。
Unit Selection Synthesis(単位選択型の合成方式)
立派な名前がつけられていますが、アイデアは単純です。事前に作成した音声の断片を連結して読み上げ音声を合成する方式です。
例えば駅の自動放送がこの方式に該当します。「1番線、2番線、3番線」「普通、快速、特急」「到着します、出発します」といった音声の断片を事前に作成し、必要に応じて連結することで所望の音声を合成します。
この方式で読み上げ可能なテキストは事前に作成された音声断片の組み合わせに限られます。そこで、音声の断片をより細かく刻むことで任意のテキストを読み上げできるように工夫します。
日本語の発音一覧
音声断片の刻み方を考える前に、まずは日本語にどのような発音のパターンがあるか振り返ってみましょう。以下、日本語として発音可能な死因と母音の組み合わせを示します。
新世紀エヴァンゲリオンの「う」に濁点のような発音は除外しています。
表 日本語の発音パターン一覧
発音ラベル | 読み方 |
---|---|
a | ア |
i | イ |
u | ウ |
e | エ |
o | オ |
k a | カ |
k i | キ |
k u | ク |
k e | ケ |
k o | コ |
ky a | キャ |
ky u | キュ |
ky o | キョ |
g a | ガ |
g i | ギ |
g u | グ |
g e | ゲ |
g o | ゴ |
gy a | ギャ |
gy u | ギュ |
gy o | ギョ |
s a | サ |
sh i | シ |
s u | ス |
s e | セ |
s o | ソ |
sh a | シャ |
sh u | シュ |
sh e | シェ |
sh o | ショ |
z a | ザ |
j i | ジ |
z u | ズ |
z e | ゼ |
z o | ゾ |
j a | ジャ |
j u | ジュ |
j e | ジェ |
j o | ジョ |
t a | タ |
t i | ティ |
t u | トゥ |
t e | テ |
t o | ト |
ch a | チャ |
ch i | チ |
ch u | チュ |
ch e | チェ |
ch o | チョ |
ts a | ツァ |
ts i | ツィ |
ts u | ツ |
ts e | ツェ |
ts o | ツォ |
ty a | テャ |
ty u | テュ |
ty e | テェ |
ty o | テョ |
d a | ダ |
d i | ディ |
d e | デ |
d o | ド |
dy a | デャ |
dy u | デュ |
dy o | デョ |
n a | ナ |
n i | ニ |
n u | ヌ |
n e | ネ |
n o | ノ |
ny a | ニャ |
ny u | ニュ |
ny e | ニェ |
ny o | ニョ |
h a | ハ |
h i | ヒ |
h u | フ |
h e | ヘ |
h o | ホ |
hy a | ヒャ |
hy u | ヒュ |
hy o | ヒョ |
f a | ファ |
f i | フィ |
f u | フュ |
f e | フェ |
f o | フォ |
p a | パ |
p i | ピ |
p u | プ |
p e | ペ |
p o | ポ |
py a | ピャ |
py u | ピュ |
py e | ピェ |
py o | ピョ |
b a | バ |
b i | ビ |
b u | ブ |
b e | ベ |
b o | ボ |
by a | ビャ |
by u | ビュ |
by e | ビェ |
by o | ビョ |
m a | マ |
m i | ミ |
m u | ム |
m e | メ |
m o | モ |
my a | ミャ |
my u | ミュ |
my e | ミェ |
my o | ミョ |
y a | ヤ |
y u | ユ |
y o | ヨ |
r a | ラ |
r i | リ |
r u | ル |
r e | レ |
r o | ロ |
ry a | リャ |
ry u | リュ |
ry e | リェ |
ry o | リョ |
w a | ワ |
w i | ウィ |
w e | ウェ |
w o | ウォ |
N | ン |
cl | 小文字の「つ」 |
子音と母音
日本語の発音は子音と母音の繰り返しで表現できます。例えば「おはようございます」はo, h-a, y-o, u, g-o, z-a, i, m-a, su
のようなパターンで構成されます。
加えて、cl
とN
を導入します。cl
は小文字の「つ」、N
は「ん」の発音に対応します。
例
- ギットハブ→
g-i, cl, t-o, h-a, b-u
- コメント→
k-o, m-e, N, t
子音で分割してみる
それでは音声合成の第一歩を踏み出しましょう。例として、「コミット」と「ラック」の末尾にある1文字を交換して「コミック」と発音させてみましょう。
macOSであれば標準で音声合成コマンドsay
が利用できます。さらに、Homebrewでbrew install sox
とすれば音声編集コマンドsox
が利用できます。
# 「コミット」と「ラック」の音声を合成してファイルに書き出す
say -v Kyoko コミット -o komitto.aiff
say -v Kyoko ラック -o rakku.aiff
# 音声を分割する(トリミングの数値は適当です)
sox komitto.aiff komi.aiff trim 0 0.45
sox rakku.aiff kku.aiff trim 0.15
# 分割した音声を連結して再生する
play komi.aiff kku.aiff
いかがでしょうか。特に違和感なく聞こえたかと思います。
なめらかに変化する音
「コミット」と「ラック」から「コミック」という単語を合成できたのは、あえて合成可能なパターンを選んだためです。結論を先に述べると、子音で音声を分割するアプローチはうまくいきません。
例えば「コメント」をゆっくり発音してみてください。「クゥオォムゥエェントォ」のように前後の音は滑らかに接続されています。
「かきくけこ」や「たちつてと」は発音する際、瞬間的に空気の流れを止めます。仮に不連続な発音だけで日本語が構成されていれば、空気の流れが止まって無音になる瞬間を抽出し、そこで音声断片に分割するアプローチが可能です。
残念ながら日本語の発音パターンを眺めると、なめらかに変化する音が多数を占めています。従って、子音を基準に音声の断片を分割するアプローチでは自然な声を合成できません。
母音を含めて分割する
不自然さを解消するため、子音と母音をひとつの塊として音声を分割します。冒頭で紹介した音声合成エンジンはこのアプローチで実装されています。完璧とは言えませんが、それなりに自然な音声が合成できます。
以降は子音(Consonant)を記号C
、母音(Vowel)を記号V
で表現します。また、小文字の「つ」と「ん」の発音については、それぞれ記号cl
と記号N
を用います。
例
- 「コメント」→
ko-me-N-to
→CV-CV-N-CV
- 「アットマーク」→
a-cl-to-ma-a-ku
→V-cl-CV-CV-V-CV
VCV単位で分割する
子音と母音で分割すると説明しましたが、正確には前の母音を含めた単位、つまりVCV単位で分割するのがポイントです。滑らかに変化する直前の発音を含めることで、不自然さを解消するのです。
さらに、N
が出現した場合は後の子音も音声断片の単位として含めることにします。記号で表すとVNCV
になります。
例えば「ごめんね」をゆっくり発音してみてください。「ん」と「ね」の発音に注目すると、教会は曖昧であり「ね」の発音がどこから開始しているのか判別できません。そこで、N
も連続する母音として音声断片の単位に含めるのです。
例を示します。単語「ごめんね」を分割する手順は次のとおりです。最終的に3つの音声断片に分割されます。
- 「ごめんね」
go-me-N-ne
CV-CV-N-CV
CV, VCV, VNCV
go, o-me, e-N-ne
分割パターン一覧
音声断片の分割パターン一覧を示すにあたり、新しく2つの記号を導入します。
-
S
: 発音開始を示す -
E
: 発音終了を示す。
発音パターンの一覧は次のとおりです。
SCV
SNCV
SV
VCV
VE
VNCV
VNE
VNV
VVCV
VVE
VVNCV
VVNE
-
VVV
上記のパターンにもとづいて音声断片を作成すれば、データの準備は完了です。後はテキストをパターンに分割し、そのパターンに対応する音声の断片を順番に連結すれば所望の音声が得られます。
音声データについては24 kHz / 16 bit integerで作成した場合、総容量は160 MBほどになります。組み込みデバイスには厳しいですが、スマホのアプリであればデータをすべてメモリに展開できる程度の容量です。
クロスフェード
パターンに従って音声の断片を順番に連結するだけで所望の音声が得られます。ただし、このままでは断片の繋ぎ目が耳障りです。
これを解消する方法として最も単純なのがクロスフェードです。冒頭で紹介した音声合成エンジンもクロスフェードを適用して音声断片の繋ぎ目が目立たないようにしています。
問題点
音声合成の実現方法についての説明は以上で終わりです。ここからは、問題点をいくつか列挙します。
抑揚がつけられない
最も大きな問題は抑揚をつけられないことです。例えば声の高さを10段階で表現する場合、母音「あ」から母音「あ」へのパターンだけで100パターン存在します。組み合わせ爆発するため、全パターンの音声断片を用意する総当たり的なアプローチは非現実的です。
話速を変えられない
音声断片を連結しているだけですから、元の話速より遅く発音することができません。音声の再生速度を変えるアルゴリズムは存在しますが、不自然な聞こえ方になります。
同様に「あーーー」のように長く伸ばす音をうまく表現できません。冒頭で紹介した音声合成エンジンでは、音を伸ばす場合「あーーー」を「ああああ」に置き換えることで対処していますが、聞こえ方は不自然になります。
おわりに
棒読み音声であれば、単純なパターンに従って音声断片を作成し、それらを連結するだけで所望の音声が合成できます。皆さんも自作の棒読み音声合成エンジンを作ってみてください。
Discussion