using UnityEngine; using System.Collections; namespace Crosstales.RTVoice { /// /// Example for a custom voice provider (TTS-system) with all callbacks (only for demonstration - it doesn't do anything). /// NOTE: please make sure you understand the Wrapper and its variables /// [HelpURL("https://www.crosstales.com/media/data/assets/rtvoice/api/class_crosstales_1_1_r_t_voice_1_1_voice_provider_example.html")] public class VoiceProviderExample : Provider.BaseCustomVoiceProvider { #region Properties public override string AudioFileExtension { get { // SKELETON CODE - replace with your own code! // DEFINE: audio extension of the generated audio files from the provider (e.g. '.wav') return ".wav"; } } public override AudioType AudioFileType { get { // SKELETON CODE - replace with your own code! // DEFINE: audio type of the generated audio files from the provider return AudioType.WAV; } } public override string DefaultVoiceName { get { // SKELETON CODE - replace with your own code! // DEFINE: default voice name return "Marisa"; } } public override bool isWorkingInEditor { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is working directly inside the Unity Editor (without 'Play'-mode) return true; } } public override bool isWorkingInPlaymode { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is working with 'Play'-mode inside the Unity Editor return true; } } public override bool isPlatformSupported { get { // SKELETON CODE - replace with your own code! // DEFINE: checks if the current platform is supported return true; } } public override int MaxTextLength { get { // SKELETON CODE - replace with your own code! // DEFINE: Maximal length of the speech text (in characters) that could be processed by the provider return 32000; } } public override bool isSpeakNativeSupported { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is supporting SpeakNative return false; } } public override bool isSpeakSupported { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is supporting Speak return true; } } public override bool isSSMLSupported { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is supporting SSML return true; } } public override bool isOnlineService { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is an online service like MaryTTS or AWS Polly return false; } } public override bool hasCoRoutines { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider uses co-routines return true; } } public override bool isIL2CPPSupported { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider is supporting IL2CPP return true; } } public override bool hasVoicesInEditor { get { // SKELETON CODE - replace with your own code! // DEFINE: Indicates if this provider returns voices in the Editor mode return true; } } #endregion #region Implemented methods public override void Load(bool forceReload = false) { Debug.Log("Load", this); if (cachedVoices?.Count == 0 || forceReload) { Debug.Log("Reload all voices", this); // SKELETON CODE - replace with your own code! cachedVoices = new System.Collections.Generic.List { new Model.Voice("Marisa", "RTV custom provider test -> female", Model.Enum.Gender.FEMALE, "adult", "en-US"), new Model.Voice("Stefan", "RTV custom provider test -> male", Model.Enum.Gender.MALE, "adult", "en-US") }; } onVoicesReady(); } public override IEnumerator Generate(Model.Wrapper wrapper) { Debug.Log("Generate: " + wrapper, this); // SKELETON CODE - replace with your own code! if (wrapper == null) { Debug.LogWarning("'wrapper' is null!", this); } else { if (string.IsNullOrEmpty(wrapper.Text)) { Debug.LogWarning("'wrapper.Text' is null or empty!", this); } else { yield return null; //return to the main process (uid) silence = false; // GENERATE: audio file based on the Wrapper configuration (pitch, rate, volume, output file etc.) //... onSpeakAudioGenerationStart(wrapper); //NECESSARY // WAIT: wait until speech is generated //... //... load file (with WWW) and add the clip to the AudioSource (see VoiceProviderWindows) //... // SIMULATE GENERATE (remove this) foreach (char character in wrapper.Text) { Debug.Log(character, this); yield return null; } onSpeakAudioGenerationComplete(wrapper); //NECESSARY //OPTIONAL: broadcast possible errors //onErrorInfo(wrapper, "errorMessage"); } } } public override IEnumerator Speak(Model.Wrapper wrapper) { Debug.Log("Speak: " + wrapper, this); // SKELETON CODE - replace with your own code! if (wrapper == null) { Debug.LogWarning("'wrapper' is null!", this); } else { if (string.IsNullOrEmpty(wrapper.Text)) { Debug.LogWarning("'wrapper.Text' is null or empty!", this); } else { yield return null; //return to the main process (uid) silence = false; //GENERATE: audio file based on the Wrapper configuration (pitch, rate, volume, output file etc.) //... onSpeakAudioGenerationStart(wrapper); //NECESSARY // WAIT: wait until speech is generated //... //... load file (with WWW) and add the clip to the AudioSource (see VoiceProviderWindows) //... onSpeakAudioGenerationComplete(wrapper); //NECESSARY //OPTIONAL: play audio file //... if (wrapper.SpeakImmediately) { onSpeakStart(wrapper); //NECESSARY // WAIT: wait until speech is finished //... //SIMULATE SPEECH (remove this) foreach (char character in wrapper.Text) { Debug.Log(character, this); yield return null; } onSpeakComplete(wrapper); //NECESSARY } //OPTIONAL: broadcast possible errors //onErrorInfo(wrapper, "errorMessage"); } } } public override IEnumerator SpeakNative(Model.Wrapper wrapper) { Debug.Log("SpeakNative: " + wrapper, this); // SKELETON CODE - replace with your own code! if (wrapper == null) { Debug.LogWarning("'wrapper' is null!", this); } else { if (string.IsNullOrEmpty(wrapper.Text)) { Debug.LogWarning("'wrapper.Text' is null or empty!", this); } else { yield return null; //return to the main process (uid) silence = false; // SPEAK: speech based on the Wrapper configuration (pitch, rate, volume, output file etc.) //... onSpeakStart(wrapper); //NECESSARY // WAIT: wait until speech is finished //... //SIMULATE SPEECH (remove this) foreach (char character in wrapper.Text) { //OPTIONAL: enable if you have access to the words, phonemes and visemes //onSpeakCurrentWord(wrapper, speechTextArray, wordIndex - 1); onSpeakCurrentPhoneme(wrapper, character.ToString()); onSpeakCurrentViseme(wrapper, character.ToString()); Debug.Log(character, this); yield return null; } onSpeakComplete(wrapper); //NECESSARY //OPTIONAL: broadcast possible errors //onErrorInfo(wrapper, "errorMessage"); } } } #endregion #region Editor-only methods #if UNITY_EDITOR public override void GenerateInEditor(Model.Wrapper wrapper) { Debug.Log("Generate: " + wrapper, this); // SKELETON CODE - replace with your own code! if (wrapper == null) { Debug.LogWarning("'wrapper' is null!", this); } else { if (string.IsNullOrEmpty(wrapper.Text)) { Debug.LogWarning("'wrapper.Text' is null or empty!", this); } else { silence = false; // GENERATE: audio file based on the Wrapper configuration (pitch, rate, volume, output file etc.) //... onSpeakAudioGenerationStart(wrapper); //NECESSARY // WAIT: wait until speech is generated //... //... load file (with WWW) and add the clip to the AudioSource (see VoiceProviderWindows) //... // SIMULATE GENERATE (remove this) foreach (char character in wrapper.Text) { Debug.Log(character, this); System.Threading.Thread.Sleep(100); } onSpeakAudioGenerationComplete(wrapper); //NECESSARY //OPTIONAL: broadcast possible errors //onErrorInfo(wrapper, "errorMessage"); } } } public override void SpeakNativeInEditor(Model.Wrapper wrapper) { Debug.Log("SpeakNative: " + wrapper, this); // SKELETON CODE - replace with your own code! if (wrapper == null) { Debug.LogWarning("'wrapper' is null!", this); } else { if (string.IsNullOrEmpty(wrapper.Text)) { Debug.LogWarning("'wrapper.Text' is null or empty!", this); } else { silence = false; // SPEAK: speech based on the Wrapper configuration (pitch, rate, volume, output file etc.) //... onSpeakStart(wrapper); //NECESSARY // WAIT: wait until speech is finished //... //SIMULATE SPEECH (remove this) foreach (char character in wrapper.Text) { //OPTIONAL: enable if you have access to the words, phonemes and visemes //onSpeakCurrentWord(wrapper, speechTextArray, wordIndex - 1); onSpeakCurrentPhoneme(wrapper, character.ToString()); onSpeakCurrentViseme(wrapper, character.ToString()); Debug.Log(character, this); System.Threading.Thread.Sleep(100); } onSpeakComplete(wrapper); //NECESSARY //OPTIONAL: broadcast possible errors //onErrorInfo(wrapper, "errorMessage"); } } } #endif #endregion } } // © 2018-2020 crosstales LLC (https://www.crosstales.com)