Node.js で音声認識
      X



                Presented by
                @hecomi
LTの趣旨:
  C++ モジュール
色々出来て楽しいよ!
自己紹介
•    名前      :   hecomi
•    Node歴   :   半年とちょっと
•    言語      :   C++ / JavaScript
•    Web     :   http://d.hatena.ne.jp/hecomi/




                              最近は音声認識リモコンとか
                              未来のお部屋を作る活動をしています
賢いお部屋              ⑤ピピッ!




①エアコン26度!
                 ④26℃の信号出します




               音声認識エンジン
      集音マイク
                               iRemocon
               ②26度っすね



      音声合成

    ③26度にします               今部屋30℃か…
                  ZigBee
賢いお部屋             ⑤ピピッ!




①エアコン26度!
                ④26℃の信号出します




              音声認識エンジン
      集音マイク
                              iRemocon
               ②26度っすね



      音声合成

    ③26度にします 上で動かそうとしています今部屋30℃か…
         Node.js
                  ZigBee
賢いお部屋               ⑤ピピッ!

        Node.js 上で動かそうとしています

①エアコン26度!
                  ④26℃の信号出します




                音声認識エンジン
      集音マイク
                                iRemocon
                ②26度っすね


              この部分について紹介します
      音声合成

    ③26度にします 上で動かそうとしています今部屋30℃か…
         Node.js
                    ZigBee
•  オープンソースの音声認識エンジン
 –  http://julius.sourceforge.jp/

•  いろんな機能があります
 –  認識方法
   •  ディクテーション(文章自動筆記、誤認識多)
   •  ★文法認識   (特定の文章、 誤認識少)
 –  実行方法
   •  ★ローカル   (普通に実行)
   •  サーバモード  (認識結果を XML でくれます)
C++/Juliusの設定ファイルを

       普通に書いていると…
…(前略)
Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv));

if (jconf == nullptr) {
    std::cout << "Error @ j_config_load_args_new" << std::endl;
    return -1;
}

Recog *recog = j_create_instance_from_jconf(jconf);
if (recog == nullptr) {
    std::cout << "Error @ j_create_instance_from_jconf" << std::endl;
    return -1;
}

callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) {
    std::cout << "<<< PLEASE SPEAK! >>>" << std::endl;
}, nullptr);

callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) {
  std::cout << "...SPEECH START..." << std::endl;
}, nullptr);

callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr);
…(後略)
C++/Juliusの設定ファイルを

       普通に書いていると…
…(前略)
Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv));

if (jconf == nullptr) {
    std::cout << "Error @ j_config_load_args_new" << std::endl;
    return -1;
}

Recog *recog = j_create_instance_from_jconf(jconf);
if (recog == nullptr) {
    std::cout << "Error @ j_create_instance_from_jconf" << std::endl;
    return -1;
}

callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) {
    std::cout << "<<< PLEASE SPEAK! >>>" << std::endl;
}, nullptr);

callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) {
                           S      : NS_B KADEN_ NOISE PLEASE NS_E
  std::cout << "...SPEECH START..." NS_B JUMON NS_E
                           S      : << std::endl;
}, nullptr);               KADEN_ : KADEN
                           KADEN_ : KADEN WO% KADEN
callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr);
…(後略)
C++/Juliusの設定ファイルを

                     普通に書いていると…
                …(前略)
                Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv));
% KADEN
テレビ        t eif (jconf == nullptr) {
               r e b i
電気         d e n k i
% WO            std::cout << "Error @ j_config_load_args_new" << std::endl;
を          w o return -1;
を          o }
% PLEASE
つけて        t   uRecog t*recog = j_create_instance_from_jconf(jconf);
                  k e   e
消して        k   eif (recog == nullptr) {
                  s i t e
切替         k   i r i k a e
                   std::cout << "Error @ j_create_instance_from_jconf" << std::endl;
次          t   u g i
                   return -1;
前          m   a e
                }
% JUMON
バルス        b a r u s u
              callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) {
% NOISE
<sp>       sp    std::cout << "<<< PLEASE SPEAK! >>>" << std::endl;
% NS_B        }, nullptr);
<s>        silB
% NS_E        callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) {
<s>        silE                          S      : NS_B KADEN_ NOISE PLEASE NS_E
                std::cout << "...SPEECH START..." NS_B JUMON NS_E
                                         S      : << std::endl;
              }, nullptr);               KADEN_ : KADEN
                                         KADEN_ : KADEN WO% KADEN
                callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr);
                …(後略)
