FC2ブログ

スマフォのアプリを作りたい(34):スマフォだけで動作する音声認識⑨

   プログラミング [2020/12/15]
えーーと、どこまで行ったんでしたっけ?
前の状況を書いてからあまりに間が空いたので、前の記事を自分で読み返してます。

もう何のためにこれをやり始めたのかも忘れそうです。
「スマフォのアプリを作りたい」シリーズのサブテーマ「スマフォだけで動作する音声認識」も9回目になっちゃった。
進み具合が悪くて、かなりダラケ気味になってました。
とりあえずこのサブテーマまでは、何とか完結させます。


前回、AndroidStudio環境で作成したJava+C/C++ライブラリ(calljulius)のビルド環境をReact Nativeの環境に持ち込んでcalljuliusのビルドが走り出すところまでを「見る」ことができました。

残りの課題は、
1)calljuliusで参照する設定ファイル等のapkへの取込み
2)今のフォルダ構成の見直し
3)他Javaコードからcalljuliusの呼出し実装/テスト
になるかと思ってました。

では、ボヤキを再開します。


◆calljuliusライブラリの前提ファイルの追加
AndroidStudioでcalljuliusを作成した段階で、calljulius(JuliusLib(C/C++):音声認識エンジンライブラリ)では、単語辞書/音響モデル?/設定ファイルが必要な作りになっています。
それらのファイルはassetsフォルダで、APK内に組み込まれて動作時にライブラリから参照されます。

VTChat(React Nativeのお試しアプリ)としてのAPK内にcalljuliusの関連ファイルはどういうビルド設定をすれば取り込まれるようになるのかな?
Android/Gradleビルド環境を未だに全く理解できてないので、かなり面倒そうだなーとか思っていました。

まず手始めに、前回calljulius環境込みのビルド結果のAPKの中身を見てみます。

すると、なんということでしょう~!
JuliusLib(C/C++)ライブラリ本体:libjuliusword.so もassetsフォルダに仕込むべきファイルたちも既に入ってるではありませんか!


このシリーズ初の「あれ? ラッキー!」です。
どういう仕組みなのかは、全く分かりませんが、AndroidStudio上で組んだビルドのOutput構成が、そのままReactNative側のOutput構成に取り込まれたみたいです。

なので、この課題はこれで解決・・・です。(たぶん)

React NativeプロジェクトのAPKファイルは以下に収まってました。
「(プロジェクトフォルダ)\android\\app\build\outputs\apk\debug\app-debug.apk」
※リリース版のビルドを意識してビルドさせたことはないので、デバッグ版らしきこのファイルで確認するしかないのかな。

APKの中は以下の感じです。
「\lib\(CPU毎フォルダ)\libjuliusword.so」
「\assets\jdata\*」・・・AndroidStudio環境時に設定したままの内容


-ちょっと余談:APKの中の確認方法-
APKファイルってzip形式の圧縮ファイルなんだそうだ。
なので、拡張子を「.zip」にリネームして、「プログラムから開く」でエクスプローラを指定すれば中のフォルダ構成を参照できるかもしれません。
拡張子が「.apk」のままでは通常ダメでした。関連プログラム設定を行えばできるのかも。圧縮ファイルとしての「解凍」はできるかと思います。

ちなみに、さるがずっと以前に作った「ZIPファイル内のファイルリストを出力するツール」※があったので、そのプログラムに食わせてみました。
拡張子が「.apk」のままでも、ファイルリストを出力しました。

