UnityでMicrosoft Cognitive Speech Servicesによる音声認識をAndroidスマホで実装する

Androidスマホで動作するUnityの３Dアプリに音声認識をさせる必要が生じたので、簡単なサンプルを作るところから始めました。音声認識には「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 の無料アカウントを作ってみた](
https://qiita.com/shinyay/items/a6106936b4a640ab0dc4)」

## Cognitive Servicesのプロジェクトを作る
「[Azure Cognitive ServicesのSpeech to Textで書き起こしをしてみよう](
https://qiita.com/yamachu/items/dda2624abbfe4364328a)」の「Cognitive Servicesのプロジェクトを作る」を参考にします。
ただし、「S0」で作っているけど、無料の「F0」がいいと思います。Nameは、これから作るプロジェクトに合わせて「csharp-unity」にしておきます。

#(2) Unity 用 Speech SDKのインストール
「[クイック スタート:開発環境を設定する](https://docs.microsoft.com/ja-jp/azure/cognitive-services/speech-service/quickstarts/setup-platform?tabs=unity%2Cwindows%2Cjre&pivots=programming-language-csharp)」を参考に、Unity 用 Speech SDKをダウンロードします。パッケージのインポートはプロジェクトを作ってからやるので、とりあえずダウンロードだけでOKです。

#(3) Unityでプロジェクトを作成する
「[クイック スタート:マイクから音声を認識する](https://docs.microsoft.com/ja-jp/azure/cognitive-services/speech-service/quickstarts/speech-to-text-from-microphone?tabs=unity%2Cx-android%2Clinux%2Cjava-runtime&pivots=programming-language-csharp)」を参考にUnityのプロジェクトを作ります。

サイトの、「サンプル コードを追加する」項目の「5. YourSubscriptionKey という文字列を探し、実際の Speech サービスのサブスクリプション キーに置き換えます。」で使うキーは**(1)で作ったKey1**をコピーします。また、リージョンは「東日本」を選んでいれば**「japaneast」**になります。

プロジェクトを作ったら、上記「(2) Unity 用 Speech SDKのインストール」でダウンロードした「Speech SDK」をダブルクリックして展開し、「import」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222292/afa6bbf4-d69a-7f53-bbea-4172587da38d.png)

#(4) PCで実行確認
とりあえず、PCで実行確認すると、ちゃんとSpeech Serviceが機能して音声入力できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222292/48b07052-83a1-0e5e-716b-039257147b04.png)

#(5) Android端末で実行確認（失敗）
ここでハマりました。「connection failed (no connection to the remote host)・・・」なんてエラーが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222292/e0033e9b-a4e2-80cf-c454-559b0534b6cc.png)

要はこのプログラムはインターネットのアクセス許可がないようです。Androidで動かしているので、とりあえずAndroidManifestを探します。ファイル検索すると「temp」の中にありました。開いてみるとインターネット許可のパーミッションがありません。
「[Unity ドキュメントのAndroid マニフェストの説明](https://docs.unity3d.com/jp/current/Manual/android-manifest.html)」によると、「Unity は適切なマニフェストを生成しますが、そのコンテンツを直接制御したい場合もあるでしょう。Unity 以外で作成した Android マニフェストを使用するには、以下の場所にその Android マニフェストファイルを置いてください。 Assets/Plugins/Android/AndroidManifest.xml 。こうすることによって、デフォルトの Unity が作成したマニフェストをオーバーライドすることができます。」とあるので、「csharp-unity\Temp\StagingArea」の中にあったAndroidManifest.xmlをコピーして
```<uses-permission android:name="android.permission.INTERNET" />
  <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端末で実行確認（成功）
今度は無事に動きました。めでたし、めでたし。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222292/a8a13a0a-1d41-9602-3897-070c4f3895e9.png)