simplestarの技術ブログ

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

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

音声合成でリアルタイムにしゃべってもらう話は以前しましたね。
simplestar-tech.hatenablog.com
ここで、予告通り今回はオウム返しするシステムを作ります。
マイクでしゃべったことをテキストに変換する話も以前しましたね。
simplestar-tech.hatenablog.com
ここで

これらをつなげるだけで行けました。
結果、マイクに向かってしゃべったことをCeVIOが返してくれました。
誰かと会話しているみたいで、もはや独り言ではなかった。(新感覚)

f:id:simplestar_tech:20160131160341p:plain

これを実現したコードを次に示します。

using UnityEngine;
using System.Collections;

public class RepeatBehaviour : MonoBehaviour {

    public CeVIOControlBehaviour CeVIOCtrl;
    public VoiceRecognitionBehaviour VoiceRecognition;

    private Queue _sentenceQueue = new Queue();

	// Use this for initialization
	void Start () {
        VoiceRecognition.OnRecData += VoiceRecognition_OnRecData;
	}
	
	// Update is called once per frame
	void Update () {

        if (0 < _sentenceQueue.Count)
        {
            string sentence = (string)_sentenceQueue.Dequeue();
            VoiceRecognition.StopRec();
            Debug.Log(sentence);
            CeVIOCtrl.StartTalk(sentence);
            VoiceRecognition.StartRec();
        }
	}

    void VoiceRecognition_OnRecData(string sentence)
    {
        _sentenceQueue.Enqueue(sentence);
    }
}

VoiceRecognitionBehaviour
のイベントにハンドラを設定し
認識結果の文字列を受け取ったら、それを
CeVIOControlBehaviour
Talk関数に渡すというものです。

無限ループに陥らないように、CeVIOがトーク中は音声認識を切っている点が工夫したところですね。

あとは既存記事をたどれば以下のコードの意味も読み解けると思います。
VoiceRecognitionBehaviour

using UnityEngine;
using System.Collections;

public class VoiceRecognitionBehaviour : MonoBehaviour {

    #region Inspector Config Vars
    public enum RecognitionType
    {
        Dictation,
        CommandControl
    }

    public RecognitionType mode = RecognitionType.Dictation;
    public string[] commands;
    private string cur_deviceName = "VF0800"; //Optional: F200 Device Name
    #endregion

    #region SenseInput Vars
    private PXCMSession _session;
    private PXCMAudioSource _source;
    private PXCMSpeechRecognition _sr;
    private PXCMSpeechRecognition.Handler _handler;
    private pxcmStatus _sts;
    #endregion

    #region Public Events To Subscribe
    public delegate void OnRecDataDelegate(string sentence);

    public event OnRecDataDelegate OnRecData;

    public delegate void OnAlertDelegate(string alertlabel);

    public event OnAlertDelegate OnAlertData;

    public delegate void OnShutDownDelegate();

    public event OnShutDownDelegate OnShutdown;

    #endregion

    internal void StopRec()
    {
        _sr.StopRec();
    }

    internal void StartRec()
    {
        _sr.StartRec(_source, _handler);
    }

    private void OnRecognition(PXCMSpeechRecognition.RecognitionData data)
    {
        if (mode == RecognitionType.CommandControl)
        {
            if (data.scores[0].confidence > 50)
                if (OnRecData != null)
                    OnRecData(data.scores[0].sentence);
        }
        else
        {
            if (OnRecData != null)
                OnRecData(data.scores[0].sentence);
        }
    }

    private void OnAlert(PXCMSpeechRecognition.AlertData data)
    {
        if (OnAlertData != null)
            OnAlertData(data.label.ToString());
    }

    // Use this for initialization
    void Start()
    {
        _SetupRecognition();

        _sts = _sr.StartRec(_source, _handler); // for default device: source = null
        if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
            Debug.LogError("Failed to Start Recognition: " + _sts);
    }

    private void _SetupRecognition()
    {
        _session = PXCMSession.CreateInstance();
        if (_session == null)
            Debug.LogError("Failed to create a session");
        _source = _session.CreateAudioSource();
        _SetInputDevice();

        _sts = _session.CreateImpl<PXCMSpeechRecognition>(out _sr);
        if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
            Debug.LogError("Failed to create a Speech Recognition Instance: " + _sts);
        _SetLanguage();

        /* Set handler: OnRecognition & OnAlert */
        _handler = new PXCMSpeechRecognition.Handler();
        _handler.onRecognition = OnRecognition;
        _handler.onAlert = OnAlert;

        _SetRecognitionMode();
    }

