FC2ブログ

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

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

前回、JuliusLib(libjulius&libsent)をAndroidStudioのC++プロジェクト内に持ち込んで、
ビルドして、実行してみるところまでやりました。
とはいっても、肝心な音声認識を確認するところまでは届いてなくて、「起動はした」止まりです。

続きです。


◆JuliusLibの動作のさせかた調査
「ちょっと、移植作業としての順番がおかしくねーか」と言われそうですが、こんな感じです。

提供されているjulius-simple.cをビルドしたバイナリは、dictation-kit-4.5には含まれていないみたい。
実際にPC版とかを動かして確認するのはちょっと面倒そう。
なので、JuliusBookとソース等からJuliusLib&julius-simpleの使い方を調べます。

1)まずはJuliusLibの起動パラメタには何を指定するのか?
julius-simpleのオリジナルコードは起動パラメタをそのままライブラリに渡している。
指定できるパラメタ(オプション)に「-C」があることをライブラリ内のソースから確認しました。
その他は、-AM|-AM_GMM|-LM|-SR|-GLOBAL|-sectioncheck|-nosectioncheck|・・・(※多すぎたので省略)などがありました。

ともかく、-C.jconfファイルが指定できれば、上記のその他の動作パラメタは.jconfファイルに記載して指定できるはず。


2)じゃあ、.jconfファイルってどんな?
ディクテーションキット内に00readme-ja.txt(英:00readme.txt)というファイルがあり、その中に仕様が記述されていました。
dictation-kit-4.5\src\julius-4.5\libjulius\00readme-ja.txt
(前記の調べは、このファイルを読めば分かることでした。^^;;)
また、サンプルの.jconfファイルも含まれていた。コメントとしてパラメタの概説も載ってます。
dictation-kit-4.5\main.jconf
これをベースにします。


3)今回Android上で確認したい機能と制約は?
試したいのは個別単語辞書に基づいた単語認識です。
「スマフォのアプリを作りたい(25)」で試した動作をスマフォで確認したい。
「julius.exe -w 個別単語辞書 -h 音響モデル -input mic」と同様のことを試すということです。
ただし、前回のビルド段階で「Juliusが前提としているサウンドシステムは、Android SDK(NDK)標準では含まれていない」ことが分かったので、マイク入力は使えません。

よって、.jconfファイル/個別単語辞書/音響モデル/音声ファイルをAPK含めて、
native-lib.cppでは、argv[]指定を「-C .jconfファイル」※として起動して、単語認識が動作するか確認します。
(※この点は、予想通りなので、native-lib.cppの修正はせずに済みそう・・・?)


4)単語辞書と音声ファイル等を準備する
【単語辞書】
「スマフォのアプリを作りたい(25)」で作った単語辞書は、「読み」の記述の仕方が正しくありませんでした。
例えば、「体操  たいそう」は、「体操  たいそー」にしないといけない。
作り直します。やり方は、「スマフォのアプリを作りたい(25)」に書いてあります。

【音響モデル】
ディクテーションキットに含まれていたjnas-mono-16mix-gid.binhmmを使用させていただきます。
dictation-kit-4.5\model\phone_m\jnas-mono-16mix-gid.binhmm

【音声ファイル】
音声ファイルはwavでいいみたいなので、開発タブレットPCで自分の声を録音して作ります。
と簡単に思って始めたが、結構面倒だった。

まずは、Windowsのサウンドレコーダーでよかろうと思って探したが・・・ない!
Windows10からはボイスレコーダーっていう名前に変わってたのね。
早速、起動してマイク入力して録音してみた。
録音音声はどこに?
ライブラリ>ドキュメント>サウンドレコーディング(C:\Users\(ユーザID)\Documents\サウンド レコーディング)でした。
ファイル拡張子が.m4aです。(.wavでよかったのに。)
サウンドレコーダー自体に.wavで保存する機能は・・・ない!んだよねきっと。

audacityをインストールしてなかったか?・・・ない!
(以前のHDDパンクの折にアンインストールしてしまったようだ。)
再ダウンロード&インストールを実施し、audacity起動して、録音した.m4aを開こうとしたら・・・エラーが出た。
FFmpegというやつが必要なのだそうだ。
FFmpegをダウンロードしてインストール。(最初からaudacityで録音すれば良い話なのだが・・・)
audacityを再起動して、やっと.m4aが開けた。

