UnityでVOICEVOXをREST-API経由で利用する方法

# 概要

- UnityでREST-API経由でVOICEVOXを使用する方法の紹介

- VOICEVOXのREST-APIクライアントクラスの作成

- 実際にUnityで喋らせるためのテストスクリプトの作成

# 手順

## 【１】VOICEVOXの起動

まだ、VOICEVOXをインストールしていない場合、
以下のサイトよりVOICEVOXをインストールしましょう！

**VOICEVOXは、無料で利用できる高品質な音声合成ソフトです！**

https://voicevox.hiroshiba.jp/

VOICEVOXを起動すると、ローカルにHTTPサーバが立ち上がります。

::: note info
Docker使える方は、DockerイメージからHTTPサーバーを起動するのでもOK！
https://github.com/VOICEVOX/voicevox_engine#docker-%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8
:::

VOICEVOX起動後に以下のURLにブラウザからアクセスするとAPI仕様書が表示されます。
http://localhost:50021/docs

![VOICEVOX_docs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124829/ef30ed63-ba0e-04b1-f617-804b76003713.png)

**Docsがブラウザで表示できていれば、サーバーの立ち上げは成功しているため、REST-APIが利用できます。**

## 【２】VoiceVoxのREST-APIクライアント作成

以下のクラスを「VoiceVoxApiClient.cs」という名称でUnityに追加します。
VOICEVOXとの通信回りの処理はここに記載します。

```cs:VoiceVoxApiClient.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// VOICEVOXのREST-APIクライアント
/// </summary>
public class VoiceVoxApiClient
{
    /// <summary> 基本 URL </summary>
    private const string BASE = "localhost:50021";
    /// <summary> 音声クエリ取得 URL </summary>
    private const string AUDIO_QUERY_URL = BASE + "/audio_query";
    /// <summary> 音声合成 URL </summary>
    private const string SYNTHESIS_URL = BASE + "/synthesis";

    /// <summary> 音声クエリ（Byte配列） </summary>
    private byte[] _audioQueryBytes;
    /// <summary> 音声クエリ（Json文字列） </summary>
    private string _audioQuery;
    /// <summary> 音声クリップ </summary>
    private AudioClip _audioClip;

    /// <summary> 音声クエリ（Json文字列） </summary>
    public string AudioQuery { get => _audioQuery; }
    /// <summary> 音声クリップ </summary>
    public AudioClip AudioClip { get => _audioClip; }

    /// <summary>
    /// 指定したテキストを音声合成、AudioClipとして出力
    /// </summary>
    /// <param name="speakerId">話者ID</param>
    /// <param name="text">テキスト</param>
    /// <returns></returns>
    [Obsolete]
    public IEnumerator TextToAudioClip(int speakerId, string text)
    {
        // 音声クエリを生成
        yield return PostAudioQuery(speakerId, text);

        // 音声クエリから音声合成
        yield return PostSynthesis(speakerId, _audioQueryBytes);
    }

    /// <summary>
    /// 音声合成用のクエリ生成
    /// </summary>
    /// <param name="speakerId">話者ID</param>
    /// <param name="text">テキスト</param>
    /// <returns></returns>
    public IEnumerator PostAudioQuery(int speakerId, string text)
    {
        _audioQuery = "";
        _audioQueryBytes = null;
        // URL
        string webUrl = $"{AUDIO_QUERY_URL}?speaker={speakerId}&text={text}";
        // POST通信
        using (UnityWebRequest request = new UnityWebRequest(webUrl, "POST"))
        {
            request.downloadHandler = new DownloadHandlerBuffer();
            // リクエスト（レスポンスがあるまで待機）
            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.ConnectionError)
            {
                // 接続エラー
                Debug.Log("AudioQuery:" + request.error);
            }
            else
            {
                if (request.responseCode == 200)
                {
                    // リクエスト成功
                    _audioQuery = request.downloadHandler.text;
                    _audioQueryBytes = request.downloadHandler.data;
                    Debug.Log("AudioQuery:" + request.downloadHandler.text);
                }
                else
                {
                    // リクエスト失敗
                    Debug.Log("AudioQuery:" + request.responseCode);
                }
            }
        }
    }

    /// <summary>
    /// 音声合成
    /// </summary>
    /// <param name="speakerID">話者ID</param>
    /// <param name="audioQuery">音声クエリ</param>
    /// <returns></returns>
    [Obsolete]
    public IEnumerator PostSynthesis(int speakerID, string audioQuery)
    {
        return PostSynthesis(speakerID, Encoding.UTF8.GetBytes(audioQuery));
    }

    /// <summary>
    /// 音声合成
    /// </summary>
    /// <param name="speakerId">話者ID</param>
    /// <param name="audioQuery">音声クエリ(Byte配列)</param>
    /// <returns></returns>
    [Obsolete]
    private IEnumerator PostSynthesis(int speakerId, byte[] audioQuery)
    {
        _audioClip = null;
        // URL
        string webUrl = $"{SYNTHESIS_URL}?speaker={speakerId}";
        // ヘッダー情報
        Dictionary<string, string> headers = new Dictionary<string, string>();
        headers.Add("Content-Type", "application/json");

        using (WWW www = new WWW(webUrl, audioQuery, headers))
        {
            // レスポンスが返るまで待機
            yield return www;

            if (!string.IsNullOrEmpty(www.error))
            {
                // エラー
                Debug.Log("Synthesis : " + www.error);
            }
            else
            {
                // レスポンス結果をAudioClipで取得
                _audioClip = www.GetAudioClip(false, false, AudioType.WAV);
            }
        }
    }
}
```

