simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:音声合成でリアルタイムにコンピュータにしゃべってもらう その2でCeVIO

前回
simplestar-tech.hatenablog.com
の続きです。

cevio.jp
を触ってみました。

起動するとトラックごとのテキスト入力欄があるので、今回は「CeVIOだよ!」と入力

f:id:simplestar_tech:20160117115152p:plain

再生すると次の音声ファイルが作られました。

http://file.blenderbluelog.anime-movie.net/CeVIO_dayo.mp3

CeVIO のうれしい機能は、発音記号ごとに音程を変えられるというところ

f:id:simplestar_tech:20160117122910p:plain

マウスの直感的な操作でぬるりとイントネーションをいじれます。
気になるイントネーションをいじると、より自然に聞こえたりします
(まだ自然に聞こえないのは自分のスキルが足りないためでもあるので、ここは訓練あるのみ)

http://file.blenderbluelog.anime-movie.net/CeVIO_dayo2.mp3

さて、公式に外部インタフェースも公開されているとのことで
.NETアセンブリとして利用 - CeVIO Creative Studio ユーザーズガイド
こちらを読んでみました。

CeVIO本体を起動していないとCeVIOは動作しないとのことです。
なるほど、音を鳴らすのも CeVIO 本体という訳ですね。

手始めに Unity から"こんにちは"を"さとうささら"にしゃべってもらうコードを書いてみます。
Unity 5 64bit 版で直接 dll を参照する場合、v3.5 以下の .NET バージョンの DLL が必要です。
CeVIO は v4.0 以上なので直接参照はできません。
そこで Unity から外部プロセスを実行する機能を利用して、その外部プロセスにテキストを渡す形で CeVIO を動かします。
リップシンクを目的として音素記号が欲しい場合はコンソール出力より文字列を受け取れば良いので、外部プロセス形式でも関数のような利用が可能です。

まずは Unity 側のコードを先に書いておきましょうか
次の記事を参考にしました。
Unityから外部プログラム(プロセス)を実行する - Neareal
実際のコードはこんな感じです。

using UnityEngine;
using System.Collections;
using System.Diagnostics;

public class CeVIOControl : MonoBehaviour {
    
    private Process _process;
	
    // Use this for initialization
	void Start () {
        _process = null;
	}

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (null == _process)
            {
                _process = new Process();
                _process.StartInfo.FileName = Application.dataPath + "/External/CeVIOCtrl.exe";
                _process.StartInfo.Arguments = "こんにちわ さとうささらです。 はじめまして!";
                // for Redirect
                {
                    _process.StartInfo.CreateNoWindow = true;
                    _process.StartInfo.UseShellExecute = false;
                    _process.StartInfo.RedirectStandardOutput = true;
                }
                // for ExitEvent
                {
                    _process.EnableRaisingEvents = true;
                    _process.Exited += Process_Exited;
                }
                _process.Start();
                // Redirect
                string output = _process.StandardOutput.ReadToEnd();
                UnityEngine.Debug.Log(output);
            }
        }
        if (Input.GetKeyDown(KeyCode.Return))
        {
            if (null != _process)
            {
                if (!_process.HasExited)
                {
                    _process.CloseMainWindow();
                    _process.Dispose();
                    _process = null;
                }
            }
        }
    }

    void Process_Exited(object sender, System.EventArgs e)
    {
        UnityEngine.Debug.Log("Process_Exited");
        _process.Dispose();
        _process = null;
    }
}

使い方
Space キーを押すと "こんにちわ" とPCがしゃべります。
途中で中断したい場合は Return キーを押すと中断します。
(注意:上記コードは StandardOutput の ReadToEnd を呼んでいるので中断しません。これをコメントアウトすれば中断できます。)

続いて CeVIOCtrl.exe の作り方を示します。

VisualStudio より C# WPF アプリをテンプレートに作成します。
名前は CeVIOCtrl としました。
参照追加より C:\Program Files (x86)\CeVIO\CeVIO Creative Studio\CeVIO.Talk.RemoteService.dll
を追加して、次のクラスを用意します。

using CeVIO.Talk.RemoteService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CeVIOCtrl
{
    class CeVIOController
    {
        internal static void Talk(string text)
        {
            ServiceControl.StartHost(false);
            Talker talker = new Talker();
            foreach (string cast in Talker.AvailableCasts)
            {
                talker.Cast = cast;
                talker.Speak(text).Wait();
                break;
            }
            foreach (var item in talker.GetPhonemes(text))
            {
                Console.Write(item.Phoneme);
            }
            Console.WriteLine();
        }
    }
}

受け取ったテキストの読み上げと、音素解析を行ってそれをコンソール出力しています。
プログラムの細かい事はCeVIO公式ページに示されているので大丈夫です。
そちらを読みましょう。

黒いコンソール画面が出るのが嫌で、Windows アプリとしました。
MainWindow の Visibility を Hidden に設定します。
そしてコマンドライン引数を受け取って動かすように
App.xaml にて StartUp イベントハンドラを登録し、下記の通り実装します。

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace CeVIOCtrl
{
    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        private void Application_Startup(object sender, StartupEventArgs e)
        {
            foreach (string arg in e.Args)
            {
                CeVIOController.Talk(arg);
            }
            Shutdown(0);
        }
    }
}

これで CeVIOCtrl.exe "こんにちわ"
と、引数指定で実行すれば Window が出ないまま音声だけが流れます。
(注意:CeVIO本体のウィンドウは表示されます。)

しかしですね、Unity 側のコードで書きましたが CreateNoWindow = true; とすれば
コンソールアプリでもウィンドウは出ません。
どうも Windows アプリで起動するとカーソルが一瞬砂時計になってしまうようなので、コンソールアプリに切り替えました。
(アプリの切り替えは簡単、Properties の出力の種類にて Windows アプリケーションからコンソールアプリケーションに切り替えるだけです。)

さて、CeVIO の音声再生と音素記号の出力とUnity側で音素記号の受け取りまで、まとめて書いてしまいましたので、使用する目的に合わせて機能の切り分けを行ってください。

リップシンクさせてユニティちゃんなどのキャラクターにしゃべってもらうということがこれで出来そうです。
次の記事では Intel RealSense の音声認識と合わせてオウム返しをするプログラムを作ってみます。

今回の成果物は下記に配置しました。
上記で示した内容と同じものです。

CeVIOCtrlプロジェクト一式
http://file.blenderbluelog.anime-movie.net/CeVIOCtrl.zip

CeVIOCtrlを呼ぶUnityパッケージ
http://file.blenderbluelog.anime-movie.net/CeVIOTest.unitypackage