JuliusBookにあるように、入力音声のタイプとして指定できる属性もあるが、デフォルトでサポートしているのはモノラル/16bit/16000hzのようなので、それに合わせて.wav出力する。
audacityの場合、サンプリングレートの変換はプロジェクトの属性を予め出力するレートに変更しておく必要があるようでした。

【ファイル名リスト】
JuliusBookによると、音声ファイルを入力音源として使用する場合、「-input=rawfile -filelist ファイル名リスト」として指定するみたいです。
なので、ファイル名リストというのも準備します。
単に「音声ファイル名[改行]」の繰り返しのテキストファイルを作成しました。
念のため文字コード指定はUTF-8にしておきました。

【.jconf】
dictation-kit-4.5\main.jconfをコピーして、natibe-lib.cpp内で指定してあった「simple_keyword.jconf」という名前でセーブ。
以下の記述を追加しました。
----simple_keyword.jconf---------------------------

#!!-C simple_keyword.jconf

-input rawfile

-filelist invoicename.txt

-w teburarecword.dict

-h jnas-mono-16mix-gid.binhmm

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


これで、JuliusLibに必要なデータ(ファイル)類と動作パラメタの指定の仕方がざっくりわかりました。
では、これらをAPKに入れて、プログラムから使わせる方へ話を進めます。
・・・


以降、図の表示が小さくて見えない場合は、クリックすると文字が見える程度の画像で表示すると思います。

