0
votes

I am attempting to use the example script "ExampleTextToSpeech" that comes with the Watson Unity SDK. However, the Watson Text to Speech example throws error when attempting to synthesize. My service IAM Key appears to be correct. I am not specifying the AIM URL or the Service Url. I assume the default values should be correct since I am in the North American Region and this has worked for both the Watson Assistant and Speech to Text. The problem appears to be with the Synthesize call. I've listed the errors below.

Sorry I failed to post the actual code the first time. I assumed I didn't need to since it is the same code from the SDK demos. Please find the code below. I have removed some extraneous code bits such as customizing words from the code. The errors can be found at the end of the code block.

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

public class TTS : MonoBehaviour
{
    //Iam Apikey: v0qlUa8hojlVW8rKBh48fO0JtQYTO9vfNkGMG6wpptbs
    #region PLEASE SET THESE VARIABLES IN THE INSPECTOR
    [Space(10)]
    [Tooltip("The service URL (optional). This defaults to \"https://stream.watsonplatform.net/text-to-speech/api\"")]
    [SerializeField]
    private string _serviceUrl;
    [Header("CF Authentication")]
    [Tooltip("The authentication username.")]
    [SerializeField]
    private string _username;
    [Tooltip("The authentication password.")]
    [SerializeField]
    private string _password;
    [Header("IAM Authentication")]
    [Tooltip("The IAM apikey.")]
    [SerializeField]
    private string _iamApikey;
    [Tooltip("The IAM url used to authenticate the apikey (optional). This defaults to \"https://iam.bluemix.net/identity/token\".")]
    [SerializeField]
    private string _iamUrl;

    #endregion

    TextToSpeech _service;
    string _testString = "<speak version=\"1.0\"><say-as interpret-as=\"letters\">I'm sorry</say-as>. <prosody pitch=\"150Hz\">This is Text to Speech!</prosody><express-as type=\"GoodNews\">I'm sorry. This is Text to Speech!</express-as></speak>";

    string _createdCustomizationId;
    CustomVoiceUpdate _customVoiceUpdate;
    string _customizationName = "unity-example-customization";
    string _customizationLanguage = "en-US";
    string _customizationDescription = "A text to speech voice customization created within Unity.";
    string _testWord = "Watson";

    private bool _synthesizeTested = false;
    private bool _getVoicesTested = false;
    private bool _getVoiceTested = false;
    private bool _getPronuciationTested = false;
    private bool _getCustomizationsTested = false;
    private bool _createCustomizationTested = false;
    private bool _deleteCustomizationTested = false;
    private bool _getCustomizationTested = false;
    private bool _updateCustomizationTested = false;
    private bool _getCustomizationWordsTested = false;
    private bool _addCustomizationWordsTested = false;
    private bool _deleteCustomizationWordTested = false;
    private bool _getCustomizationWordTested = false;

    void Start()
    {
        LogSystem.InstallDefaultReactors();
        Runnable.Run(CreateService());
    }

    private IEnumerator CreateService()
    {
        //  Create credential and instantiate service
        Credentials credentials = null;
        if (!string.IsNullOrEmpty(_username) && !string.IsNullOrEmpty(_password))
        {
            //  Authenticate using username and password
            credentials = new Credentials(_username, _password, _serviceUrl);
        }
        else if (!string.IsNullOrEmpty(_iamApikey))
        {
            //  Authenticate using iamApikey
            TokenOptions tokenOptions = new TokenOptions()
            {
                IamApiKey = _iamApikey,
                IamUrl = _iamUrl
            };

            credentials = new Credentials(tokenOptions, _serviceUrl);

            //  Wait for tokendata
            while (!credentials.HasIamTokenData())
                yield return null;
        }
        else
        {
            throw new WatsonException("Please provide either username and password or IAM apikey to authenticate the service.");
        }

        _service = new TextToSpeech(credentials);

        Runnable.Run(Examples());
    }

    private IEnumerator Examples()
    {
        //  Synthesize
        Log.Debug("ExampleTextToSpeech.Examples()", "Attempting synthesize.");
        _service.Voice = VoiceType.en_US_Allison;
        _service.ToSpeech(HandleToSpeechCallback, OnFail, _testString, true);
        while (!_synthesizeTested)
            yield return null;

        //  Get Voices
        Log.Debug("ExampleTextToSpeech.Examples()", "Attempting to get voices.");
        _service.GetVoices(OnGetVoices, OnFail);
        while (!_getVoicesTested)
            yield return null;

        //  Get Voice
        Log.Debug("ExampleTextToSpeech.Examples()", "Attempting to get voice {0}.", VoiceType.en_US_Allison);
        _service.GetVoice(OnGetVoice, OnFail, VoiceType.en_US_Allison);
        while (!_getVoiceTested)
            yield return null;

        Log.Debug("ExampleTextToSpeech.Examples()", "Text to Speech examples complete.");
    }

    void HandleToSpeechCallback(AudioClip clip, Dictionary<string, object> customData = null)
    {
        PlayClip(clip);
    }

