簡単にできる！音声認識と音声合成を使ってRaspberrypiと会話

いきさつ
-
少し前にタ○ラト○ーのおもちゃハッカソンに申し込んだのですが、抽選に受からず。。。
「ならば自力でOH○NASの対抗馬を作ってやる！」と思いたったのがきっかけで、簡易会話ロボをRasタソを作ってみました。笑
(*実際の人物や団体等とは一切関係ありません)

やりたい事
-
・マイクからの音声入力に対して、合成音声で返答をする O○aN○Sもどきの作成

スペック
-
・Raspberry Pi B+
・raspbian 8 Jessie
・[SANWA SUPPLY MM-MCUSB16 USBマイクロホン](http://www.amazon.co.jp/exec/obidos/ASIN/B0027WPY82/hyottokotok08-22/)
・イヤフォンはiphoneのやつ

![Evernote Camera Roll 20160118 005007.jpg](https://qiita-image-store.s3.amazonaws.com/0/93763/6bd9665f-8570-2f69-59e9-3a6aa360d1e3.jpeg)

役者
-
・requests (PythonのHTTPライブラリ)
・pyaudio (pythonでマイク入力)
・AquesTalkPi (合成音声)
・docomo雑談API (会話するためのAPI)
・docomo音声認識API (音声を認識してTEXTに変換するAPI)
※ はじめはJuliusを使おうかと思っていたのですが、軽く触った印象で処理速度と正確さがdocomo音声認識APIの方が良かったのでdocomo音声認識APIを使用する事にしました。

マイクの設定
-
まずはRaspberryPiでマイクを使用する為の準備を行います。([こちらを参考にしました](http://karaage.hatenadiary.jp/entry/2015/08/24/073000))
マイクをさした状態でRaspberryPiを起動して、ちゃんと認識されているかを確認します。

```bash
$ lsusb
Bus 001 Device 005: ID 056e:4008 Elecom Co., Ltd
Bus 001 Device 004: ID 0d8c:0134 C-Media Electronics, Inc. 
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```
良くわからないけど、今回の例だと ``` Bus 001 Device 004: ID 0d8c:0134 C-Media Electronics, Inc. ```の部分がマイクの事らしいです。

USBオーディオアダプタの優先度を確認します。

```bash
$ cat /proc/asound/modules 
  0 snd_bcm2835
  1 snd_usb_audio
```
この```snd_usb_audio ```がマイクの事っぽいので、これを最優先にしないといけません。
ちなみにRaspberryPiは[ALSA](https://ja.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture)を使用しているので、設定を変えるにはalsa-base.confを書き換えます。


ふむふむ、ではalsa-base.confを書き換えますか。。。ん？！
そして事件がおきました!!
なんと```/etc/modprobe.d/alsa-base.conf```を開こうとすると、ファイルが存在していない!
これ結構ハマりましたorz
Jessie(OS)はalsa-base.confの代わりに```/usr/share/alsa/alsa.conf```で修正するんやで、みたいな記事を見つけて、やってみるものの全くうまく行かず、調べても全然わからずにかなりハマりましたorz

結局、「無いのなら作ればよろし、confファイル(字余り)」という事で、alsa-base.confを作成したら解決できました。笑

```bash
$ sudo cat /etc/modprobe.d/alsa-base.conf
options snd slots=snd_usb_audio,snd_bcm2835
options snd_usb_audio index=0
options snd_bcm2835 index=1
```
上記のようにalsa-base.confを新規に作成し、RaspberryPiを再起動します。
そして再度、USBオーディオアダプタの優先度を確認します。
すると、ちゃんと優先度が変更されてるよー！(*´∀`*)

```bash
$ cat /proc/asound/modules
  0 snd_usb_audio
  1 snd_bcm2835
```
解決できたおかげで、モチベーションが一気に復活しました。笑
さて、次に一応マイクの感度を調整しておきます。

```
$ amixer sset Mic 50
Simple mixer control 'Mic',0
  Capabilities: cvolume cvolume-joined cswitch cswitch-joined
  Capture channels: Mono
  Limits: Capture 0 - 62
  Mono: Capture 50 [81%] [16.59dB] [on]
```
では実際に録音して、マイクに問題が無いか確認します。
arecordコマンドで録音を開始して 、Ctrl + Cで終了します。

```
$ arecord -r 16000 -f S16_LE test.wav
録音中 WAVE 'test.wav' : Signed 16 bit Little Endian, レート 16000 Hz, モノラル
^C 
シグナル 割り込み で中断...
```
lsコマンドなどで、ファイルができている事を確認したら、実際に再生してみます。

再生コマンドでは下記の様にサウンドカードナンバーとデバイスナンバーを指定する必要があります。
```aplay -Dhw:[カードナンバー, デバイスナンバー] test.wav```

RaspberryPi B+には標準でイヤホンジャックがついているので、イヤホンジャックのサウンドカードナンバーとデバイスナンバーを確認します。

```bash
 $ aplay -l
**** ハードウェアデバイス PLAYBACK のリスト ****
カード 1: ALSA [bcm2835 ALSA], デバイス 0: bcm2835 ALSA [bcm2835 ALSA]
  サブデバイス: 8/8
  サブデバイス #0: subdevice #0
  サブデバイス #1: subdevice #1
  サブデバイス #2: subdevice #2
  サブデバイス #3: subdevice #3
  サブデバイス #4: subdevice #4
  サブデバイス #5: subdevice #5
  サブデバイス #6: subdevice #6
  サブデバイス #7: subdevice #7
カード 1: ALSA [bcm2835 ALSA], デバイス 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
```
上記の内容だと
カード:1 デバイス:0 が 【スピーカ出力のオーディオ】
カード:1 デバイス:1 が 【HDMI出力のオーディオ】
の事らしいです。
なので今回使用するのは カード:1 デバイス:0 のですね。

では再生してみます。

```
$ aplay -Dhw:1,0 test.wav
再生中 WAVE 'test.wav' : Signed 16 bit Little Endian, レート 16000 Hz, モノラル
```
再生されました！
これで無事に、録音から再生まで一通り問題無い事が確認できました。

docomoAPI
-
次にdocomoAPIを使ってみます。
まずは[docomo Developer support](https://dev.smt.docomo.ne.jp/?p=index)に登録します。
登録するとAPIKEYが発行されるので、今後はそのAPIKEYを使ってAPIを使用します。
では、まずは雑談APIから試してみます。

```bash
$ python
>>> import requests
>>> import json

>>> url = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY={}".format(APIKEY)
>>> payload = {
  "utt": "こんにちは",
  "context": "",
  "nickname": "光",
  "nickname_y": "ヒカリ",
  "sex": "女",
  "bloodtype": "B",
  "birthdateY": "1997",
  "birthdateM": "5",
  "birthdateD": "30",
  "age": "16",
  "constellations": "双子座",
  "place": "東京",
  "mode": "dialog",
}
>>> r = requests.post(url, data=json.dumps(payload))
>>> print r.json()['utt']
こんちは
```
上記の例は「こんにちは」っていう話しかけに対してAPIが「こんちは」って返してます。
簡単ですね！

次に音声認識APIを試します。
こちらは音声データを渡す必要があるので、もう一度ドキュメントを見てみます。
>音声データのフォーマット：PCM(MSB)16khz/16bit

とあります。
PCMというのは非圧縮の音声データで、[wiki](https://ja.wikipedia.org/wiki/%E9%9F%B3%E5%A3%B0%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88)には下記のようにあります
>圧縮しない音声フォーマットはPCMそのものであり、Windowsでは .wavとして、Mac OS では .aiffとして格納される。

ちょうど先ほど作成したtest.wav(16khz/16bit)と条件も一致しているので、test.wavを使って試してみます。
ちなみにtest.wavには「なんでやねん」と言っている音声が入っています。
音声認識APIなので、この音声を認識して「なんでやねん」というテキストが返ってきたら良い訳ですね。

```bash
$ python
>>> import requests
>>> path = '/home/pi/test.wav'
>>> url = "https://api.apigw.smt.docomo.ne.jp/amiVoice/v1/recognize?APIKEY={}".format(APIKEY)
>>>files = {"a": open(path, 'rb'), "v":"on"}
>>>r = requests.post(url, files=files)
>>> print r.json()['text']
なんでやねん。
```
返ってきました！成功です！
これでdocomoAPIの使用は問題無さそうですね。

pyaudioで録音する
-
さて、さきほどマイク入力のテストをしましたが、やはり制御ができないと厳しいので、Pythonでオーディオ制御ができるpyaudioを使って録音を行ってみます。
pyaudioに関しては[こちらを参考](http://hanpakousaku.tumblr.com/post/105771613672/raspberrypi-%E3%81%A8-pyaudio%E3%81%A7%E9%8C%B2%E9%9F%B3%E9%9F%B3%E5%A3%B0%E6%B3%A2%E5%BD%A2%E5%87%A6%E7%90%86)にさせて頂きました。
ではまず、pyaudioをインストールします。

```bash
$ sudo apt-get install python-pyaudio
```

[参考サイト]((http://hanpakousaku.tumblr.com/post/105771613672/raspberrypi-%E3%81%A8-pyaudio%E3%81%A7%E9%8C%B2%E9%9F%B3%E9%9F%B3%E5%A3%B0%E6%B3%A2%E5%BD%A2%E5%87%A6%E7%90%86))のソースをほぼそのまんまコピーして、動作確認します。

```bash
$ python pyaudio_test.py
Please input recoding time>>> 3
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
ここでマイク入力を行います。
```
では、録音したらmono.wavファイルができている事を確認します。
ファイルができていれば先ほどと同じコマンドで、下記のように再生してみて録音ができてればOKです。

```bash
$ aplay -Dhw:1,0 mono.wav
```
うむ、ばっちり録音されておるわ！

AquesTalkで合成音声
-
さてさて、これで 音声入力 からの 音声認識 からの 雑談 まではできる様になりました。
あとは雑談APIにて、返ってきた返答をRaspberryPiことRasタソに発声させます。

まずはAquesTalkを[公式サイト](http://www.a-quest.com/products/aquestalkpi.html)からダウンロードし、RaspberryPiにコピーします。

```bash
localpc ~ $ scp  ~/Desktop/aquestalkpi-20130827.tgz user@host:/home/pi/
aquestalkpi-20130827.tgz
```
RaspberryPiにコピーできたら、RaspberryPi内で解凍します。

```bash
$ tar xvf aquestalkpi-20130827.tar
```
解凍ができたら試しに実行してみます。
イヤホンから合成音声が聞こえてきたらOKです。

```
$ cd aquestalkpi
$ ./AquesTalkPi "オンギャーオンギャー" |  aplay -Dhw:1,0
```
再生！！
＼オンギャーオンギャー/
き。。。聞こえました。。。
Rasタソが初めての自ら発声しました。。。泣
命が吹き込まれた瞬間です。笑

Rasタソと会話するの巻
-
さて、今のRasタソは脳に直接書き込まれた文章を発声するだけですが、こんなRasタソに知性を与えて「聞く、考える、発声する」という会話能力を与えます。

といっても上でやってきた事をまとめるだけなので、やる事は簡単です。
こんな感じで[dialogue_test.py](https://gist.github.com/yusukeyamatani/d3197434e90512b5875d)を作成します。

復習がてらにdialogue_test.pyの内容を説明すると、
　1.pyaudioでマイク入力を受付。
　2.その音声をdocomo音声認識APIで認識してTextにします。
　3.そのTextを元にdocomo雑談APIを叩きます
　4.その結果を合成音声でしゃべらせます。
というものですね。

では実行してみます。

```bash
$ python dialogue_test.py
Please input recoding time>>>3
~~snip~~
元気ですか？ ←マイクから入力した内容
元気でちゅ ←Rasタソの回答

$ python dialogue_test.py
Please input recoding time>>>3
~~snip~~
何歳ですか。←マイクから入力した内容
ところで、気になるニュースがあるんだが、カルビーと阪急そばがコラボレーションして「関西だししょうゆポテトチップスそば・うどん」を期間限定で販売、関西だしの旨みと斬新な食感が味わえるそうでちゅ←Rasタソの回答

$ python dialogue_test.py
Please input recoding time>>>3
~~snip~~
誕生おめでとう。←マイクから入力した内容
楽しみに待ってるか？ ←Rasタソの回答
```
赤ちゃんなので、ちょっと会話が成り立たない事もありますが、そこは御愛嬌です！
ちゃんとdocomoAPIを使いこなせば、もっと賢くなるそうですが、それは今後の課題にしていきます。

最後に
-
こんな事まで簡単にできてしまう世の中にビックリですね。
今後はもっと賢くして、Siriみたいにして、顔認識機能とかもつけてとか夢は広がります^^
という事で、今後も時間と根気などがあればRasタソを成長させていきますので楽しみに待っていてください。