◆APKへのデータファイルの追加
「Androidでは、アプリをAPKという形式でスマフォに持っていってインストールさせている」くらいの認識しかさるにはなくて、この辺の中身のことを全く理解できてないので、まずはググります。
参考:https://groups.google.com/forum/#!topic/android-group-japan/VnMmx0663nA
えっ何かコードを書かないと使えるようにできないの?マジ?
参考:http://yan-note.blogspot.com/2010/09/android-apk.html
どうも、本当っぽい。(*´Д`)=3ハァ・・・
けど、上記の参考サイトの情報源は、その前の参考サイトのようです。

WikipediaでAPKの説明を読みます。

これまでも、「assets」って出てきたけど、「resフォルダに入れられないファイルのためのAPK内のフォルダ」みたいなことを書いてます。
ちょっとまだ意味が分かりません。
でも、インストール時にアプリのフォルダに展開されるみたい。
書き込みもできるフォルダのようですが、専用のAPI(AssetsManager)でないとファイルにアクセスできないみたい。

もう一つ「res/raw」というフォルダもmiscellaneousなデータを入れる目的で使用することができるそうです。
こっちも、インストール時に展開されるものらしい。こっちのファイルにリソースIDなるものが割り振られるとある。
(なんか、react-native-sound-playerパッケージのソースを追っかけたときにリソースIDって出てきたような。)
読み込めても書き込めない。サブフォルダは作れないとのこと。
やっぱり専用のAPI(クラス?)が必要で、Cの標準関数ではアクセスできないしろもののようです。

早い話、JuliusLibがAndroidのこれら専用のAPIを使っているとはとても思えない。
なので、やっぱり他のフツーのフォルダにコピーするしかないんでしょうかね。

まずは、APKのassetsフォルダにデータファイルを仕込む手順です。
1)AndroidStudioの左側(Projectタブ)で「Android」を選択します。
2)「Android」ペイン(というみたい)の「app」を右クリックして[File]-[New]-[Folder]-[Assets Folder]の順で選択します。
20200710_1.jpg
3)「New Android Component」ダイアログでは、「Target Source Set」は「main」のまま[Finish]ボタンをクリックします。
4)「Android」ペインの「app」の下に「assets」フォルダが表示されます。
5)実際のフォルダは、(プロジェクトフォルダ)\app\src\main\assetsになってるようなので、ここにデータファイルを入れます。
※この手順は、後で訂正が入ります。

6)AndroidStudioでBuildを実行します。
(プロジェクトフォルダ)\app\build\outputs\apk\debugに.apkファイルが作成されていました。
7)AndroidStudioの左側(Projectタブ)で「Project」を選択します。
8)「Project」ペインの「(プロジェクト名)/app/build/output/apk/debug」下の.apkをダブルクリックします。
20200710_2.jpg

9)右側に.apkの中身が表示されるので、assetsフォルダ内に指定のファイルがあるかを確認してください。


◆assetsから通常フォルダへのコピー処理の追加
JuliusLibでは、おそらくデータ類は、フツーのファイルアクセスで中身を取り出そうとするだろうから、
前出の参考サイトに書いてあったように、アプリ起動時(javaコード内)にassetsフォルダから通常フォルダにファイルをコピーします。

MainActivity.java内に仮の処理コードを書いてデバッグ実行してみました。

ところが、assets内のファイル名リストになっているはずの配列の先頭がimagesでした。予想外です。
そのまま次に進むと、assetMgr.open()で、FileNotFoundです。
images以外にもsounds、webkitとかの名前も見えます。
おそらく、デフォルトで組み込まれるフォルダ構造なのかと思います。

それらを除外するためにassets内のファイルの属性を取得するメソッドが無いか検索してみましたが、無いみたい。
ローカルルールで、拡張子のあるなしで区別する手も考えましたが、
assets以下にサブディレクトリを作ってその中だけで検索コピーする運用に変更します。

従って、前節の5)の手順は以下に変更です。
5)(プロジェクトフォルダ)\app\src\main\assets以下にサブフォルダ(jdata)を作成し、データファイルを入れます。


コードもそれ用に修正して、一応assetsからアプリの標準?のフォルダへのコピーは成功したようです。

MainActivity.javaはこんなコードにしました。
----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; //"File" Object
import java.io.InputStream;
import java.io.FileOutputStream;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

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

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

initDataFiles();

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

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


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

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

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();
}
}

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

※コピー先指定をファイル名のみの指定としたため、どこにコピーされるか見るためにgetFileDir()を実行しています。
logcatには「/data/user/0/com.teburarec.call_julius/files」と記録されてました。


「adb shell」コマンドでコピー結果を確認してみました。
----コマンドプロンプト-----------------------------
C:\WINDOWS\system32>cd /d d:\appmake\android/sdk/platform-tools

d:\AppMake\Android\Sdk\platform-tools>adb shell
root@generic_x86:/ #
:
root@generic_x86:/ # cd /data/user/0/com.teburarec.call_julius/files
root@generic_x86:/data/user/0/com.teburarec.call_julius/files # ls
invoicename.txt
jnas-mono-16mix-gid.binhmm
keywords1.wav
keywords2.wav
simple_keyword.jconf
teburarecword.dict
root@generic_x86:/data/user/0/com.teburarec.call_julius/files # nl invoicename.txt
1 keyword1.wav
2 keyword2.wav
root@generic_x86:/data/user/0/com.teburarec.call_julius/files #
---------------------------------------------------


これで、JuliusLibを動作させる段取りは一通りやったつもりですが、相変わらず終了メッセージは「(意)JCONFが見つからない」です。

さて、そろそろ移植の本丸でJuliusLib側(native-lib.so)のデバッグになるわけですが、手順的に済ませられるほどスムーズには行きませんでした。

このあと約五日間の足踏み状態(進展なし)が発生。


◆ネイティブ側コード上でブレークできない
Cで書かれているnative-lib.so側のコードにブレークポイントを設定して、実動調査&デバッグしようとしたら、
デバッグ実行が止まりません。ブレークできませーん。
Javaコード側では、ブレークします。

いっち番最初にテンプレートのままビルドして、デバッグ実行したときはブレーク出来てたような気がします。
では、なぜ? 理由がさっぱり分からない。

ともかく、同様の事例を検索しました。
結構色々ヒットしますが、前提のAndroidStudio、SDK(NDK)、Cmakeとかの環境の組み合わせが色々あって、自分の使っている環境にも同様のことが言えるのかも判断できない。
なので、そこの対応策を手当たり次第にやってみました。
もう、何をやったのか全部は覚えてない。

ともかく、やった対応策で記憶と形で残っていることを上げておきます。(備忘録なんで。)
都度動作(ブレークできるかどうか)を確認していますが、最後の処置で結果的にブレークできたので、途中が不要だったのかは確認してません。

1)build.gradleに「debuggable true」を追加 △
「android{・・・buildTypes{+・・・}}」内に「debug{debuggable true}」を追加した。

読んだまま、デバッグ可能なビルドをするという意味です。
buildTypes辺りの設定を「ビルドバリアント」とか呼んでます。
「buildTypes{}」の中には既に「release{}」というタイプがデフォルトで定義されていました。
ここに単に「debug{}」と指定しても、AndroidStudio上では有効(カレント設定)になってないことを後で気付いた。


2)build.gradleに「minifyEnabled false」を追加 △
「android{・・・buildTypes{debug{+}・・・}}」内に「minifyEnabled false」を追加した。

「buildTypes{・・・release{*}}」内には既に「minifyEnabled false」が指定されていました。
ビルド後のサイズを小さくすると言う意味で、「true」にしちゃうとデバッグに必要なシンボル情報なんかが含まれなくなるみたいなことが。
なので、本来release用として指定するなら、「true」にすべきなのかもしれません。


3)使用するデバッガの設定の確認と変更&トライ △
Android本家の説明にもありましたが、デバッガにも色々あるそうで、AndroidStudio上で指定できるんだそうです。
-[Run]-[Edit Configurations]を選択
-表示されるダイアログで、[Debug]タブを選択
-「Debug Type」欄で、適切なデバッガを選択し[Apply]か[OK]
20200710_3.jpg

デフォルトで「Detect Automatically」が指定されてました。
他の選択肢(「Java Only」「Native Only」「Dual(Java+Native)」)を全部試しましたが、改善されず。
本来「Java Only」以外であればCコード上のブレークポイントで停止できるはずだが、そうならない。
結局は、「Detect Automatically」に戻した。


4)デバッグ開始時の「Debug」タブ内のコンソールメッセージ等の確認
まあ、この現象の最初の頃から眺めてはいたのですが、エラーらしいメッセージは出てません。

「Debug」タブ内には「app」と「app-java」というタブが出ます。
Javaオンリーのアプリの場合は、1つしか出ないそうです。

デバッグモードに移行したタイミングで出るコンソールメッセージはそれぞれ「Debugger attached to process xxxx」、「Connected to the target VM, address: 'localhost:・・・」と出てて、読む限りそれぞれのデバッガに正常に接続できたと読めます。何かエラーを示しているようには見えません。
「app」-「Debugger」タブにすると、「Valiables」の隣に「LLDB」タブがあって、それを選択してLLDB(デバッガ)が出力したであろうメッセージを見ても、何も引っかかる記述はなかった。(先頭に「error: module importing failed: No module named renderers.jb_lldb_declarative_formatters・・・」とあったが、Java側臭い。)

でも、デバッグモードに入るとCソース上に表示されているブレークポイント(ウォッチポイント?)のマーク()が駐車禁止マークに変わります。
20200710_4.jpg

5)シンボルフォルダの指定 ×
本来は、AndroidStudioのプロジェクト外でデバッグビルドしたようなもの?を含めてデバッグしたい場合にやる行為のように読めたのですが、念のためやってみました。
そのせいか、プロジェクトのシンボルファイルがどんな名前で、どこにあるのかはっきり分かりません。

指定の仕方は、3)で説明した[Edit Configurations]の[Debugger]タブ上の「Symbol Directories」で指定します。

「Symbol Directories」の設定例の画面が載っているところから、「\app\build\inter...」と見えたので、「(プロジェクトフォルダ)\app\build\intermediates\symbols\debug」を指定しました。

結果、何の変化も認められませんでした。
そもそもシンボルが見つからないなら、そういうエラーメッセージが出るようです。
※この指定は、取り消ししました。


6)Build Typesの設定 △
AndroidStudioの[Build]メニューの中に[Edit Build Types]というメニューがあります。

開いて見ると、ダイアログ右側の[Build Types]というタブに「debug」と「release」という選択肢が表示されました。
設定項目の中に「Debuggable」という設定項目があります。
20200710_5.jpg

どうも、app\build.gradleの記述にリンクしているみたいです。
つまり、build.gradleで「buildTypes{}」内に複数のブロックを定義すると、ここでどちらを使うか指定しないといけないような気がします。

さらに、その画面の設定項目の中に「Jni Debuggable」というのがあって、「true」にすることができます。

『あ! これ?』と思って、「Jni Debuggable」を「true」に設定して、勇んで試してみましたが・・・結局変化なしでした。残念!

※app\build.gradleの内容を確認してみたところ、果たして「jniDebuggable true」という指定が、「android{・・・buildTypes{debug{+}・・・}}」内に追加されてました。

ここまでやって行き詰まりました。こっからしばし迷走気味です。


7)「Attach Debuggaer to Android Process」をやってみる ×
AndroidStudio画面の右上のアイコンボタンが並んでいる中に「Attach Debuggaer to Android Process」というアイコン(虫の上に矢印が書かれたやつ)があります。
本来、起動済みのアプリに後付けでデバッガを接続させるような使い方のしろものらしい。

細かい操作手順は正確に覚えてないのですが、ともかくやってみました。
通常のデバッグ実行時と同じに、画面下側の[Bedug]内にタブが2つ出ました。
でも、結果は、変わりなし。


8)デバッガを諦めてログメッセージでのデバッグに切替え? ?
やり掛けましたが、JuliusLib内で多用しているfprintf()によるメッセージ出力が、実はlogcatでは見れないことが判明。
実際に出てない。

標準出力のredirect指定等、何やらしないといけないらしい。
これも少し行き詰まり気味になりかけたので、この件は先送り。
※すいまっせん、対策方法はさらに次回に先送りします。


9)新規プロジェクトを作成してブレークを試す ×
「最初はCコード上でもブレークできてたような」との曖昧な記憶があったので、新規にC++のプロジェクトを作成して、デバッグ実行してみました。
ちゃんとブレークできたら、こっちのプロジェクトに移植作業をやり直すつもりだった。

テンプレートコードのままなので、さすがにまともに動くはずと思ったのですが、さにあらず。
同じ現象が発生しました。
少なくとも、「AndroidStudio4.0でC++プロジェクトを作成した場合には、C/C++コードでブレークできない問題がある。」ということになりそう。


となると・・・途中で意図せずやったAndroidStudioのアップデート(4.0にバージョンアップ)したのが原因かも。
その条件で事例検索したが、ヒットしませんでした。


10)実機スマフォを使ってみる ×
「もしかして、エミュレータとの組み合わせに何かあるのいかも」と疑って、実機にAPKをダウンロードして実行してみることにしました。

でも、デバッガに接続するタイミングで「permission error」が発生。

・・・もう・・・めんどくさくなってこれも先送り。


11)AndroidManifest.xmlに「extractNativeLibs="true"」を追加 〇
参考:https://www.it-swarm-ja.tech/ja/android/ndk%e3%83%8d%e3%82%a4%e3%83%86%e3%82%a3%e3%83%96%e3%82%b3%e3%83%bc%e3%83%89%e3%81%a7%e3%83%96%e3%83%ac%e3%83%bc%e3%82%af%e3%83%9d%e3%82%a4%e3%83%b3%e3%83%88%e3%82%92%e5%8f%96%e5%be%97%e3%81%97%e3%80%81android-studio%e3%81%a7%e3%83%8d%e3%82%a4%e3%83%86%e3%82%a3%e3%83%96%e3%82%b3%e3%83%bc%e3%83%89%e3%82%92%e3%83%87%e3%83%90%e3%83%83%e3%82%b0%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95%e3%81%af%ef%bc%9f/1051275928/

上記のサイトには、これまでやってきた内容も含めて、色々な対策が書いてありました。
その中に、AndroidManifest.xmlで「extractNativeLibs="false"」が設定されていると、「ネイティブコードのブレークポイントが機能しない」ことがあると書いてます。

AndroidManifest.xmlを見てみると、「extractNativeLibs」自体設定されてません。
「=true」を入れてみろとあるので、半信半疑で入れて再ビルドしてデバッグを開始してみると、

止 まった・・・Cソース側のブレークポイント!
 \(ToT)/




つまり、修正すべきは、AndroidManifest.xmlとbuild.gradleだったのかと思います。
----app\src\main\AndroidManifest.xml---------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.teburarec.call_julius">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
:
:
android:theme="@style/AppTheme"
android:extractNativeLibs="true">
<activity android:name=".MainActivity">
:
:
</application>

</manifest>
---------------------------------------------------


----app\build.gradle-------------------------------
apply plugin: 'com.android.application'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.teburarec.call_julius"
minSdkVersion 23
:
:
}
buildTypes {
debug {
debuggable true
minifyEnabled false
jniDebuggable true
}

release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
:
:
}
---------------------------------------------------




約一週間ぶりにデバッグを先に進められる環境になりました。
早速、試した感じでは、
 ▼.so側に制御が渡った場合のカレントディレクトリが、java側が動作していた時とは違う?
 ▼やっぱり、JuliusLib内で多用されている標準出力へのメッセージをなんとか見えるようにせねば
が、まずは当面の片付けるべき問題です。
まだまだ先が長そうです。

想定では上記くらいは今回で片付いているはずだったのですが・・・
また次回に送ります。


では、ご機嫌よう。
(m__)m
スポンサーサイト





コメントの投稿

非公開コメント

カレンダー
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