developerWorks記事「Watson×Unity!初心者でもできる、VR 空間で Unity ちゃんとおしゃべりアプリ!」を試してみる

IBM の developerWorks サイトの記事 Watson×Unity!初心者でもできる、VR 空間で Unity ちゃんとおしゃべりアプリ! を現時点(2018年4月15日)の最新環境(バージョン)で試してみましたので、メモを公開します。

最近、Unity の勉強を始めたので、Watson の復習も兼ねてのトライです。

Unity ちゃんのデータをゲット

私の使用している Unity のバージョンは 2017.4.0f1 で、Windows 10 64bit版で動作しています。

image.png

Unity-chan を Asset store からインポートして、シーンに表示するまでは簡単でした。最初は「ダウンロード」ボタンが表示されていましたが、一度クリックしてダウンロードすれば「インポート」ボタンに変化して後は説明どおり。

image.png

シーンに Unity-chan を表示したところで先に進みます。

UnitySDK のダウンロード

Unity-SDK のバージョンは v2.2.1 とだいぶ変わっているようです。

ダウンロードして解凍し、unity-sdk-2.2.1 というフォルダ名を watson に変更し、Project ビュー内の Assets にコピー。2つほど警告が出ていますが…

image.png

クリティカルな場所でないのと、never used 警告なので、とりあえずはスルーしておきます。

image.png

これで準備は完了ってことで、先に進んでみましょう。

Speech to text を Unity に適用

カタログの中から、「Speech To Text」を見つけ作成します。

image.png

参照: Speech To Text APIの日本語説明ページ

そして「資格情報」をコピペして Unity に戻りますが…

image.png

あれ?メニューが全然足りない?

image.png

これじゃ先に進めませんね。

UnitySDK のバージョンを変えてみる

「Watson APIs Unity SDK Configuration Editor 出ない」でググってみると、以下の記事が見つかります。

UnityとWatsonをつないで、VR空間でAIと会話できるようにしてみた

Unity に関してと

2017年12月時点では、最新版は不具合が起こり得るため、過去バージョンの2017.2.x版を推奨らしいです

Unity SDK に関して

現在(2017年12月)最新版であるv2.0.0で実施したところ、メニューバーに現れるはずのボタン(Configuration Editorなどの便利ボタン)が出ないという現象が起きました。
調べたところ、v1.0.0以降では出なくなってしまったようです。

これらから考えるに、バージョンアップによる不整合(不具合)が発生していて、どちらか、もしくは両方のバージョンを戻さないと問題の解決は難しそうです。

できれば Unity の再導入は避けたいので、まずは Unity SDKのリリース履歴 を眺めてみます。

で、だいぶ戻した v1.0.0 で試しましたが、結果は v2.2.1 と同じ。メニュー項目 Configuration Editor が出ませんでした。

そこで記事と同じ v0.13.0 で試したところ、コンソールに警告ではなくエラーが表示されてしまい、元の Watson メニューすら表示されませんでした。新しい Unity に対応できていない様子。

image.png

あかん、これ面倒なやつや…

UnitySDK v2.2.1 を眺めてみる

ここで、ふと疑問に思ったんですよ。

v1.0.0 以降、Configuration Editorなどメニューからごっそり消えているけど、コンソールにエラー出ていないよね

ってことに。なので UnitySDK の中身をみていくと、Unity にメニューを追加する Scripts/Editor の中身がごっそり消えていることに気がつきました。

image.png

左が v2.2.1 で、右が v0.13.0 です。ここだけでなく、全体を見回しても ConfigurationEditor.cs が見当たらないんですよ。もしかして、削除されたんと違うか?

んで、GitHub の UnitySDK ページ の README.md を読んでいくと、Authentication セクションには Credentials インスタンスを生成するコードだけが記載されていて、Configuration Editor の説明はない。

ということで、便利ツールである Configuration Editor を使わず、利用するコード側で Credentials 管理をするのが v1.0.0 以降のお作法であり、正解なのかな?が私の結論です。

というわけで対応策としては

まず、元のページにある Configuration Editor の部分はさくっとスキップします。

そして最初のサンプルコードの最初に以下の using 文を追加し

SampleSpeechToText.cs
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Utilities;

最初のほうにある m_SpeechToText の初期化部分を以下のように変更します。

SampleSpeechToText.cs
//private SpeechToText m_SpeechToText = new SpeechToText();
private SpeechToText m_SpeechToText;

インスタンスを new している部分を消したので、代わりに、IEnumerator Start() メソッドの先頭に以下のコードを追加します。