※Vectorサイト(https://www.vector.co.jp/soft/dl/winnt/prog/se488403.html)からダウンロードできるようにしています。
ただし、ダウンロード時に「危ないやつかも知れんぜ!」的な警告はでるし、ダウンロードしたzipファイルからプログラム(saruziplist.exe)を取り出して実行させようとしたときも「こいつ提供元が分かんない怪しいやつですぜ!」的な警告が出ました。しかも起動に物凄く時間が掛りました。
まあ、だいぶ以前にマイナーな開発環境(PHP)で作った怪しいWindowsプログラムなので、致し方ないのかな。



◆calljuliusのフォルダ位置変更
前回、AndroidStudioからcalljuliusのビルド環境を React Native(VTChatプロジェクト)環境に持ち込んだ際に、その所在を「(プロジェクトフォルダ)\node_modiles\react-native-google-speech-api\calljulius」としました。
そのフォルダ直下にAnroidStudioで作ったビルド環境をまるっと持ってきてしまってます。※
※Gradle環境で親プロジェクト内に子プロジェクト(ライブラリ)のビルド環境を作成する例がそうなってたから。

でも、React Nativeのパッケージ類で動作デバイス依存のあるものは、パッケージルートの下に、android、ios、windowsなどのデバイス毎のフォルダを作成して、その下にソース等を配置している。

なので、calljuliusもそうします。
「(プロジェクトフォルダ)\node_modiles\react-native-google-speech-api\calljulius」

「(プロジェクトフォルダ)\node_modiles\react-native-google-speech-api\calljulius\android

それに伴って、ルートプロジェクトのsettings.gladleとreact-native-google-speech-apiのbuild.gradleを修正します。
----settings.gradle--------------------------------
rootProject.name = 'VTChat'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app', ':calljulius:android'
project(':calljulius:android').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-google-speech-api/calljulius/android')


---------------------------------------------------

----build.gradle-----------------------------------


dependencies {


implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

//# calljulius
implementation project(":calljulius:android")
//implementation 'com.teburarec.calljulius:JlibGate:0.0.1'
}
---------------------------------------------------


上記の状態で、「rect-native run-android」を実行して、ビルドできることを確認しました。


さあ、このサブテーマの最終段階です。
JuliusLibを含んだC/C++ライブラリをNeactNative(JS)側から呼出すところの実装です。

続きます。


◆react-natve-google-speech-apiからのcalljulius呼出し
App.jsを起点としたJSコードからJavaのコードを呼出して音声認識させる部分の仕組みは、react-natve-google-speech-apiを取り込んだ際に既に含まれています。
react-natve-google-speech-api内のJavaから、calljuliusライブラリのI/Fクラス(Java)を呼び出す仕掛けさえ実装すれば、動作を確認できるはず。


(1)Javaのコールバックの仕方
最初に引っかかったのは、react-native-google-speech-api(Java)側へのコールバックの実装です。
React Native的なコールバックの手法(メソッド)もありそうなのですが、ここはJava初心者がフツー?にやるような「interface」でやってみました。
参考にしたサンプルは、ほとんどが
・コールバックする側もされる側も同一ソース内に実装している。
・class外でinterfaceを定義していたりする。
・コールバック処理がシンプルでコールバックを指示する側のクラスのメンバ変数等を使ってない。
ちょっとそのままマネする感じにならないものが多かった。

『「interface」って結局のところ、クラスの名前と中身の無いメソッド(型)を定義するもので、クラス・インスタンスとそのメソッドの実際の処理は、コールバックされる側で実装するもの。』と理解。(ちょっと違うかも。)

なので、
-コールバックを呼ぶ側のclass内でinterfaceを定義しました。
 →interfaceの定義にpublicを指定しないとエラーになります。
-コールバック・クラス(コールバックとして呼ばれる側)は、
 コールバックをこれだと指定する側の内部クラスとして実装。
 →コールバック処理内では「外部クラス名.this.メンバ変数名」と書いて外部クラスのメンバを使用する。


(2)インナー・クラスでThreadを実装
calljuliusは、別スレッドでJuliusLibの処理シーケンスを順次実行するように作りました。
そのスレッドのシーケンスは、ローカルなステータスコードをキーにして、音声入力側と同期を取ります。

この変更の影響で、スレッドクラスからC側I/F関数をコールする部分で「メソッドがstaticじゃない」とエラーが出た。
なので、何も考えずに全てstaticに変更したのだが・・・

NJIでは、Java側で定義しているC関数のメソッドをstaticにすると、C側で受け取る第2引数がjobjectからjclassに変わるんだそうです。
それを全く知らずに、ただコンパイルを通して実行したもんだから、アプリがABENDします。

さるの現状のReactNative環境では、Javaコード用デバッガがないので、元のAndroidStudioの環境にコードをフィードバックしてデバッグして見ました。
なので、この原因を究明するのにまたまた2週間くらい要した。

さらに、直し方もなかなかよくわからない。
・Java側のThreadサブクラス内にコールバック関数を入れたり外したり
・C側I/F関数のJava側での定義にstaticを付けたり取ったり
・C側からコールバックされるJava側関数にstaticを付けたり取ったり
どれかやるたびにビルドエラー、もしくはABENDが発生します。

Javaの「static」の意味を理解しないままやってたのがそもそもダメな原因です。
ちょこっと、Javaの「static」の効能/制限について調べてみました。
結果、インナー・クラスに「static」を指定したのが間違いの始まりだったようです。
参考にしたサンプルコードがそうしてたからなんだけど、mustなことではなかったようです。

だいだい分かったことは、
-インナー・クラス内にJNIのC側I/F関数呼び出しメソッドの定義はできないみたい。
-staticなインナー・クラスから非staticなアウター・クラスのメソッドはコールできない。(ごく一般的なことなんですね。)
-JNIのC側I/F関数に渡る、jobject/jclassパラメタは、Java側のI/Fメソッドを定義したクラスを示す。
-なので、Java側のコールバックメソッドはI/Fメソッドと同じクラス内にいないとダメ?(少なくとも厄介なことにまります。)
-Java側のI/Fメソッドにstaticを指定すると、JNIのC側I/F関数に渡る第2引数はjclassになる。
-C側からコールバックするJavaのメソッドにstaticを指定したらダメっぽい。



(3)音声入力、録音、単語認識、音声認識の処理の同期
「最初に単語(短い言葉)をcalljuliusライブラリで認識させて、それが終わったらクラウドでしゃべる内容をテキストにする。」というつもりで処理を実装してみました。
音声入力処理、録音処理、音声認識処理を同期を取りながら実施するようになっていたコードに、さらに、単語認識処理を同期させるように制御を追加するのに四苦八苦した。

Javaでの同期の作法としては、synchronizedで括って/ステートを見ながらwait()させて/ステートが変わったら目的の処理を実施して/notifyAll()で他スレッドを起こしてやる/というやり方しか知らないもんで、「基本的な作り」がこんなんでいいのかって言われたらたぶんダメな部類だと思う。

ステートを表す変数が何種類もあって、だいぶスパゲッティなコードになった。
いくらか動いちゃいるけど、どうも動きが不安定で、何回か認識させるとハングします。
それと、エミュレータ上でのマイク音質のせいだと思っていた単語認識が悪い現象が出ているのですが、実際のスマフォで確認しても同じでした。
根本的な問題があるものと思われます。入力音声データのタイプ指定が違ってるのかも。

まだ、解決すべき課題はあるものの、区切り付けるためにこのサブテーマを終りとします。
辛うじて、JuliusLibを使った単語認識をスマフォ上で動かすパスが通ったと言っていいかと思います。


ともかく、この件これで終わりです。
このあと、このシリーズで調べた各機能の実現方法を応用してそれなりのアプリを作って見たいと思います。

今後、React Nativeらしいファイル交換の仕方と暗号化について調べが必要かと思ってます。


はぁ、でも、本当に飽きた。
こんなにまで、動かすのが面倒だとは予想しなかった。

今回、AndroidStudio上でJavaのアプリ(テンプレート)を弄ったのですが、React Native環境よりずっと扱いが楽です。
選択環境を誤ったと思い始めてます。

では、スマフォで動く音声(単語)認識についてはこの辺で。
御機嫌よう。m(__)m



※以前載せたコードと違う部分(凡そ)の色を変えています。ちゃんと動かないのでゴミです。
<RN-Google-speech>:<プロジェクト・フォルダ>\node_modules\react-native-google-speech-api\android\src\main\java\com\reactlibrary

<RN-Google-speech>\GoogleSpeechApiModule.java
----GoogleSpeechApiModule.java---------------------
package com.reactlibrary;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import java.lang.Thread.UncaughtExceptionHandler;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.reactlibrary.speech_service.SpeechEvent;
import com.reactlibrary.speech_service.SpeechService;
import com.reactlibrary.voice_recorder.VoiceEvent;
import com.reactlibrary.voice_recorder.VoiceRecorder;
import com.reactlibrary.voice_encoder.VoiceEncoder;
import com.teburarec.calljulius.JlibGate; //## JuliusLib I/F

import io.reactivex.disposables.CompositeDisposable;

public class GoogleSpeechApiModule extends ReactContextBaseJavaModule {

private static final String TAG = "GoogleSpeechApi";
private static final String KEY_TEXT = "text";
private static final String KEY_IS_FINAL = "isFinal";
private static final String KEY_REASON_CODE = "reasonCode";
private static final String KEY_MESSAGE = "message";
private static final String ON_SPEECH_RECOGNIZED = "onSpeechRecognized";
private static final String ON_SPEECH_RECOGNIZED_ERROR = "onSpeechRecognizedError";
private static final int STEP_STARTWORD = 1;
private static final int STEP_OBJECTWORD = 2;
private static final int STEP_ITEMWORD = 3;
private static final int STEP_FREESPEECH = 0;


private VoiceRecorder voiceRecorder = new VoiceRecorder();
private VoiceEncoder voiceEnc = new VoiceEncoder();
private boolean Encoding = false;
private SpeechService speechService;
private CompositeDisposable compositeDisposable;
private String apiKey;
private String langCode;
private int maxVoiceBlank;
private String EncFilePath;
private String speechText;
private String tempText;
//## standalone regognization engine
private JlibGate jlibGate = null; //## JuliusLib I/F object
private int RecogStep = STEP_STARTWORD; //## state of recognizing step.


private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
speechService = SpeechService.from(service);

compositeDisposable.add(
speechService.getSpeechEventObservable()
.subscribe(GoogleSpeechApiModule.this::handleSpeechEvent)
);
compositeDisposable.add(
speechService.getSpeechErrorEventObservable()
.doAfterNext(event -> stop())
.subscribe(GoogleSpeechApiModule.this::handleErrorEvent)
);
compositeDisposable.add(
voiceRecorder.getVoiceEventObservable()
.subscribe(GoogleSpeechApiModule.this::handleVoiceEvent)
);
compositeDisposable.add(
voiceRecorder.getVoiceErrorEventObservable()
.doAfterNext(event -> stop())
.subscribe(GoogleSpeechApiModule.this::handleErrorEvent)
);

//## create a instance of JuliusLib and start it.
if (jlibGate == null) {
jlibGate = new JlibGate(getCurrentActivity());
if (!jlibGate.jlib_call_start(getSingleWord)) {
Log.i(TAG, "Faile to start the JuliusLib.");
RecogStep = STEP_STARTWORD;
}
}

voiceRecorder.start(maxVoiceBlank);
//# Start the encoder.
if (!Encoding) {
if (voiceEnc.start(voiceRecorder.getSampleRate(), 128000, EncFilePath) != VoiceEncoder.RETURN_ABEND) {
Encoding = true;
}
}
}

@Override
public void onServiceDisconnected(ComponentName name) {
speechService = null;
//## stop JuliusLib.
jlibGate.jlib_call_stop();
jlibGate = null;

}
};

