#if UNITY_EDITOR using UnityEngine; using UnityEditor; using Crosstales.RTVoice.EditorUtil; namespace Crosstales.RTVoice.EditorExtension { /// Custom editor for the 'Speaker'-class. [InitializeOnLoad] [CustomEditor(typeof(Speaker))] public class SpeakerEditor : Editor { #region Variables private string text = "Hello world!"; private int voiceIndex; private float rate = 1f; private float pitch = 1f; private float volume = 1f; private Speaker script; private bool showVoices; private Object customProvider; private bool customMode; private bool eSpeakMode; private Model.Enum.ESpeakModifiers eSpeakModifier; private string eSpeakApp; private string eSpeakData; private string androidEngine; private bool autoClearTags; private bool caching; //private bool windowsLegacy; //private bool wsaNative; private bool silenceOnDisable; private bool silenceOnFocusLost; private bool handleFocus; private bool dontDestroy; #endregion #region Static constructor static SpeakerEditor() { EditorApplication.hierarchyWindowItemOnGUI += hierarchyItemCB; } #endregion #region Editor methods public void OnEnable() { script = (Speaker)target; } public void OnDisable() { if (Util.Helper.isEditorMode) script.Silence(); } public override bool RequiresConstantRepaint() { return true; } public override void OnInspectorGUI() { EditorHelper.BannerOC(); if (script.enforcedStandaloneTTS) { EditorGUILayout.HelpBox("Standalone TTS is used for development. The TTS on the current build target may have other voices and features.", MessageType.Warning); } if (script.Voices?.Count == 0) { if (script.isPlatformSupported && !script.isWorkingInPlaymode) { EditorGUILayout.HelpBox("The current TTS only works in builds!", MessageType.Error); } else if (!script.isPlatformSupported) { EditorGUILayout.HelpBox("The current platform is not supported by the active voice provider. Please use MaryTTS or a custom provider (e.g. Klattersynth).", MessageType.Error); } else { if (script.hasVoicesInEditor) EditorGUILayout.HelpBox("TTS with the current settings is not possible!", MessageType.Error); } } if (Util.Helper.isIL2CPP && !script.isIL2CPPSupported) { GUILayout.Space(6); EditorGUILayout.HelpBox("IL2CPP is not supported by the current voice provider. Please use Mono, MaryTTS or a custom provider (e.g. Klattersynth).", MessageType.Error); } serializedObject.Update(); GUILayout.Label("Custom Provider", EditorStyles.boldLabel); customMode = EditorGUILayout.BeginToggleGroup(new GUIContent("Active", "Enables or disables the custom provider (default: false)."), script.CustomMode); if (customMode != script.CustomMode) { serializedObject.FindProperty("customMode").boolValue = customMode; serializedObject.ApplyModifiedProperties(); voiceIndex = 0; script.ReloadProvider(); } EditorGUI.indentLevel++; customProvider = EditorGUILayout.ObjectField("Custom Provider", script.CustomProvider, typeof(Provider.BaseCustomVoiceProvider), true); if (customProvider != script.CustomProvider) { serializedObject.FindProperty("customProvider").objectReferenceValue = customProvider; serializedObject.ApplyModifiedProperties(); voiceIndex = 0; script.ReloadProvider(); } EditorGUI.indentLevel--; EditorGUILayout.EndToggleGroup(); if (customMode) { if (script.CustomProvider == null) { EditorGUILayout.HelpBox("'Custom Provider' is null! Please add a valid provider.", MessageType.Warning); } else { if (!script.CustomProvider.isPlatformSupported) { EditorGUILayout.HelpBox("'Custom Provider' does not support the current platform!", MessageType.Warning); } } } GUILayout.Space(8); GUILayout.Label("eSpeak Settings", EditorStyles.boldLabel); eSpeakMode = EditorGUILayout.BeginToggleGroup(new GUIContent("Active", "Enable or disable eSpeak for standalone platforms (default: false)."), script.ESpeakMode); if (eSpeakMode != script.ESpeakMode) { serializedObject.FindProperty("eSpeakMode").boolValue = eSpeakMode; serializedObject.ApplyModifiedProperties(); voiceIndex = 0; script.ReloadProvider(); } EditorGUI.indentLevel++; eSpeakApp = EditorGUILayout.TextField(new GUIContent("Application", "eSpeak application name/path (default: 'espeak')."), script.ESpeakApplication); if (!eSpeakApp.Equals(script.ESpeakApplication)) { serializedObject.FindProperty("eSpeakApplication").stringValue = eSpeakApp; serializedObject.ApplyModifiedProperties(); //script.ReloadProvider(); } eSpeakData = EditorGUILayout.TextField(new GUIContent("Data Path", "eSpeak application data path (default: empty)."), script.ESpeakDataPath); if (!eSpeakData.Equals(script.ESpeakDataPath)) { serializedObject.FindProperty("eSpeakDataPath").stringValue = eSpeakData; serializedObject.ApplyModifiedProperties(); //script.ReloadProvider(); } eSpeakModifier = (Model.Enum.ESpeakModifiers)EditorGUILayout.EnumPopup(new GUIContent("Modifier", "Active modifier for all eSpeak voices (default: none, m1-m6 = male, f1-f4 = female)."), script.ESpeakModifier); if (eSpeakModifier != script.ESpeakModifier) { serializedObject.FindProperty("eSpeakModifier").enumValueIndex = (int)eSpeakModifier; serializedObject.ApplyModifiedProperties(); } EditorGUI.indentLevel--; EditorGUILayout.EndToggleGroup(); if (eSpeakMode && !Provider.VoiceProviderLinux.isSupported) { EditorGUILayout.HelpBox("'eSpeak' is not supported on the current platform!", MessageType.Warning); } GUILayout.Space(8); GUILayout.Label("Android Settings", EditorStyles.boldLabel); androidEngine = EditorGUILayout.TextField(new GUIContent("Engine", "Active speech engine under Android (default: empty)."), script.AndroidEngine); if (!androidEngine.Equals(script.AndroidEngine)) { serializedObject.FindProperty("androidEngine").stringValue = androidEngine; serializedObject.ApplyModifiedProperties(); //script.ReloadProvider(); } GUILayout.Space(8); GUILayout.Label("Advanced Settings", EditorStyles.boldLabel); autoClearTags = EditorGUILayout.Toggle(new GUIContent("Auto Clear Tags", "Automatically clear tags from speeches depending on the capabilities of the current TTS-system (default: false)."), script.AutoClearTags); if (autoClearTags != script.AutoClearTags) { serializedObject.FindProperty("autoClearTags").boolValue = autoClearTags; serializedObject.ApplyModifiedProperties(); } caching = EditorGUILayout.Toggle(new GUIContent("Caching", "Enable or disable the caching of generated speeches (default: true)."), script.Caching); if (caching != script.Caching) { serializedObject.FindProperty("caching").boolValue = caching; serializedObject.ApplyModifiedProperties(); } GUILayout.Space(8); GUILayout.Label("Behaviour Settings", EditorStyles.boldLabel); silenceOnDisable = EditorGUILayout.Toggle(new GUIContent("Silence On Disable", "Silence any speeches if this component gets disabled (default: false)."), script.SilenceOnDisable); if (silenceOnDisable != script.SilenceOnDisable) { serializedObject.FindProperty("silenceOnDisable").boolValue = silenceOnDisable; serializedObject.ApplyModifiedProperties(); } silenceOnFocusLost = EditorGUILayout.Toggle(new GUIContent("Silence On Focus Lost", "Silence any speeches if the application loses the focus (default: true)."), script.SilenceOnFocusLost); if (silenceOnFocusLost != script.SilenceOnFocusLost) { serializedObject.FindProperty("silenceOnFocusLost").boolValue = silenceOnFocusLost; serializedObject.ApplyModifiedProperties(); } handleFocus = EditorGUILayout.Toggle(new GUIContent("Handle Focus", "Starts and stops the Speaker depending on the focus and running state (default: true)."), script.HandleFocus); if (handleFocus != script.HandleFocus) { serializedObject.FindProperty("handleFocus").boolValue = handleFocus; serializedObject.ApplyModifiedProperties(); } dontDestroy = EditorGUILayout.Toggle(new GUIContent("Dont Destroy", "Don't destroy gameobject during scene switches (default: true)."), script.DontDestroy); if (dontDestroy != script.DontDestroy) { serializedObject.FindProperty("dontDestroy").boolValue = dontDestroy; serializedObject.ApplyModifiedProperties(); } SerializedProperty onReady = serializedObject.FindProperty("OnReady"); EditorGUILayout.PropertyField(onReady); SerializedProperty onSpeakStarted = serializedObject.FindProperty("OnSpeakStarted"); EditorGUILayout.PropertyField(onSpeakStarted); SerializedProperty onSpeakCompleted = serializedObject.FindProperty("OnSpeakCompleted"); EditorGUILayout.PropertyField(onSpeakCompleted); SerializedProperty onProviderChanged = serializedObject.FindProperty("OnProviderChanged"); EditorGUILayout.PropertyField(onProviderChanged); SerializedProperty onError = serializedObject.FindProperty("OnError"); EditorGUILayout.PropertyField(onError); EditorHelper.SeparatorUI(); if (script.isActiveAndEnabled) { GUILayout.Label("Data", EditorStyles.boldLabel); showVoices = EditorGUILayout.Foldout(showVoices, "Voices (" + script.Voices.Count + ")"); if (showVoices) { EditorGUI.indentLevel++; foreach (string voice in script.Voices.CTToString()) { EditorGUILayout.SelectableLabel(voice, GUILayout.Height(16), GUILayout.ExpandHeight(false)); } EditorGUI.indentLevel--; } if (GUILayout.Button(new GUIContent(" Reload", EditorHelper.Icon_Refresh, "Reload the provider."))) { script.ReloadProvider(); } EditorHelper.SeparatorUI(); GUILayout.Label("Test-Drive", EditorStyles.boldLabel); if (script.Voices.Count > 0) { //EditorHelper.SeparatorUI(); //GUILayout.Label("Test-Drive", EditorStyles.boldLabel); if (Util.Helper.isEditorMode) { if (script.isWorkingInEditor) { GUI.enabled = !script.isSpeaking; text = EditorGUILayout.TextField("Text: ", text); voiceIndex = EditorGUILayout.Popup("Voice", voiceIndex, script.Voices.CTToString().ToArray()); rate = EditorGUILayout.Slider("Rate", rate, 0f, 3f); if (Util.Helper.isWindowsPlatform) { pitch = EditorGUILayout.Slider("Pitch", pitch, 0f, 2f); volume = EditorGUILayout.Slider("Volume", volume, 0f, 1f); } GUI.enabled = true; GUILayout.Space(8); if (script.isSpeaking) { if (GUILayout.Button(new GUIContent(" Silence", EditorHelper.Icon_Silence, "Silence all active speakers."))) { script.Silence(); } } else { if (GUILayout.Button(new GUIContent(" Speak", EditorHelper.Icon_Speak, "Speaks the text with the selected voice and settings."))) { //script.SpeakNative("You have selected " + script.Voices[voiceIndex].Name, script.Voices[voiceIndex], rate, pitch, volume); script.SpeakNative(text, script.Voices[voiceIndex], rate, pitch, volume); } } } else { EditorGUILayout.HelpBox("Test-Drive is not supported for the current TTS-system.", MessageType.Info); } } else { EditorGUILayout.HelpBox("Disabled in Play-mode!", MessageType.Info); } } else { if (Util.Helper.isEditorMode) { if (!script.isWorkingInEditor) { EditorGUILayout.HelpBox("Test-Drive is not supported for the current TTS-system.", MessageType.Info); } } else { EditorGUILayout.HelpBox("Disabled in Play-mode!", MessageType.Info); } } /* else { if (script.isPlatformSupported && !script.isWorkingInPlaymode) { EditorGUILayout.HelpBox("The current TTS only works in builds!", MessageType.Error); } else if (!script.isPlatformSupported) { EditorGUILayout.HelpBox("The current platform is not supported by the active voice provider. Please use MaryTTS or a custom provider (e.g. Klattersynth).", MessageType.Error); } else { EditorGUILayout.HelpBox("TTS with the current settings is not possible!", MessageType.Error); } } */ EditorHelper.SeparatorUI(); GUILayout.Label("Information", EditorStyles.boldLabel); GUILayout.Label("Speech Count:\t" + script.SpeechCount); GUILayout.Label("Total Speeches:\t" + Util.Context.NumberOfSpeeches); #if UNITY_2019_1_OR_NEWER GUILayout.Label("Total Files:\t" + Util.Context.NumberOfAudioFiles); #else GUILayout.Label("Total Audio Files:\t" + Util.Context.NumberOfAudioFiles); #endif /* if (script.Caching) { GUILayout.Space(12); GUILayout.Label($"Cached Speeches:\t{Util.Helper.FormatBytesToHRF(GlobalCache.Instance.CurrentClipCacheSize)}/{Util.Helper.FormatBytesToHRF(GlobalCache.Instance.ClipCacheSize)} ({GlobalCache.Instance.Clips.Count})"); GUILayout.Label($"Cache Efficiency:\t{Util.Context.CacheEfficiency:P}"); } */ } else { EditorGUILayout.HelpBox("Script is disabled!", MessageType.Info); } serializedObject.ApplyModifiedProperties(); } #endregion #region Private methods private static void hierarchyItemCB(int instanceID, Rect selectionRect) { if (EditorConfig.HIERARCHY_ICON) { GameObject go = EditorUtility.InstanceIDToObject(instanceID) as GameObject; if (go != null && go.GetComponent()) { Rect r = new Rect(selectionRect); r.x = r.width - 4; //Debug.Log("HierarchyItemCB: " + r); GUI.Label(r, EditorHelper.Logo_Asset_Small); } } } #endregion } } #endif // © 2016-2020 crosstales LLC (https://www.crosstales.com)