Raspberry Pi と Electron で自立型の音声アシスタント

RaspberryPiと外部ブラウザ（Chrome）を利用して会話するのを以前試したのですが（ [Raspberry Pi とブラウザで会話する。](http://qiita.com/PonDad/items/72bfcbbe8313f46a8890))これを自立型で出来ないかあれこれ考えていたところ、ブラジルの方でElectronを利用して実現されている方を見つけました。

[Raspberry Pi 3 + NodeJS + Electron - Cloud and Mobile](http://adndevblog.typepad.com/cloud_and_mobile/2016/04/raspberry-pi-3-nodejs-electron.html)

動画を拝見すると確かにRaspberryPiのみを使って会話をしています。JavaScriptライブラリを使うことで簡単に実装できるよ！みたいなことが書かれているので参考に試してみました。

## こんな感じ

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/aIXfHY62syE/0.jpg)](https://www.youtube.com/watch?v=aIXfHY62syE)

[Raspberry Pi Voice Assistsnt - YouTube](https://www.youtube.com/watch?v=aIXfHY62syE)

キーワードに対して２〜３の簡単な返答はできるようになりました。基本ブラジルのAugusto Goncalvesさんのコードを少しいじっただけなのですが、少々ハマった部分もありましたのでまとめてみます。

## 環境

Raspberry Pi2 ModelB 
- Ubuntu MATE 16.04
- Node.js v-6.2.0
- Electron 1.0.2

## Ubuntu MATE

動画のモニターを見ていただけると分かると思うのですがOSはRaspbianではなくてUbuntu MATEを使用しています。Macで上手く動作したんでいざラズパイで試そうとしたところ、Electronのインストールでエラーが出てしまいました。

[Electron not starting on Raspberry Pi #145](https://github.com/MichMich/MagicMirror/issues/145)

検索で調べたところ、どうもRaspbianのバグで今のところインストール時にエラーになってしまう様です。仕方がないのでUbuntu MATEを利用することにしました。

機会があればまとめたいのですが、ラズパイに最適化されているRaspbianと比べて設定しなければいけないことが多くて少々面倒です。SSHが初期インストールされていないのでセットアップにモニターとキーボード・マウスが必要になります。

ただし、使用感は上々でElectronもすんなりインストールする事が出来ました。ダウンロードは以下より可能です。

[Download Ubuntu MATE](https://ubuntu-mate.org/download/)

## Annyang.js

ChromeのWeb Speech Recognition APIはさすがにGoogleさんが作っているだけあって恐ろしく正確に日本語も解析してくれます。ただし、ブラウザで利用する際には安全面からか数分経つと自動的に音声読み込みを遮断してしまう仕様になっています。

以下のライブラリを使用することで音声読み込みが切れることなく利用する事が出来ます。

[annyang! SpeechRecognition that just works](https://www.talater.com/annyang/)

`setTimeout(annyang.start, 1000-timeSinceLastStart);`こんな記述が見えるので、タイマーを利用して1秒毎に音声検索を実行するようにしている様です。

これでマウスクリックやタップを利用せずに音声認識を使用する事が出来ます。

## Electron desktop app using annyang

上記のライブラリを制作されているイスラエルのTal Aterさんがさらに気の利いたことにElectronで利用出来るライブラリを公開されていました。

[TalAter/annyang-electron-demo](https://github.com/TalAter/annyang-electron-demo)

`const annyang = require('annyang');`でライブラリを読み込む事が出来ます。最新のElectron1.0でも利用可能でした。

## ResponsiveVoice.js

MacやWindowsなどには音声発話エンジンが搭載されていますが、Raspberry Piで音声発話させる際にはライブラリを読み込むか外部APIより読み込まなくてはなりません。

そこで以下のライブラリを利用します。

[ResponsiveVoice.js](http://responsivevoice.org/)

こちらも優れもので、発話エンジンが搭載されている場合はそれを利用します。以下Macで使用した時の動画です。

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/FoVRnqwGGes/0.jpg)](https://www.youtube.com/watch?v=FoVRnqwGGes)

[Electron App Voice Assistant - YouTube](https://www.youtube.com/watch?v=FoVRnqwGGes)

Macで利用する際はお馴染みKyokoさんを呼び出して発話させてくれます。

上記のラズパイで利用する際は、
`http://responsivevoice.org/responsivevoice/getvoice.php?t=`
こんな記述が見れるのでAPIのエンドポイントに自動的にリクエストを送り、帰って来た音声データを`audio`属性で自動的に再生してくれます。

Web経由の音声発話は、やはり組み込みと比べると1,2秒タイムラグが出来てしまいますね。

## 音声発話エラー時の対応

ここが一番のポイントになると思うのですが、APIリクエストで音声発話をさせると結構頻繁にエラーが起きます。

冒頭のAugusto Goncalvesさんはこんな風に記述していました。

```app.js
function say(msg, callback) {
    console.log('Pause annyang');
    console.log('Saying: ' + msg);
    lastSentence=msg;
    annyang.abort();
    responsiveVoice.speak(msg, CONFIG.voiceSpeakingLanguage, {
        onend: function () {
            console.log('Resume annyang');
            annyang.start();
        }
    });
}
```

音声発話のタイミングで一旦音声認識を停止させて、発話が終わったタイミングで音声認識を開始させています。

ラズパイで試すと結構音声発話にエラーが発生します。キーボード無しで操作する際はそこでエラーが出て停止状態になってしまいます。

上記のコードに加えて、音声発話がエラーになった時も音声認識を再開する様にしてみました。（冒頭動画の30秒位で一度発話エラーが起きています。エラー後に再度音声認識を自動で再開させています）

##　余談

どうでも良いことですが、この音声アシスタント「サマンサOS2」は映画「[her/世界でひとつの彼女](http://her.asmik-ace.co.jp/)」に登場するAI「SamanthaOS1」（声：[スカーレット・ヨハンソン](https://www.google.co.jp/search?q=%E3%82%B9%E3%82%AB%E3%83%BC%E3%83%AC%E3%83%83%E3%83%88%E3%83%BB%E3%83%A8%E3%83%8F%E3%83%B3%E3%82%BD%E3%83%B3&stick=H4sIAAAAAAAAAONgFuLSz9U3yCk0S0syVeIEsY0MDU1KtHh888syU0PyHZNL8osAde3egycAAAA&biw=1269&bih=892&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjy9IeV-O3MAhUkFqYKHfGLAVwQ_AUIBigB) )にインスパイアされています。

本当にどうでも良いですね。

## まとめ

コードは一応GitHubに上げてあります。

[PonDad/samanthaos2](https://github.com/PonDad/samanthaos2)

映画の「SamanthaOS1」の様に気の利いた会話が出来る様になるにはまだまだ先になりそうですが。

## 参考にさせていただきました。

[augustogoncalves/personal-assistant](https://github.com/augustogoncalves/personal-assistant/blob/master/js/app.js)

[TalAter/annyang-electron-demo](annyang-electron-demo/index.html
)

[anchetaWern/responsive-voice.js](https://gist.github.com/anchetaWern/ed6e841555602656e6ae)