SampleSpeechToText.cs
string cre_id = "4934xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx99";                  // 資格情報より
string cre_pw = "MxxxxxxxxxxE";                                          // 資格情報より
string cre_url = "https://stream.watsonplatform.net/speech-to-text/api"; // 資格情報より
Credentials credentials = new Credentials(cre_id, cre_pw, cre_url);
m_SpeechToText = new SpeechToText(credentials);
m_SpeechToText.Keywords = new string[] { "ibm" };
m_SpeechToText.KeywordsThreshold = 0.1f;

cre_id, cre_pw, cre_url は Watson でサービス登録した際に表示された「資格情報」をみて設定してください。

Keywords と KeywordsThreshold は空のままだとWatson API 呼び出し時にエラーになってしまうようで、適当な値を設定してあります。

そして肝心の Recognize 関数も引数が変わっているようなので、以下のように書き換えて…

SampleSpeechToText.cs
//m_SpeechToText.Recognize(HandleOnRecognize, audioSource.clip);
m_SpeechToText.Recognize(HandleOnRecognize, OnFail, audioSource.clip);

不足している OnFail 関数を新たに定義してあげます。

SampleSpeechToText.cs
private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
{
    Debug.Log("SampleSpeechToText.OnFail() Error received: " + error.ToString());
}

デバッグのため SpeechToText.cs の中身読んだり、対応は意外と大変でした… が、原因がわかれば対応は見えてきます。

さあ、これでやっと最初のサンプルコードが動きます。Unity に戻って実行してみましょう!

image.png

ふう、動いてよかったっす…

最初のサンプルコード

書き換えた最初のサンプルコードの最終版を掲載しておきます。私が修正で加えたコードの著作権は放棄しますので、コードの扱いは元の developerWorks 記事 と同じでかまいません。

SampleSpeechToText.cs
//SampleSpeechToText.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using IBM.Watson.DeveloperCloud.Services.SpeechToText.v1;
using IBM.Watson.DeveloperCloud.Connection;
using IBM.Watson.DeveloperCloud.Utilities;

public class SampleSpeechToText : MonoBehaviour
{

    [SerializeField]
    private AudioClip m_AudioClip = new AudioClip();
    //private SpeechToText m_SpeechToText = new SpeechToText();
    private SpeechToText m_SpeechToText;

    // Use this for initialization
    IEnumerator Start()
    {
        string cre_id = "4934xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx99";                  // 資格情報より
        string cre_pw = "MxxxxxxxxxxE";                                          // 資格情報より
        string cre_url = "https://stream.watsonplatform.net/speech-to-text/api"; // 資格情報より
        Credentials credentials = new Credentials(cre_id, cre_pw, cre_url);
        m_SpeechToText = new SpeechToText(credentials);
        m_SpeechToText.Keywords = new string[] { "ibm" };
        m_SpeechToText.KeywordsThreshold = 0.1f;

        // 音声をマイクから 3 秒間取得する
        Debug.Log("Start record"); //集音開始
        var audioSource = GetComponent<AudioSource>();
        audioSource.clip = Microphone.Start(null, true, 10, 44100);
        audioSource.loop = false;
        audioSource.spatialBlend = 0.0f;
        yield return new WaitForSeconds(3f);
        Microphone.End(null); //集音終了
        Debug.Log("Finish record");

        // ためしに録音内容を再生してみる
        audioSource.Play();

        // SpeechToText を日本語指定して、録音音声をテキストに変換
        m_SpeechToText.RecognizeModel = "ja-JP_BroadbandModel";
        //m_SpeechToText.Recognize(HandleOnRecognize, audioSource.clip);
        m_SpeechToText.Recognize(HandleOnRecognize, OnFail, audioSource.clip);
    }

    void HandleOnRecognize(SpeechRecognitionEvent result, Dictionary<string, object> customData)
    {
        if (result != null && result.results.Length > 0)
        {
            foreach (var res in result.results)
            {
                foreach (var alt in res.alternatives)
                {
                    string text = alt.transcript;
                    Debug.Log(string.Format("{0} ({1}, {2:0.00})\n", text, res.final ? "Final" : "Interim", alt.confidence));
                }
            }
        }
    }
    private void OnFail(RESTConnector.Error error, Dictionary<string, object> customData)
    {
        Debug.Log("SampleSpeechToText.OnFail() Error received: " + error.ToString());
    }

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

    }
}

というわけで

最初のサンプルだけで長くなってしまったので、とりあえず今回の記事は終わりにします。動かなかった原因と、対応するためのコードがわかりましたので、以後もこの要領で対応していけるのではないか?とおもいます。

この先も自分で試すと思いますので、何か面白い点がありましたら後半もメモ作成するかもしれません。また、もしご要望でもあれば記事書くかもしれません。時間が欲しい…

【追記】残りの2つのAPI用のサンプルも修正したので 続きのページ でコードを公開しました。

ではでは。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.