Android(Kotlin)で標準の音声合成(TextToSpeech)を使う

#概要
Androidでは、TextToSpeechを利用することによって音声合成機能を実装することが可能です。
Android1.6からTextToSpeech自体は利用が可能でしたが、日本語は音声のモデルデータが端末に存在していない等、気軽に使えない期間も存在していました。最近は日本語の音声合成も不自由なく使える端末が出揃っています。今回はAndroid(Kotlin)でTextToSpeechを利用する方法について軽くメモします。

# 実装

音声の読み上げに必要な実装を掻い摘んでメモしていきます。

## 初期化
TextToSpeechを生成します。

```kotlin
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {

    private var textToSpeech: TextToSpeech? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        // ~省略~
        textToSpeech = TextToSpeech(this, this)
        // ~省略~
    }
```

```kotlin
        textToSpeech = TextToSpeech(this, this)
```

TextToSpeechのインスタンスを生成します。
第一引数にコンテキスト、第２引数にはListenerを渡します。
ListenerはTextToSpeech.OnInitListenerになるので、実装をしていきます。

## TextToSpeech.OnInitListener

TextToSpeech.OnInitListenerを実装していきます。

```kotlin
override fun onInit(status: Int) {
        if (status == TextToSpeech.SUCCESS) {
            textToSpeech?.let { tts ->
                val locale = Locale.JAPAN
                if (tts.isLanguageAvailable(locale) > TextToSpeech.LANG_AVAILABLE) {
                    tts.language = Locale.JAPAN
                } else {
                    // 言語の設定に失敗
                }
            }


        } else {
            // Tts init 失敗
        }
}
```

実装が必要なのはTextToSpeechのonInitメソッドです。
TextToSpeechのinitの結果がコールバックされstatusとして渡ってくるので、statusに応じて処理をしてあげます。
TextToSpeechが正常にinitされたら言語を設定します。
利用したい言語が利用できるかどうかはTextToSpeechの```isLanguageAvailable```メソッドを利用します。

```kotlin
                if (tts.isLanguageAvailable(locale) >= TextToSpeech.LANG_AVAILABLE) {
                    tts.language = Locale.JAPAN
                } else {
                    // 言語の設定に失敗
                }
```


```isLanguageAvailable``` メソッドはBoolではなくintを返すので、上記のようにTextToSpeech.LANG_AVAILABLEと返り値を比較してあげる必要があります。

ちなみに ```isLanguageAvailable ```が返す値は下記の通り。

- LANG_AVAILABLE: 0
- LANG_COUNTRY_AVAILABLE: 1
- LANG_COUNTRY_VAR_AVAILABLE: 2
- LANG_MISSING_DATA: -1
- LANG_NOT_SUPPORTED: -2

つまりLANG_AVAILABLE(0)以上であれば、指定した言語は利用が可能です。

## 読み上げ
上記で正常にinitが完了していれば、読み上げを行うことができます。
今回はサンプルとして、下記のようなメソッドを用意しました。

```kotlin

    private fun startSpeak(text: String, isImmediately: Boolean){
        textToSpeech?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "utteranceId")
    }
```

TextToSpeechはspeakメソッドを呼ぶことで読み上げができます。
引数
text: 読み上げるテキスト
queueMode: キューイングモード(QUEUE_ADDまたはQUEUE_FLUSH)
params: パラメータを渡せます。(KEY_PARAM_STREAM,KEY_PARAM_VOLUME,KEY_PARAM_PAN)
utteranceId: 固有の識別子

キューイングモードでは、QUEUE_ADDまたはQUEUE_FLUSHが選択でき、下記のような違いがあります。
- QUEUE_ADD: キューに追加され、順番に読み上げられる
- QUEUE_FLUSH: すぐに読み上げを開始

パラメータとしてbundleを渡すことができ、利用が可能なKEYは下記の4つです。
- KEY_PARAM_STREAM: オーディオストリームを変更する際に利用
- KEY_PARAM_VOLUME: 音のボリュームを調整するのに利用
- KEY_PARAM_PAN: 音の定位を調整するのに利用

従来は下記のspeakメソッドが利用されていたが、APK LEVEL 21以降はdeprecated

```
speak（文字列テキスト、int queueMode、 HashMap < String、  String > params）
```

## 読み上げのコールバックを受け取る

TextToSpeechが持っている```setOnUtteranceProgressListener```を利用することで読み上げのコールバックをハンドリングできます。(speakメソッドは非同期で動作します。)

```kotlin

        textToSpeech?.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
            override fun onDone(utteranceId: String) {
            }

            override fun onError(utteranceId: String) {

            }

            override fun onStart(utteranceId: String) {

            }
        })
```

読み上げさせる時にutteranceIdを管理しておくことで、コールバックがどの読み上げなのかを判別できます。
上記のコールバックを利用することで、読み上げ中は特定の文字を赤くするといったような実装にも活用ができます。

## shutdown

textToSpeechは使わなくなるタイミングでshutdownメソッドを呼んであげます。(ネイティブリソース解放のため)

```kotlin

    override fun onDestroy() {
        textToSpeech?.shutdown()
        super.onDestroy()
    }
```

簡単ですが、以上がAndoridでTextToSpeechを利用する際のメモになります。
AndroidでTextToSpeechを利用する方の参考になれば幸いです。