    private void _SetRecognitionMode()
    {
        switch (mode)
        {
            case RecognitionType.Dictation:
                _sts = _sr.SetDictation(); // Set Dictation Mode
                if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    Debug.LogError("Failed to set Dictation Mode: " + _sts);
                break;
            case RecognitionType.CommandControl:
                if (commands.Length == 0)
                {
                    Debug.LogError("Grammar list is Empty");
                    // return;
                }

                _sts = _sr.BuildGrammarFromStringList(1, commands, null); // Build the grammar
                if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    Debug.LogError("Failed to Build Grammar from list: " + _sts);

                _sts = _sr.SetGrammar(1); // Set active grammar
                if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    Debug.LogError("Failed to set Grammar: " + _sts);
                break;
        }
    }

    private void _SetLanguage()
    {
        PXCMSpeechRecognition.ProfileInfo pinfo;
        _sts = _sr.QueryProfile(out pinfo);
        if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
            Debug.LogError("Failed to retrieve a Speech Recognition Profile: " + _sts);
        pinfo.language = PXCMSpeechRecognition.LanguageType.LANGUAGE_JP_JAPANESE;
        _sr.SetProfile(pinfo);
        if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
            Debug.LogError("Failed to set language Profile: " + _sts);
    }

    private void _SetInputDevice()
    {
        _source.ScanDevices();
        bool isDeviceSet = false;
        PXCMAudioSource.DeviceInfo dinfo = null;
        int deviceNum = _source.QueryDeviceNum();
        Debug.Log("Found " + deviceNum + " input devices.");

        for (int d = 0; d < deviceNum; d++)
        {
            _sts = _source.QueryDeviceInfo(d, out dinfo);
            if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                continue;

            if (dinfo.name.Contains(cur_deviceName))
            {
                _sts = _source.SetDevice(dinfo);
                if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                {
                    Debug.LogError("Failed to Set Device: " + _sts);
                }
                else
                {
                    isDeviceSet = true;
                    Debug.Log("Selected F200 Input: " + dinfo.name);
                }
                break;
            }
        }

        /* If F200 not found select last found Input device */
        if (!isDeviceSet)
        {
            _sts = _source.SetDevice(dinfo);
            if (_sts < pxcmStatus.PXCM_STATUS_NO_ERROR) Debug.LogError("Failed to Set Device: " + _sts);
            else Debug.Log("Selected: " + dinfo.name);
        }
    }

    // Use this for Cleap Up
    void OnDisable()
    {
        /* Inform Subscribers */
        if (OnShutdown != null)
            OnShutdown();

        /* Stop the session */
        if (_sr != null)
        {
            // Stop recognition
            _sr.StopRec();

            // Destroy the Speech Recognition Instance
            _sr.Dispose();
        }

        /* Destroy the session */
        if (_session != null)
            _session.Dispose();
    }
}

CeVIOControlBehaviour

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

public class CeVIOControlBehaviour : MonoBehaviour
{
    
    private Process _process;

    public void StartTalk(string text)
    {
        if (null == _process)
        {
            _process = new Process();
            _process.StartInfo.FileName = Application.dataPath + "/External/CeVIOCtrl.exe";
            _process.StartInfo.Arguments = text;
            // 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();
            _process.WaitForExit();
        }
    }

    public void ExitTalk()
    {
        if (null != _process)
        {
            if (!_process.HasExited)
            {
                _process.CloseMainWindow();
                _process.Dispose();
                _process = null;
            }
        }
    }
	
    // Use this for initialization
	void Start () {
        _process = null;
	}

    // Update is called once per frame
    void Update()
    {
    }

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

さて、動作を確認するには
CeVIO4.0のインストールと
RealsenseSDK 関連のアセットを取得してUnityにインポートする必要があります。

それ以外のものをアップしておきます。
上記で示したものと同一のサンプルです。
http://file.blenderbluelog.anime-movie.net/Echo.zip