ラズベリーパイで簡単に音声認識+音声合成をしてみよう!!

今回はラズベリーパイをつかって音声認識・音声合成をやっていきましょう。

最終的にイメージしているものとしては、こちらからラズベリーパイに話しかけると、ラズベリーパイは話しかけた内容を聞き取りテキストに変換し、そのテキストを言語処理してなにかしらの返答を合成音声でレスポンスとして返そうというものです。

それでは、実際にやっていきましょう!

1,準備


13467549_1075937485829133_215026929_o

用意するものは、
・ラズベリーパイ
・キーボード
・マウス
・無線LANアダプター(USBに挿入):必要に応じて
・ヘッドホン(イヤホンジャックに挿入):音声出力用
・USBマイクロホン(USBに挿入):音声入力用

また、ラズベリーパイのOSは今回Jessieを使いました。

参考までに、
USBマイクロホンはこちら

無線LANアダプターはこちら

 

2,ラズベリーパイで音を出力するための設定


音声出力のテストを行なうために、ラズベリーパイ上で以下のコマンドを実行します。

この状態でイヤホンジャックにヘッドホンを挿入していることを確認し、以下のコマンドを実行します。

これで、音が再生されるはずです。
次にWAVという音声ファイルが再生できるかを確認します。
先ほどのrebuild.shの実行によりテスト用wav音声ファイルがあるのでそちらを再生してみましょう。

これで音がなれば音声出力の準備ができました。

もし音が出なかった場合は出力を固定して再度試してみてください。

 

3,流れの説明


これで準備ができました。

ここで、一応これから音声認識・合成を行ないレスポンスを返すために行なう実装の流れを説明します。

1、USBオーディオアダプタの優先度の設定

ラズベリーパイでは、サウンドカードのデバイスドライバとしてLinuxカーネルのコンポーネントであるALSAを採用しています。そこでALSAの設定ファイルでUSBオーディオの優先度を上げるように設定します。(ここは、なんとなくそういうことかーという程度で大丈夫です。)

2,音声の録音・再生

マイクからの入力を音声ファイルとして保存し、再生できるか確認します。

3,言語処理の実装

マイクから入力された内容に対する、返答のテキストを用意します。今回はdocomoが提供する雑談APIを用いてみました。

4,返答テキストを合成音声に変換して出力

最後に、入力された内容にたいする返答を合成音声で出力します。
この音声合成には、AquesTalkを用います。

それでは、早速やっていきましょう!

 

4,USBオーディオアダプタの優先度の設定


USBオーディオアダプタの優先度の設定の設定をしていきましょう。

まず、こちらのコマンド

により現在のUSBオーディオアダプタの優先度が確認できます。このとき、snd_bcm2835が現状優先度としては高くなっています。今回つかいたいマイクはsnd_usb_audioなので、これを変更します。

を実行すると、

のようなサウンドカードのデバイスが確認できます。
例えば、この例だと、サウンドカード1デバイス0が今回のマイクです。

したがって、環境変数をこれにしたがって変更します。

これを実行した後、再起動をします。

再起動が完了したら、再度

を実行すると優先度が変更していることが確認できます。

4,音声の録音・再生


次に音声の録音・再生を行ないます。今回は、pythonで扱いやすいpyaudioを用います。

まずはじめに、マイクのボリュームを調整します。

この設定では、マイクの音量調整を0−62の値で設定できます。今回は50にしました。

ここで、pyaudioをインストールしておきます。

インストールが完了したら、pyaudio_test.pyを作成します。

ソース元: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

 

# -*- 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 = 44100
   #録音時間
    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()

このファイルを実行すると、音声入力結果がmono.wavファイルに録音されます。

この録音したファイルを再生してみると

音声が確認できます。

5,言語処理の実装


ここまででマイクから音声入力ができるようになりました。次に、音声入力を実際に会話情報へと音声認識でテキストに変換し、また、テキストに対する返答を出力しましょう。

今回はdocomoが提供しているAPIを用いました。

まずは、こちらのDocomo Developer Supportサイトにて登録をすませ、APIキーを取得しておいてください。

APIキーを取得したらaction.pyを作成します

action.py

# -*- coding: utf-8 -*-
import requests
import json

url = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY={}".format("API_KEY_IS_HERE")
payload = {
	"utt": "こんにちは",
  	"context": "",
  	"nickname": "涼介",
  	"nickname_y": "リョウスケ",
  	"sex": "男",
  	"bloodtype": "B",
  	"birthdateY": "1993",
 	"birthdateM": "2",
  	"birthdateD": "14",
  	"age": "16",
  	"constellations": "しし座",
  	"place": "東京",
  	"mode": "dialog",
	}
r = requests.post(url, data=json.dumps(payload))
print r.json()['utt']

ここでは、”こんにちは”という入力を投げかけレスポンスを取得します。
このファイルを実行すると適当なレスポンスが返ってきます。

簡単ですね!

 

6、合成音声で返答を出力


さて、マイクで音声を入力すること、質問をなげかけてその返答をテキストで出力することができました。最後に、これらを組み合わせて、返答を合成音声で出力しましょう!

音声合成では、AquesTalk APIを使います。

こちらのサイトから、自分のローカルにダウンロードします。

ダウンロードしたファイルをscpなどでラズベリーパイ上に転送し、解凍して下さい。

解凍するとaquestalkというディレクトリができるので

で移動し、ためしに適当な言葉で合成音声を試してみましょう。

これで合成音声が再生されていれば完璧です。

最後にexecute.pyファイルを作成し実行してみましょう。
execute.py
ソースは(https://gist.github.com/yusukeyamatani/d3197434e90512b5875d)、こちらを参考にさせて頂きましたが、このままでは動かなかったので、一部修正をくわえました。修正箇所は以下コード内でコメントアウトしてあるtry ~ error構文のところです。

# -*- 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 = 44100
    RECORD_SECONDS = input('Please input recoding time>>>')
     
    p = pyaudio.PyAudio()
 
    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):
        #!!!!修正箇所!!!!
        try:
        	data = stream.read(chunk)
    	except IOError:
        	pass

        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()

それでは、ようやく実行してみましょう!!

こちらが実行結果になります。

実行結果1
質問:名前は何
返答:ところで、ジャンボジェット機って接着剤で組み立てられてるそうや

実行結果2:
質問:好きな食べ物は何。
返答:味が濃いやん

うん、返答がいまいちでした、、
自然言語処理まわりががんばりどころですね。

 

さいごに


評価としては、音声認識はかなり性能としてはいいといえるのではないでしょうか。
一方で、入力テキストにたいする言語処理がまだまだだなという印象です。
やっぱり、これからNLPがあつくなってきそうですね!!

この辺の自然言語処理あたりにも挑戦していきたいところです。

参考)