C++/Juliusの設定ファイルを

                     普通に書いていると…
                …(前略)
                Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv));
% KADEN
テレビ        t eif (jconf == nullptr) {
                r e b i
電気         d e n k i
% WO             std::cout << "Error @ j_config_load_args_new" << std::endl;
を          w o return -1;
を          o }                                <?xml version="1.0" encoding="UTF-8"?>
% PLEASE                                      <iRemocon>
つけて             k e   e                         <command word="テレビ(を|)(つけて|消して)" num="1" />
           t uRecog t*recog = j_create_instance_from_jconf(jconf);
消して        k eif (recog == nullptr) {
                s i t e                         <command word="テレビ(を|)切替" num="2" />
切替         k i r i k a e                        <command word="テレビ(を|)次" num="3" />
                 std::cout << "Error @ j_create_instance_from_jconf" << std::endl;
                                                <command word="テレビ(を|)前" num="4" />
次          t u g i
                 return -1;                     <command word="電気(を|)つけて" num="11" />
前          m a e
              }                                 <command word="電気(を|)消して" num="12" />
% JUMON
バルス        b a r u s u                        </iRemocon>
              callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) {
% NOISE
<sp>       sp    std::cout << "<<< PLEASE SPEAK! >>>" << std::endl;
% NS_B        }, nullptr);
<s>        silB
% NS_E        callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) {
<s>        silE                          S      : NS_B KADEN_ NOISE PLEASE NS_E
                std::cout << "...SPEECH START..." NS_B JUMON NS_E
                                         S      : << std::endl;
              }, nullptr);               KADEN_ : KADEN
                                         KADEN_ : KADEN WO% KADEN
                callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr);
                …(後略)
C++/Juliusの設定ファイルを

                     普通に書いていると…
                …(前略)
                Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv));
% KADEN
テレビ        t eif (jconf == nullptr) {
                r e b i
電気         d e n k i
% WO             std::cout << "Error @ j_config_load_args_new" << std::endl;
を
を          o }      _人人人人人人人人人人_
           w o return -1;
                                              <?xml version="1.0" encoding="UTF-8"?>
                                              <iRemocon>
% PLEASE
つけて
消して
                k e >               <
                      e     _,,..,,,,_
           k eif (recog == nullptr) {
                s i t e
                                                <command word="テレビ(を|)(つけて|消して)" num="1" />
           t uRecog t*recog = j_create_instance_from_jconf(jconf);
                                                <command word="テレビ(を|)切替" num="2" />
                                                <command word="テレビ(を|)次" num="3" />
切替
次
前
           t u g i
           m a e
                    >               <
           k i r i k a e
                        /  ,'  3   `ヽーっ
                 std::cout << "Error @ j_create_instance_from_jconf" << std::endl;
                 return -1;
                                                <command word="テレビ(を|)前" num="4" />
                                                <command word="電気(を|)つけて" num="11" />

                        l    ⊃ ⌒_つ
                    >               <
              }                                 <command word="電気(を|)消して" num="12" />