    private void PlayClip(AudioClip clip)
    {
        if (Application.isPlaying && clip != null)
        {
            GameObject audioObject = new GameObject("AudioObject");
            AudioSource source = audioObject.AddComponent<AudioSource>();
            source.spatialBlend = 0.0f;
            source.loop = false;
            source.clip = clip;
            source.Play();

            Destroy(audioObject, clip.length);

            _synthesizeTested = true;
        }
    }

    private void OnGetVoices(Voices voices, Dictionary<string, object> customData = null)
    {
        Log.Debug("ExampleTextToSpeech.OnGetVoices()", "Text to Speech - Get voices response: {0}", customData["json"].ToString());
        _getVoicesTested = true;
    }

    private void OnGetVoice(Voice voice, Dictionary<string, object> customData = null)
    {
        Log.Debug("ExampleTextToSpeech.OnGetVoice()", "Text to Speech - Get voice  response: {0}", customData["json"].ToString());
        _getVoiceTested = true;
    }

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




    [12/11/2018 16:57:45][RESTConnector.ProcessRequestQueue()][ERROR] URL: /v1/synthesize?accept=audio%2fwav&voice=en-US_AllisonVoice&text=%3cspeak+version%3d%221.0%22%3e%3csay-as+interpret-as%3d%22letters%22%3eI%27m+sorry%3c%2fsay-as%3e.+%3cprosody+pitch%3d%22150Hz%22%3eThis+is+Text+to+Speech%21%3c%2fprosody%3e%3cexpress-as+type%3d%22GoodNews%22%3eI%27m+sorry.+This+is+Text+to+Speech%21%3c%2fexpress-as%3e%3c%2fspeak%3e, ErrorCode: 0, Error: Cannot connect to destination host, Response: 
    UnityEngine.Debug:LogError(Object)
    IBM.Watson.DeveloperCloud.Debug.DebugReactor:ProcessLog(LogRecord) (at Assets/Watson/Scripts/Debug/DebugReactor.cs:60)
    IBM.Watson.DeveloperCloud.Logging.LogSystem:ProcessLog(LogRecord) (at Assets/Watson/Scripts/Logging/Logger.cs:206)
    IBM.Watson.DeveloperCloud.Logging.Log:Error(String, String, Object[]) (at Assets/Watson/Scripts/Logging/Logger.cs:279)
    IBM.Watson.DeveloperCloud.Connection.<ProcessRequestQueue>c__Iterator0:MoveNext() (at Assets/Watson/Scripts/Connection/RESTConnector.cs:607)
    IBM.Watson.DeveloperCloud.Utilities.Routine:MoveNext() (at Assets/Watson/Scripts/Utilities/Runnable.cs:131)
    UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)



    [12/11/2018 16:57:45][ExampleTextToSpeech.OnFail()][ERROR] Error received: URL: /v1/synthesize?accept=audio%2fwav&voice=en-US_AllisonVoice&text=%3cspeak+version%3d%221.0%22%3e%3csay-as+interpret-as%3d%22letters%22%3eI%27m+sorry%3c%2fsay-as%3e.+%3cprosody+pitch%3d%22150Hz%22%3eThis+is+Text+to+Speech%21%3c%2fprosody%3e%3cexpress-as+type%3d%22GoodNews%22%3eI%27m+sorry.+This+is+Text+to+Speech%21%3c%2fexpress-as%3e%3c%2fspeak%3e, ErrorCode: 0, Error: Cannot connect to destination host, Response: 
    UnityEngine.Debug:LogError(Object)
    IBM.Watson.DeveloperCloud.Debug.DebugReactor:ProcessLog(LogRecord) (at Assets/Watson/Scripts/Debug/DebugReactor.cs:60)
    IBM.Watson.DeveloperCloud.Logging.LogSystem:ProcessLog(LogRecord) (at Assets/Watson/Scripts/Logging/Logger.cs:206)
    IBM.Watson.DeveloperCloud.Logging.Log:Error(String, String, Object[]) (at Assets/Watson/Scripts/Logging/Logger.cs:279)
    TTS:OnFail(Error, Dictionary`2) (at Assets/Scripts/TTS.cs:158)
    IBM.Watson.DeveloperCloud.Services.TextToSpeech.v1.TextToSpeech:ToSpeechResponse(Request, Response) (at Assets/Watson/Scripts/Services/TextToSpeech/v1/TextToSpeech.cs:492)
    IBM.Watson.DeveloperCloud.Connection.<ProcessRequestQueue>c__Iterator0:MoveNext() (at Assets/Watson/Scripts/Connection/RESTConnector.cs:647)
    IBM.Watson.DeveloperCloud.Utilities.Routine:MoveNext() (at Assets/Watson/Scripts/Utilities/Runnable.cs:131)
    UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
1
Please show the code that is generating these error messages. Also format the error messages in "code" blocks to make it more readable. - Stephen M -on strike-
Thank you for your advice. I have edited my post accordingly. - Mindy

1 Answers

0
votes

You should explicitly use the service URL for Text to Speech for your region. This is the URL for US South

https://stream.watsonplatform.net/text-to-speech/api

There is an issue with the SDK where the default service URL is not used. The issue will be fixed once this pull request is merged.

https://github.com/watson-developer-cloud/unity-sdk/pull/486