Raspberry Piで音声認識


Raspberry Piにマイクを付けて話した日本語を認識させたいと思います。
いくつか参考になるサイトがありましたが、自分の環境と微妙に異なってはまっていたのでまとめておきます。

##参考サイト
- [日本語音声認識](http://cubic9.com/Devel/%C5%C5%BB%D2%B9%A9%BA%EE/RaspberryPi/%C6%FC%CB%DC%B8%EC%B2%BB%C0%BC%C7%A7%BC%B1/)
- [RaspberryPIにUSBマイクで音声入力](http://izumogeiger.blogspot.jp/2013/08/raspberrypiusb.html)
- [Raspberry Piで録音再生](http://shokai.org/blog/archives/7914)
- [Raspberry PiでJuliusを使った音声認識(1)](http://hyottokoaloha.hatenablog.com/entry/2015/06/30/105906)
- [ALSA で snd_pcm_dmix_open が unable to open slave になる場合](http://seussnu.b.osdn.me/?p=150)
- [Can't set default sound device with ALSA [SOLVED]](https://bbs.archlinux.org/viewtopic.php?id=124380)

##使った機器

- Raspberry Pi 2 Model B
- SANWA SUPPLY MM-MCUSB16 USBマイクロホン
- ELECOM USB3.0ハブ ACアダプター付き セルフパワー サイドポート付き 4ポート ブラック U3H-A408SBK
- BUFFALO 無線LAN子機 コンパクトモデル 11n技術・11g/b対応 WLI-UC-GNM
- ELECOM MS-P06U コンパクトスピーカー/USB電源

##USBマイクの確認
まずUSBマイクが認識されるか確認します。

### ライブラリのインストール

```bash
$ sudo apt-get install alsa-utils sox libsox-fmt-all
```

apt-getで失敗する場合、パッケージが古い可能性があるので最新にしておきましょう。

```bash
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo rpi-update
```

`404 Not Found`が発生する場合は、[Raspberry Pi -> Mac -> Internet環境でsudo apt-get update時に404 Not Found](http://qiita.com/t_oginogin/items/bcd3ff56225dfaf02c4e)もご覧ください。

### USBポートの確認

`lsusb`を実行します。

```bash
$ lsusb
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 0d8c:0134 C-Media Electronics, Inc.
Bus 001 Device 005: ID 2109:2812
Bus 001 Device 006: ID 0411:01a2 BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]
```

`C-Media Electronics, Inc.`というやつがUSBマイクです。
ラズパイのUSBポートに挿している状態です。

なぜかUSBバスの方に挿し込むと認識されませんでした。（無線LANはUSBバスでも認識されています）

### オーディオモジュールの優先順位の確認
`cat /proc/asound/modules`を実行し、モジュールの優先順位を確認します。

```bash
$ cat /proc/asound/modules
 0 snd_bcm2835
 1 snd_usb_audio
```

USB入力を優先させるため、`/etc/modprobe.d/alsa-base.conf`を書き換えます。

```bash:/etc/modprobe.d/alsa-base.conf
:
# Keep snd-usb-audio from beeing loaded as first soundcard
options snd-usb-audio index=-2
: 
```

となっているところを

```bash:/etc/modprobe.d/alsa-base.conf
: 
# Keep snd-usb-audio from beeing loaded as first soundcard
#options snd-usb-audio index=-2
options snd-usb-audio index=0
: 
```

に変更します。

一旦リブートします。

```bash
$ sudo reboot
```

再度確認すると次のようになります。

```bash
$ cat /proc/asound/modules
 0 snd_usb_audio
 1 snd_bcm2835
```

### マイクボリューム調整

マイクボリュームが小さいと声が拾えないのでボリュームを調整します。

```bash
$ 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`というのがボリュームです。62で最大ボリュームです。

`-c 0`というのはカードNoです。

オプションなしで実行するとエラーが出ました。

```bash
$ amixer sset Mic 62
amixer: Unable to find simple control 'Mic',0
```

ヘルプを見るとカードNoを指定できることがわかったのでオプションに付けたところボリュームを変更できました。

```bash
$ amixer -h
Usage: amixer <options> [command]

Available options:
  -h,--help       this help
  -c,--card N     select the card
  -D,--device N   select the device, default 'default'
  : 
```

カードNoの調べ方は次の通りです。

```bash
$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: Device [USB PnP Audio Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

私の環境では、card 0 device 0です。

後でわかったのですが、~/.asoundrcに

```bash:~/.asoundrc
defaults.ctl.card 1

ctl.!default {
  type hw
  card 1
}
```

という記述を入れていためデフォルトがカード1になっていました。
card 0に変更すると`amixer sset Mic 62`でもボリューム変更できます。

### 録音してみる
マイクの設定が終わったので録音してみます。

```bash
$ arecord -D plughw:0,0 -d 10 -f cd test.wav
```

`plughw:0,0`の0,0はマイクのカードNoとデバイスNoです。（私の環境では0と0）

録音したファイルは、Macにscpでとってきて再生しました。
（Raspberry Piにスピーカーを付けて再生させるのも設定が必要だったので後述を参照してください）

```bash
$ scp pi@192.168.xxx.xxx:~/test.wav .
$ afplay test.wav
```

### Raspberry Piで再生してみる

先ほど録音したファイルを再生します。

```bash
$ aplay test.wav
ALSA lib pcm_dmix.c:1018:(snd_pcm_dmix_open) unable to open slave
aplay: main:682: audio open error: No such file or directory
```

エラーが出たのでデバイスを確認してみます。

```bash
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 1: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 1: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

出力のデバイスはcard 1にありました。

この情報を~/.asoundrcに記載します。
入力のUSBマイクはcard0だったのでその情報も記載しておきます。

```bash:~/.asoundrc
defaluts.pcm.card 1
defaults.ctl.card 0

pcm.!default {
  type hw
  card 1
}

ctl.!default {
  type hw
  card 0
}
```

配置できたらalsa-utilsを再起動します。

```bash
$ sudo /etc/init.d/alsa-utils restart
```

もう一度再生してみます。

```bash
$ aplay test.wav
Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
```

今度はうまく再生できました。

※単にスピーカー出力を試すだけなら次の方法でも良いかと思います。

```bash
$ aplay /usr/share/sounds/alsa/Front_Center.wav
```
```bash
$ speaker-test -t sine -f 600
```


## Juliusの設定

音声認識エンジンとしてJuliusを使うことにしました。
（Google Speech APIも魅力的ですが、1日の利用回数制限があったので、今回はやめました。）

### インストール

[本家サイト](http://julius.osdn.jp/)からソースを取得しコンパイルします。

```bash
$ wget --trust-server-names 'http://osdn.jp/frs/redir.php?m=iij&f=%2Fjulius%2F60273%2Fjulius-4.3.1.tar.gz'
$ tar xvzf julius-4.3.1.tar.gz
$ cd julius-4.3.1/
$ ./configure
$ make
```

同じくディクテーションファイルも取得します。
（実はこちらにもビルド済みのjuliusがバンドルされているので、そちらを使っても良いと思います）

```bash
$ mkdir ~/julius-kits
$ cd ~/julius-kits
$ wget --trust-server-names 'http://osdn.jp/frs/redir.php?m=iij&f=%2Fjulius%2F60416%2Fdictation-kit-v4.3.1-linux.tgz'
$ tar xvzf dictation-kit-v4.3.1-linux.tgz
```

### Juliusの実行

```bash
$ ALSADEV="plughw:0,0" ~/julius-4.3.1/julius/julius -C ~/julius-kits/dictation-kit-v4.3.1-linux/main.jconf -C ~/julius-kits/dictation-kit-v4.3.1-linux/am-gmm.jconf -nostrip
```

起動コマンドは`dictation-kit-v4.3.1-linux/run.sh`の中を参考にしました。

`plughw:0,0`の0,0はマイクのカードNoとデバイスNoです。（私の環境では0と0）

もし

```bash
Stat: adin_oss: device name = /dev/dsp (application default)
Error: adin_oss: failed to open /dev/dsp
```

というエラーが発生した場合は、

```bash
$ sudo sh -c "echo snd-pcm-oss >> /etc/modules"
$ sudo reboot
```

を実行します。

`-charconv EUC-JP UTF-8`というオプションをつけて実行すると

```bash
InternalError: codeconv: invalid multibyte sequence in the input
```

というエラーが発生していました。

再起動後、Juliusを実行します。

```bash
$ ALSADEV="plughw:0,0" ~/julius-4.3.1/julius/julius -C ~/julius-kits/dictation-kit-v4.3.1-linux/main.jconf -C ~/julius-kits/dictation-kit-v4.3.1-linux/am-gmm.jconf -nostrip

 : 

### read waveform input
Stat: adin_oss: device name = /dev/dsp (application default)
Stat: adin_oss: sampling rate = 16000Hz
Stat: adin_oss: going to set latency to 50 msec
Stat: adin_oss: audio I/O Latency = 32 msec (fragment size = 512 samples)
STAT: AD-in thread created
<<< please speak >>>
```

うまく起動できました。

この状態で「こんにちは」と話してみましょう。

```bash

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

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

phseq1: silB | k o N n i ch i w a | silE
cmscore1: 0.440 0.458 1.000
score1: -2959.848633

<<< please speak >>>
```

うまく認識できました。

## 認識時間と認識率を高める工夫
ここまでの設定ではマイクで話してから認識するまでに2-3秒かかるのと、誤認識も多いという問題がありました。
改善する方法の一つを紹介しておきます。

[Raspberry Piで音声認識（パフォーマンス改善）](http://qiita.com/t_oginogin/items/0634000e6713f9174b5f)