public GoogleSpeechApiModule(ReactApplicationContext reactContext) {
super(reactContext);

//# FOR DEBUG
//# When an exception occurred at unknown point, enable the following statements.
final UncaughtExceptionHandler savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.d(TAG, "UncaughtException", ex);
savedUncaughtExceptionHandler.uncaughtException(thread, ex);
}
});
}

@ReactMethod
public void start(String encFilePath) {
Log.i(TAG, "start");
EncFilePath = encFilePath;
if (apiKey == null) {
sendJSErrorEvent("call setApiKey() with valid access token before calling start()", 0);
}
if (compositeDisposable != null) {
compositeDisposable.dispose();
}
compositeDisposable = new CompositeDisposable();
if (speechService == null) {
Intent serviceIntent = new Intent(getReactApplicationContext(), SpeechService.class);
getReactApplicationContext().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
speechText = "";
tempText = "";
RecogStep = STEP_STARTWORD;
} else {
sendJSErrorEvent("Another instance of SpeechService is already running", 0);
}
}

@ReactMethod
public void stop() {
Log.i(TAG, "stop");
voiceRecorder.stop();
compositeDisposable.dispose();
getReactApplicationContext().unbindService(serviceConnection);
speechService = null;
Log.d(TAG, "stoped.");
}

@ReactMethod
public void setApiKey(String apiKey) {
Log.i(TAG, "setApiKey");
this.apiKey = apiKey;
}

@ReactMethod
public void setLangCode(String langCode) {
Log.i(TAG, "setLangCode");
this.langCode = langCode;
}
@ReactMethod
public void setMaxVoiceBlank(int maxVoiceBlank) {
Log.i(TAG, "setMaxVoiceBlank");
this.maxVoiceBlank = maxVoiceBlank;
}

@Override
public String getName() {
return TAG;
}

private void handleVoiceEvent(VoiceEvent event) {
switch (event.getState()) {
case START:
onVoiceStart();
break;
case VOICE:
onVoice(event.getData(), event.getSize());
break;
case END:
onVoiceEnd(event.getReason());
break;
}
}

private void onVoiceStart() {
Log.i(TAG, "onVoiceStart");
boolean fstarted = false;
//## start recognizing
if (RecogStep != STEP_FREESPEECH) {
fstarted = jlibGate.jlib_call_recognize();
}

if (!fstarted && speechService != null) {
speechService.startRecognizing(voiceRecorder.getSampleRate(), apiKey, langCode);
}
Log.v(TAG, "onVoiceStart end.");
}

private void onVoice(byte[] data, int size) {
Log.i(TAG, "onVoice");
//## send voice data
if (RecogStep != STEP_FREESPEECH) {
jlibGate.jlib_call_send(data);
Log.i(TAG, "send size=("+size+")");
}
else {

if (speechService != null) {
speechService.recognize(data, size);
}
}

//# Send a voice data buffer to the encoder.
if (Encoding && voiceEnc.send(data, size) == VoiceEncoder.RETURN_ABEND) {
Encoding = false;
}
}

private void onVoiceEnd(int reason) {
Log.i(TAG, "onVoiceEnd("+reason+")");
if (RecogStep != STEP_FREESPEECH) {
jlibGate.jlib_call_send(null);
Log.i(TAG, "send blank");
}
else {

if (speechService != null) {
speechService.finishRecognizing();
}
}
if (reason == VoiceRecorder.STOP_REASON_BLOCK) {
//# Stop the encoder.
if (Encoding) {
if (voiceEnc.send(null, VoiceEncoder.STOP_STREAM) != VoiceEncoder.RETURN_ABEND) {
Log.d(TAG, "onVoiceEnd sended end of stream");
voiceEnc.stop();
}
Encoding = false;
}
//# The calling of stop() on here has risk that produce an exception.
//# Therefore, another error event is issued by VoiceRecorder after this event.
//stop();
Log.d(TAG, "onVoiceEnd push text="+speechText+"temp="+tempText);
speechText = speechText + tempText + ".";
WritableMap params = Arguments.createMap();
params.putString(KEY_TEXT, speechText);
params.putBoolean(KEY_IS_FINAL, true);
sendJSEvent(getReactApplicationContext(), ON_SPEECH_RECOGNIZED, params);
}
Log.d(TAG, "onVoiceEnd exit");
}

private void handleSpeechEvent(SpeechEvent speechEvent) {
Log.i(TAG, speechEvent.getText() + " " + speechEvent.isFinal());
WritableMap params = Arguments.createMap();

//# I changed the specification here.
//# Even if the "isFinal" was returned from the SpeechService, the voices inputting and the speech
//# regonition is continued. The text of "isFinal" is merged with previouse texts.

//if (!TextUtils.isEmpty(speechEvent.getText())) {
// params.putString(KEY_TEXT, speechEvent.getText());
//} else {
// params.putString(KEY_TEXT, "");
//}
//params.putBoolean(KEY_IS_FINAL, speechEvent.isFinal());
//sendJSEvent(getReactApplicationContext(), ON_SPEECH_RECOGNIZED, params);
//if (speechEvent.isFinal()) {
// stop();
//}

boolean isfinal = speechEvent.isFinal();
String currenttext = speechEvent.getText();
if (TextUtils.isEmpty(currenttext)) {
currenttext = "";
}
if (isfinal) {
speechText = speechText + currenttext + ".";
tempText = "";
params.putString(KEY_TEXT, speechText);
}
else {
tempText = currenttext;
params.putString(KEY_TEXT, (speechText + tempText + "."));
}
params.putBoolean(KEY_IS_FINAL, false);
sendJSEvent(getReactApplicationContext(), ON_SPEECH_RECOGNIZED, params);
}

