最近, Sirius: An Open Intelligent Personal Assistantが話題を集めていますが, 手軽に音声認識を行えるJuliusに関する話。
JuliusをGolangで扱う場合, 他の言語と同様2つのアプローチがあります。
・JuliusLibを組み込む。Golangの場合cgoパッケージを用いる。
・サーバモードで起動しTCP/IPで結果を取得する。
結論を言うと, JuliusLibを組み込む方は上手くいっていません。
環境はOSX 10.10.2です。
Golangでlibjuliusを組み込む
julius-4.3.1.tar.gzをダウンロードし展開します。
C言語クライアントのjulius-simple/julius-simple.cを参考にアプリケーションを書いてみます。
Golangではcgoパッケージを使って, JuliusLibをLinkします。
最小だと以下のようになり, buildが通ります。
package main
/*
#include
#cgo CPPFLAGS: -I/usr/local/include -I/usr/local/include
#cgo LDFLAGS: -L/usr/local/lib -ljulius -lpthread -L/usr/local/lib -lsent -Wl,-framework -Wl,CoreServices -Wl,-framework -Wl,CoreAudio -Wl,-framework -Wl,AudioUnit -Wl,-framework -Wl,AudioToolbox -lz -liconv -lm
*/
import "C"
import "fmt"
func main() {
fmt.Println("hoge")
}
julius-simple.cをGolangに移植してみようとしましたが, callbackを登録する関数でコケました。
C.callback_add(recog, C.CALLBACK_EVENT_SPEECH_READY, status_recready, nil);
$ go build julius-simple.go
./julius-go.go:86: cannot use status_recready (type func(*C.struct___Recog__, *C.void)) as type *[0]byte in argument to _Cfunc_callback_add
*[0]byteでcastとか試したけどなんとかならなかったので, 組み込む作戦は一旦断念。
調べている中で見つけた情報は以下。
[1] CGo error : Cannot use GoCallback (type func(_Ctype_int, unsafe.Pointer)) as type *[0]byte in function argument
[2] Passing function arguments to C
[3] golang で string を []byte にキャストしてもメモリコピーが走らない方法を考えてみる
JuliusサーバモードにGolang-Clientで認識結果を取得する
portaudioのインストール。
$ brew install portaudio
dictation-kit-v4.3.1-osxをダウンロードして展開します。
バイナリも付属してきます。早速juliusをサーバモードで起動します。
$ cd dictation-kit-v4.3.1-osx
$ ./bin/julius -C main.jconf -C am-gmm.jconf -module
以下ではGoroutineで処理を切り離していますが, 認識結果を取得するだけなら全く必要ありません。
package main
import (
"bufio"
"fmt"
"net"
"strings"
"time"
)
func rcvJuliusResp(_ch chan string) {
for word := range _ch {
fmt.Println(time.Now(), " : ", word)
}
}
func main() {
conn, _ := net.Dial("tcp", "localhost:10500")
ch := make(chan string)
go rcvJuliusResp(ch)
for {
message, _ := bufio.NewReader(conn).ReadString('\n')
isContain := strings.Contains(message, "WORD")
if isContain == true {
word := strings.Split(message, "\"")
ch <- word[1]
}
}
}
結果を見る限り, 音響モデルのチューニングが必要。
$ go run julius-client.go
2015-03-28 13:18:59.866475432 +0900 JST : 本
2015-03-28 13:19:28.449968828 +0900 JST : 出
2015-03-28 13:19:31.455237858 +0900 JST : て
2015-03-28 13:19:35.514823058 +0900 JST : ヘレン
...
言語モデルの作成
おはよう, こんにちは, さようならの3単語を含むjulius言語モデルを作ってみます。
greeting.grammarとgreeting.vocaを作成します。構文は[5]第7章 言語モデルを参考に。
$ mkdir greeting
$ cd greeting
$ vim greeting.grammar
S : NS_B GREETING NS_E
# おはよう/こんにちは/さようなら
GREETING : OHAYOU
GREETING : KONNITIWA
GREETING : SAYOUNARA
$ vim greeting.voca
% OHAYOU
おはよう o h a y o u
% KONNITIWA
こんにちは k o n n i t i w a
% SAYOUNARA
さようなら s a y o u n a r a
% NS_B
silB
% NS_E
silE
mkdfa.plでgreeting.dfa, greeting.term, greeting.dictを生成します。
$ ../dictation-kit-v4.3.1-osx/bin/mkdfa.pl greeting
greeting.grammar has 4 rules
greeting.voca has 5 categories and 5 words
---
Now parsing grammar file
Now modifying grammar to minimize states[-1]
Now parsing vocabulary file
Now making nondeterministic finite automaton[4/4]
Now making deterministic finite automaton[4/4]
Now making triplet list[4/4]
5 categories, 4 nodes, 5 arcs
-> minimized: 4 nodes, 5 arcs
---
generated: greeting.dfa greeting.term greeting.dict
generateコマンドで確認します。
$ ../../dictation-kit-v4.3.1-osx/bin/generate greeting
Stat: init_voca: read 5 words
Reading in term file (optional)...done
5 categories, 5 words
DFA has 4 nodes and 5 arcs
-----
さようなら
おはよう
こんにちは
no further sentence in the last 300 trial
作成した言語モデルを試してみます。認識結果はそこそこな印象です。
$ ../dictation-kit-v4.3.1-osx/bin/julius -gram greeting -h ../dictation-kit-v4.3.1-osx/model/phone_m/jnas-mono-16mix-gid.binhmm -input mic -rejectshort 800
ちなみに, Siriusの論文はこちら。
* この記事はkarota-projectの活動に関する記事です。