音声合成システム WORLD に触れてみる

この記事は [BrainPad AdventCalendar 2017](https://qiita.com/advent-calendar/2017/brainpad) 1日目の記事です。

本記事では、音声合成や声質変換でよく使われているWORLDというツールの紹介をしようと思います。

# 音声合成と声質変換

ついに[Google Home](https://store.google.com/product/google_home) や [Amazon Echo](https://www.amazon.co.jp/dp/B071ZF5KCM) が日本に上陸しました。
スマートスピーカが日本国内でどの程度普及するかは定かでありませんが、今後音声インターフェイスがますます重要になってくるのは間違いないと思います。

音声インターフェイスで重要となってくる技術の一つに音声合成(speech synthesis, text to speech)があります。Macであれば簡単に試すことができて、

```bash
$ say 'おはよう'
```

で「おはよう」としゃべってくれます。ただ、`say` コマンドでは、少し長い文章を入れるとわかるように、機械特有のあじけない喋り方になってしまいます。如何に人間っぽく発話をさせるかという研究は昔から進められていますが、深層学習の発展により、最近では人間と区別が出来ないくらいなめらかな発話が生成できるようになってきています。

音声合成と近いところで、声質変換という技術もあります。こちらは、入力がテキストではなく声になっているもので、「ヒラリー氏の声とトランプ氏の声を入れ替える」ような技術です。エンターテイメント領域での利用はすぐに思いつきますが、実は「声を失ってしまった人にもともとの声を与える」などの使い方もあり、社会的な意義も大きい研究です。

# 音声合成や声質変換の難しさ

さて、音声合成や声質変換は案外むずかしいものです。
簡単な例として「声を1オクターブあげる(高音にする)」処理を考えましょう。声はあくまで音ですから、高音にするには周波数をあげてやればよいでしょう。一番簡単な方法は、「2x速で再生する」ですが、これでは高音にしようとすればするほど、早口になってしまいます。

では、喋るスピードを変えずに高音にするにはどうしたら良いでしょうか?
まず思いつくのが、2xに早送りにした音声を短い時間間隔で切って、2回ずつ繰り返す方法です。
「あいうえお」を、「ああいいううええおお」とするようなイメージです。
しかし、この方法を単純に適用しても、何とも言えない声になってしまいます。波同士がうまくつながらないためです。

このように、単純に声の高さを変えるだけでも考えなければいけないことがたくさんあり、なかなか一筋縄ではいきません。

# 音声から特徴量を抽出する

上述の通り、音声の波形を直接いじろうとしても難しいので、昔から、音声を一旦特徴量に変換してから加工し、波形に戻すという方法がとられてきました。音声合成・声質変換で使われる特徴量として有名なものには以下の様なものがあります。

1. 基本周波数
   - 声のベースとなる高さをあらわします
2. スペクトル包絡
   - いわゆるスペクトルを滑らかにしたもので、音色をあらわします
3. 非周期性指標
   - 声帯振動のゆらぎや雑音混入による影響をあらわします

それぞれの指標は独立にいじることができ、声の高さを変えたいのであれば F0 をいじり、声の雰囲気・音色を変えたいのであればスペクトル包絡をいじります。

# 音声合成・声質変換ツール WORLD

上記の特徴量は、実は簡単に抽出できるようなものではなく、ある種のモデルをつかって、元波形から推定される量です。そのため、特徴量の抽出を自前で0から実装するには、この分野に関する深い知識が必要になります。

[WORLD](http://ml.cs.yamanashi.ac.jp/world/)は、そういった特徴量の抽出や、特徴量からの波形の合成をしてくれる便利なツールです。もとものこの分野で広く使われていた STRAIGHT というソフトとちがい、修正BSDライセンスでだれでも利用できます。

# WORLD のインストール

WORLDは公式には[C++版](https://github.com/mmorise/World)と[Matlab版](http://ml.cs.yamanashi.ac.jp/world/download.html)が用意されていますが、[PyWorldVocoder](https://github.com/JeremyCCHsu/Python-Wrapper-for-World-Vocoder)というPythonのラッパも公開されています。PyWorldVocoderは、`pip`で簡単にインストールすることができるので、今回はそれを利用します。

```bash
$ pip install scipy  # 先にscipyを入れておかないとpyworldのインストールでコケることがある
$ pip install pyworld
```

簡単ですね。

# WORLD を使ってみる

それでは実際に WORLD を使ってみましょう。ここでは、音声から特徴量を抽出し、抽出した特徴量から音声を合成してみます。

まずはwavファイルの読み込みです。

```python
from scipy.io import wavfile
import pyworld as pw

WAV_FILE = 'path_to_the_wav_file'

fs, data = wavfile.read(WAV_FILE)
data = data.astype(np.float)  # WORLDはfloat前提のコードになっているのでfloat型にしておく
```

次に pyworld を使って特徴量を抽出します。

```python
_f0, t = pw.dio(data, fs)  # 基本周波数の抽出
f0 = pw.stonemask(data, _f0, t, fs)  # 基本周波数の修正
sp = pw.cheaptrick(data, f0, t, fs)  # スペクトル包絡の抽出
ap = pw.d4c(data, f0, t, fs)  # 非周期性指標の抽出
```

得られた特徴量からを音声を合成するには `synthesize` メソッドを使います。

```python
synthesized = pw.synthesize(f0, sp, ap, fs)
```

簡単ですね。

## WAVファイルを再生する

`scipy.io.wavfile.write` を使ってファイルを書き出して再生してもよいですが、もしJupyterを使っているのであれば、`IPython.display`モジュールを使うと楽です。
(Gist にアップロードしたらみえなくなってた。なんでだろ)

```python
import IPython.display as display

print('original')
display.display(
    display.Audio(y, rate=fs)
)

print('synthesized')
display.display(
    display.Audio(synthesized, rate=fs)
)
```

[original.wav](https://www.dropbox.com/s/i8dqwcyrm1j5jfl/original.wav?dl=0)
[synthesized.wav](https://www.dropbox.com/s/o6ae5vfyw4xpwmu/synthesized.wav?dl=0)

## 声を変換してみる

では、実際に声を変換してみましょう。まずは、始めの例と同じく、1オクターブ高い声に変換します。F0をいじることで声の高さを変えることが出来ます。

```python
high_freq = pw.synthesize(f0*2.0, sp, ap, fs)  # 周波数を2倍にすると1オクターブ上がる

print('high_freq')
display.display(
    display.Audio(high_freq, rate=fs)
)
```

[high_freq.wav](https://www.dropbox.com/s/sgm0u3bz38glz5f/high_freq.wav?dl=0)

男性の声が女性のような、というか無理して高音を出しているような感じになりました。次にF0を一定にしてみます。[こちら](http://aidiary.hatenablog.com/entry/20150225/1424863972)にあるように、ロボットのような機械的で感情味の無い声になります。

```
robot_like_f0 = np.ones_like(f0)*100  # 100は適当な数字
robot_like = pw.synthesize(robot_like_f0, sp, ap, fs)

print('robot_like')
display.display(
    display.Audio(robot_like, rate=fs)
)
```

[robot_like.wav](https://www.dropbox.com/s/ywdwb0colm6awr5/robot_like.wav?dl=0)

最後にスペクトル包絡もいじって、音色を変えてみましょう。
先程F0を2倍にしたのに加え、スペクトル包絡を1.2倍すると、より女性的な発音になります。

```python
female_like_sp = np.zeros_like(sp)
for f in range(female_like_sp.shape[1]):
    female_like_sp[:, f] = sp[:, int(f/1.2)]
female_like = pw.synthesize(f0*2, female_like_sp, ap, fs)

print('female_like')
display.display(
    display.Audio(female_like, rate=fs)
)
```

[female_like.wav](https://www.dropbox.com/s/trx6o4tkqg9nxr3/female_like.wav)


このように、音声を生の波形データとしてではなく特徴量の組で表現すると、お手軽に声を変換して遊ぶことが出来ます。

## 音声合成・声質変換と機械学習

とは言え、各特徴量の変換方法は無数にあるため、人手で調整するのは至難の技です。そこで登場するのが機械学習です。機械学習では、元のデータと変換後のデータのサンプルが十分にあれば、自動的にパラメータをチューニングしてくれます。従来はHMMと混合ガウスモデルやNMFを用いる手法が主流だったようですが、冒頭でも述べたとおり、最近は深層学習を用いた手法が盛んに研究されています。
最後に、最近の手法を幾つか紹介してこの記事をしめたいと思います。
(網羅的なものではなく、私が個人的に気になった手法ですので、あしからず)

## 最近の手法

1. [Voice Conversion from Non-parallel Corpora Using Variational Auto-encoder](https://arxiv.org/abs/1610.04019)
   - 深層学習の1手法であるVAEを声質変換に適用した例です。スペクトルとスピーカーの情報を入力にします。この論文で面白いのは「Non-parallel Corpora」を使っている点です。従来の手法では、学習用のデータとして、変換元と変換後で同じ言葉を話したものを利用していましたが、この手法ではそういった対応が不要になります。
1. [WaveNet: A Generative Model for Raw Audio](https://arxiv.org/abs/1609.03499)
   - Googleが2016年に発表したモデルで、入力データとして特徴量ではなく生の波形データを利用し、自己回帰モデルを用いることで、非常になめらかな音声合成を実現しました。計算量の問題はありますが、間違いなく1つのブレークスルーでした。
2. [Neural Discrete Representation Learning](https://arxiv.org/abs/1711.00937)
   - VAEの一種であるVQ-VAEという手法で、潜在空間を離散化しているのが特徴的です。デコーダ部分にWaveNetと同じ構造のネットワークを用いることで、声質変換にも利用できます。[著者のページ](https://avdnoord.github.io/homepage/vqvae/)のデモを聞く限り、とてもいい感じに変換されています。
2. [Statistical Parametric Speech Synthesis Incorporating Generative Adversarial Networks](https://arxiv.org/abs/1709.08041)
   - 声質変換や音声合成に、最近流行りのGANを利用しようという話です。
 
## まとめ

ここ2年くらいずっと薄く気になっている声質変換・音声合成について、基本的なところをまとめました。pyworldはインストールが簡単で使いやすくて面白いので、是非色々いじってみてください。

## 参考文献

- [音声合成システムWORLD](http://ml.cs.yamanashi.ac.jp/world/)
- [WORLDチュートリアル(PDF)](http://ml.cs.yamanashi.ac.jp/media/20170921/20170921.pdf)
- [Python-Wrapper-for-World-Vocoder](https://github.com/JeremyCCHsu/Python-Wrapper-for-World-Vocoder)
- [人工知能に関する断創録](http://aidiary.hatenablog.com/entry/20110514/1305377659)
- [WORLD_intro.ipynb(この記事を書くときに使った Jupyter Notebook)](https://gist.github.com/ohtaman/74312f2184b27c79f773a435b3f52a58)