AndroidのSpeechRecognizerとMLKit Translateを使ってオンデバイスでリアルタイムスピーチ翻訳をUnityのプラグインとして実装してみた
ソースパッケージはGistで公開
概要はGistのREADMEに書いたので、ここでは作ってみて思ったことを書き残しておく
Unityプラグイン実装
よく見る記事ではAndroidネイティブAPIをUnityプラグインにする場合、AndroidStudio使って.aarにしてUnityのPlugins/Androidに置くとか書いてあるのが多いが、ものすごくめんどくさいので直接ネイティブソースを置いてUnityのgradleでビルドするようにした。
ソースファイルだけではだめで、AndroidManifest.xml、gradleTemplate.properties、mainTemplate.gradleファイルが必要。これらのファイルは、Android Player Settings>Publishing SettingsのBuildで、Custom Main Manifest、Custom Main Gradle Template、Custom Gradle Properties Templateにチェックを入れると自動的にPlugins/Androidにファイルが作成される。よくUnityインストールフォルダの奥深くから、これらのファイルを自分でコピーしてくるようなことを書いてある記事を見かけるが、そんな必要はない。
AndroidManifest.xmlに
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
gradleTemplate.propertiesに
android.useAndroidX=true
mainTemplate.gradleのdependenciesに
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:18.0.2'
implementation 'com.google.mlkit:language-id:17.0.4'
implementation 'com.google.mlkit:translate:17.0.1'
を追記。translateバージョンは17.0.1を入れたがその時点の最新を入れればいいと思う。
GistのunityPackageにはこれらのファイルは含まれている。
因みに、Unityのバージョンは2021.3.13f1を使用。2020.3.xxでもビルド可能だけど、Gradleなどのツールチェーンが今のAndroidStudioに比べかなり古いのでできるだけ新しいUnityバージョンを使うのがよさそう。2021.3.13f1で出力したAndroidProjectをAndroidStudioで開いてビルドしても問題なくビルドできた。
API Level
Player Settings>Other Settings:IdentificationのMinimum API LevelとTarget API Levelだけど、Minimumには24、TargetはHighest(30)を指定した。
SpeechRecognizer
品質はどうやらOSバージョンに強く依存するようだ。OS9くらいだとめちゃめちゃで使い物にならない。OS12だとまあ使える。OS13だともっといいかもしれない。
1回認識するとリスナーが終わってしまうので継続させるためには再度リスナー起動する必要がある。これが結構厄介。onResultsとonErrorイベント受けたタイミングでリスナー再起動している。本来ならonEndOfSpeechのタイミングが適切なのでは?と思うが、ここだといまいち安定しない。時々ERROR_CLIENTが出る。
何もしゃべらなかったとき、ERROR_SPEECH_TIMEOUTになるかなと思いきやERROR_NO_MATCHが出る。バグってない?と思ってしまう。
EXTRA_MAX_RESULTSで結果の最大数設定できるので1と設定しているが、結果が1つだけになるのはよっぽど自信があるときだけ。微妙な時は2~4つ候補が返ってくる。どんな仕様?
EXTRA_BIASING_STRINGSの使い方がさっぱり分からない。ドキュメントには
Optional list of strings, towards which the recognizer should bias the recognition results. These are separate from the device context.
と書かれているだけ。探しても具体的な設定方法とか効果とかがさっぱり分からない。
この設定で誤認識を少しは改善できるのでは?と思っているのだが。。。
onRmsChangedイベントって何?って思ったけど、簡単に言うと音量の事。rmsはRoot Mean Squareの略で一定時間内の平均音量ってこと。今音声拾ってますよというレベルメーターに使えるデータをずーっと返してくる。0..4はノイズ成分ぽいので捨ててます。
音声文字変換&音検知通知アプリもそうだけど、、
Pixel6に標準搭載された音声文字変換アプリに単語登録機能があるが、何入れても何の効果も感じられない。この機能がひょっとしてEXTRA_BIASING_STRINGSを使ってる部分かな?知らんけど。
結局EndToEndなんだよなぁ。学習モデルによるIN/OUTなので、しゃべる>直接日本語テキストが出てくる。出力結果に変化を与えたいと思ったら再学習させるしかないのだよね。
しゃべる>ひらがな認識>日本語入力エンジン>日本語テキストなら、途中のIME段階で辞書学習させれば誤認識も改善できそうなものだが。
固有名詞の誤認識を直したいときは、Unity上でonResultsで得られた文字列に対して特定の文字列があったら正しい文字列に直す、みたいな方法しか取れないかな。