Androidスマホで動作するUnityの3Dアプリに音声認識をさせる必要が生じたので、簡単なサンプルを作るところから始めました。音声認識には「Microsoft Cognitive Speech Services」を使うことにしました。ちょっとハマったので、備忘録も兼ねて書いておきます。
#開発環境
Windows 10
Unity Ver 2019.2.17f1
Android 9.0 (Galaxy Note9)
#(1)Microsoft Cognitive Speech Servicesの準備
##Microsoft Azure の無料アカウントを作成
まず、Microsoft Azure の無料アカウントを作ります。ちょっと古いけど、この辺が参考になると思います。
「Microsoft Azure の無料アカウントを作ってみた」
Cognitive Servicesのプロジェクトを作る
「Azure Cognitive ServicesのSpeech to Textで書き起こしをしてみよう」の「Cognitive Servicesのプロジェクトを作る」を参考にします。
ただし、「S0」で作っているけど、無料の「F0」がいいと思います。Nameは、これから作るプロジェクトに合わせて「csharp-unity」にしておきます。
#(2) Unity 用 Speech SDKのインストール
「クイック スタート:開発環境を設定する」を参考に、Unity 用 Speech SDKをダウンロードします。パッケージのインポートはプロジェクトを作ってからやるので、とりあえずダウンロードだけでOKです。
#(3) Unityでプロジェクトを作成する
「クイック スタート:マイクから音声を認識する」を参考にUnityのプロジェクトを作ります。
サイトの、「サンプル コードを追加する」項目の「5. YourSubscriptionKey という文字列を探し、実際の Speech サービスのサブスクリプション キーに置き換えます。」で使うキーは**(1)で作ったKey1をコピーします。また、リージョンは「東日本」を選んでいれば「japaneast」**になります。
プロジェクトを作ったら、上記「(2) Unity 用 Speech SDKのインストール」でダウンロードした「Speech SDK」をダブルクリックして展開し、「import」します。
#(4) PCで実行確認
とりあえず、PCで実行確認すると、ちゃんとSpeech Serviceが機能して音声入力できました。
#(5) Android端末で実行確認(失敗)
ここでハマりました。「connection failed (no connection to the remote host)・・・」なんてエラーが出ます。
要はこのプログラムはインターネットのアクセス許可がないようです。Androidで動かしているので、とりあえずAndroidManifestを探します。ファイル検索すると「temp」の中にありました。開いてみるとインターネット許可のパーミッションがありません。
「Unity ドキュメントのAndroid マニフェストの説明」によると、「Unity は適切なマニフェストを生成しますが、そのコンテンツを直接制御したい場合もあるでしょう。Unity 以外で作成した Android マニフェストを使用するには、以下の場所にその Android マニフェストファイルを置いてください。 Assets/Plugins/Android/AndroidManifest.xml 。こうすることによって、デフォルトの Unity が作成したマニフェストをオーバーライドすることができます。」とあるので、「csharp-unity\Temp\StagingArea」の中にあったAndroidManifest.xmlをコピーして
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />```
を追加してみましたが駄目でした。
でも、このページを再度読むと、
「パーミッション
Unityは、Android Player 設定と、アプリケーションがスクリプトから呼び出す Unity API に基づいて、マニフェストに必要なパーミッションを自動的に加えます。以下はその例です。
・Network クラスにより INTERNET パーミッションを加えます。」
とあります。つまり、「Network クラスのメソッドをなんか使えばいいんじゃね?」ということで、HelloWorld.csの62行目あたりを次のように編集してみました。
```C#:HelloWorld.csの62行目あたり
else if (result.Reason == ResultReason.Canceled)
{
var internet = Application.internetReachability;
var cancellation = CancellationDetails.FromResult(result);
newMessage = $"CANCELED: Reason={cancellation.Reason} ErrorDetails={cancellation.ErrorDetails} {internet}";
}
#(6) Android端末で実行確認(成功)
今度は無事に動きました。めでたし、めでたし。