% JUMON
バルス        b a r u s u                        </iRemocon>
              callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) {
% NOISE
<sp>
% NS_B
           sp       >               <
                          `'ー---­‐'''''"
                 std::cout << "<<< PLEASE SPEAK! >>>" << std::endl;
              }, nullptr);
<s>
% NS_E
<s>
           silB
                    >               <
                           面倒くさい!
              callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) {
           silE                          S      : NS_B KADEN_ NOISE PLEASE NS_E

                     ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
                std::cout << "...SPEECH START..." NS_B JUMON NS_E
                                         S      : << std::endl;
              }, nullptr);               KADEN_ : KADEN
                                         KADEN_ : KADEN WO% KADEN
                callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr);
                …(後略)
もっと簡単に
できるようにしたい!
どうせなら
色んな機能と連携したい…
そうだ! Node.js の
C++ モジュール化しよう!
C++ モジュール
作ったことある方?
C++ モジュール 難しくないです!
•  C++ ですが v8 / node.js のお作法に従うだけです
•  例: hello() すると 'world' が返ってくる関数
 #include <node.h>
                                       JavaScript に引き渡す関数
 using namespace v8;

 Handle<Value> Method(const Arguments& args) {
   HandleScope scope;
   return scope.Close( String::New("world") );
 }

 void init(Handle<Object> target) {
   NODE_SET_METHOD (target, "hello", Method);    Node.js の世界へ C++ の
 }                                               関数を送り出す

 NODE_MODULE(hello, init)
C++ モジュール 難しくないです!
•  C++ ですが v8 / node.js のお作法に従うだけです
•  例: hello() すると 'world' が返ってくる関数
 #include <node.h>
                                       JavaScript に引き渡す関数
 using namespace v8;

 Handle<Value> Method(const Arguments& args) {
   HandleScope scope;
   return scope.Close( String::New("world") );
 }

 void init(Handle<Object> target) {
   NODE_SET_METHOD (target, "hello", Method);    Node.js の世界へ C++ の
 }                                               関数を送り出す

 NODE_MODULE(hello, init)
C++ モジュール作り方
                                                    この辺りが今回のミソ
•  公式サイト … 大体ひと通り書いてある
  –  Node.js v0.8.14 マニュアル & ドキュメンテーション
     •  http://nodejs.jp/nodejs.org_ja/docs/v0.8/api/addons.html

•  V8 について … v8 の世界について詳しく知りたい時
  –  Embedder's Guide - V8 JavaScript Engine
     •  http://javascript.g.hatena.ne.jp/edvakf/20100407/1270626241

•  EventEmitter 利用方法 … libuv 用お作法
  –  Node.js で C++ アドオンから EventEmitter のイベントリスナ
     を呼ぶ - 凹みTips
     •  http://d.hatena.ne.jp/hecomi/20121020/1350764244

•  マルチスレッド対応方法 … libuv の uv_queue_work を利用
  –  Node.js でマルチスレッド対応のネイティブモジュールを作成
     する- 凹みTips
     •  http://d.hatena.ne.jp/hecomi/20121021/1350819390
出来たモジュールを使ったコード1
var Julius  = require('julius')
  , grammar = new Julius.Grammar() ;   認識したい言葉を覚えさせる

grammar.add('(テレビ|エアコン)を?(つけて|消して)');
grammar.add(‘お早う御座います');

grammar.compile(function(err, result) {
    if (err) throw err;
    var julius = new Julius( grammar.getJconf() );

      julius.on('result', function(str) {
          console.log('認識結果:', str);
      });
                                             コールバックの登録
      julius.start();                        他にも発話のタイミング等
});                                          各種コールバック登録可
デモ
(別途エントリ書きます)
まとめ




C++ モジュール
 楽しいです!
ご清聴ありがとうございました

Node.js × 音声認識 - 東京Node学園 2012 LT枠 6番目

  • 1.
    Node.js で音声認識 X Presented by @hecomi
  • 2.
    LTの趣旨: C++モジュール 色々出来て楽しいよ!
  • 3.
    自己紹介 •  名前 : hecomi •  Node歴 : 半年とちょっと •  言語 : C++ / JavaScript •  Web : http://d.hatena.ne.jp/hecomi/ 最近は音声認識リモコンとか 未来のお部屋を作る活動をしています
  • 4.
    賢いお部屋 ⑤ピピッ! ①エアコン26度! ④26℃の信号出します 音声認識エンジン 集音マイク iRemocon ②26度っすね 音声合成 ③26度にします 今部屋30℃か… ZigBee
  • 5.
    賢いお部屋 ⑤ピピッ! ①エアコン26度! ④26℃の信号出します 音声認識エンジン 集音マイク iRemocon ②26度っすね 音声合成 ③26度にします 上で動かそうとしています今部屋30℃か… Node.js ZigBee
  • 6.
    賢いお部屋 ⑤ピピッ! Node.js 上で動かそうとしています ①エアコン26度! ④26℃の信号出します 音声認識エンジン 集音マイク iRemocon ②26度っすね この部分について紹介します 音声合成 ③26度にします 上で動かそうとしています今部屋30℃か… Node.js ZigBee
  • 7.
    •  オープンソースの音声認識エンジン – http://julius.sourceforge.jp/ •  いろんな機能があります –  認識方法 •  ディクテーション(文章自動筆記、誤認識多) •  ★文法認識   (特定の文章、 誤認識少) –  実行方法 •  ★ローカル   (普通に実行) •  サーバモード (認識結果を XML でくれます)
  • 8.
    C++/Juliusの設定ファイルを
 普通に書いていると… …(前略) Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv)); if (jconf == nullptr) { std::cout << "Error @ j_config_load_args_new" << std::endl; return -1; } Recog *recog = j_create_instance_from_jconf(jconf); if (recog == nullptr) { std::cout << "Error @ j_create_instance_from_jconf" << std::endl; return -1; } callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; }, nullptr); callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { std::cout << "...SPEECH START..." << std::endl; }, nullptr); callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr); …(後略)
  • 9.
    C++/Juliusの設定ファイルを
 普通に書いていると… …(前略) Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv)); if (jconf == nullptr) { std::cout << "Error @ j_config_load_args_new" << std::endl; return -1; } Recog *recog = j_create_instance_from_jconf(jconf); if (recog == nullptr) { std::cout << "Error @ j_create_instance_from_jconf" << std::endl; return -1; } callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; }, nullptr); callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { S : NS_B KADEN_ NOISE PLEASE NS_E std::cout << "...SPEECH START..." NS_B JUMON NS_E S : << std::endl; }, nullptr); KADEN_ : KADEN KADEN_ : KADEN WO% KADEN callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr); …(後略)
  • 10.
    C++/Juliusの設定ファイルを
 普通に書いていると… …(前略) Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv)); % KADEN テレビ t eif (jconf == nullptr) { r e b i 電気 d e n k i % WO std::cout << "Error @ j_config_load_args_new" << std::endl; を w o return -1; を o } % PLEASE つけて t uRecog t*recog = j_create_instance_from_jconf(jconf); k e e 消して k eif (recog == nullptr) { s i t e 切替 k i r i k a e std::cout << "Error @ j_create_instance_from_jconf" << std::endl; 次 t u g i return -1; 前 m a e } % JUMON バルス b a r u s u callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { % NOISE <sp> sp std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; % NS_B }, nullptr); <s> silB % NS_E callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { <s> silE S : NS_B KADEN_ NOISE PLEASE NS_E std::cout << "...SPEECH START..." NS_B JUMON NS_E S : << std::endl; }, nullptr); KADEN_ : KADEN KADEN_ : KADEN WO% KADEN callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr); …(後略)
  • 11.
    C++/Juliusの設定ファイルを
 普通に書いていると… …(前略) Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv)); % KADEN テレビ t eif (jconf == nullptr) { r e b i 電気 d e n k i % WO std::cout << "Error @ j_config_load_args_new" << std::endl; を w o return -1; を o } <?xml version="1.0" encoding="UTF-8"?> % PLEASE <iRemocon> つけて k e e <command word="テレビ(を|)(つけて|消して)" num="1" /> t uRecog t*recog = j_create_instance_from_jconf(jconf); 消して k eif (recog == nullptr) { s i t e <command word="テレビ(を|)切替" num="2" /> 切替 k i r i k a e <command word="テレビ(を|)次" num="3" /> std::cout << "Error @ j_create_instance_from_jconf" << std::endl; <command word="テレビ(を|)前" num="4" /> 次 t u g i return -1; <command word="電気(を|)つけて" num="11" /> 前 m a e } <command word="電気(を|)消して" num="12" /> % JUMON バルス b a r u s u </iRemocon> callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { % NOISE <sp> sp std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; % NS_B }, nullptr); <s> silB % NS_E callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { <s> silE S : NS_B KADEN_ NOISE PLEASE NS_E std::cout << "...SPEECH START..." NS_B JUMON NS_E S : << std::endl; }, nullptr); KADEN_ : KADEN KADEN_ : KADEN WO% KADEN callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr); …(後略)
  • 12.
    C++/Juliusの設定ファイルを
 普通に書いていると… …(前略) Jconf *jconf = j_config_load_args_new(j_argc, const_cast<char**>(j_argv)); % KADEN テレビ t eif (jconf == nullptr) { r e b i 電気 d e n k i % WO std::cout << "Error @ j_config_load_args_new" << std::endl; を を o } _人人人人人人人人人人_ w o return -1; <?xml version="1.0" encoding="UTF-8"?> <iRemocon> % PLEASE つけて 消して k e >               < e    _,,..,,,,_ k eif (recog == nullptr) { s i t e <command word="テレビ(を|)(つけて|消して)" num="1" /> t uRecog t*recog = j_create_instance_from_jconf(jconf); <command word="テレビ(を|)切替" num="2" /> <command word="テレビ(を|)次" num="3" /> 切替 次 前 t u g i m a e >               < k i r i k a e /  ,'  3   `ヽーっ std::cout << "Error @ j_create_instance_from_jconf" << std::endl; return -1; <command word="テレビ(を|)前" num="4" /> <command word="電気(を|)つけて" num="11" /> l    ⊃ ⌒_つ >               < } <command word="電気(を|)消して" num="12" /> % JUMON バルス b a r u s u </iRemocon> callback_add(recog, CALLBACK_EVENT_SPEECH_READY, [](Recog *recog, void*) { % NOISE <sp> % NS_B sp >               <  `'ー---­‐'''''" std::cout << "<<< PLEASE SPEAK! >>>" << std::endl; }, nullptr); <s> % NS_E <s> silB >               < 面倒くさい! callback_add(recog, CALLBACK_EVENT_SPEECH_START, [](Recog *recog, void*) { silE S : NS_B KADEN_ NOISE PLEASE NS_E  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄ std::cout << "...SPEECH START..." NS_B JUMON NS_E S : << std::endl; }, nullptr); KADEN_ : KADEN KADEN_ : KADEN WO% KADEN callback_add(recog, CALLBACK_RESULT, OnOutputResult, nullptr); …(後略)
  • 13.
  • 14.
  • 15.
    そうだ! Node.js の C++モジュール化しよう!
  • 16.
  • 17.
    C++ モジュール 難しくないです! • C++ ですが v8 / node.js のお作法に従うだけです •  例: hello() すると 'world' が返ってくる関数 #include <node.h> JavaScript に引き渡す関数 using namespace v8; Handle<Value> Method(const Arguments& args) { HandleScope scope; return scope.Close( String::New("world") ); } void init(Handle<Object> target) { NODE_SET_METHOD (target, "hello", Method); Node.js の世界へ C++ の } 関数を送り出す NODE_MODULE(hello, init)
  • 18.
    C++ モジュール 難しくないです! • C++ ですが v8 / node.js のお作法に従うだけです •  例: hello() すると 'world' が返ってくる関数 #include <node.h> JavaScript に引き渡す関数 using namespace v8; Handle<Value> Method(const Arguments& args) { HandleScope scope; return scope.Close( String::New("world") ); } void init(Handle<Object> target) { NODE_SET_METHOD (target, "hello", Method); Node.js の世界へ C++ の } 関数を送り出す NODE_MODULE(hello, init)
  • 19.
    C++ モジュール作り方 この辺りが今回のミソ •  公式サイト … 大体ひと通り書いてある –  Node.js v0.8.14 マニュアル & ドキュメンテーション •  http://nodejs.jp/nodejs.org_ja/docs/v0.8/api/addons.html •  V8 について … v8 の世界について詳しく知りたい時 –  Embedder's Guide - V8 JavaScript Engine •  http://javascript.g.hatena.ne.jp/edvakf/20100407/1270626241 •  EventEmitter 利用方法 … libuv 用お作法 –  Node.js で C++ アドオンから EventEmitter のイベントリスナ を呼ぶ - 凹みTips •  http://d.hatena.ne.jp/hecomi/20121020/1350764244 •  マルチスレッド対応方法 … libuv の uv_queue_work を利用 –  Node.js でマルチスレッド対応のネイティブモジュールを作成 する- 凹みTips •  http://d.hatena.ne.jp/hecomi/20121021/1350819390
  • 20.
    出来たモジュールを使ったコード1 var Julius  =require('julius') , grammar = new Julius.Grammar() ;   認識したい言葉を覚えさせる grammar.add('(テレビ|エアコン)を?(つけて|消して)'); grammar.add(‘お早う御座います'); grammar.compile(function(err, result) { if (err) throw err; var julius = new Julius( grammar.getJconf() ); julius.on('result', function(str) { console.log('認識結果:', str); }); コールバックの登録 julius.start(); 他にも発話のタイミング等 }); 各種コールバック登録可
  • 21.
  • 22.
  • 23.