Raspberry pi で音声会話をする

#はじめに

それでは作っていきましょう（こんなに簡単に書いていますが
結構手こずりました）
#用意すべきもの
Raspberry Pi 1台 ~~買い方は知らん~~
スピーカまたはイヤフォン　1個　（自分は100円ショップの）
mic  16khz/16bit で出力できるもの　**ここ重要!**
例えばこういうの[SANWA SUPPLY MM-MCUSB16](https://www.amazon.co.jp/gp/product/B0027WPY82/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1)
あとボタン[＜表面実装用タクトスイッチ　4.5mm×4.5mmスイッチ　全高3.8mm＞10個＜1sw-000＞](https://www.amazon.co.jp/gp/product/B00U24QRXS/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1)又は[表面実装用タクトスイッチ ＳＫＲＰＡＣＥ０１０（５個入）: パーツ一般 秋月電子通商 電子部品 ネット通販](http://akizukidenshi.com/catalog/g/gP-06185/)

L型ヘッダーソケット[【共立エレショップ】>> L型ヘッダーソケット 1列2P: 【能動・受動・機構パーツ】 << 電子部品,半導体,キットの通販](http://eleshop.jp/shop/g/gE2H364/)
######任意で用意すべきもの
SD card reader 1個
wi-fi 無線子器　1個　~~設定の仕方?そんなもん知らん~~（特に必要ないはず...）
#主な参考リンク
[簡単にできる！音声認識と音声合成を使ってRaspberrypiと会話](http://qiita.com/kinpira/items/75513eaab6eed19da9a3)
[Raspberry Piで音声認識](http://qiita.com/t_oginogin/items/f0ba9d2eb622c05558f4)
#作り方Ⅰ（マイクの設定）

```/etc/modprobe.d/alsa-base.conf
sudo nano /etc/modprobe.d/alsa-base.conf
```
でその中にこれをぶち込めばいいのです

```/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
```
こうするとモジュールの優先順位が変わり
USBマイクが使えるようになります（何のことかわからない？
なら飛ばせばいい）
では見てみましょう（絶対にしろと入っていない）

```ruby:command
$ cat /proc/asound/modules
 0 snd_usb_audio
 1 snd_bcm2835
```

～ちなみに～
マイクの音量調整は

```
$ amixer sset Mic 16 -c 0
Simple mixer control 'Mic',0
  Capabilities: cvolume cvolume-joined cswitch cswitch-joined penum
  Capture channels: Mono
  Limits: Capture 0 - 62
  Mono: Capture 62 [100%] [22.50dB] [on]
```
みたいな感じでしてください（16でよいと言っていない）
#設定Ⅱ（スピーカの設定）
オーディオをミニプラグに出力させる場合

```
$ amixer cset numid=3 1
```
オーディオをHDMIに出力させる場合

```
$ amixer cset numid=3 2
```
**スピーカテスト**

```
$ aplay /usr/share/sounds/alsa/Front_Center.wav
```
又は

```
$ aplay /usr/share/sounds/alsa/Front_Center.wav
```

で音が出ればよい
#設定Ⅲ（pyaudio）
このサイトを参考にした
[raspberryPi と 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)
```
$ sudo apt-get install python-pyaudio
```
でpyaudioをインストール

```
$ sudo nano pyaudio_test.py
```
でpyaudio_test.pyを作成して

```pyaudio_test.py
# -*- coding: utf-8 -*-
#マイク0番からの入力を受ける。一定時間(RECROD_SECONDS)だけ録音し、ファイル名：mono.wavで保存する。
 
import pyaudio
import sys
import time
import wave
 
if __name__ == '__main__':
    chunk = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    #サンプリングレート、マイク性能に依存
    RATE = 16000
     #録音時間
    RECORD_SECONDS = input('Please input recoding time>>>')

    #pyaudio
    p = pyaudio.PyAudio()

    #マイク0番を設定
    input_device_index = 0
    #マイクからデータ取得
    stream = p.open(format = FORMAT,
                    channels = CHANNELS,
                    rate = RATE,
                    input = True,
                    frames_per_buffer = chunk)
    all = []
    for i in range(0, RATE / chunk * RECORD_SECONDS):
        data = stream.read(chunk)
        all.append(data)
    
    stream.close()
    data = ''.join(all)
    out = wave.open('mono.wav','w')
    out.setnchannels(1) #mono
    out.setsampwidth(2) #16bits
    out.setframerate(RATE)
    out.writeframes(data) out.close()

    p.terminate()
```
で、Ctrl+Xで保存して退出して

```
$ python pyaudio_test.py
```
で実行
警告？がたくさん出るが気にしない
これで"can not start"の後に喋ってみよう/home/piにmono.wavができる
再生してみて確かめてみるのもよい
もし"over flow"又は"indivi..."みたいな表示が最後に出て
mono.wavが生成されない場合マイクがレート16000に対応していない
ほかのマイクにするしかない
*~~すべての責任はdcomo APIにある！~~*
#ⅣAquestalk
[公式サイト](https://www.a-quest.com/products/aquestalkpi.html)から/home/piにdownloadする

```
$ tar xzvf aquestalkpi-20130827.tar.gz
```
で解凍（もしこれで出来ない場合は$ tar xzvfの後を保存したファイルのpathにしてください）
では喋らせてみましょう

```
$ cd aquestalkpi
$ ./AquesTalkPi "あれこの船潜水できない？" |  aplay -Dhw:1,0
```
で、再生された声に聞き覚えはありませんか？
そうです魔理沙です！！
（と、感動するのは我だけか...)
#Ⅴdocomo API
[docomo Developer support](https://dev.smt.docomo.ne.jp/?p=index)でまずアカウントを作成します。(サイトの右端にあるログイン/新規登録から)
次にログインしてAPI利用申請・管理を選択で、新規API利用申請
で必須の項目を適当に埋めて（コールバックURLに関しては*https://dummy*を入力）次に進む
APIの機能選択は「雑談対話」と「音声認識【Powered by アドバンスト・メディア】」を選択で最終確認をして申請
申請が終わったらAPI利用申請・管理からさっき申請した奴を探して
APIkeyの項目にある文字列をどこかに保存する（しなくてもいい）
#Ⅵ完成
dialogue_test.pyを作る
の前に
[ボタン・システム音[1]](https://soundeffect-lab.info/sound/button/)の中の警告音1を/home/piに
ダウンロードしてください
で、dialogue_test.pyを以下のような感じで作ってください

```
sudo nano dialogue_test.py
```

```dialogue_test.py

# -*- coding: utf-8 -*-
#マイク0番からの入力を受ける。一定時間(RECROD_SECONDS)だけ録音し、ファイル名：mono.wavで保存する。
 
import pyaudio
import sys
import time
import wave
import requests
import os
import json
import subprocess
import pygame.mixer

def recognize():
    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)
    message = r.json()['text']
    print message
    return message
    
def dialogue(message="こんにちは"):
    url = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY={}".format(APIKEY)
    payload = {
      "utt": message,
      "context": "",
      "nickname": "魔理沙",
      "nickname_y": "マリサ",
      "sex": "女",
      "bloodtype": "B",
      "birthdateY": "2000",
      "birthdateM": "12",
      "birthdateD": "31",
      "age": "17",
      "constellations": "双子座",
      "place": "東京",
      "mode": "dialog",
      "t":20
    }
    r = requests.post(url, data=json.dumps(payload))
    print r.json()['utt']
    return r.json()['utt']
    
def talk(message="こんにちは",card=1,device=0):    	
        res = subprocess.check_output('/home/pi/aquestalkpi/AquesTalkPi " ' + message.encode('utf-8') + ' " | aplay -Dhw:{},{}'.format(card, device), stderr=subprocess.STDOUT, shell=True)
  
pygame.mixer.init()
if __name__ == '__main__':
    chunk = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    PATH = '/var/tmp/tmp.wav'
    APIKEY='xxxxxxxxxxxxx' #DocomoAPI Key
    CARD = 1 #OUTPUTの指定
    DEVICE = 0 #OUTPUTの指定
    #サンプリングレート、マイク性能に依存
    RATE = 16000
    #録音時間
    RECORD_SECONDS = 3
    # pygame
    pygame.mixer.music.load("/home/pi/warning1.mp3")
    pygame.mixer.music.play()
    time.sleep(1)
    pygame.mixer.music.stop()     
    #pyaudio
    p = pyaudio.PyAudio()
 
    #マイク0番を設定
    input_device_index = 0
    #マイクからデータ取得
    stream = p.open(format = FORMAT,
                    channels = CHANNELS,
                    rate = RATE,
                    input = True,
                    frames_per_buffer = chunk)
    all = []
    for i in range(0, RATE / chunk * RECORD_SECONDS):
        data = stream.read(chunk)
        all.append(data)
 
    stream.close()    
    data = ''.join(all)                    
    out = wave.open(PATH,'w')
    out.setnchannels(1) #mono
    out.setsampwidth(2) #16bits
    out.setframerate(RATE)
    out.writeframes(data)
    out.close()
     
    p.terminate()
    
    message = recognize()
    talk_message = dialogue(message)
    talk(talk_message, CARD, DEVICE)


```
で、 APIKEY='xxxxxxxxxxxxx' #DocomoAPI Keyのxxxxxxxxxxxxxの部分を先ほど取得したAPIkeyに書き換えてください
それでは試してみましょう

```
$ python dialogue_test.py
Please input recoding time>>>3
~~snip~~
戦争をどう思う？ ←マイクから入力した内容
非効率だ ←Rasタソの回答
```
みたいな感じになります
ちなみに"jack server is not running or cannot be started"と出た後に喋ってください
~~#最後に
今後追加で編集する予定です~~
#ボタンを作る
まずシェルを書きます[Raspberry Piに電源スイッチを付けたくてシェルクスリプトについて少し調べた話](http://engineernote.hatenablog.com/entry/2015/03/10/013210)

```
sudo nano /home/pi/push_botton.sh
```

```/home/pi/push_botton.sh
#!/bin/sh
GPIO=22     #使用するGPIOポート
PUSHTIME=1  #押す秒数

#初期設定
echo "$GPIO" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio$GPIO/direction
echo "low" > /sys/class/gpio/gpio$GPIO/direction

#1秒間押されるまで待つ
cnt=0
while [ $cnt -lt $PUSHTIME ] ; do
  data=`cat /sys/class/gpio/gpio$GPIO/value`
  if [ "$data" -eq "1" ] ; then
    cnt=`expr $cnt + 1`
  else
    cnt=0
  fi
  sleep 1
done

#dialogue_test.pyの実行
python /home/pi/dialogue_test.py
done
```
でスイッチを用意します
[＜表面実装用タクトスイッチ　4.5mm×4.5mmスイッチ　全高3.8mm＞10個＜1sw-000＞](https://www.amazon.co.jp/gp/product/B00U24QRXS/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1)
or
[表面実装用タクトスイッチ ＳＫＲＰＡＣＥ０１０（５個入）: パーツ一般 秋月電子通商 電子部品 ネット通販](http://akizukidenshi.com/catalog/g/gP-06185/)
and
[【共立エレショップ】>> L型ヘッダーソケット 1列2P: 【能動・受動・機構パーツ】 << 電子部品,半導体,キットの通販](http://eleshop.jp/shop/g/gE2H364/)

※＜表面実装用タクトスイッチ　4.5mm×4.5mmスイッチ　全高3.8mm＞10個＜1sw-000＞はL
型ヘッダーソケットと若干方が合っていない *自分でどうにかしよう*
で次の写真のようにつける
![alt](http://cdn-ak.f.st-hatena.com/images/fotolife/y/yosuke_kirihata/20150309/20150309161639.jpg)
（参考サイトのやつです自分で撮るのが面倒だった）

```
sudo nano /etc/rc.local
```

```/etc/rc.local
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

su -c /home/pi/push_botton.sh #追加
exit 0
```

で最後に再起動
そうしてボタンを押してみましょう
音声会話ができるはずです
#最後に
↑これを書くのは２回目ですが、
大体こんな感じでgoogle speakerに匹敵?するものが作れたのではないでしょうか
少なくとも1/2の価格で...
最後に（三回目）
まだ追加編集をするかもしれません
きっとraspberry piはshoutdown処理が必要だぞこれでは実用性がないではないか
という人がいると思うのでshoutdown機構の追加方法をいつか書きたいと思います
（今はいろいろと事情があって書けないだなんて言えない...)