Docomo音声合成APIをSDKなしで利用する [Android]

# 概要

※Docomo Developer Suport経由の音声合成API(エーアイ社提供は終了しました)

日頃、個人開発でDocomo Developer Supportにて提供をされている[音声合成API(エーアイ社提供)](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=text_to_speech&p_name=api_usage_scenario)にお世話になっているのですが、昨年12月こんな新着情報が。。

<img width="608" alt="スクリーンショット 2019-03-31 1.32.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/66341/4f15994c-ebc3-5b46-1fd2-4b9be9b7317f.png">

2019年3月よりSDKを利用していたコードは動かなくなるとのことでした。
それに伴って既存のSDKを利用していたAndroidのソースコードを改修したので、その時のメモです。

※2019.12月追記: Docomo Developer Support経由の音声合成API(エーアイ社提供)はすでに終了しています。

# 実装
## 必要なもの
・Docomo Developer Supportへの登録
・アプリケーション登録(API KEYの入手)
・ガイドラインの確認

## 仕様
必要な情報は[機能別リファレンス](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=text_to_speech&p_name=api_1#tag01)に一通り記載があります。

これまで提供されていたSDKは、主に
・音声合成APIとの通信処理
・レスポンスの再生処理
を行なっていたので、自前でその機能を用意してやります。

## 通信部分の処理

OkHttp3を使っている利用した前提でのメモになります。

#### リクエスト

```kotlin

            Request.Builder()
                    .url(ApiUrls.AI_TALK_API_URL + "?APIKEY=$apiKey")
                    .addHeader("Content-Type", "application/ssml+xml")
                    .addHeader("Accept", "audio/L16")
                    .post(createRequestBody()) // 後述
                    .build()
```
リクエストで指定するのは、ApiKey、Content-Type、Accecptの３点です。
Content-Typeに関しては機能別リファレンスを確認して、利用するContent-Typeを指定しましょう。
今回は「application/ssml+xml」を指定しています。

リクエストのBodyについては下記。

```kotlin
    private fun createRequestBody(text: String): RequestBody =
            RequestBody.create(
                    MediaType.parse("application/ssml+xml"),
                    "<?xml version=\"1.0\" encoding=\"utf-8\" ?><speak version=\"1.1\"><voice name=\"nozomi\">$text</voice></speak>"
            )
```
Bodyに必要なのは、MediaTypeとSSMLの中身。
SSMLは直書きしていますが、実運用する際は記述を工夫した方が良いです。textは読み上げたい文字列です。

※SSMLとは、音声合成用のマークアップ言語のことで、XML形式で読み上げのスピードやピッチ等を指定することができます。
https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=text_to_speech&p_name=api_1#tag01


#### レスポンスの処理
上記のリクエストを行うと音声データがBinary形式でレスポンスが返ってきます。(Acceptにaudio/L16を指定した場合)
音声データは下記のフォーマットになっているので、AudioTrackを対応したパラメータを指定して生成してやれば、再生が可能です。

【符号化方式】
リニアPCM
【チャネル数】
1(モノラル)
【サンプル周波数】
16000
【ビット深度】
16bit(ビッグエンディアン)

```kotlin
                val body = response.body().bytes()
                convertByteOrder16(body)
                val bufSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)
                val player = AudioTrack.Builder()
                        .setAudioFormat(AudioFormat.Builder()
                                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                                .setSampleRate(16000)
                                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                                .build())
                        .setBufferSizeInBytes(bufSize)
                        .build()

```
途中登場している「convertByteOrder16(body)」は、AudioTrackで再生をするためにビッグエンディアンをリトルエンディアンに変換しています。

```kotlin
fun convertByteOrder16(var1: ByteArray?) {
        if (var1 == null) {
            throw IllegalArgumentException("Input Parameter is Invalid.")
        } else {
            var var3 = 0
            while (var3 < var1.size) {
                val var2 = var1[var3 + 1]
                var1[var3 + 1] = var1[var3]
                var1[var3] = var2
                var3 += 2
            }

        }
    }
```

あとはAudioTrack再生するだけです。

``` kotlin
   player.play()
   player.write(body, 0, body.size)
```

以上、細かいところをかい摘んでのメモではありますが、Docomo音声合成APIをSDKなしで利用した際の記録でした。