こんにちは!
オルターブースでエンジニアをしているはっしーです!
最近、社内部活動でバンドが結成され、ギターを始めました!!
初めてのバンド練習でウキウキで弾いていたところ、無事に不協和音認定をいただきました!
和音にはなっていた?みたいで良かったです。(たぶん違う)
イントロダクション
概要
UnityでChatGPTから得た回答をrinnaAPIを使用して音声で出力する方法を解説します!
対象
- Unityで3Dキャラクターをかわいくおしゃべりさせたい方
- リアルタイムでテキストから音声を作成したい方
- rinnaAPIに興味がある方
やること
- rinnaAPIの使用方法の説明
- UnityからrinnaAPIを使用してテキストを音声として出力する実装の解説
やらないこと
- ChatGPTの説明
- Uintyの使い方の説明
- UnityからChatGPT APIを使用する方法の解説
- Unityでテキストを入力する方法の解説
- Unityで音声を再生する方法の解説
着手動機と課題
UnityでChatGPTから得た回答を3Dキャラクターにかわいく読み上げてほしいと思ったので着手してみました。 そこでまず、以下の記事を参考にUnityでChatGPT APIを使用できるようにしました。
次に取得したメッセージを音声に変換したいと思い、以下のような条件で音声変換の方法を探しました。
- WebAPI
- かわいい声に変換できること
- 無料で試せること
しかし上記を満たし、手軽に使用できるものがなかなか見つかりませんでした。
始めはCoeFont APIを考えていたのですが、APIの利用はEnterpriseプランからとのことで断念。
CoeFont (コエフォント) : ナレーション ゲーム 動画制作 オーディオブック制作 …
Azure Cognitive ServiceのText to Speechで、音声をカスタムして使用しようと考えていました。 カスタム音声の作り方がなにやら難しそうでした。
テキスト読み上げの概要 - 音声サービス - Azure Cognitive Services | Microsoft Learn
解決策
そんなときに、弊社の松村さんがrinnaAPIを紹介してくれました。
rinnaAPIとは
rinnaAPIは、rinna社が開発した言語モデルを使用できるサービスです。
rinna社にはAIりんなというAIキャラクターがいます。
AIりんなはYouTubeなど配信を行っています。
www.youtube.com
rinnaAPIでは、このキャラクターの声に近い(もしくは同じ?)かわいい音声を使用することができます。
しかも、APIはひと月あたり1000リクエストまで無料で使用できます。(2023/05/01現在)
まさに、前項で挙げた条件を満たすAPIでした! 松村さんありがとうございます!
実装の手順
手順
- rinna Developers アカウントの作成
- デベロッパーコンソールで試してみる
- UnityからrinnaAPIにリクエストを投げる処理を実装
- Unityで音声を再生する
1. rinna Developers アカウントの作成
サインインページにアクセスします。
https://developers.rinna.co.jp/signin
Sign In with Rinna IDを選択します。サインインポップアップが表示されます。
今すぐサインアップを選択します。
画面に従いユーザー登録を行います。
ユーザー登録が完了したら、サインインをします。
2. デベロッパーコンソールで試してみる
アカウントを作成できたら、Text To Speech API v2を試してみます。 ホーム画面にアクセスします。
ホーム画面から次のように選択していきます。 Our APIs => Free => Text To Speech API - v2
Text To Speech API - v2への直接のリンクはコチラです。
では、さっそくAPIを試してみましょう!
画面から Try it を選択します。
APIに試すための画面が表示されます。 下スクロールして、Sendボタンを選択します。
返ってきたレスポンスの mediaContentUrl のURLにブラウザなどでアクセスすると音声ファイルを取得することができます。
リクエストBodyの textプロパティの文字を変えることで任意の音声を取得できます。
3. UnityからrinnaAPIにリクエストを投げる処理を実装
今回Unityのバージョンは2021.3.18.f1を使用しました。 実装コードはコチラです。
RinnaAPIConnection.cs
using Cysharp.Threading.Tasks; using System; using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.Networking; namespace AAA.Rinna { public class RinnaAPIConnection { private readonly string _subscriptionKey; public RinnaAPIConnection(string subscriptionKey) { _subscriptionKey = subscriptionKey; } /// <summary> /// rinnaAPIでテキストを音声に変換する /// </summary> /// <param name="textMessage">変換したいテキスト</param> /// <returns>rinnaAPIレスポンスモデル</returns> public async UniTask<RinnaAPIResponseModel> RequestAsync(string textMessage) { // 音声生成rinnaAPIのエンドポイントを設定 var apiUrl = "https://api.rinna.co.jp/models/cttse/v2"; // rinnaAPIに送信するリクエストの準備 var options = new RinnaAPIRequestModel() { sid = 27, tid = 1, speed = 1.0, text = textMessage, volume = 10.0, format = "wav" }; Debug.Log("RinnAPIに送信するテキスト: " + textMessage); // リクエストをJSONに変換 var jsonOptions = JsonUtility.ToJson(options); var postData = Encoding.UTF8.GetBytes(jsonOptions); Debug.Log("RinnAPIに送信するリクエスト: " + jsonOptions); // リクエストを作成 using var request = new UnityWebRequest(apiUrl, "POST") { uploadHandler = new UploadHandlerRaw(postData), downloadHandler = new DownloadHandlerBuffer() }; //rinnaAPIのAPIリクエストに必要なヘッダー情報を設定 var headers = new Dictionary<string, string> { {"Content-type", "application/json"}, {"Cache-Control", "no-cache"}, {"Ocp-Apim-Subscription-Key", _subscriptionKey}, }; foreach (var header in headers) { request.SetRequestHeader(header.Key, header.Value); } // rinnaAPIにAPIリクエストを送信し、結果を変数に格納 await request.SendWebRequest(); if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError) { Debug.LogError(request.error); throw new Exception(); } else { var responseString = request.downloadHandler.text; var responseObject = JsonUtility.FromJson<RinnaAPIResponseModel>(responseString); var responseMediaURL = responseObject.mediaContentUrl; Debug.Log("作成された音声のURL:" + responseMediaURL); return responseObject; } } } /// <summary> /// rinnaAPIのリクエストモデル /// </summary> [Serializable] public class RinnaAPIRequestModel { public int sid; public int tid; public double speed; public string text; public double volume; public string format; } /// <summary> /// rinnaAPIのレスポンスモデル /// </summary> [Serializable] public class RinnaAPIResponseModel { public string mediaContentUrl; public string type; } }
AudioManager.cs
using System.Collections; using UnityEngine; using AAA.Rinna; using UnityEngine.Networking; using System; public class TestManager : MonoBehaviour { AudioSource audioSource; // Start is called before the first frame update void Start() { audioSource = GameObject.Find("Audio Source").GetComponent<AudioSource>(); PlayAudio(); } public async void PlayAudio() { // rinnaAPIで音声を作成する var responceMessage = "りんなエーピーアイをお試し中!"; // ここにChatGPTからの返答をいれてあげると良い var rinnaAPIConnection = new RinnaAPIConnection({ご自身のサブスクリプションキー}); var resRinna = await rinnaAPIConnection.RequestAsync(responceMessage); var audioUrl = resRinna.mediaContentUrl; // urlから音声を取得し、再生する StartCoroutine(SoundAudioClip(audioUrl)); Debug.Log("生成された音声が再生されました。"); } IEnumerator SoundAudioClip(string url) { // UnityWebRequest を作成 var www = UnityWebRequestMultimedia.GetAudioClip(url, AudioType.WAV); // リクエストを送信し、完了するまで待機 yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.Success) { // ダウンロードしたデータを AudioClip に変換 var responceAudioClip = DownloadHandlerAudioClip.GetContent(www); // 音声を再生する audioSource.PlayOneShot(responceAudioClip); } else { // エラーをログに出力 Debug.Log(www.error); throw new Exception(); } } }
※その他必要なこと
UniTaskを使用しているのでこちらの参考にパッケージをインポートします。
https://shibuya24.info/entry/unity-start-unitask
4. Unityで音声を再生する
作成したAudioManager.csスクリプトをMainCameraにアタッチします。
Audio Sorceをヒエラルキーに追加します。
シーンを再生すると音声が流れます。
サンプルはコチラ github.com
結論
APIを叩くだけでかわいい音声に変換してくれるのはとてもありがたいですね。
rinnaAPIには音声変換以外にもAPIがあるので試してみたいです。
以上となります。