LoginSignup
3
7

More than 5 years have passed since last update.

JuliusLibを使った音声認識の組み込みアプリケーション開発(for Mac)

Last updated at Posted at 2018-10-12

はじめに

以前,音声認識エンジン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

sample.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
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

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7