//## define callback for calljulius
private JlibGate.JlibCallback getSingleWord = new JlibGate.JlibCallback() {
public int NotifyRecognized(String word) {
Log.i(TAG, "recognized string:"+word);
WritableMap params = Arguments.createMap();
String currenttext = word;
//## check a word and progress the step.
//case STEP_STARTWORD:
//case STEP_OBJECTWORD:
//case STEP_ITEMWORD:
//case STEP_FREESPEECH:
if (!TextUtils.isEmpty(currenttext)) {
GoogleSpeechApiModule.this.speechText += (currenttext + "/");
params.putString(KEY_TEXT, GoogleSpeechApiModule.this.speechText);
params.putBoolean(GoogleSpeechApiModule.this.KEY_IS_FINAL, false);
GoogleSpeechApiModule.this.sendJSEvent(getReactApplicationContext(),
GoogleSpeechApiModule.this.ON_SPEECH_RECOGNIZED, params);
}
//## free speech start.
RecogStep = STEP_FREESPEECH;
return RecogStep;
}
};


//# In serviceConnection, the calling this method be set to execute stop() after here.
private void handleErrorEvent(Throwable throwable) {
sendJSErrorEvent(throwable.getMessage(), 0);
}

private void sendJSErrorEvent(String message, int reasonCode){
Log.d(TAG, "ssendJSErrorEvent message="+message+", reasonCode="+reasonCode);
//# If the event from VoiceRecoder was "VR_STOP_MSG", this event is not notified to App.
//# In this case, the event is issued only to call stop() safely.
//# Its event is issued when voice blank reached the timeout.
if (!message.equals(VoiceRecorder.VR_STOP_MSG)) {
WritableMap params = Arguments.createMap();
params.putString(KEY_MESSAGE, message);
params.putInt(KEY_REASON_CODE, reasonCode);
sendJSEvent(getReactApplicationContext(), ON_SPEECH_RECOGNIZED_ERROR, params);
}
}

private void sendJSEvent(ReactContext reactContext,
String eventName,
WritableMap params) {
Log.d(TAG, "sendJSEvent params="+params);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
Log.d(TAG, "sendJSEvent exit");
}
}
---------------------------------------------------


<RN-Google-speech>\voice_recorder\VoiceRecorder.java
----VoiceRecorder.java-----------------------------
package com.reactlibrary.voice_recorder;

import android.util.Log;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;

import static com.reactlibrary.voice_recorder.VoiceUtils.isHearingVoice;

public class VoiceRecorder {

private static final String TAG = "VoiceRecorder";
private static final int[] SAMPLE_RATE_CANDIDATES = new int[]{16000, 11025, 22050, 44100};

private static final int SPEECH_TIMEOUT_MILLIS = 1000;
private static final int MAX_SPEECH_LENGTH_MILLIS = 30 * 1000;
private static final int DEFAULT_SAMPLE_RATE = 16000;

public static final int STOP_REASON_PAUSE = 0;
public static final int STOP_REASON_BLOCK = 1;
public static final String VR_STOP_MSG = "Stopped inputting the voice.";

private int maxVoiceBlank = SPEECH_TIMEOUT_MILLIS;
private boolean fVoiceless = true;

private PublishSubject voiceEventPublishSubject = PublishSubject.create();

private PublishSubject voiceErrorEventPublishSubject = PublishSubject.create();

private Disposable voiceEventDisposable;

private AudioRecord audioRecord;

private byte[] buffer;

/**
* The timestamp of the last time that voice is heard.
*/
private long lastVoiceHeardMillis = Long.MAX_VALUE;

/**
* The timestamp when the current voice is started.
*/
private long voiceStartedMillis = Long.MAX_VALUE;

/**
* Starts recording audio.
*
*

The caller is responsible for calling {@link #stop()} later.


*/
public void start(int maxVoiceBlank) {
// Stop recording if it is currently ongoing.
stop();
if (maxVoiceBlank > SPEECH_TIMEOUT_MILLIS) {
this.maxVoiceBlank = maxVoiceBlank;
}
// Try to create a new recording session.
audioRecord = createAudioRecord();
if (audioRecord == null) {
voiceErrorEventPublishSubject.onNext(new RuntimeException("Cannot instantiate VoiceRecorder. Probably the android.permission.RECORD_AUDIO was not granted."));
return;
}
// Start recording.
audioRecord.startRecording();
// Start processing the captured audio.
voiceEventDisposable = createVoiceEventObservable()
.subscribeOn(Schedulers.io())
.subscribe(voiceEventPublishSubject::onNext);
}

/**
* Stops recording audio.
*/
public void stop() {
if (voiceEventDisposable != null) {
voiceEventDisposable.dispose();
}
dismiss();
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
buffer = null;
}

/**
* Dismisses the currently ongoing utterance.
*/
public void dismiss() {
if (lastVoiceHeardMillis != Long.MAX_VALUE) {
voiceStartedMillis = Long.MAX_VALUE;
lastVoiceHeardMillis = Long.MAX_VALUE;
voiceEventPublishSubject.onNext(VoiceEvent.end(STOP_REASON_PAUSE));
}
}

/**
* Retrieves the sample rate currently used to record audio.
*
* @return The sample rate of recorded audio.
*/
public int getSampleRate() {
if (audioRecord != null) {
return audioRecord.getSampleRate();
}
return DEFAULT_SAMPLE_RATE;
}

public Observable getVoiceEventObservable() {
return voiceEventPublishSubject;
}

public Observable getVoiceErrorEventObservable() {
return voiceErrorEventPublishSubject;
}

/**
* Creates a new {@link AudioRecord}.
*
* @return A newly created {@link AudioRecord}, or null if it cannot be created (missing
* permissions?).
*/
private AudioRecord createAudioRecord() {
for (int sampleRate : SAMPLE_RATE_CANDIDATES) {
final int sizeInBytes = AudioRecord.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
);
if (sizeInBytes == AudioRecord.ERROR_BAD_VALUE) {
//Log.d(TAG, "invalid samplrate="+sampleRate);
continue;
}
//Log.d(TAG, "valid samplrate="+sampleRate);
final AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
sizeInBytes
);
if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
buffer = new byte[sizeInBytes];
return audioRecord;
} else {
audioRecord.release();
}
}
return null;
}

