環境構築1_音声合成 OpenJTalkのhello world まで

#初めに
この記事は、[前回投稿した記事](https://qiita.com/magonotegaeru/items/617c21f9548f3f9ef81e)の続きである。
確認されることを推奨する。
wsl2上で、python3が実装されている前提で話を進める。

前回記事から初めてプログラムをする人にとっては、正直順序を飛ばしている感が非常にある。
人によっては、pythonのfor文やif文などの使い方から学んだほうが肌に合う人もいるだろう。
ただ、自分は、とりあえず動いて楽しいものを、コードのコピペでもなんでもいいから実装して、そのあとで、中身を理解していく、という順序のほうが肌に合っているので、print("hello world")の次に、"OpenJTalkを用いた音声合成"を持ってきた。
以下に、サンプルコードや環境構築のコマンドなどを載せているが、一回で全部理解する必要はない。できたおもちゃで遊んでいくうちになんとなく理解していけばいいだろう。

#目次
0. OpenJTalkについて
0. OpenJTalkのインストール
0. wslから.wavファイルを再生できるようにするための作業
0. pythonを使ったOpenJTalkのhello world

#OpenJTalkについて
OpenJTalkは、「入力された日本語テキストに基づいて自由な音声を生成するHMMテキスト音声合成システム」である。
[公式サイト](http://open-jtalk.sp.nitech.ac.jp/#:~:text=Open%20JTalk%20%2D%20HMM%2Dbased%20Text,Open%20JTalk%E3%81%AE%E3%83%87%E3%83%A2%E3%83%B3%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E3%81%99%EF%BC%8E&text=%E5%A3%B0%E8%B3%AA%E3%81%AE%E5%80%A4%E3%82%92%E5%B0%8F%E3%81%95%E3%81%8F,%E3%81%AA%E5%A3%B0%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%EF%BC%8E)より抜粋。
平たく言うと、日本語の文字列をもとに、.wavファイルを作成してくれる、ありがたい道具である。
感謝しながら使いましょう。
.wavファイルとは、.mp3や.m4aと同じように、音声データを表現するファイルの拡張子の一種である。

OpenJTalkを使用するために、必要なものは、大きく以下の３つである。
・OpenJTalk本体
・辞書モデル
・音響モデル

OpenJTalk本体:
[公式サイト](http://open-jtalk.sourceforge.net/)で配布されている。
windowsで使用するときは、ここからダウンロードしてくればよいが、今回は、wsk上にコマンドプロンプトでダウンロードするので、特にwebページに足を運ぶ必要はない。

辞書モデル：
今回は、一般に使用される日本語辞書の"MeCab"を使用する。

音響モデル：
音響モデルは、.htsvoiceファイルなどで表現される。
今回は、代表的な音響モデルの一つである"mei"を使用する。
なお、meiには、"angry"、"bashful"、"happy"、"normal"、"sad"、の感情モデルがそれぞれ存在する。
これらの扱いについても後程述べる。
また、音響モデルは、様々な有志が作成し、ネットで公開してくださっている。([このサイト](http://akihiro0105.web.fc2.com/Downloads/Downloads-htsvoice.html)など)
利用規約を守って使用してみるとよい。

また、日本語の.wavファイルを生成するだけなら、"SofTalk"や"SHABERU_Light"などのアプリが配布されている。
非常に便利なアプリである。
ただ、これらは完成されたアプリで、自分のコードに基本的に組み込めないため、今回は特に触れない。

#OpenJTalkのインストール
__以下の作業はすべてwsl上での作業である。__
注意せよ。
ただ、

以下のコマンドを、wsl上の好きなところで実行せよ。
ただ、前回も述べた通り、"workspace"などの名前のディレクトリを作成して、その中で行うほうがマナーが良い。

```:おまじない
sudo apt update
sudo apt upgrade
```

```:環境構築に使用する道具のインストール
sudo apt install -y wget
sudo apt install -y unzip
```
wgwt : web上から、URL指定でファイルをダウンロードする道具
unzip : .zipファイルを解凍するための道具

```:OpenJTalk本体のインストール
sudo apt install -y open-jtalk
```
実体としては、wsl上の、/usr/bin に、"open_jtalk"として存在する。
(環境によっては、実体の場所は変わるのかも。詳しくないので、有識者の方、いらっしゃいましたら教えてください。)
なお、__この"実体として存在する場所"は、実際にpythonで使用するときに必要になってくる__ので、よく覚えておこう。

```:辞書モデルのインストール
sudo apt install -y open-jtalk-mecab-naist-jdic
```

実体としては、wsl上の、/var/lib/mecab/dic/open-jtalk/naist-jdic に存在する。
__この場所は覚えておこう。__

```:音響モデル実装装置(みたいなもの)のインストール
sudo apt install -y hts-voice-nitech-jp-atr503-m001
```

これの場所は特に覚えておかなくてよい。

```:音響モデル(mei)本体の.zipファイルのダウンロード
wget http://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/MMDAgent_Example-1.7/MMDAgent_Example-1.7.zip
```
```:上記.zipファイルの解凍
sudo unzip MMDAgent_Example-1.7.zip
```
この中の、MMDAgent_Example-1.7/Voice/meiが、"mei"音響モデルの全体であり、その下の階層に、
前述した、"angry"、"bashful"、"happy"、"normal"、"sad"の感情の音響モデル(.htsvoiceファイル)が存在する。

__この場所__(~/workspace/MMDAgent_Example-1.7/Voice/meなど)__は、覚えておく必要がある。__
ただ、この音響モデル("mei")は、よく使うので、どこからでもアクセスできる場所にコピーして、その場所を覚えたほうが、__風情がある__。
以下のコマンドを実行し、ゲットした"mei"を、別のところに移動させよう。

```:meiを扱いやすい場所に移動。
sudo cp -r MMDAgent_Example-1.7/Voice/mei/ /usr/share/hts-voice/
```
/usr/share という場所に、"hts-voice"という場所を作成し、そこに"mei"を保管。
この場所はちょっと特殊な場所なので、cpコマンドの前に、sudoをつける必要がある。
この、__/usr/share/hts-voice/mei/の場所は、覚えておく必要がある。__

以上で、OpenJTalkのインストールは終了。

#wslから.wavファイルを再生できるようにするための作業
ここでは、wsl側から、音を再生できるようにするための作業である。
おそらく、macを使用している場合は、ここでの作業はスキップしてよいと思う。
__この章の作業は、windows側の作業と、wsl側の作業の二つがある。注意せよ。__
音を再生させるには、ハードウェアが必要で、ハードウェアを管理しているのは、windowsなので、windows側でも作業する必要がある。
なお、ここでのコマンドなどはすべて理解しなくてもよい。難しいから。

作業は、[この神サイト](https://astherier.com/blog/2020/08/wsl2-ubuntu-sound-setting/)に沿って行う。
非常に分かりやすくて丁寧なサイトである。

というか
###これより丁寧に説明できる気がしないので、上記の記事を読んでm(_ _)m

この章の作業は、正直めんどい。
そして、音声合成(.wavファイル生成)自体には、本質的に関係ない。
そのため、__この章の作業を飛ばして、次の章へ進んでも、特に問題はないと思う。__（この章を飛ばしても、ロボットなどの別のハードウェアにしゃべらせる場合は、問題ない。）
この章を飛ばす場合は、生成した.wavファイルを、windows側から指定して、再生させる必要がある。

#pythonを使ったOpenJTalkのhello world
詳しい話をする前に、以下のコマンドを、wslのマナーの良い場所(workspace上など)で、実行してみてほしい。
前章を飛ばした人は、window側からアクセスできるように、/mnt 以下の場所で実行してみてほしい。
以下のコマンドは、[このサイト](https://www.johf.com/log/20130309a.html)を参照して、作成した。

```:OpenJTalkを使用して、.wavファイルを生成するための、最低限のコマンド。
echo 'おはようございます。僕は元気です。' | open_jtalk -x /var/lib/mecab/dic/open-jtalk/naist-jdic -m /usr/share/hts-voice/mei/mei_happy.htsvoice -ow test.wav
```
うまくいけば、カレントディレクトリ（今いる場所）に、"test.wav"という名前の、.wavファイルができていると思う。
これを再生すれば、meiがhappyな感情で"おはようございます。僕は元気です。"としゃべってくれる。

ただし、このような使い方は基本的にしない。だるいから。
上記のコマンド（シェルコマンドという）を、pythonを使用して、可読性と汎用性を高めつつまとめたのが、以下のコード。
[ここ](https://qiita.com/kkoba84/items/b828229c374a249965a9)などにも類似コードが書かれている。まとめ方の参考にすると良い。
このpythonファイルを実行するためには、"subprocess"と"os.path"というライブラリが必要である。
__一般に、ライブラリは、外部からインストールしなければ使えない__が、これらは、__"python標準ライブラリ"__と飛ばれるもので、外部のモジュールのような下準備なしで、いきなり使用(import)できる。

```python:make_wav.py
import subprocess #シェルコマンドをpythonから命令するのに必要なもの
import os.path

# OpenJTalkをインストールしたパス
OPENJTALK_BINPATH = '/usr/bin' #OpenJTalk本体の場所
OPENJTALK_DICPATH = '/var/lib/mecab/dic/open-jtalk/naist-jdic' #辞書モデルの場所
OPENJTALK_VOICEPATH = '/usr/share/hts-voice/mei/mei_{emotion}.htsvoice' #音響モデルの場所

def make_wav(text, speed=1.0, emotion='normal', output_file='__temp.wav', output_dir=os.getcwd()):
    """
    OpenJTalkを使ってmeiの声で音声合成してwavファイルを作る関数。
    引数emotionには'normal', 'happy', 'bashful', 'angry', 'sad'のいずれかを指定可能。
    """

#以下は、上記のだるいコマンドをまとめたもの。
    open_jtalk = [OPENJTALK_BINPATH + '/open_jtalk']
    mech = ['-x', OPENJTALK_DICPATH]
    htsvoice = ['-m', OPENJTALK_VOICEPATH.format(emotion=emotion)]
    speed = ['-r', str(speed)]
    outwav = ['-ow', os.path.join(output_dir, output_file)]
    cmd = open_jtalk + mech + htsvoice + speed + outwav
    c = subprocess.Popen(cmd,stdin=subprocess.PIPE)
    c.stdin.write(text.encode('utf-8'))
    c.stdin.close()
    c.wait()

if __name__ == '__main__':
    make_wav("おはようございます。僕は元気です。", speed=0.8, emotion='happy', output_file='test.wav', output_dir=os.getcwd())
```
これを、実行すれば、.wavファイルが生成される。
このpythonファイルに書かれていることは簡単で、上記のだるいコマンドを、ただまとめただけである。
ついでに、発話のスピードと、感情を変更させることができるようにしてある。
OpenJTalkインストール時に、場所を覚えておけ、といったのは、ここで必要になってくるからである。

if __name__ == '__main__':の意味は、おまじない、である。

pythonを日ごろから扱っている人にとっては、"pythonっぽくない泥臭いコードだなぁ"と思われるかもしれないが、多分これがpython + OpenJTalkの使い方だと思う。(有識者の方、いらっしゃいましたら、コメントください。)

以上で、OpenJTalkのことを__完全に理解した__と思う。

[次の環境構築](https://qiita.com/magonotegaeru/items/d704b2d5cf3be93da8e0)