::: note info
「WWW」クラスは最新版のUnityでは非推奨で、
「UnityWebRequestMultimedia」クラスを使用するのを推奨している。

ただ、「UnityWebRequestMultimedia」クラスはGET通信のみしか対応していなかったため今回は非採用。
https://docs.unity3d.com/ja/2018.4/ScriptReference/Networking.UnityWebRequestMultimedia.GetAudioClip.html
:::

## 【３】テスト用のスクリプト作成

ひとまず、Unityでテスト実行したいので
アプリ実行時に指定した文章を喋るC#スクリプトを作成しましょう！

以下はアプリ実行時に「春日部つむぎ」さんに「こんにちは！みんなもUnityでVOICEVOXを使おう！」と喋らせるコードです。

### VoiceVoxTest.cs

```cs
using System.Collections;
using UnityEngine;

public class VoiceVoxTest : MonoBehaviour
{
    [SerializeField]
    private AudioSource _audioSource;

    void Start()
    {
        StartCoroutine(SpeakTest("こんにちは！みんなもUnityでVOICEVOXを使おう！"));
    }

    IEnumerator SpeakTest (string text)
    {
        // VOICEVOXのREST-APIクライアント
        VoiceVoxApiClient client = new VoiceVoxApiClient();

        // テキストからAudioClipを生成（話者は「8:春日部つむぎ」）
        yield return client.TextToAudioClip(8, text);

        if (client.AudioClip != null)
        {
            // AudioClipを取得し、AudioSourceにアタッチ
            _audioSource.clip = client.AudioClip;
            // AudioSourceで再生
            _audioSource.Play();
        }
    }
}
```

## 【４】Unityに空のGameObjectを用意

**ここからはScriptのアタッチ方法なので、Unityに詳しい方は読まなくても大丈夫です！**

空のGameObjectを用意
※ [Hierarchy] で右クリック [Create Empty]から作成できます。

ここでは **VoiceVoxTest** という名称のGameObjectを用意

![GameObject追加_VoiceVoxTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124829/ec9516a4-a952-93f7-0465-d4458c360d58.png)

## 【５】コンポーネントを追加

1. 用意した **VoiceVoxTest(GameObject)** に **VoiceVoxTest(Script)** をアタッチ

1. **VoiceVoxTest(GameObject)** にAudioSourceをアタッチ（Add Componentから追加できます）

1. アタッチした **VoiceVoxTest(Script)** のフィールド [AudioSource] に [2.] で用意したAudioSourceをアタッチ

![Inspector_VoiceVoxTest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124829/d9617212-7ef8-5999-1d15-d6a1cbe842e1.png)

以下に一連の流れをGIFアニメ化しました。

![Unity_VoiceVoxアタッチ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124829/fd76ee14-0584-ca89-85a7-95a23d41f240.gif)

## 【６】実行！

アプリ実行してみて、
「こんにちは！みんなもUnityでVOICEVOXを使おう！」と音声が聞こえれば成功です！

# 補足

### 話者IDって何？

話者を決めるIDです。
この話者IDの定義はVOICEVOXのREST-API経由で取得できます。

**VOICEVOXを起動して、ブラウザから以下のURLにアクセスすると閲覧できます。**

http://localhost:50021/speakers

json形式で取得するため、別途ブラウザのプラグインなどを用意するとよいかと思います。
（以下Chromeのプラグイン）

https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa

以下から「春日部つむぎ（ノーマル）」の話者IDが「8」であることが分かります。
![VoiceVox_Speakers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3124829/8363f42f-4993-d46e-0599-a01bc0394c1f.png)

# 最後に

今回はUnityでVOICEVOXを使用する手順を簡単に紹介しました。

サンプルコードではアプリ起動時に喋る処理にしていますが、UGUIのInputFieldからテキストを取得、ButtonのOnClickイベントでVOICEVOXのAPIを叩くようにすればチャットアプリみたいなのも作れるかと思います。

また、VOICEVOXを使用する場合は必ず利用規約を守り、クレジット表記するようにしましょう！

https://voicevox.hiroshiba.jp/term/

# 参考サイト

https://qiita.com/A_T_B/items/1531d78944d8b796b9fa

https://docs.unity3d.com/ja/2019.4/ScriptReference/Networking.UnityWebRequest.Post.html