はじめに
過去のエントリでは Julius をサーバモードとして動かして結果を取ってきていました。今回からは JuliusLib を利用して直接 Julius を突っ込んでみました。
Juliusの用意
最新版(2012/02/26現在:Julius-4.2.1)を以下のページからダウンロードしてきます。
次に model が含まれる以下の記述文法音声認識実行キットを以下のページからダウンロードしてきます。
そして次のように配置&ファイルを作成します。
<DIR> juliuslib_test ├ <DIR> julius-4.2.1 ├ <DIR> model (grammar-kit内のものを移動) ├ <DIR> gram │ └ kaden.*(記述文法ファイル、過去のエントリで使ったもの) ├ hmm_mono.jconf(grammar-kit内のものを移動) ├ Makefile(後述) └ test.cpp(後述)
ここまでのものは github にも上げてあります(後のエントリによって色々付け足されているかもしれませんが…)。
hecomi/JuliuslibTest · GitHub
次に Julius をコンパイルします。
$ cd julius-4.2.1 $ ./configure --with-mictype=alsa $ make
これで Julius の準備は完了です。
プログラム
Juliusにくっついているサンプルコード(Julius: julius-simple/julius-simple.c ソースファイル)を参考にします。ちょっと長いので、音声認識→結果表示に必要な部分だけ抽出してみました。
test.cpp
#include <iostream> #include <julius/juliuslib.h> int main(int argc, char* argv[]) { // Jconf: configuration parameters // load configurations from command arguments Jconf *jconf = j_config_load_args_new(argc, argv); if (jconf == NULL) { std::cout << "Error @ j_config_load_args_new" << std::endl; return -1; } // Recog: Top level instance for the whole recognition process // create recognition instance according to the jconf Recog *recog = j_create_instance_from_jconf(jconf); if (recog == NULL) { std::cout << "Error @ j_create_instance_from_jconf" << std::endl; return -1; } // Regster callback callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; }, NULL); callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { std::cout << "...SPEECH START..." << std::endl; }, NULL); callback_add(recog, CALLBACK_RESULT, [](Recog *recog, void*) { for (const RecogProcess *r = recog->process_list; r; r = r->next) { WORD_INFO *winfo = r->lm->winfo; for (int n = 0; n < r->result.sentnum; ++n) { Sentence *s = &(r->result.sent[n]); WORD_ID *seq = s->word; int seqnum = s->word_num; for (int i = 0; i < seqnum; ++i) { std::cout << winfo->woutput[seq[i]]; } } } }, NULL); // Initialize audio input if (j_adin_init(recog) == FALSE) { return -1; } // output system information to log j_recog_info(recog); // Open input stream and recognize switch (j_open_stream(recog, NULL)) { case 0: break; // success case -1: std::cout << "Error in input stream" << std::endl; return -1; case -2: std::cout << "Failed to begin input stream" << std::endl; return -1; } // Recognition loop int ret = j_recognize_stream(recog); if (ret == -1) return -1; // exit j_close_stream(recog); j_recog_free(recog); return 0; }
Makefile
julius-simple.c 付属のものをちょっといじりました。
LIBSENT=./julius-4.2.1/libsent LIBJULIUS=./julius-4.2.1/libjulius CC=g++-4.6 CFLAGS=-g -O2 -std=c++0x CPPFLAGS=-I$(LIBJULIUS)/include -I$(LIBSENT)/include `$(LIBSENT)/libsent-config --cflags` `$(LIBJULIUS)/libjulius-config --cflags` LDFLAGS= -L$(LIBJULIUS) `$(LIBJULIUS)/libjulius-config --libs` -L$(LIBSENT) `$(LIBSENT)/libsent-config --libs` ############################################################ all: test test: test.cpp $(CC) $(CFLAGS) $(CPPFLAGS) -o test test.cpp $(LDFLAGS) clean: $(RM) *.o *.bak *~ core TAGS distclean: $(RM) *.o *.bak *~ core TAGS $(RM) test
コンパイルと実行
$ make $ ./test -C hmm_mono.jconf -gram gram/kaden -input mic
結果
(Juliusからの出力) <<< PLEASE SPEAK! >>> ...SPEECH START... <s>電気をつけて<s> <<< PLEASE SPEAK! >>> (以下繰り返し)
解説
まず、Jconf と Recog を作ります。
Jconf *jconf = j_config_load_args_new(argc, argv); Recog *recog = j_create_instance_from_jconf(jconf);
Jconf にはコマンドラインから与えられた設定パラメータが格納されます(Julius: 構造体 Jconf)。
Recog は Jconf を元にして認識全体にわたって使われるインスタンスとなります(Julius: 構造体 Recog)。
次に、コールバックを登録します。
callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; }, NULL); callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { std::cout << "...SPEECH START..." << std::endl; }, NULL); callback_add(recog, CALLBACK_RESULT, [](Recog *recog, void*) { for (const RecogProcess *r = recog->process_list; r; r = r->next) { WORD_INFO *winfo = r->lm->winfo; for (int n = 0; n < r->result.sentnum; ++n) { Sentence *s = &(r->result.sent[n]); WORD_ID *seq = s->word; int seqnum = s->word_num; for (int i = 0; i < seqnum; ++i) { std::cout << winfo->woutput[seq[i]]; } } } }, NULL);
ここでは、音声認識待機状態に入った時、発話が始まった時、音声認識結果が帰ってきた時の3つを登録しています。他に使えるコールバックに関してはJulius: libjulius/include/julius/callback.hを参考にして下さい。
CALLBACK_RESULT内のデータの解析に関しては、もう少し色々試した後、別途紹介エントリを書きたいと思います。
(2012/02/28 書きました Juliusで認識結果の取り出し方を調べてみた - 凹みTips)
そしてストリームを開きます。
switch (j_open_stream(recog, NULL)) { case 0: break; // success case -1: std::cout << "Error in input stream" << std::endl; return -1; case -2: std::cout << "Failed to begin input stream" << std::endl; return -1; }
参考:Julius: Basic API
で、j_recognize_stream で開いたストリームを繰り返し解析します。
int ret = j_recognize_stream(recog); if (ret == -1) return -1;
参考:Julius: Basic API
終わった後は後片付けをして終了します。
j_close_stream(recog); j_recog_free(recog);
ざっくりこんな感じです。
今後の展望
コールバックを使えばサーバモードの時には出来なかった、コマンド発話(hogehoge) → 音声によるトークバック → キャンセル(あっ、やっぱりmogemoge)→ mogemoge 実行といった、MMDAgent みたいな挙動も出来ると思います。がんがります。
【MMDAgent】初音ミクとおしゃべりできるソフトをつくってみた ‐ ニコニコ動画:GINZA