Raspberry Pi＋Juliusで音声を認識する

# はじめに
Raspberry Piにマイクを接続して、話しかけた音声を認識させてみます。
音声認識には国産ツールの「[Julius](http://julius.osdn.jp/)」を使用します。

# 用意するもの
- Raspberry Pi 3
- USBマイク    

USBマイクは、マイク付きWebカメラでも利用できます。
ということで、今回はLogicool C300というUSB接続のWebカメラを使います。
https://www.amazon.co.jp/%E3%83%AD%E3%82%B8%E3%82%AF%E3%83%BC%E3%83%AB-C300-%E3%82%A6%E3%82%A7%E3%83%96%E3%82%AB%E3%83%A0/dp/B002J4UAG4

# 手順
Raspberry Piで音声認識をするにはJuliusの導入のほか、マイク関連の設定が必要にです。大まかな手順は下記の通りです。

1. USBマイクをRaspberry Piで使う準備をする
1. 録音のテスト
1. Juliusをインストールする
1. Julius音声認識パッケージを取得する
1. 動作確認


# USBマイクをRaspberry Piで使う準備をする
## Raspberry PiにUSBマイクを認識させる
Raspberry Pi3と現在（3/28時点）でのRaspbianであれば、Logicool C300はUSBに接続するだけでデバイスを認識します。正しく認識されているかは、下記コマンドで確認できます。

```shell:コマンド
lsusb
```

```shell:lsusb実行結果
Bus 001 Device 004: ID 046d:0805 Logitech, Inc. Webcam C300   
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. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```

上記の実行結果では、「Bus 001 Device 004」で「Logitech, Inc. Webcam C300」と表示されているため、WebカメラがUSBデバイスとして認識されていることがわかります。

## USBマイクの優先順位をあげる
Raspberry Pi 3には、Φ3.5オーディオ出力端子が搭載されています。USBにスピーカー／マイクを接続しても、標準のオーディオ出力端子が優先されるため、USB側のスピーカー／マイクを認識してくれません。
これを解決するためには、オーディオデバイスの優先順位を変更する必要があります。
現在の優先順位を確認するには、下記のコマンドを実行します。

```shell:コマンド
cat /proc/asound/modules
```

```shell:実行結果
 0 snd_bcm2835
 1 snd_usb_audio
```

実行結果は、左から「優先順位（数値が低いほうが優先順位が高い）」、「デバイス名」の順で表示されます。上記の結果では、USBスピーカー／マイク（snd_usb_audio）がΦ3.5オーディオ出力端子（snd_bcm2835）よりも下であることがわかります。

これを変更するには、`/lib/modprobe.d/aliases.conf`に記載の設定を変更します。
viなどのテキストエディタでファイルをしましょう。（root権限の付与を忘れずに）

```shell
sudo vim /lib/modprobe.d/aliases.conf 
```

ファイル内の``options snd-usb-audio index=-2``という箇所がUSBスピーカー／マイクの設定ですので、こちらの設定を「-2」から「0」を変更します。

```shell:変更後の/lib/modprobe.d/aliases.conf 
# prevent unusual drivers from appearing as the first sound device ###########
options snd-pcsp index=-2
# options snd-usb-audio index=-2    # USB audio before 
options snd-usb-audio index=0       # USB audio after
options cx88_alsa index=-2
options snd-atiixp-modem index=-2
options snd-intel8x0m index=-2
options snd-via82xx-modem index=-2
```

設定ファイルを保存したら、Raspberry Piを再起動します。再起動後、再度``cat /proc/asound/modules``を実行し、下記のように優先順位が変わっていれば、設定変更が有効になっています。

```shell:設定変更後のオーディオデバイスの優先順位
 0 snd_usb_audio
 1 snd_bcm2835
```

# 録音のテスト
Raspberry PiとUSBマイクが正しく動作することを確認するため、USBマイクから音声を録音し、ファイルに保存してみます。
音声の録音はarecordコマンドで実現できますが、arecordコマンドで録音するにはUSBマイクに割り当てられている「カードNo.」と「デバイスNo.」を知る必要があります。それぞれの確認は、arecordコマンドの-lオプションで確認できます。

```shell:カードNo.の確認
arecord -l
```

```shell:実行結果
**** List of CAPTURE Hardware Devices ****
card 0: U0x46d0x805 [USB Device 0x46d:0x805], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

上記の場合、「card 0」「device 0」と表示されているため、カードNo.、デバイスNo.ともに「0」で認識されているということがわかります。

カードNo.、デバイスNo.がわかったところで、arecordを使って録音した音声をファイルに保存します。録音するコマンドは下記のとおりです。

```shell:カードNo.の確認
arecord -D plughw:0,0 test.wav
```

コマンド実行後、録音待ちの状態になります。マイクに向かって話しかけ、完了したらCtrl+Cを押下して終了します。終了時に、下記のようなメッセージが表示されますが、中断した際に表示されるメッセージですので、問題ありません。

```
Aborted by signal Interrupt...
arecord: pcm_read:2103: read error: Interrupted system call
```

録音されたファイル（test.wav）を再生してみましょう。音楽プレイヤーなどのソフトで話しかけた音声を聞くことができれば、成功です。


# Juliusのインストール
Juliusは、GitHubよりソースコードを取得し、ビルドすることでバイナリファイルを作成します。
公式ドキュメントにはバイナリパッケージを取得してインストールできるという記載がありますが、Githubで公開されているバイナリパッケージはWindows用のみで、Linuxなどで使用する場合は、ソースコードからコンパイルする必要があります。いずれMac/Linux環境用のバイナリパッケージも配布してほしいですね。

## Juliusのソースコードの取得する
GithubのJuliusのサイトからソースコードを取得しましょう。下記のコマンドで取得できます。

```shell
mkdir julius
cd julius
wget https://github.com/julius-speech/julius/archive/v4.4.2.1.tar.gz
```

## ソースコードをコンパイルする
下記のコマンドを実行し、ソースコードからバイナリパッケージをコンパイルします。コンパイルには時間がかかりますので、少し待ちましょう。

```shell
tar xvzf v4.4.2.1.tar.gz
cd julius-4.4.2.1
./configure
make
```

# Julius音声認識パッケージを取得する
Juliusで音声認識をするためには、音声認識パッケージをインストールする必要があります。音声認識パッケージは[公式サイト](http://julius.osdn.jp/index.php?q=dictation-kit.html)より取得します。
公式サイトでは用途に応じて複数のパッケージを提供していますが、今回は、一般的な日本語の音声認識であるディクテーションキット（dictation-kit）を使用します。
ディクテーションキットの取得は、下記コマンドで行います。

```shell
mkdir ~/julius/julius-kit
cd ~/julius/julius-kit
wget https://osdn.net/dl/julius/dictation-kit-v4.4.zip
unzip dictation-kit-v4.4.zip
```

# その他環境設定
## ALSADEV設定でデバイスを固定にする
Juliusを実行する際にもカードNo.、デバイスNo.の情報が必要です。Juliusが常に同じ設定を使用できるように、環境変数として設定しておくと便利です。
Raspberry Piの場合、~/.profileに下記の一文を追加します。

```shell:.profile
export ALSADEV="plughw:0,0"
```

変更を有効にするには、Raspberry Piを再起動、または「source ~/.profile」を実行します。
※この後の手順でRaspberry Piを再起動するため、ここでの再起動上記コマンドの実行は不要です。

## /dev/dspを有効にする
JuliusではALSAサウンドドライバのデバイスファイル/dev/dspを使用します。標準では使用できるようになっていないため、下記のコマンドで利用できるようにします。（有効にするために再起動をしています）

```shell
sudo modprobe snd-pcm-oss
sudo sh -c "echo snd-pcm-oss >> /etc/modules"
sudo reboot
```

ここまでで、Juliusの環境構築は完了です。

# 動作確認
ディクテーションキットの解凍が完了したら、いよいよJuliusを動かします。起動のコマンドは下記のとおりです。

```shell
~/julius/julius-4.4.2.1/julius/julius -C ~/julius/julius-kit/dictation-kit-v4.4/main.jconf -C ~/julius/julius-kit/dictation-kit-v4.4/am-gmm.jconf -nostrip
```

Juliusの起動が成功すると``<<< please speak >>>``と表示されます。
試しに「こんにちは」とマイクに向かって話しかけてみましょう。
音声認識が成功すると、下記のように表示されます。

```shell
pass1_best:  こんにちは 。
pass1_best_wordseq: 
 こんにちは+感動詞 

pass1_best_phonemeseq: silB | k o N n i ch i w a | silE
pass1_best_score: -4168.268555
### Recognition: 2nd pass (RL heuristic best-first)
STAT: 00 _default: 21946 generated, 1540 pushed, 319 nodes popped in 159
sentence1:  こんにちは 。
wseq1: 
 こんにちは+感動詞 

phseq1: silB | k o N n i ch i w a | silE
cmscore1: 0.351 0.416 1.000
score1: -4188.954102
```

# おわりに
Juliusの導入は想像以上に簡単にできました。どちらかというと、USBマイクを認識するためにOS側の設定変更のほうが大変でした。
それと、今回使用しているディクテーションキットの認識率ですが、Google Cloud Speech API等に比べると精度は劣るように思います（辞書の精度に大きな差があるのでしょうね）。その分、通信のオーバーヘッドがない分応答速度は早いので、用途に応じて使い分けるということになりそうです。