AndoirdからGoogle Cloud Text To Speech を使ってみる【音声を合成する】

# 概要
タイトルの通り、Google Cloud Text To Speech(以下、Google Cloud TTS)をAndroidアプリから利用する方法についてメモします。Androidから軽くGoogleCloudTTSを使ってみたい機会があったので、サンプルを実装してみた内容を書いています。

# Google Cloud Text To Speechとは？
https://cloud.google.com/text-to-speech/?hl=ja
Googleが提供するテキストを音声に変換してくれるサービス。
[アプリで利用できる音声合成](https://qiita.com/maKunugi/items/90cbefe97887470fb328)でもちょっと触れています。

# アプリ用SDK等は提供されていない
アプリから直接利用するためのSDK等は提供されていないようです。
(公式DocにKotlinやSwiftのサンプルはなし）
それもそのはず、実プロダクトでiOS/Androidアプリから直接利用されるケースはあまり想定されておらず、サーバサイドでの利用を想定されているからだと思います。

実プロダクトでアプリ等から直接利用するのは、セキュリティ面、使い勝手面、料金面等から考えてもメリットは考えられず、下記のDocumentにあるようなCloud Storageを介した方法が適していると思います。

[Android アプリに音声翻訳を追加する](https://cloud.google.com/solutions/mobile/speech-translation-android-microservice?hl=ja)

ただ、とりあえず手軽にアプリからGoogle Cloud TTSを利用してみたりする際に上記のサンプルにあるような構成を用意するのは多少手間だったりするので、今回はサクッとAndroidアプリから試せる方法を紹介します。上述の理由から実プロダクトでこの方法を利用するのはオススメしません。

# 仕様
アプリから利用する方法を紹介するに際して、まずは軽く利用するAPIの仕様を紹介します。
Google Cloud上で「Cloud Text Text-to-Speech」を有効にし、API Keyを取得ずみであることを前提に進めます。
※ API Keyで利用することもセキュリティ上好ましくないですが、「実プロダクトではなくサクッと試したい」というケースであるため利用しています。

Google Cloud Tts はREST APIを提供しています。
https://cloud.google.com/text-to-speech/docs/apis?hl=ja

下記の音声合成エンドポイントをPOSTすることで、Text-to-Speechを実行したレスポンスを返してくれます。
```
/v1beta1/text:synthesize
/v1/text:synthesize
```
v1beta1とv1がありますが、リクエスト、レスポンスの仕様に差異はないです。

## リクエスト

リクエストには下記のJsonをBodyに含めます。

```
{
  "input": {
    object (SynthesisInput)
  },
  "voice": {
    object (VoiceSelectionParams)
  },
  "audioConfig": {
    object (AudioConfig)
  }
}
```

SyntesisInput: 合成する対象となる文字列が入ります。(SSMLも可)
VoiceSelectionParams: 言語や声の指定等ができます
AudioConfig: 下記のようなAudioの設定ができます。

```
{
  "audioEncoding": enum (AudioEncoding),
  "speakingRate": number,
  "pitch": number,
  "volumeGainDb": number,
  "sampleRateHertz": number,
  "effectsProfileId": [
    string
  ]
}
```

## レスポンス

```
{
  "audioContent": string
}
```

レスポンスは上記のようなJsonが返されます。
audioContentに格納されてくる文字列はbase64エンコードされたAudioデータです。
これをAndroidではMediaPlayer等に食わせることで音声を再生できます。


## 実装方法

Android(Kotlin)でサンプルを実装をしてみたので紹介します。

利用しているライブラリ
- okhttp3
- gson
- coroutine

1. 文字列を入力
2. 入力された文字列を使って音声合成APIにリクエスト
3. レスポンス(音声)を再生する

想定するのは上記の1~3です。
１は特に説明することはないので、2から説明していきます。（全体像についてはこの記事の一番したにあるGithubリポジトリからご確認ください。）

それでは、音声合成APIにPOSTするリクエストを作っていきます。


```
{
  "input": {
    object (SynthesisInput)
  },
  "voice": {
    object (VoiceSelectionParams)
  },
  "audioConfig": {
    object (AudioConfig)
  }
}
```

こんな感じのJsonを送りたいので、下記のようなオブジェクトを用意します。

```kotlin

    data class RequestParams(
        val input: SynthesisInput,
        val voice: VoiceSelectionParams,
        val audioConfig: AudioConfig
    )

    data class SynthesisInput(
        val text: String
    )

    data class VoiceSelectionParams(
        val languageCode: String
    )

    data class AudioConfig(
        val audioEncoding: String
    )
```
※各項目は今回使いたい最小限の項目だけ入れてあります。
SynthesisInputには読み上げたい文字列が入ります。

上記のオブジェクトを使ってリクエストを生成していきます。

```kotlin


    private fun createRequest(text: String) = Request.Builder()
            .url(url)
            .addHeader("X-Goog-Api-Key", apiKey)
            .addHeader("Content-Type", "application/json; charset=utf-8")
            .post(createRequestBody(text))
            .build()

    private fun createRequestBody(text: String): RequestBody {
        val requestParams = RequestParams(
            input = SynthesisInput(text = text),
            voice = VoiceSelectionParams(languageCode = "en-US"),
            audioConfig = AudioConfig(audioEncoding = "LINEAR16")
        )
        val json = Gson().toJson(requestParams)
        return RequestBody.create(
            MediaType.parse("application/json; charset=utf-8"),
            json
        )
    }
```
リクエストのheaderにはAPI KeyとContent Typeを指定してあげます。
Urlは今回のサンプルでは下記
```
https://texttospeech.googleapis.com/v1beta1/text:synthesize
```
リクエストボディには先ほど用意したオブジェクトをGsonでjsonに変換して追加しています。

リクエストが作成できたので、APIを叩いて音声データを取得し、再生します。

```kotlin

        async(Dispatchers.Default) {
            OkHttpClient().newCall(createRequest(text)).execute()
        }.await().let { response ->
            if (response.isSuccessful) {
                response.body()?.string().let { body ->
                    val audioResponse = Gson().fromJson(
                        body,
                        AudioResponse::class.java
                    )
                    playAudio(audioResponse)
                }
            }
        }
```

正常にレスポンスが返されるとbase64エンコードされたAudioデータが返されるので、Parseして再生します。
サンプルではGsonでParseするために下記のオブジェクトを用意しています。

```kotlin

    data class AudioResponse(
        val audioContent: String?
    )
```

最後に、取得したレスポンスを再生します。(今回はMediaPlayerを使っています)

```kotlin

    private fun playAudio(audioResponse: AudioResponse) {
        try {
            val url = "data:audio/mp3;base64,${audioResponse.audioContent}"
            mediaPlayer = MediaPlayer().apply {
                setDataSource(url)
                prepare()
                start()
            }
        } catch (IoEx: IOException) {
            // TODO: error handling
        }

    }
```
とにかく再生したいだけなのでコールバック等は何も考えていませんが、上記の方法でGoogle Cloud TTS の音声合成をAndroidアプリ上から試すことが可能です。アプリからサクッと試してみたい方はぜひ参考にしていただけると幸いです。



今回書いたサンプルは下記のGithubリポジトリに置いてあります
https://github.com/k-masashi/GoogleCloudTtsAndroidSample