using UnityEngine; namespace Crosstales.RTVoice.Model { /// Wrapper for "Speak"-function calls. [System.Serializable] public class Wrapper { #region Variables [Tooltip("Text for the speech."), TextArea(1, 5), SerializeField] private string text = string.Empty; [Tooltip("AudioSource for the speech."), SerializeField] private AudioSource source; [Tooltip("Voice for the speech."), SerializeField] private Voice voice; [Tooltip("Speak immediately after the audio generation. Only works if 'Source' is not null."), SerializeField] private bool speakImmediately = true; [Tooltip("Speech rate of the speaker in percent (1 = 100%, default: 1, optional)."), Range(0.01f, 3f), SerializeField] private float rate = 1f; [Tooltip("Speech pitch of the speaker in percent (1 = 100%, default: 1, optional)."), Range(0f, 2f), SerializeField] private float pitch = 1f; [Tooltip("Volume of the speaker in percent (1 = 100%, default: 1, optional)."), Range(0f, 1f), SerializeField] private float volume = 1f; [Tooltip("Output file (without extension) for the generated audio."), SerializeField] private string outputFile; [Tooltip("Force SSML on supported platforms."), SerializeField] private bool forceSSML = true; [Tooltip("Is the current wrapper just a part of a speech (only used in iOS)."), SerializeField] private bool _isPartial; private string uid; private string cachedString; private readonly System.DateTime created = System.DateTime.Now; #endregion #region Properties /// Text for the speech. public string Text { get { if (cachedString == null) { string result = cachedString = Util.Helper.CleanText(text, Speaker.Instance.AutoClearTags /*&& !(Speaker.isMaryMode /* || Util.Helper.isWindowsPlatform )*/); if (result.Length > Speaker.Instance.MaxTextLength) { Debug.LogWarning("Text is too long! It will be shortened to " + Speaker.Instance.MaxTextLength + " characters: " + this); cachedString = result.Substring(0, Speaker.Instance.MaxTextLength); } } return cachedString; } set { cachedString = null; text = value; } } /// AudioSource for the speech. public AudioSource Source { get => source; set => source = value; } /// Voice for the speech. public Voice Voice { get => voice; set => voice = value; } /// Speak immediately after the audio generation. Only works if 'Source' is not null. public bool SpeakImmediately { get => speakImmediately; set => speakImmediately = value; } /// Rate of the speech (range: 0.01-3). public float Rate { get => rate; set => rate = Mathf.Clamp(value, 0.01f, 3f); } /// Pitch of the speech (range: 0-2). public float Pitch { get => pitch; set => pitch = Mathf.Clamp(value, 0f, 2f); } /// Volume of the speech (range: 0-1). public float Volume { get => volume; set => volume = Mathf.Clamp(value, 0f, 1f); } /// Output file (without extension) for the generated audio. public string OutputFile { get => outputFile; set => outputFile = value; } /// Force SSML on supported platforms. public bool ForceSSML { get => forceSSML; set => forceSSML = value; } /// Is the current wrapper just a part of a speech (only used in iOS). public bool isPartial { get => _isPartial; set => _isPartial = value; } /// UID of the speech. public string Uid { get => uid; private set => uid = value; } /// Returns the creation time of the Wrapper. /// Creation time of the Wrapper. public System.DateTime Created => created; /// Returns the speech time in seconds (0: no audio file was generated). /// Speech time in seconds. public float SpeechTime { get { if (!Util.Helper.isEditorMode && source != null && source.clip != null) { return source.clip.length; } return 0f; } } #endregion #region Constructors /// Default. public Wrapper() { uid = System.Guid.NewGuid().ToString(); } /// Instantiate the class. /// Text for the speech. /// Voice for the speech (default: null, optional). /// Rate of the speech (values: 0-3, default: 1, optional). /// Pitch of the speech (values: 0-2, default: 1, optional). /// Volume of the speech (values: 0-1, default: 1, optional). /// Force SSML on supported platforms (default: true, optional). public Wrapper(string text, Voice voice = null, float rate = 1f, float pitch = 1f, float volume = 1f, bool forceSSML = true) { uid = System.Guid.NewGuid().ToString(); Text = text; this.voice = voice; Rate = rate; Pitch = pitch; Volume = volume; this.forceSSML = forceSSML; } /// Instantiate the class. /// Text for the speech. /// Voice for the speech (default: null, optional). /// Rate of the speech (values: 0-3, default: 1, optional). /// Pitch of the speech (values: 0-2, default: 1, optional). /// Volume of the speech (values: 0-1, default: 1, optional). /// AudioSource for the speech (default: null, optional). /// Speak immediately after the audio generation. Only works if 'Source' is not null (default: true, optional). /// Output file (without extension) for the generated audio (default: empty, optional). /// Force SSML on supported platforms (default: true, optional). public Wrapper(string text, Voice voice = null, float rate = 1f, float pitch = 1f, float volume = 1f, AudioSource source = null, bool speakImmediately = true, string outputFile = "", bool forceSSML = true) { uid = System.Guid.NewGuid().ToString(); Text = text; this.source = source; this.voice = voice; this.speakImmediately = speakImmediately; Rate = rate; Pitch = pitch; Volume = volume; this.outputFile = outputFile; this.forceSSML = forceSSML; } /// Instantiate the class. /// UID of the speech. /// Text for the speech. /// Voice for the speech (default: null, optional). /// Rate of the speech (values: 0-3, default: 1, optional). /// Pitch of the speech (values: 0-2, default: 1, optional). /// Volume of the speech (values: 0-1, default: 1, optional). /// AudioSource for the speech (default: null, optional). /// Speak immediately after the audio generation. Only works if 'Source' is not null (default: true, optional). /// Output file (without extension) for the generated audio (default: empty, optional). /// Force SSML on supported platforms (default: true, optional). public Wrapper(string uid, string text, Voice voice = null, float rate = 1f, float pitch = 1f, float volume = 1f, AudioSource source = null, bool speakImmediately = true, string outputFile = "", bool forceSSML = true) : this(text, voice, rate, pitch, volume, source, speakImmediately, outputFile, forceSSML) { this.uid = uid; } #endregion #region Overridden methods public override string ToString() { System.Text.StringBuilder result = new System.Text.StringBuilder(); result.Append(GetType().Name); result.Append(Util.Constants.TEXT_TOSTRING_START); result.Append("Uid='"); result.Append(uid); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Text='"); result.Append(text); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Source='"); result.Append(source); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Voice='"); result.Append(voice); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("SpeakImmediately='"); result.Append(speakImmediately); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Rate='"); result.Append(rate); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Pitch='"); result.Append(pitch); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Volume='"); result.Append(volume); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("OutputFile='"); result.Append(outputFile); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("ForceSSML='"); result.Append(forceSSML); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("isPartial='"); result.Append(isPartial); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER); result.Append("Created='"); result.Append(Created); result.Append(Util.Constants.TEXT_TOSTRING_DELIMITER_END); result.Append(Util.Constants.TEXT_TOSTRING_END); return result.ToString(); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) return false; Wrapper o = (Wrapper)obj; bool result = Text == o.Text && voice == o.voice && System.Math.Abs(Rate - o.Rate) < Util.Constants.FLOAT_TOLERANCE && System.Math.Abs(Pitch - o.Pitch) < Util.Constants.FLOAT_TOLERANCE; return result; } public override int GetHashCode() { int hash = 0; if (Text != null) hash += Text.GetHashCode(); if (voice != null) hash += voice.GetHashCode(); hash += (int)(Rate * 100) * 17; hash += (int)(Pitch * 100) * 17; return hash; } #endregion } } // © 2015-2020 crosstales LLC (https://www.crosstales.com)