音声合成ができる「Koeiromap」の情報を調べてみて JavaScript を使って軽く試してみる【2023年4月8日時点】

## はじめに
@yskmjp さんが enebular との組み合わせで使われている話を SNS で投稿などされていた[「Koeiromap」](http://koeiromap.rinna.jp/)が気になって、そして API を JavaScript で扱えたらと思い、技術情報周りを調べたりなどした際のメモです。

※ 見かけた投稿やツイートの一例はこちら↓

https://twitter.com/yskmjp/status/1644324344255090688

### Koeiromap の概要
まず、「Koeiromap」に関する大まかな情報を書いてみます。

:::note warn
以下の情報は、記事執筆時点（2023年4月8日時点）のものです。最新情報は、公式ページをご確認ください。
:::

- 音声合成が行えるサービス
- 公式サイト上・API経由で行える音声合成は、Koeiromap および関連するサービスの検証、開発、実用化等を目的として、期間限定で無償提供中（※ 利用に関して、注意事項あり）
   - 注意時項の 1つ ⇒ 生成された音声の利用は「非商用利用限定」
   - その他、データ利用範囲や動作保証などに関する内容があり、詳細は「Koeiromap & Koeiro APIご利用上の注意」を参照

上記の内容は[「Koeiromap のページ」](http://koeiromap.rinna.jp/)にアクセスした際に見られる、「Koeiromap & Koeiro APIご利用上の注意」の内容を見て書きました。
![Koeiromap & Koeiro APIご利用上の注意](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/5cef9875-57ac-0b56-0c7a-caa8d3f8a244.png)

### 提供元
公式の Twitterアカウントは以下で、提供元は「rinna（株）」になるようです。

●Koeiromap【正式サービス化決定】（@ koeiromap）さん / Twitter
　https://twitter.com/koeiromap

## Koeiromap の仕様
### 音声合成のパラメータに関する仕様
音声合成の仕様について、API のパラメータ仕様から内容を確認してみます。

#### 入力パラメータ
入力パラメータを見てみると、以下になるようです。
![API のパラメータ仕様（入力）](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4d2a71e0-deca-feb1-dd9e-95e4112a397f.png)

[「Koeiromap のページ」](http://koeiromap.rinna.jp/)で試す場合、上で書かれていたパラメータのうち「seed以外」が GUI上で設定できるっぽいです。
以下は、Koeiromap のページ上で text（合成したい文章）のみを指定してみた際の画面のキャプチャです。
![Koeiromap のページ上での音声合成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a5fbd602-e997-85aa-5153-6706922eb5af.png)

「speaker_x」・「speaker_y」の値や、「style（全6通り： "talk", "happy", "sad", "angry", "fear", "surprised"）」を変化させた時の合成音声への影響は、ページ上で実際に確認してみてください。

なお、音声合成を API経由で利用する場合は、パラメータの値をランダムに決める「seed」というシード値も指定できるようです。

#### 出力パラメータ
出力パラメータの仕様は、以下となるようです。
![API のパラメータ仕様（出力）](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/29322445-e99d-85e5-5c65-69ead43d373a.png)

音声合成用のデータは、「wav形式の音声データがbase64エンコードされた文字列」で返ってくるようです。

### 音声合成を API経由で試してみる
実際に、API経由で音声合成を試してみます。その場合、公式の情報を見ると POSTリクエストで扱う形のようです。

公式の APIガイドで、curl を用いた例が示されています。
![公式の APIガイド](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/28418138-47af-513d-375a-2c629e3fd856.png)

#### curl を使って試す
上で書いた、入力パラメータに関する仕様の記載によると、必須パラメータは「合成したい文章を日本語で書いた "text" のみ」のようです。

公式サンプルで、必須パラメータのみを残したコマンドは、以下になるようです。

```bash
curl --location 'https://api.rinna.co.jp/models/cttse/koeiro' --header 'Content-Type: application/json' --data '{"text": "こんにちは"}'
```

上記のコマンドを実行して、レスポンスが得られることを確認できました。

実行結果では、「wav形式の音声データがbase64エンコードされた文字列」の部分がかなり長くなりました。そのため、以下のコマンドを実行して、レスポンスをテキストに書き出し、その内容のエンコードされた文字列部分などを省略した内容を掲載してみます。

```bash:実行したコマンド
curl --location 'https://api.rinna.co.jp/models/cttse/koeiro' --header 'Content-Type: application/json' --data '{"text": "こんにちは"}' -o 【出力先のパス】/koeiro.txt
```

```json:得られたレスポンス（一部省略、少し整形）
{
  "audio": "data:audio/x-wav;base64,【base64エンコードされた文字列】",
  "phonemes": [
    "sil","k","o","N","n","i","ch","i","w","a","sil"
  ],
  "seed": 【用いられたSeed値】
}
```

なお、オプションのパラメータ「speaker_x・speaker_y・style」を指定した場合も、レスポンスとして得られる JSON の構造は、上記と同じでした。

#### JavaScript で試す
それでは、JavaScript から API を使ってみます。

とりあえず簡単に試せる方法として、ブラウザの Fetch API を使ったやり方を試します。

プログラムは、以下の必須パラメータのみを指定した curlコマンドを元に、ChatGPT（GPT-3.5版）に作ってもらいました。

```bash
curl --location 'https://api.rinna.co.jp/models/cttse/koeiro' --header 'Content-Type: application/json' --data '{"text": "こんにちは"}'
```

ChatGPT のページ上でのやりとりの様子は以下のとおりです。

![ChatGPT にプログラムを作ってもらう](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/91e12aa7-92a0-bc78-a405-7d41fe2b9e47.png)

得られた結果は以下のとおりで、そのまま動作させられるものが出てきました。

```javascript
fetch('https://api.rinna.co.jp/models/cttse/koeiro', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    text: 'こんにちは'
  })
})
  .then(response => response.json())
  .then(data => console.log(data));
```

それでは、簡単に試せるやり方で、具体的にはブラウザの開発者ツールのコンソールで実行するやり方で、レスポンスの内容を確認します。その結果、以下のように「wav形式の音声データがbase64エンコードされた文字列」が得られていることを確認できました。

https://twitter.com/youtoy/status/1644553704706809859

あとは、音声データを再生する部分を付け足します。

この処理の前提として、「単発で実行して、とりあえず試せれば良い（そして、シンプルな処理にする）」という想定をしています。
具体的には、得られたレスポンスの `audio` の部分、つまり「wav形式の音声データがbase64エンコードされた文字列」を取り出し、そのデータを再生する処理を付け加えれば OK です。

ブラウザで動かすなら、[HTMLAudioElement](https://developer.mozilla.org/ja/docs/Web/API/HTMLAudioElement) を使った、以下の処理で再生できます。

```javascript
const voice = new Audio("data:audio/x-wav;base64,【base64エンコードされた文字列】");
voice.play();
```

それを加えた内容は、以下のとおりです。

```javascript
fetch("https://api.rinna.co.jp/models/cttse/koeiro", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "こんにちは",
  }),
})
  .then((response) => response.json())
  .then((data) => {
    console.log(data.audio);
    const voice = new Audio(data.audio);
    voice.play();
  });
```

これを実行して、合成された音声が再生されることを確認できました。

## 【追記】 その後のお試し
その後、p5.js を使った実装も試してみました。
こちらでは、base64エンコードされた文字列の声データを、音として再生する部分で「[p5.MediaElement](https://p5js.org/reference/#/p5.MediaElement)」を使っています。

https://twitter.com/youtoy/status/1644572604903350272