private Observable createVoiceEventObservable() {
return Observable.create(emitter -> {
while (!emitter.isDisposed()) {
final int size = audioRecord.read(buffer, 0, buffer.length);
final long now = System.currentTimeMillis();
//# There are the sounds.
if (isHearingVoice(buffer, size)) {
if (voiceStartedMillis == Long.MAX_VALUE) {
voiceStartedMillis = now;
}
if (fVoiceless) {
emitter.onNext(VoiceEvent.start());
fVoiceless = false;
}
Log.i(TAG, "input voice("+size+").");
emitter.onNext(VoiceEvent.voice(buffer, size));
lastVoiceHeardMillis = now;
//# break off long speech with the max time.
if (now - voiceStartedMillis > MAX_SPEECH_LENGTH_MILLIS) {
Log.i(TAG, "reached the limit of long speech("+MAX_SPEECH_LENGTH_MILLIS+")!!!");
voiceStartedMillis = Long.MAX_VALUE;
lastVoiceHeardMillis = Long.MAX_VALUE;
fVoiceless = true;
emitter.onNext(VoiceEvent.end(STOP_REASON_BLOCK));
//# If the callee's stop() is executed in above event handlling, a exection occur.
//# Therefore, after the event handlling of the following callback, the stop() of callee is executed.
voiceErrorEventPublishSubject.onNext(new RuntimeException(VR_STOP_MSG));
}
//# silence 0: recode to start a speech.
} else if (lastVoiceHeardMillis == Long.MAX_VALUE) {
lastVoiceHeardMillis = now;
} else {
//# silence 1: is not silence-state yet.
if (voiceStartedMillis != Long.MAX_VALUE && !fVoiceless) {
emitter.onNext(VoiceEvent.voice(buffer, size));
}
//# silence 3: reached the longest silence-state.
if (now - lastVoiceHeardMillis > maxVoiceBlank) {
Log.i(TAG, "max voice blank("+maxVoiceBlank+")!!!");
voiceStartedMillis = Long.MAX_VALUE;
lastVoiceHeardMillis = Long.MAX_VALUE;
emitter.onNext(VoiceEvent.end(STOP_REASON_BLOCK));
//# If the callee's stop() is executed in above event handlling, a exection occur.
//# Therefore, after the event handlling of the following callback, the stop() of callee is executed.
voiceErrorEventPublishSubject.onNext(new RuntimeException(VR_STOP_MSG));
}
//# silence 2: stop of sound data
else if (now - lastVoiceHeardMillis > SPEECH_TIMEOUT_MILLIS && !fVoiceless) {
Log.d(TAG, "found silence("+SPEECH_TIMEOUT_MILLIS+")!!!");
fVoiceless = true;
emitter.onNext(VoiceEvent.end(STOP_REASON_PAUSE));
}
}
}
});
}
}
---------------------------------------------------


<Call-Julius>:<プロジェクト・フォルダ>\node_modules\react-native-google-speech-api\calljulius\android\src\main\java\com\teburarec\calljulius

<Call-Julius>\JlibGate.java
----JlibGate.java----------------------------------
package com.teburarec.calljulius;

import android.util.Log;
import java.lang.Thread;
import java.lang.Exception;
import android.content.Context;
import android.app.Activity;
import android.content.res.AssetManager;
import java.io.File; //"File" Object
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class JlibGate {
private static final String TAG = "CallJulius";
private static final String ASSETSUBDIR = "jdata";
private static final int RECOG_RECOGNIZING = 1;
private static final int RECOG_STANDBY = 0;
private static final int RECOG_COMPLETED = -1;
private static final int INPUT_NOREQ = 0;
private static final int INPUT_REQ = 1;


private static Activity myAct;
private static JlibGate myObj;
private static int recogState = RECOG_COMPLETED;
private static int inputState = INPUT_REQ;
private static boolean fthread_live = false;
private static int ret_lng; //destination space length
private static boolean fblank = false; //previouse input is blank.
private static int now_needs = 1; //0:speech, not 0:word


// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("juliusword");
}

//### callback interface
public static interface JlibCallback {
int NotifyRecognized(String word);
}
private static JlibCallback myCallback = null;


//### constractor
public JlibGate(Activity act) {
Log.d(TAG, "activity context="+act);
myAct = act;
myObj = this;
initDataFiles();

//### start Jlib thread
Thread thread = new JlibCallThread();
thread.start();
}

