FC2ブログ

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

   プログラミング [2020/07/19]
JuliusをAndroidスマフォ上で動作させたいと思ってます。
概ね
Ⅰ.まずはJavaコードからC関数を呼び出すようなアプリの作り方を調べます。
Ⅱ.Javaコード(Cライブラリ含む)をReact Nativeで使えるパッケージ化方法を調べます。
Ⅲ.React Nativeにパッケージを取り込んで、Build&Go。
の順番で進めてますが、5回目にもなるのに未だの途中です。

前回、AndroidStudioのデバッグ機能を有効に動作させるための設定に1週間程費やしてしまいました。
今回は、その続き。

なんとかデバッグできる環境にはなりましたが、未だ不足している部分があります。


◆標準出力に出るメッセージが見たい
JuliusLib(libjulius&linsent)内で、メッセージ出力にprintf/fprintfが使われています。
「それってAndroid上ではどうなるの?」と調べたら、「logcatに出力される。」という記述をどこぞのサイトを斜めに読んで見つけた。

でも、実際はそんな簡単ではなくて、「だまっててもlogcatに出力される」ようにはなっていませんでした。

「リダイレクトすれば見える」というような記述もあって、その通りやってみた。

----コマンドプロンプト-----------------------------
C:\WINDOWS\system32>cd /d d:\appmake\android/sdk/platform-tools

d:\AppMake\Android\Sdk\platform-tools>adb shell
root@generic_x86:/ # stop
root@generic_x86:/ # setprop log.redirect-stdio true
root@generic_x86:/ # start
root@generic_x86:/ #
---------------------------------------------------


でも、どうも出てるようには見えない。
さらに調べると、なんか色んなことが書いてあって却って面倒そう。

しかたなくJuliusLibのソースに少し付け足しすることに方針転換しました。
プリプロセッサのマクロ定義を使って、printf/fprintfをAndroidのlogcat出力関数に置き換えてみました。

まずは、julius.hの終わりにヘッダファイルの#includeを追加します。
----julius.h---------------------------------------


/* read libsent includes */
#include <sent/stddefs.h>


#include <julius/extern.h>

#if defined(__ANDROID__)
# include <julius/print-android-macro.h>
#endif


#endif /* __J_JULIUS_H__ */
---------------------------------------------------


新規追加したヘッダの中身は、こんな感じ。
----print-android-macro.h--------------------------
#include <jni.h>
#include <android/log.h>

#define printf(...) __android_log_print(ANDROID_LOG_INFO, MYTAG, __VA_ARGS__);

#define fprintf(fptr, ...) { \
if(fptr==_STD_OUT){__android_log_print(ANDROID_LOG_INFO, MYTAG, __VA_ARGS__);} \
else if(fptr==_STD_ERR){__android_log_print(ANDROID_LOG_DEBUG, MYTAG, __VA_ARGS__);} \
else {fprintf(fptr, __VA_ARGS__);} \
};

#define MYTAG "JuliusLib"
#define _STD_OUT stdout
#define _STD_ERR stderr

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

julius.hと同じフォルダ「(プロジェクト・フォルダ)\app\src\main\cpp\libjulius\include\julius」に追加しました。


ちなみに、マクロ・ヘッダの#includeは「#include <stdio.h>」より前にあるとダメです。
本体の関数定義も置き換えようとしちゃうから。

ちょっと、こんなことしていいのか判断できてないですがいちおう動いてはいるみたいです。
stdout/stderr以外を使って、通常のファイルに出力しようとするfprintf()があった場合に、ちゃんとファイル出力できてるか未確認です。


◆.so側に制御が渡ったときのカレントディレクトリ
JavaのコードからJNI(Java Native Interface)を経由してC/C++の.soを呼び出したとき、元のカレントディレクトリが引き継がれるのかと思いきや。
.so呼び出し直後のカレントディレクトリは、ルート「/」でした。
なので、APKのassetsからアプリ専用フォルダにコピーしたデータファイル※をアクセスするためには、そのパスを.so側に教えてあげる必要があります。
※詳細は、前回記事をご参照願います。

JNIの機構では、Java側の関数呼び出しの際に指定した引数が、C側の第3引数以降になるようです。
また、Javaの変数値をCの変数型の値に変換する関数が用意されているとのこと。
解説サイトを参考にMainActivity.javaとnative-lib.cppを以下のように変更しました。

----MainActivity.java------------------------------
package com.teburarec.call_julius;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.TextView;
import java.io.IOException;
import android.content.res.AssetManager;
import java.io.File;
import java.io.InputStream;
import java.io.FileOutputStream;
import android.util.Log;


public class MainActivity extends AppCompatActivity {

String myDataPath;

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

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

File myDirObj = getFilesDir();
this.myDataPath = myDirObj.getPath();
Log.d("to dir path", this.myDataPath);


initDataFiles();

setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
String rtmsg = stringFromJNI(this.myDataPath);
tv.setText(rtmsg);
}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI(String curpath);

private void initDataFiles() {
AssetManager assetMgr = getResources().getAssets();
try {
byte[] buffer = new byte[4096];

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

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


渡されたパスをカレントディレクトリに設定すれば、後はJuliusLibを動作させるための各種データファイルをファイル名だけでアクセスできる・・・ハズ。
----native-lib.cpp---------------------------------
:
:
extern "C" JNIEXPORT jstring JNICALL
Java_com_teburarec_call_1julius_MainActivity_stringFromJNI(
JNIEnv* env,
jobject jObj /* this */,
jstring curpath
) {

const int argc = 3;
const char *argv[] = {"julius-sample", "-C", "simple_keyword.jconf"};



int ret;

const char *jdataPath = env->GetStringUTFChars(curpath, NULL);
chdir(jdataPath);
env->ReleaseStringUTFChars(curpath, jdataPath);


/* by default, all messages will be output to standard out */
/* to disable output, uncomment below */
//jlog_set_output(NULL);

/* output log to a file */
//FILE *fp; fp = fopen("log.txt", "w"); jlog_set_output(fp);



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


この対策をやって、「-C simple_keyword.jconf」と指定した、.jconfはアクセスされるようになりました。

logcat(printf/fprintf)に出力されるエラーメッセージもさほど詳しいものはないことが、この時点で分かった。
なので・・・

続きを読む

スポンサーサイト





カレンダー
06 | 2020/07 | 08
- - - 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 30 31 -
プロフィール

さるもすなる

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

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

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



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



マニュアルのお申し込み



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

この人とブロともになる

QRコード
QR