#はじめに
以前,音声認識エンジンJuliusのデモの動かし方を書きましたので,
今回は実際に音声認識を用いたアプリケーションの開発を始めたいと思います.
以前の記事:https://qiita.com/sgr0416st/items/928fb2bde28e17eda274
また,とても分かりやすい記事があったのでこちらを大変参考に(ほぼ写し)させていただきました.
http://tips.hecomi.com/entry/20120226/1330260359
#ゴール
・自作のC++アプリケーションに音声認識を組み込む方法を理解する
・エンターを押したら停止するアプリケーションを作る
#事前準備
・ダウンロードがまだの方は以下からJulius,dictation-kitのダウンロード
https://ja.osdn.net/projects/julius/
(今回はjulius4.4.2.1,dictation-kit-v4.4を使用)
・Juliusのインストールがまだの方は以前の記事を参照
https://qiita.com/sgr0416st/items/928fb2bde28e17eda274
##開発
開発に当たって,まずディレクトリを整理します.
とりあえず作業ディレクトリを以下のようにしました.
(作業ディレクトリ)
├
libsent(Julius4.4.2.1に入ってるやつを持ってくる)├ libjulius(Julius4.4.2.1に入ってるやつを持ってくる)
├ model (以下dictation-kitに入ってるやつを持ってくる)
├ am-gmm.jconf → GMM-HMM音響モデルを使う場合
├ am-dnn.jconf → DNN-HMM音響モデルを使う場合
├ julius.dnnconf → DNN-HMM音響モデルを使う場合
└ main.jconf
次に,dictation-kit 内にある,JuliusLibを使ったサンプルコードを眺めます.
(http://quruli.ivory.ne.jp/document/julius_4.2/julius-simple_8c_source.html)
...が,長くて少々解読しづらいです.
そこで,少し整理されたコードを用意します.
参考:http://tips.hecomi.com/entry/20120226/1330260359
#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;
}
LIBSENT=./libsent
LIBJULIUS=./libjulius
CC=g++
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: sample
sample: sample.cpp
$(CC) $(CFLAGS) $(CPPFLAGS) -o sample sample.cpp $(LDFLAGS)
clean:
$(RM) *.o *.bak *~ core TAGS
distclean:
$(RM) *.o *.bak *~ core TAGS
$(RM) sample
この2つのファイルを用意して、それぞれ作業ディレクトリに入れましょう。
その後、ターミナルで作業ディレクトリへ移動し、
make
./sample -C main.jconf -C am-dnn.jconf -dnnconf julius.dnnconf
と実行してみましょう。
これで、用意したsample.cppが実行されます。
あとは、このsample.cppを使いたい様に書き換えて、make して実行するだけです!
※追記
以下のようなエラーが出た方は、 Juliusのインストールをしていないまま「libjulius」「licsent」フォルダを持ってきてしまった方だと思います。
./libjulius/include/julius/julius.h:33:10: fatal error: 'julius/config.h' file
not found
#include <julius/config.h>
^~~~~~~~~~~~~~~~~
1 error generated.
このサイトを参考にJuliusをインストールし、インストール済みの(makeをした)Juliusフォルダの中にある「libjulius」「licsent」をもう一度持ってきてみてください。
#コード解説
とりあえず動けば良いという方はこちらを読み飛ばしてください。
それではざっとコードを見てみましょう。
Jconf *jconf = j_config_load_args_new(argc, argv);
最初の方にあるこのコードは、コマンド引数からjconfを読み込みます。
jconfファイルは様々な設定をまとめたテキストファイルです。
Recog *recog = j_create_instance_from_jconf(jconf);
次にjconfファイルに従って,新たなエンジンインスタンスを起動・生成します。
callback_add(recog, CALLBACK_〇〇〇〇, [](Recog *recog, void*) {
{処理内容}
}, NULL);
ここで、必要なコールバック関数の登録をします。具体的には
・CALLBACK_EVENT_SPEECH_READY:発話検出ができるようになるタイミング
・CALLBACK_EVENT_SPEECH_START:発話を検出したタイミング
・CALLBACK_RESULT:認識が終わったタイミング
の3つでどの様な処理をするかそれぞれ決定しています。
つまり、認識結果を使いたい時は、
CALLBACK_RESULT のコールバック関数で処理をはさみましょう。
その後初期化設定等をして、
// Recognition loop
int ret = j_recognize_stream(recog);
if (ret == -1) return -1;
上の部分で認識ループをしています。
ちなみに、juliusはデフォルトではコールバックから復帰すると自動で再び認識を始めるようになっています。
キーを押して終了する1つの方法として、スレッドを使うことが考えられます。
例えば、上の部分を以下の様に書き換えてみてください。
// Recognition loop
boost::thread thr([&]() {
int ret = j_recognize_stream(recog);
if (ret == -1) return -1;
});
// Exit if Enter key pressed
std::string line;
getline(std::cin, line);
j_close_stream(recog);
thr.join();
j_recog_free(recog);
これでキーを押したら停止するようになりました!
##参考URL
Juliusを実行ファイルに組み込む
http://tips.hecomi.com/entry/20120226/1330260359