//### setup the asset files
private void initDataFiles() {
AssetManager assetMgr = myAct.getResources().getAssets();
try {
byte[] buffer = new byte[4096];

String files[] = assetMgr.list(ASSETSUBDIR);
for(int i = 0; i < files.length; i++) {
Log.d(TAG, "assets file: " + files[i]);
String assetPath = ASSETSUBDIR + "/" + files[i];
InputStream infile = assetMgr.open(assetPath);
FileOutputStream outfile = myAct.openFileOutput(files[i], myAct.MODE_PRIVATE);
int n = 0;
while (-1 != (n = infile.read(buffer))) {
outfile.write(buffer, 0, n);
}
outfile.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
}

//### Jlib call thread
class JlibCallThread extends Thread {
public String myDataPath;

@Override
public void run() {
String rtmsg;
Log.i(TAG, "Start Jlib thread.");

synchronized (myObj) {
fthread_live = true;
File myDirObj = myAct.getFilesDir();
myDataPath = myDirObj.getPath();
Log.d(TAG, "My dir path: " + myDataPath);

rtmsg = jlibStart(myDataPath);
if (rtmsg.indexOf("ERROR:") == 0) {
Log.i(TAG, rtmsg);
//return;
}
recogState = RECOG_STANDBY;

while (recogState != RECOG_COMPLETED) {
while (recogState == RECOG_STANDBY) {
try {
Log.v(TAG, ">>> wait for recognize");
myObj.wait();
}
catch (Exception e) {
Log.e(TAG, "# Jlib thread: exception!", e);
return;
}
}
if (recogState == RECOG_RECOGNIZING) {
Log.v(TAG, "start recognizing");
rtmsg = jlibRecognize();
if (rtmsg.indexOf("ERROR:") != 0) {
now_needs = myCallback.NotifyRecognized(rtmsg);
}
else {
Log.d(TAG, rtmsg);
}
recogState = RECOG_STANDBY;
}
myObj.notifyAll();
}

jlibStop();
fthread_live = false;
} //end synchronized block
Log.i(TAG, "End Jlib thread.");
} //end run()


} //end inner class JlibCallThread

//### JNI C functions
public native String jlibStart(String curpath);
public native String jlibRecognize();
public native String jlibStop();

public native int jlibCopy(byte[] srcbuf);

//### callback gate from JuliusLib.
public int inputVoice() {
Log.v(TAG, ">>> input voice");
if (fblank) {
Log.v(TAG, "input: escape/no data.");
fblank = false;
return -1;
}
synchronized (myObj) {
if (recogState != RECOG_COMPLETED) {
inputState = INPUT_REQ;
}
myObj.notifyAll();
while (recogState == RECOG_RECOGNIZING && inputState == INPUT_REQ) {
try {
myObj.wait();
}
catch (Exception e) {
Log.e(TAG, "input voice: exception!", e);
recogState = RECOG_COMPLETED;
myObj.notifyAll();
return -1;
}
}
} //end synchronized block
if (ret_lng == -1) fblank = true;
return ret_lng;
}



//### start the JuliusLib
public boolean jlib_call_start(JlibCallback callback) {
Log.v(TAG, ">>> start");
myCallback = callback;
return true;
}


//### execute a recognizing
public boolean jlib_call_recognize() {
Log.v(TAG, ">>> recognize");
if (myCallback == null) {
Log.e(TAG, "recognize: not started!");
return false;
}
synchronized (myObj) {
now_needs = 1;
while (recogState == RECOG_RECOGNIZING) {
try {
myObj.wait();
//if RECOGNIZING-state occur at the same time, an error returns after the hold.
if (now_needs == 0) {
myObj.notifyAll();
return false;
}
}
catch (Exception e) {
Log.e(TAG, "recognize: exception!", e);
recogState = RECOG_COMPLETED;
myObj.notifyAll();
return false;
}
}
if (recogState == RECOG_STANDBY) {
recogState = RECOG_RECOGNIZING;
}
myObj.notifyAll();
} //end synchronized block
Log.v(TAG, "recognize: return");
return true;
}



//### send the voice data. call JlibCopy
public void jlib_call_send(byte[] sendbuf) {
Log.v(TAG, ">>> send");
synchronized (myObj) {
while (recogState == RECOG_RECOGNIZING && inputState == INPUT_NOREQ) {
try {
myObj.wait();
}
catch (Exception e) {
Log.e(TAG, "send: exception!", e);
recogState = RECOG_COMPLETED;
ret_lng = -1;
myObj.notifyAll();
return;
}
}
if (recogState == RECOG_RECOGNIZING && sendbuf != null) {
ret_lng = jlibCopy(sendbuf);
}
else {
Log.v(TAG, "send: escape!");
ret_lng = -1;
}
if (recogState != RECOG_COMPLETED) {
inputState = INPUT_NOREQ;
}
myObj.notifyAll();
} //end synchronized block
return;
}


//### end the JuliusLib
public void jlib_call_stop() {
Log.v(TAG, ">>> stop");
try {
Thread.sleep(2);

synchronized (myObj) {
recogState = RECOG_COMPLETED;
myObj.notifyAll();
}
}
catch (Exception e) {
Log.e(TAG, "stop: exception!", e);
}
}

}
---------------------------------------------------


<Julius-Lib>:<プロジェクト・フォルダ>\node_modules\react-native-google-speech-api\calljulius\android\src\main\cpp

<Julius-Lib>\julius-entry.cpp
----julius-entry.cpp-------------------------------
#include <string>

#include <julius/juliuslib.h> //include top header of Julius library

#define MY_RETURN_MAX 240

char g_ret_str[MY_RETURN_MAX+16]; //認識結果リターン文字列
jmethodID g_inVoiceCallback; //JAVA側コールバックメソッドのID
JNIEnv* g_env; //コールバック用JNIインスタンス
jobject g_jObj; //
//jclass g_jcls; //Java側でstatic指定だった場合
Jconf* g_jconf; //configuration parameter holder
Recog* g_recog = NULL; //Recognition instance

int g_tmpsize = 0; //一時保存データ長
char g_tmpbuf[MAXSPEECHLEN]; //一時保存バッファ320kB(最大バッファの半分)
//コピー関数用の共用変数
char* g_dst_curp; //カレントのバッファポインタ
int g_dst_lng; //バッファの残りのサイズ

//******************************************************************************
// function :Callback to be called when start waiting speech input.
// parameter :recog -認識エンジンのインスタンス
// dummy -未使用パラメタ
// return :
//******************************************************************************
static void
status_recready(
Recog* recog,
void* dummy
)
{
if (recog->jconf->input.speech_input == SP_MIC
|| recog->jconf->input.speech_input == SP_NETAUDIO) {
fprintf(stderr, "<<< please speak >>>");
}
else {
fprintf(stderr, "*** It's ready. >>>");
}
}

//******************************************************************************
// function :Callback to be called when speech input is triggered.
// parameter :recog -認識エンジンのインスタンス
// dummy -未使用パラメタ
// return :なし
//******************************************************************************
static void
status_recstart(
Recog* recog,
void* dummy
)
{
if (recog->jconf->input.speech_input == SP_MIC
|| recog->jconf->input.speech_input == SP_NETAUDIO) {
fprintf(stderr, "\r \r");
}
else {
fprintf(stderr, "*** Start the recognizing. >>>");
}
}

//******************************************************************************
// function :Sub function to output phoneme sequence.
// parameter :seq -音素シーケンスのアドレス
// n -
// winfo -
// return :なし
//******************************************************************************
static void
put_hypo_phoneme(
WORD_ID* seq,
int n,
WORD_INFO* winfo
)
{
int i,
j;
WORD_ID w;

static char buf[MAX_HMMNAME_LEN];

if (seq != NULL) {
for (i=0;i if (i > 0) printf(" |");
w = seq[i];
for (j=0;jwlen[w];j++) {
center_name(winfo->wseq[w][j]->name, buf);
printf(" %s", buf);
}
}
}
printf("\n");
}

//******************************************************************************
// function :Callback to output final recognition result.
// This function will be called just after recognition of an input ends
// parameter :recog -認識エンジンのインスタンス
// vp-ret -リターン文字列バッファ
// return :なし
//******************************************************************************
static void
output_result(
Recog* recog,
void* vp_ret
)
{
int i,
j;
int len;
WORD_INFO* winfo;
WORD_ID* seq;
int seqnum;
int n;
Sentence* s;
RecogProcess* r;
HMM_Logical* p;
SentenceAlign* align;

char* cur_p = (char *) vp_ret;
int ret_len;

sprintf(cur_p, "RETURN:");
ret_len = strlen(cur_p);
cur_p += ret_len;

//all recognition results are stored at each recognition process instance
for(r=recog->process_list;r;r=r->next) {
//skip the process if the process is not alive
if (! r->live) continue;

//result are in r->result. See recog.h for details
//check result status
if (r->result.status < 0) { //no results obtained
//outout message according to the status code
switch(r->result.status) {
case J_RESULT_STATUS_REJECT_POWER:
printf("<nput rejected by power>\n");
break;
case J_RESULT_STATUS_TERMINATE:
printf("<input teminated by request>\n");
break;
case J_RESULT_STATUS_ONLY_SILENCE:
printf("<input rejected by decoder (silence input result)>\n");
break;
case J_RESULT_STATUS_REJECT_GMM:
printf("<input rejected by GMM>\n");
break;
case J_RESULT_STATUS_REJECT_SHORT:
printf("<input rejected by short input>\n");
break;
case J_RESULT_STATUS_REJECT_LONG:
printf("<input rejected by long input>\n");
break;
case J_RESULT_STATUS_FAIL:
printf("<search failed>\n");
break;
}
//continue to next process instance
continue;
}

//output results for all the obtained sentences
winfo = r->lm->winfo;

for(n = 0; n < r->result.sentnum; n++) { //for all sentences
s = &(r->result.sent[n]);
seq = s->word;
seqnum = s->word_num;

//output word sequence like Julius
printf("sentence%d:", n+1);
for(i=0;i printf(" %s", winfo->woutput[seq[i]]);
if (ret_len + strlen(winfo->woutput[seq[i]]) < MY_RETURN_MAX) {
sprintf(cur_p, "%s", winfo->woutput[seq[i]]);
ret_len += strlen(cur_p);
cur_p += strlen(cur_p);
}
}
printf("\n");
sprintf(cur_p, "\t");
ret_len++;
cur_p++;
//LM entry sequence
printf("wseq%d:", n+1);
for(i=0;iwname[seq[i]]);
printf("\n");
sprintf(cur_p, "\t");
ret_len++;
cur_p++;
//phoneme sequence
printf("phseq%d:", n+1);
put_hypo_phoneme(seq, seqnum, winfo);
printf("\n");
//confidence scores
printf("cmscore%d:", n+1);
for (i=0;i printf(" %5.3f", s->confidence[i]);
if (ret_len + 6 < MY_RETURN_MAX) {
sprintf(cur_p, "%d ", (int) (s->confidence[i]*1000));
ret_len += strlen(cur_p);
cur_p += strlen(cur_p);
}
}
printf("\n");
sprintf(cur_p, "\t");
ret_len++;
cur_p++;
//AM and LM scores
printf("score%d: %f", n+1, s->score);
if (r->lmtype == LM_PROB) { //if this process uses N-gram
printf(" (AM: %f LM: %f)", s->score_am, s->score_lm);
}
printf("\n");

if (r->lmtype == LM_DFA) { //if this process uses DFA grammar
//output which grammar the hypothesis belongs to when using multiple grammars
if (multigram_get_all_num(r->lm) > 1) {
printf("grammar%d: %d\n", n+1, s->gram_id);
}
}

//output alignment result if exist
for (align = s->align; align; align = align->next) {
printf("=== begin forced alignment ===\n");
switch(align->unittype) {
case PER_WORD:
printf("-- word alignment --\n"); break;
case PER_PHONEME:
printf("-- phoneme alignment --\n"); break;
case PER_STATE:
printf("-- state alignment --\n"); break;
}
printf(" id: from to n_score unit\n");
printf(" ----------------------------------------\n");
for(i=0;inum;i++) {
printf("[%4d %4d] %f ", align->begin_frame[i], align->end_frame[i], align->avgscore[i]);
switch(align->unittype) {
case PER_WORD:
printf("%s\t[%s]\n", winfo->wname[align->w[i]], winfo->woutput[align->w[i]]);
break;
case PER_PHONEME:
p = align->ph[i];
if (p->is_pseudo) {
printf("{%s}\n", p->name);
} else if (strmatch(p->name, p->body.defined->name)) {
printf("%s\n", p->name);
} else {
printf("%s[%s]\n", p->name, p->body.defined->name);
}
break;
case PER_STATE:
p = align->ph[i];
if (p->is_pseudo) {
printf("{%s}", p->name);
} else if (strmatch(p->name, p->body.defined->name)) {
printf("%s", p->name);
} else {
printf("%s[%s]", p->name, p->body.defined->name);
}
if (r->am->hmminfo->multipath) {
if (align->is_iwsp[i]) {
printf(" #%d (sp)\n", align->loc[i]);
} else {
printf(" #%d\n", align->loc[i]);
}
} else {
printf(" #%d\n", align->loc[i]);
}
break;
} //end switch
} //end for < align->num

printf("re-computed AM score: %f\n", align->allscore);

printf("=== end forced alignment ===\n");
} //end for s->align chain
} //end for < result.sentnum
} //end for process_list

//flush output buffer
fflush(stdout);
}


//******************************************************************************
// function :音声データ入力用コールバック関数への入り口
// parameter :buffer -バッファアドレス
// buf_size -バッファサイズ
// return :音声データ長
//******************************************************************************
int InVoiceBridge(
char* buffer,
int buf_size
)
{
char* dst_curp = buffer;
int dst_lng = buf_size;
int cpy_lng;
int ret_lng;

try {
fprintf(stderr, "*** InVoiceBridge. >>>");
//一時保存があればコピー
if (g_tmpsize > 0) {
cpy_lng = min(buf_size, g_tmpsize);
memcpy(dst_curp, g_tmpbuf, cpy_lng);
//それでも一時ファイルにデータがあった場合、一時保存データシフト
g_tmpsize -= cpy_lng;
if (g_tmpsize > 0) {
memcpy(g_tmpbuf, g_tmpbuf+cpy_lng, g_tmpsize);
}
//入力バッファがフルの場合、一旦リターン
if (buf_size <= cpy_lng) return cpy_lng;
dst_curp += cpy_lng;
dst_lng -= cpy_lng;
}
//バッファに空きがあればコールバックする
while (dst_lng > 0) {
//Javaコールバックのコール
g_dst_lng = dst_lng;
g_dst_curp = dst_curp;
ret_lng = (int) g_env->CallIntMethod(g_jObj, g_inVoiceCallback);
//ret_lng = (int) g_env->CallStaticIntMethod(g_jcls, g_inVoiceCallback);
if (ret_lng < 0) break;
if (dst_lng > ret_lng) dst_curp += dst_lng - ret_lng;
dst_lng = ret_lng;
}
return (buf_size - dst_lng);
}
catch (std::exception &e) {
strcpy(g_ret_str, "ERROR: EXCEPTION AT CALLBACK");
return 0;
}
}

//******************************************************************************
// function :コールバック内で使用する、音声データのコピー処理
// parameter :env -JNIインタフェース構造体へのポインタ
// jObj -呼出し元JAVAクラスのインスタンス
// jvoicebuf -Java音声データバッファ
// return :コピー先バッファの残りのサイズ
//******************************************************************************
extern "C" JNIEXPORT jint JNICALL
Java_com_teburarec_calljulius_JlibGate_jlibCopy(
JNIEnv* env,
jobject jObj,
//jclass jcls,
jbyteArray jvoicebuf //データ格納先Javaバッファ
)
{
int dst_lng = g_dst_lng;
char* dst_curp = g_dst_curp;
int cpy_lng;
char* src_buf;
int src_lng;
jboolean isCopy;

fprintf(stderr, "*** jlibCopy. >>>");

src_lng = (int) env->GetArrayLength(jvoicebuf);
fprintf(stderr, "data size=%d", src_lng);
if (src_lng <= 0) return -1;
//jbyteArrayバッファポインタの取得
src_buf = (char *) env->GetByteArrayElements(jvoicebuf, &isCopy);
cpy_lng = min(dst_lng, src_lng);
memcpy(dst_curp, src_buf, cpy_lng);
dst_curp += cpy_lng;
dst_lng -= cpy_lng;
//バッファに入りきらなかった分を一時保存
src_lng -= cpy_lng;
if (src_lng > 0) {
memcpy(g_tmpbuf, src_buf+cpy_lng, src_lng);
g_tmpsize = src_lng;
}
if (isCopy) {
//jbyteArrayバッファポインタの解放
env->ReleaseByteArrayElements(jvoicebuf, (jbyte *)src_buf, JNI_ABORT);
}

return (jint) dst_lng;
}

//******************************************************************************
// function :動作パラメタの読込と解析、認識インスタンスの初期化
// parameter :env -JNIインタフェース構造体へのポインタ
// jObj -呼出し元JAVAクラスのインスタンス
// curpath -カレントディレクトリ・パス
// return :結果メッセージ(文字列)
//******************************************************************************
extern "C" JNIEXPORT jstring JNICALL
Java_com_teburarec_calljulius_JlibGate_jlibStart(
JNIEnv* env,
jobject jObj,
//jclass jcls,
jstring curpath//, //データファイル格納先ディレクトリ
//void* voicesrc //
)
{
//動作パラメタはすべてsimple_keyword.jconfに記載する
const int argc = 3;
const char* argv[] = {"juliusword", "-C", "simple_keyword.jconf"};

int ret;

//2重起動チェック
if (g_recog != NULL) {
return env->NewStringUTF("ERROR: WAS STARTED ALREADY");
}

//.jconf、単語辞書、音響モデルの格納先をカレントディレクトリに設定する
const char* jdataPath = env->GetStringUTFChars(curpath, NULL);
chdir(jdataPath);
env->ReleaseStringUTFChars(curpath, jdataPath);

//## test code
char cwd[512] = "?";
getcwd(cwd, sizeof(cwd));
fprintf(stdout, "current dir=%s\nset dir=%s", cwd, jdataPath);

//ログファイルのオープン
//jlog_set_output(NULL);
FILE* fp;
fp = fopen("log.txt", "w");
if (fp == NULL) {
fprintf(stderr, "ERROR: faile to open the log.txt.");
}
jlog_set_output(fp);

//動作パラメタの解析/セッティング
g_jconf = j_config_load_args_new(argc, (char **) argv);
if (g_jconf == NULL) {
fprintf(stderr, "ERROR: found any invalid parameter in JCONF.\n");
return env->NewStringUTF("ERROR: MISSING JCONF");
}

//認識インスタンスの生成
g_recog = j_create_instance_from_jconf(g_jconf);
//fprintf(stdout, "created an instance");
if (g_recog == NULL) {
fprintf(stderr, "ERROR: faile to startup\n");
return env->NewStringUTF("ERROR: FAILED TO CREATE INSTANCE");
}

//コールバック関数の設定
callback_add(g_recog, CALLBACK_EVENT_SPEECH_READY, status_recready, NULL);
callback_add(g_recog, CALLBACK_EVENT_SPEECH_START, status_recstart, NULL);
callback_add(g_recog, CALLBACK_RESULT, output_result, (void *) g_ret_str);

//Javaコールバック関数のメソッドIDの取得
//※Java側でこの関数を定義する際、staticを指定すると、第2引数の型がjobject→jclassになる。
jclass javagate = env->GetObjectClass(jObj);
g_inVoiceCallback = env->GetMethodID(javagate, "inputVoice", "()I");
//g_inVoiceCallback = env->GetMethodID(jcls, "inputVoice", "()I");
if (!g_inVoiceCallback) {
fprintf(stdout, "INTERNAL ERROR: failed to get callback of java!!!");
return env->NewStringUTF("ERROR: FAILED TO GET CALLBACK");
}


//音声データ入力用のC側コールバックの指定
g_recog->callback_for_adin = (void *) &InVoiceBridge;

//音声入力デバイスの初期化
if (j_adin_init(g_recog) == FALSE) {
return env->NewStringUTF("ERROR: CAN NOT USE INPUT DEVICE");
}

//認識システム情報のログへの出力
j_recog_info(g_recog);

fprintf(stdout, "return jlibStart");
return env->NewStringUTF("");
}


//******************************************************************************
// function :音声の入力と認識の実行
// parameter :env -JNIインタフェース構造体へのポインタ
// jObj -呼出し元JAVAクラスのインスタンス
// return :結果メッセージ(文字列)-認識結果かエラーメッセージ
//******************************************************************************
extern "C" JNIEXPORT jstring JNICALL
Java_com_teburarec_calljulius_JlibGate_jlibRecognize(
JNIEnv* env,
jobject jObj
//jclass jcls
)
{
int ret;

//初期化済みチェック
if (g_recog == NULL) {
return env->NewStringUTF("ERROR: IS NOT STARTED");
}

//コールバック用にenvを保存
g_env = env;
g_jObj = jObj;
//g_jcls = jcls;

//音声入力と認識
//Raw file(wav) input
if (g_jconf->input.speech_input == SP_RAWFILE) {
ret = j_open_stream(g_recog, NULL);
if (ret == 0) {
fprintf(stderr, "*** start input stream.\n");
ret = j_recognize_stream(g_recog);
if (ret == -1) {
return env->NewStringUTF("ERROR: RECOGNIZE ERROR");
}
}
else if (ret == -1) {
fprintf(stderr, "error in input stream\n");
return env->NewStringUTF("ERROR: INPUT STREAM");
}
else if (ret == -2) {
fprintf(stderr, "failed to begin input stream\n");
return env->NewStringUTF("ERROR: FAILED TO BEGIN");
}
} //end raw speech process

//※本来、以下はエラー条件。結果は保証しない。
//MFCC file or OUTPROB file input
else if (g_jconf->input.speech_input == SP_MFCFILE
|| g_jconf->input.speech_input == SP_OUTPROBFILE) {
static char speechfilename[MAXPATHLEN]; //speech file name for MFCC file input

while (get_line_from_stdin(speechfilename, MAXPATHLEN, (char *) "enter MFCC filename->") != NULL) {
if (verbose_flag) printf("\ninput MFCC file: %s\n", speechfilename);
//open the input file
ret = j_open_stream(g_recog, speechfilename);
switch (ret) {
case 0: //succeeded
break;
case -1: //error
//go on to the next input
continue;
case -2: //end of recognition
//return;
return env->NewStringUTF("END: RECOGNITION");
}
//recognition loop
ret = j_recognize_stream(g_recog);
if (ret == -1) //return -1;
return env->NewStringUTF("ERROR: RECOGNIZE ERROR");
//reach here when an input ends
}
}

//other raw speech input (microphone etc.)
else {
switch(j_open_stream(g_recog, NULL)) {
case 0: //succeeded
break;
case -1: //error
fprintf(stderr, "error in input stream\n");
//return;
return env->NewStringUTF("ERROR: INPUT STREAM");
case -2: //end of recognition process
fprintf(stderr, "failed to begin input stream\n");
//return;
return env->NewStringUTF("ERROR: FAILED TO BEGIN");
}

//******************************************************
//Recognization Loop
//******************************************************
//enter main loop to recognize the input stream
//finish after whole input has been processed and input reaches end
ret = j_recognize_stream(g_recog);
if (ret == -1) //return -1;
return env->NewStringUTF("ERROR: RECOGNIZE ERROR");
}

return env->NewStringUTF((const char *) g_ret_str);
}


//******************************************************************************
// function :認識処理の終了(インスタンスの解放)
// parameter :env -JNIインタフェース構造体へのポインタ
// jObj -呼出し元JAVAクラスのインスタンス
// return :結果メッセージ(文字列)-未使用
//******************************************************************************
extern "C" JNIEXPORT jstring JNICALL
Java_com_teburarec_calljulius_JlibGate_jlibStop(
JNIEnv* env,
jobject jObj
//jclass jcls
)
{
if (g_recog != NULL) {
j_close_stream(g_recog);
j_recog_free(g_recog);
g_recog = NULL;
}

return env->NewStringUTF("");
}
---------------------------------------------------


スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
01 | 2024/02 | 03
- - - - 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 - -
プロフィール

さるもすなる

Author:さるもすなる
さるです。別HPサイト「さるもすなる」から侵食してきました。 山菜/きのこ、それとタイトルにしたPPバンド籠のことをメインに徒然に・・・・暇を持て余したさるの手仕事:男手芸のブログってことで。

最新記事
最新コメント
月別アーカイブ
カテゴリ
天気予報

-天気予報コム- -FC2-
本家のHPのトップ
山菜や茸の話です
PPバンドの籠作品と作り方です
投稿をお待ちしております



PVアクセスランキング にほんブログ村 にほんブログ村 ハンドメイドブログへ



マニュアルのお申し込み



検索フォーム
リンク
RSSリンクの表示
ブロとも申請フォーム

この人とブロともになる

QRコード
QR