RaspberryPiのマイクで録音した音声をテキスト化する
ヒミツのくまちゃんと話すための企画その2になります。
前回のエントリーではマイクで録音するところまで来ました。
後は録音した音声をテキスト化することができれば、拡張性(雑談APIやNobyに渡すことができるので会話っぽい表現を実装可)が増えそうです。
あたりがいいかなと思いました。(docomoさんのは内部的にはAmiVoiceっぽい) ということでdocomoさんのAPIを使用します。(後日、GoogleさんのAPIも触る予定ですが)
基本的な情報は以下のURLにあります。
基本動作は音声データを送信すると音声認識したデータをテキスト化してJSON化して返信するものです。つまり、データの送り方(REST)とデータの形式(ファイルのバイナリフォーマット)が分かれば概ね処理ができます。
今回は特別なSDKなどを使用せず、RESTで通信を行いたかったのでdocomoさんのAPIの中の音声認識API【Powered by アドバンスト・メディア】を使用します。
以下は上記サイトの引用です。
音声認識API【Powered by アドバンスト・メディア】とは 音声データをREST形式で送信するだけで音声認識をすることができます。 主な特徴 * HTTPで音声データをPOSTするだけなので、AndroidやiOSに限らず様々なプラットフォームでのご利用が可能です。 * クライアントアプリケーションに特殊なライブラリを組み込む必要がないため自由度の高い実装が可能です。 * クライアントアプリケーションにライブラリを組み込んでリアルタイム認識を行うタイプの音声認識と比較して、応答速度が遅い為、レスポンスが求められる用途には向いていません。
ドキュメントを眺めてみる
以下のURLがドキュメントページになります。
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=speech_recognition&p_name=api_amivoice_1#tag01
サービスのエンドポイント
アクセスを行うエンドポイントは以下となります。
https://api.apigw.smt.docomo.ne.jp/amiVoice/v1/recognize
amiVoiceって書いてあるw
リクエストクエリパラメータ
キー | 必須 | 説明 |
---|---|---|
APIKEY | ○ | APIにアクセスするアプリの認証に利用する |
リクエストヘッダのMIME-TYPE
HTTPで送信するデータ形式のMIMEタイプはこのようになっている?
キー | 必須 | 説明 |
---|---|---|
Content-Type | ○ | MIMEタイプmultipart/form-data; boundary=<バウンダリ文字列> |
あれRESTアクセスだし、MIMEタイプは単純なOctet-Streamなんじゃねーの?んが。
やられました。
とはいってもマルチパートでってことはファイル+制御用のJSONを送ればいいのかなと予想がつきます。
リクエストパラメータ(マルチパート化されたデータの中身)
キー | 必須 | 説明 |
---|---|---|
a | ○ | 音声のバイナリデータ。10秒を超える音声データは途中で打ち切られます。音声データのフォーマット:PCM(MSB)16khz/16bit |
v | - | 発話区間検出処理。"on"を指定すると、音声データの無音部分を無視して音声認識処理を行います。 |
このあたりをマルチパート化して送信するようです。
音声ファイルのフォーマットはサンプリングレート16kHz、16bitの10秒以内の音声でいいようなので、PyAudioへは
FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 16000
のようにパラメータを指定すれば問題なさそうです。
また、v
のキーに関しては制度を気にしなければ問題はなさそうなのですが、無音部分は無視してもらわないとかなり精度がわるそうな気がするので、指定をすることにします。
ドキュメントを見てもわかりますが以下のように通信データが送信されます。
POST https://api.apigw.smt.docomo.ne.jp/amiVoice/v1/recognize?APIKEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: multipart/form-data; boundary=<バウンダリ文字列> --<バウンダリ文字列> Content-Disposition: form-data; name="v" on --<バウンダリ文字列> Content-Disposition: form-data; name="a"; filename="sample.adc" Content-Type: application/octet-stream 音声データ(binary) --<バウンダリ文字列>--
データの格納さえうまく調整できれば、Arduinoとかmbedからでも通信できそうな印象です。
ここまでわかれば、あとはpythonのrequestsモジュールでマルチパート化してPOSTする処理が わかれば、使用できそうです。
pythonのrequestsモジュールでマルチパート化処理を行う
こちらを参考にしました。
クイックスタート — requests-docs-ja 1.0.4 documentation
ふむふむ、複数のデータはマルチパート化してPOSTするときには、以下のどちらかの形式でタプル化してpostの引数に与えれば良いようです。
- (キー, データ)
- (キー, データ, Content-Type)
- (キー, データ, Content-Type, ヘッダー)
例えば、StackOverflowのサンプルをみてみれば、
- キー=>foo、データ=>bar
- キー=>spam、データ=>eggs
というデータセットをマルチパート化する場合には以下のような処理になります。
requests.post('http://…', files=(('foo', 'bar'), ('spam', 'eggs'))) # または files = {'foo':'bar', 'spam':'eggs'} requests.post('http://…', files=files)
今回のAPIではa
とv
というキーにデータを入れることになるので
files = {"a": 【音声ファイルのデータ】, "v":"on"} r = requests.post(url, files=files)
こんな感じになるでしょう。あとはファイルの読み込みですがopen()
で読みすればよさそうです。以下は処理の抜粋になります。
path = 'voice.wav' files = {"a": open(path, 'rb'), "v":"on"} r = requests.post(url, files=files)
これで一応大体の構想ができました。
API利用登録
では、登録を行っていきます。 下記のdocomoDeveloperSupportのページからログインを行ってください。SNS系のログインでも問題ありません。
次に音声認識のページの中ほどにある【申請する】のボタンをクリックします。
すると、【API使用申請】のページに遷移します。
ここで使用するアプリケーションの情報を登録することになります。 登録時点で決まっていなくても後で修正ができるので問題ありません。
入力が終わったら【API機能選択へ】のボタンをクリックします。
【API機能選択】の一覧画面に遷移するので、その中から
- 音声認識API【Powered by アドバンスト・メディア】
を選択します。他のAPIを含めても問題ありませんが、今回は1つだけにします。選択が終了したら下の方にある【利用するAPIの利用規約に同意して次へ】をクリックします。
【確認画面】に遷移するので、内容確認後【利用申請する】ボタンをクリックします。
これで申請が完了しました。
詳細は【マイページ】にある【API利用申請・管理】画面の【登録アプリケーション一覧】に情報が表示されます。API使用のキーに関してもここで確認ができます。
音声認識のAPIにデータを送信する
音声認識のAPIにデータを送信して、認識結果のテキスト情報を確認してみます。まずは録音した音声データがあればそれを使用します。 音声のデータフォーマットが10秒以内、サンプリングレート16kHz、サンプル16bitになっていない場合にはエラーとなるので注意が必要です。 今回はvoice.wavというファイルを送信することにします。
前述の通りpythonでデータを送信するコードを作成すると以下のようになります。認識された結果はJSON形式で返されるのですが、その中でtext
のデータが認識された文書全体となります。
#!/usr/bin/env python # -*- coding: utf-8 -*- import requests path = 'voice.wav' APIKEY = '【発行されたAPIのキー】' 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']
このプログラムを実行すると以下のようになります。
$ python sample.py 、おまえ滑舌悪いんじゃないの。
無事に認識できたようです。(実験中に嫁に言われました…涙)
録音機能と連携させる
先程は事前に収録したものでテストしましたが、今度はPyAudioと連携し、録音後API側に対して要求を出すものにします。とは言っても、データは一度ファイルとして蓄積するので先程の処理とは大きく変わらないと思います。
#!/usr/bin/env python # -*- coding: utf-8 -*- #マイクからの入力を5秒間録音し、ファイル名:voice.wavで保存する。 import requests import pyaudio import sys import time import wave chunk = 1024*2 FORMAT = pyaudio.paInt16 CHANNELS = 1 #サンプリングレート、マイク性能に依存 RATE = 16000 #録音時間 RECORD_SECONDS = 5 #input('収録時間をしていしてください(秒数) >>>') print 'マイクに5秒間話しかけてください >>>' #pyaudio p = pyaudio.PyAudio() #マイク0番を設定 input_device_index = 0 #マイクからデータ取得 stream = p.open(format = FORMAT, 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('voice.wav','w') out.setnchannels(1) #mono out.setsampwidth(2) #16bit out.setframerate(RATE) out.writeframes(data) out.close() p.terminate() print '<<< 録音完了' path = 'voice.wav' APIKEY = '【発行されたAPIのキー】' 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']
実行結果
$ python pyaudio_amivoice.py マイクに5秒間話しかけてください >>> 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 <<< 録音完了 おはようございます。。
いろいろWarningは出ていますが、うまく動きました。
まとめ
一応録音した音声をテキスト化することができました。更にこれを雑談APIやNobyAPIなどに送信すれば会話を行うことができるようになります。 もう少しでクマちゃんとの会話ができるようになるのではないかと思います。
次回は会話っぽいやり取りまで進めたいと思います。