はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】OpenJTalkでテキストの音声読み上げをさせる(Windows)

はじめに

今回はOpenJTalkを利用してテキストの音声読み上げをしてみるという記事になります。

といってもオリジナルのOpenJTalkを使えるようにするのではなく、OpenJTalkForUnityという簡単にUnityで利用できるようにしてくれたものが存在したのでそちらを利用させていただきます。
(補足に記述した理由より、後で自力でC#ラッパーを作成したいと思ってます)

github.com

環境

Unity2020.3.32f1
OpenJTalkForUnity v1.1.13

インストール

PackageManagerAdd package from git URL....https://github.com/rarafy/OpenJTalkForUnity.git?path=_Project/Packages/OpenJTalkForUnityと打ち込みます。

Add package from git URL...
OpenJTalkForUnity

利用方法

利用しそうなコードを一覧にして記載しておきます。

private async void Start()
{
    // 「こんにちは」と喋る
    OpenJTalk.Speak("こんにちは");
        
    // ランダムな声で喋る
    OpenJTalk.SpeakRandomVoice("こんにちは");
        
    // 指定したボイスで喋る
    OpenJTalk.Speak("こんにちは", "tohoku-f01-sad");
        
    // 途中で話すのをやめる
    var ctx = new CancellationTokenSource();
    _ = Task.Run(() => OpenJTalk.SpeakStoppable("こんにちは"), ctx.Token);
    await Task.Delay(TimeSpan.FromSeconds(1), ctx.Token);
    OpenJTalk.StopSpeaking();
        
    // インストールされている全てのボイスをコンソールに出力する
    OpenJTalk.VoiceTypeInfo();
}

コードの中身について

最上位にいるクライアントクラスOpenJTalkを載せておきます。

もっと細かく使いたい場合はJTalkTTSを利用する形のようです。
github.com

public class OpenJTalk
{
    // 参照-------------------------------------------------------------------------------------------------------------------
    private static JTalkTTS _tts = new JTalkTTS { VoiceName = "tohoku-f01-neutral" };
    /// <summary>
    /// インストールされている全てのボイスの種類を表示します。<br/><br/>
    /// ※ボイスは"Assets\OpenJTalkForUnity\dll\voice"に格納されているものを探しに行きます。
    /// </summary>
    public static void VoiceTypeInfo()
    {
        List<string> a = new List<string>();
        a.AddRange(_tts.Voices.Select(v => v.Name).ToList<string>());

        foreach (string item in a) Debug.Log(item);
    }


    // 発声(同期)-------------------------------------------------------------------------------------------------------------------

    /// <summary>
    /// ランダムな声で喋ります。引数にstringで喋らせたい文字列を与えてください。
    /// </summary>
    /// <param name="text">喋らせたい文字列</param>
    /// <param name="speed">読み上げ速度</param>
    public static void SpeakRandomVoice(string text, double speed = 1.0)
    {
        using (JTalkTTS tts = new JTalkTTS { })
        {
            tts.Voice = tts.Voices[new System.Random().Next(tts.Voices.Count)];//Voiceを設定
            tts.Speed = speed;//読み上げ速度を設定
            tts.SpeakAsync(text);
            tts.WaitUntilDone();
        }
    }

    /// <summary>
    /// 任意のボイスで喋ります。引数に喋らせたい文字列とボイスの名前(tohoku-f01-neutral)を与えてください。<br />
    /// 何も指定しない場合は"tohoku-f01-neutral"の声で「こんにちは」と話します<br /><br />
    /// ※ボイスは"Assets\OpenJTalkForUnity\dll\voice"に格納されている必要があります。
    /// </summary>
    /// <param name="text">喋らせたい文字列</param>
    /// <param name="VoiceName">ボイスの名前</param>
    /// <param name="speed">読み上げ速度</param>
    public static void Speak(string text = "こんにちは", string VoiceName = "tohoku-f01-neutral", double speed = 1.0)
    {
        using (JTalkTTS tts = new JTalkTTS { VoiceName = VoiceName })
        {
            tts.Speed = speed;//読み上げ速度を設定
            tts.SpeakAsync(text);
            tts.WaitUntilDone();
        }
    }


    // 発声(非同期)-------------------------------------------------------------------------------------------------------------------

    private static JTalkTTS tts;
    private static CancellationTokenSource cancelToken;
    /// <summary>
    /// ランダムな声で喋ります。引数にstringで喋らせたい文字列を与えてください。
    /// 特別な事情が無ければSpeakRandomVoice()よりもこちらを使うのが良いでしょう。
    /// </summary>
    /// <param name="text">喋らせたい文字列</param>
    /// <param name="speed">読み上げ速度</param>
    public static Task SpeakRandomVoiceStoppable(string text = "こんにちは", double speed = 1.0)
    {
        tts = new JTalkTTS { };
        tts.Voice = tts.Voices[new System.Random().Next(tts.Voices.Count)];//Voiceを設定
        tts.Speed = speed;//読み上げ速度を設定
        tts.SpeakAsync(text);

        cancelToken = new CancellationTokenSource();
        while (tts.IsSpeaking) cancelToken.Token.ThrowIfCancellationRequested();
        return null;
    }
    /// <summary>
    /// 何も指定しない場合は「こんにちは」と話します。<br/>
    /// 特別な事情が無ければSpeak()よりもこちらを使うのが良いでしょう。<br/><br/>
    /// ※ボイスはProjectの"Assets\OpenJTalkForUnity\dll\voice"に格納されていることが前提です
    /// </summary>
    /// <param name="text">喋らせたい文字列</param>
    /// <param name="VoiceName">ボイスの名前</param>
    /// <param name="speed">読み上げ速度</param>
    public static Task SpeakStoppable(string text = "こんにちは", string VoiceName = "tohoku-f01-neutral", double speed = 1.0)
    {
        tts = new JTalkTTS { VoiceName = VoiceName };
        tts.Speed = speed;//読み上げ速度を設定
        tts.SpeakAsync(text);

        cancelToken = new CancellationTokenSource();
        while (tts.IsSpeaking) cancelToken.Token.ThrowIfCancellationRequested();
        return null;
    }
    /// <summary>
    /// 発声を停止します。
    /// </summary>
    public static void StopSpeaking()
    {
        if (cancelToken != null) cancelToken.Cancel();
        if (tts.IsSpeaking) tts.Stop();
    }
}

OpenJTalkForUnity/OpenJTalk.cs at main · rarafy/OpenJTalkForUnity · GitHub

エラーが出てくる問題

どうやらアセンブリ周りでエラーが出力される問題があるようです。(私の場合は再起動したら出てこなくなりました)

エラーが出てくる

ただ作者曰く、アンマネージドDLLの不具合で実使用に支障はないようです。
OpenJTalkForUnity/README_JP.md at main · rarafy/OpenJTalkForUnity · GitHub

またどうしても気になる場合は該当のファイルを削除してくださいとのことですが、manifest.json(PackageManager)経由だとファイルを削除できないんですよね・・・。

最悪.unitypackageからインポートしなおして対応するのが良いでしょう。

補足

OpenJTalk For UnityWindowsのみ動作し、MacUbuntuでは動作しないよというIssueがありました。
Do you plan to make it work on Mac or Ubuntu? · Issue #3 · rarafy/OpenJTalkForUnity · GitHub

仮にもっと幅広いプラットフォームで動作させようと思ったら、ちゃんと自分でオリジナルのOpenJTalkからC#ラッパーを作成する必要がありそうです。