Decompiled source of PeakTTS v1.2.1
PeakTTS/PeakTTS.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PeakTTS")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1")] [assembly: AssemblyProduct("PeakTTS")] [assembly: AssemblyTitle("PeakTTS")] [assembly: AssemblyVersion("1.2.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PeakTTS { internal static class ExternalTts { public static SpeechClip SynthesizePiper(string text, VoiceOption voice, string piperPath, int speakerId) { if (voice == null || voice.Backend != VoiceBackend.Piper) { throw new ArgumentException("A Piper voice is required", "voice"); } if (!File.Exists(voice.ModelPath)) { throw new FileNotFoundException("Piper .onnx model was not found", voice.ModelPath); } if (!File.Exists(voice.ConfigPath)) { throw new FileNotFoundException("Piper model config (.onnx.json) was not found", voice.ConfigPath); } if (string.IsNullOrWhiteSpace(piperPath)) { throw new InvalidOperationException("PiperPath is empty"); } string text2 = TempWavPath(); try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("--model ").Append(QuoteArg(voice.ModelPath)); stringBuilder.Append(" --output_file ").Append(QuoteArg(text2)); if (speakerId >= 0) { stringBuilder.Append(" --speaker ").Append(speakerId); } ProcessStartInfo processStartInfo = CreateProcess(piperPath, stringBuilder.ToString(), Path.GetDirectoryName(voice.ModelPath)); processStartInfo.RedirectStandardInput = true; RunProcess(processStartInfo, text ?? string.Empty, "Piper"); if (!File.Exists(text2)) { throw new FileNotFoundException("Piper did not produce a WAV file", text2); } return WavReader.ReadMono(text2); } finally { TryDelete(text2); } } public static SpeechClip SynthesizeCoqui(string text, VoiceOption voice, string coquiTtsPath, string speaker, string language, string speakerWav) { if (voice == null || voice.Backend != VoiceBackend.Coqui) { throw new ArgumentException("A Coqui voice is required", "voice"); } if (!File.Exists(voice.ModelPath)) { throw new FileNotFoundException("Coqui .pth model was not found", voice.ModelPath); } if (!File.Exists(voice.ConfigPath)) { throw new FileNotFoundException("Coqui model config (config.json) was not found", voice.ConfigPath); } if (string.IsNullOrWhiteSpace(coquiTtsPath)) { throw new InvalidOperationException("CoquiTtsPath is empty"); } string text2 = TempWavPath(); try { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("--text ").Append(QuoteArg(text ?? string.Empty)); stringBuilder.Append(" --model_path ").Append(QuoteArg(voice.ModelPath)); stringBuilder.Append(" --config_path ").Append(QuoteArg(voice.ConfigPath)); stringBuilder.Append(" --out_path ").Append(QuoteArg(text2)); if (!string.IsNullOrWhiteSpace(speaker)) { stringBuilder.Append(" --speaker_idx ").Append(QuoteArg(speaker)); } if (!string.IsNullOrWhiteSpace(language)) { stringBuilder.Append(" --language_idx ").Append(QuoteArg(language)); } string value = ResolveOptionalPath(speakerWav, Path.GetDirectoryName(voice.ModelPath)); if (!string.IsNullOrWhiteSpace(value)) { stringBuilder.Append(" --speaker_wav ").Append(QuoteArg(value)); } RunProcess(CreateProcess(coquiTtsPath, stringBuilder.ToString(), Path.GetDirectoryName(voice.ModelPath)), null, "Coqui TTS"); if (!File.Exists(text2)) { throw new FileNotFoundException("Coqui TTS did not produce a WAV file", text2); } return WavReader.ReadMono(text2); } finally { TryDelete(text2); } } private static void RunProcess(ProcessStartInfo psi, string standardInput, string label) { StringBuilder stdout = new StringBuilder(); StringBuilder stderr = new StringBuilder(); using Process process = new Process { StartInfo = psi }; process.OutputDataReceived += delegate(object _, DataReceivedEventArgs e) { if (e.Data != null) { stdout.AppendLine(e.Data); } }; process.ErrorDataReceived += delegate(object _, DataReceivedEventArgs e) { if (e.Data != null) { stderr.AppendLine(e.Data); } }; if (!process.Start()) { throw new InvalidOperationException("Could not start " + label); } process.BeginOutputReadLine(); process.BeginErrorReadLine(); if (psi.RedirectStandardInput) { process.StandardInput.Write(standardInput ?? string.Empty); process.StandardInput.WriteLine(); process.StandardInput.Close(); } process.WaitForExit(); process.WaitForExit(); if (process.ExitCode != 0) { throw new InvalidOperationException($"{label} exited with code {process.ExitCode}: {stderr}\n{stdout}"); } } private static ProcessStartInfo CreateProcess(string executable, string arguments, string workingDirectory) { return new ProcessStartInfo { FileName = executable, Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = (string.IsNullOrWhiteSpace(workingDirectory) ? Environment.CurrentDirectory : workingDirectory) }; } private static string TempWavPath() { string text = Path.Combine(Path.GetTempPath(), "PeakTTS"); Directory.CreateDirectory(text); return Path.Combine(text, Guid.NewGuid().ToString("N") + ".wav"); } private static string ResolveOptionalPath(string value, string baseDirectory) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } if (Path.IsPathRooted(value)) { return value; } return Path.Combine(baseDirectory ?? Environment.CurrentDirectory, value); } private static string QuoteArg(string value) { if (value == null) { return "\"\""; } if (value.Length > 0 && value.IndexOfAny(new char[5] { ' ', '\t', '\n', '\v', '"' }) < 0) { return value; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('"'); int num = 0; foreach (char c in value) { switch (c) { case '\\': num++; continue; case '"': stringBuilder.Append('\\', num * 2 + 1); stringBuilder.Append('"'); num = 0; continue; } if (num > 0) { stringBuilder.Append('\\', num); num = 0; } stringBuilder.Append(c); } if (num > 0) { stringBuilder.Append('\\', num * 2); } stringBuilder.Append('"'); return stringBuilder.ToString(); } private static void TryDelete(string path) { try { if (!string.IsNullOrEmpty(path) && File.Exists(path)) { File.Delete(path); } } catch { } } } internal static class GameInputBridge { private static Type _guiManagerType; private static PropertyInfo _instanceProperty; private static FieldInfo _instanceField; private static PropertyInfo _windowBlockingInputProperty; public static bool InstallPatch(Harmony harmony) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown try { _guiManagerType = AccessTools.TypeByName("GUIManager"); if (_guiManagerType == null) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)"GUIManager not found; the TTS box will still work, but PEAK input may not be blocked while typing"); } return false; } _instanceProperty = AccessTools.Property(_guiManagerType, "instance"); _instanceField = AccessTools.Field(_guiManagerType, "instance"); _windowBlockingInputProperty = AccessTools.Property(_guiManagerType, "windowBlockingInput"); MethodInfo methodInfo = AccessTools.Method(_guiManagerType, "UpdateWindowStatus", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogWarning((object)"GUIManager.UpdateWindowStatus not found; inputblock disabled"); } return false; } HarmonyMethod val = new HarmonyMethod(typeof(GameInputBridge), "UpdateWindowStatusPostfix", (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { ManualLogSource logSource3 = Plugin.LogSource; if (logSource3 != null) { logSource3.LogWarning((object)("Could not install PEAK inputblock: " + ex.Message)); } return false; } } public static bool IsBlockedByGame() { try { EnsureReflection(); object instance = GetInstance(); if (instance == null || _windowBlockingInputProperty == null) { return false; } return Convert.ToBoolean(_windowBlockingInputProperty.GetValue(instance, null)); } catch { return false; } } private static void UpdateWindowStatusPostfix() { if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.IsInputOpen) { return; } try { EnsureReflection(); object instance = GetInstance(); if (instance != null && !(_windowBlockingInputProperty == null) && _windowBlockingInputProperty.CanWrite) { _windowBlockingInputProperty.SetValue(instance, true, null); } } catch { } } private static object GetInstance() { object obj = _instanceProperty?.GetValue(null, null); if (obj == null) { FieldInfo instanceField = _instanceField; if ((object)instanceField == null) { return null; } obj = instanceField.GetValue(null); } return obj; } private static void EnsureReflection() { if (!(_guiManagerType != null)) { _guiManagerType = AccessTools.TypeByName("GUIManager"); if (!(_guiManagerType == null)) { _instanceProperty = AccessTools.Property(_guiManagerType, "instance"); _instanceField = AccessTools.Field(_guiManagerType, "instance"); _windowBlockingInputProperty = AccessTools.Property(_guiManagerType, "windowBlockingInput"); } } } } internal sealed class LanguageChoice { public string Code; public string DisplayName; public override string ToString() { return DisplayName ?? Code ?? "Language"; } } internal static class LanguageSupport { private static readonly LanguageChoice[] Choices = new LanguageChoice[25] { new LanguageChoice { Code = "auto", DisplayName = "Auto" }, new LanguageChoice { Code = "en", DisplayName = "English (en)" }, new LanguageChoice { Code = "ko", DisplayName = "Korean (ko)" }, new LanguageChoice { Code = "ja", DisplayName = "Japanese (ja)" }, new LanguageChoice { Code = "zh-cn", DisplayName = "Chinese (zh-cn)" }, new LanguageChoice { Code = "es", DisplayName = "Spanish (es)" }, new LanguageChoice { Code = "fr", DisplayName = "French (fr)" }, new LanguageChoice { Code = "de", DisplayName = "German (de)" }, new LanguageChoice { Code = "it", DisplayName = "Italian (it)" }, new LanguageChoice { Code = "pt", DisplayName = "Portuguese (pt)" }, new LanguageChoice { Code = "pl", DisplayName = "Polish (pl)" }, new LanguageChoice { Code = "tr", DisplayName = "Turkish (tr)" }, new LanguageChoice { Code = "ru", DisplayName = "Russian (ru)" }, new LanguageChoice { Code = "nl", DisplayName = "Dutch (nl)" }, new LanguageChoice { Code = "cs", DisplayName = "Czech (cs)" }, new LanguageChoice { Code = "ar", DisplayName = "Arabic (ar)" }, new LanguageChoice { Code = "hu", DisplayName = "Hungarian (hu)" }, new LanguageChoice { Code = "hi", DisplayName = "Hindi (hi)" }, new LanguageChoice { Code = "el", DisplayName = "Greek (el)" }, new LanguageChoice { Code = "he", DisplayName = "Hebrew (he)" }, new LanguageChoice { Code = "th", DisplayName = "Thai (th)" }, new LanguageChoice { Code = "sv", DisplayName = "Swedish (sv)" }, new LanguageChoice { Code = "da", DisplayName = "Danish (da)" }, new LanguageChoice { Code = "no", DisplayName = "Norwegian (no)" }, new LanguageChoice { Code = "fi", DisplayName = "Finnish (fi)" } }; private static readonly Dictionary<string, string> FriendlyNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["en"] = "English", ["ko"] = "Korean", ["ja"] = "Japanese", ["zh"] = "Chinese", ["zh-cn"] = "Chinese", ["es"] = "Spanish", ["fr"] = "French", ["de"] = "German", ["it"] = "Italian", ["pt"] = "Portuguese", ["pl"] = "Polish", ["tr"] = "Turkish", ["ru"] = "Russian", ["nl"] = "Dutch", ["cs"] = "Czech", ["ar"] = "Arabic", ["hu"] = "Hungarian", ["hi"] = "Hindi", ["el"] = "Greek", ["he"] = "Hebrew", ["th"] = "Thai", ["sv"] = "Swedish", ["da"] = "Danish", ["no"] = "Norwegian", ["fi"] = "Finnish" }; public static LanguageChoice[] SnapshotChoices() { return Choices.Select((LanguageChoice c) => new LanguageChoice { Code = c.Code, DisplayName = c.DisplayName }).ToArray(); } public static string Step(string current, int delta) { string normalized = NormalizeLanguageCode(current); if (string.IsNullOrEmpty(normalized)) { normalized = "auto"; } int num = Array.FindIndex(Choices, (LanguageChoice c) => string.Equals(c.Code, normalized, StringComparison.OrdinalIgnoreCase)); if (num < 0) { num = 0; } int num2 = (num + delta) % Choices.Length; if (num2 < 0) { num2 += Choices.Length; } return Choices[num2].Code; } public static string GetDisplayName(string code) { string normalized = NormalizeLanguageCode(code); if (string.IsNullOrEmpty(normalized) || normalized == "auto") { return "Auto"; } return Choices.FirstOrDefault((LanguageChoice c) => string.Equals(c.Code, normalized, StringComparison.OrdinalIgnoreCase))?.DisplayName ?? (FriendlyLanguageName(normalized) + " (" + normalized + ")"); } public static string FriendlyLanguageName(string code) { string text = NormalizeLanguageCode(code); if (string.IsNullOrEmpty(text) || text == "auto") { return "Auto"; } string key = LanguageFamily(text); if (FriendlyNames.TryGetValue(text, out var value)) { return value; } if (FriendlyNames.TryGetValue(key, out var value2)) { return value2; } return text; } public static string ResolveRequestedLanguage(string setting, string text) { return ResolveRequestedLanguage(setting, text, null); } public static string ResolveRequestedLanguage(string setting, string text, IEnumerable<string> preferredVoiceLanguages) { string text2 = NormalizeLanguageCode(setting); if (!string.IsNullOrEmpty(text2) && text2 != "auto") { return text2; } string text3 = DetectConfident(text); if (!string.IsNullOrEmpty(text3)) { return text3; } string text4 = PreferredVoiceLanguage(preferredVoiceLanguages); if (!string.IsNullOrEmpty(text4)) { return text4; } return "en"; } public static string Detect(string text) { string text2 = DetectConfident(text); if (!string.IsNullOrEmpty(text2)) { return text2; } return "en"; } public static string DetectConfident(string text) { if (string.IsNullOrWhiteSpace(text)) { return string.Empty; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; foreach (int num10 in text) { if ((num10 >= 44032 && num10 <= 55215) || (num10 >= 4352 && num10 <= 4607) || (num10 >= 12592 && num10 <= 12687)) { num++; } else if ((num10 >= 12352 && num10 <= 12543) || (num10 >= 12784 && num10 <= 12799)) { num2++; } else if ((num10 >= 19968 && num10 <= 40959) || (num10 >= 13312 && num10 <= 19903)) { num3++; } else if ((num10 >= 1536 && num10 <= 1791) || (num10 >= 1872 && num10 <= 1919) || (num10 >= 2208 && num10 <= 2303)) { num4++; } else if (num10 >= 1024 && num10 <= 1327) { num5++; } else if (num10 >= 2304 && num10 <= 2431) { num6++; } else if (num10 >= 880 && num10 <= 1023) { num7++; } else if (num10 >= 1424 && num10 <= 1535) { num8++; } else if (num10 >= 3584 && num10 <= 3711) { num9++; } } if (num > 0) { return "ko"; } if (num2 > 0) { return "ja"; } if (num3 > 0) { return "zh-cn"; } if (num4 > 0) { return "ar"; } if (num5 > 0) { return "ru"; } if (num6 > 0) { return "hi"; } if (num7 > 0) { return "el"; } if (num8 > 0) { return "he"; } if (num9 > 0) { return "th"; } return DetectLatinLanguage(text); } private static string PreferredVoiceLanguage(IEnumerable<string> languages) { if (languages == null) { return string.Empty; } string[] array = (from v in languages.Select(NormalizeLanguageCode) where !string.IsNullOrEmpty(v) && v != "auto" select v).ToArray(); if (array.Length == 0) { return string.Empty; } string[] array2 = (from v in array.Select(LanguageFamily) where !string.IsNullOrEmpty(v) select v).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); if (array2.Length != 1) { return string.Empty; } return array2[0]; } public static string NormalizeLanguageCode(string code) { if (string.IsNullOrWhiteSpace(code)) { return string.Empty; } string text = code.Trim().Replace('_', '-').ToLowerInvariant(); if (text == "automatic") { return "auto"; } if (text == "zh" || text.StartsWith("zh-cn", StringComparison.Ordinal) || text.StartsWith("zh-hans", StringComparison.Ordinal)) { return "zh-cn"; } if (text.StartsWith("zh-tw", StringComparison.Ordinal) || text.StartsWith("zh-hk", StringComparison.Ordinal) || text.StartsWith("zh-hant", StringComparison.Ordinal)) { return "zh"; } return text; } public static string LanguageFamily(string code) { string text = NormalizeLanguageCode(code); if (string.IsNullOrEmpty(text) || text == "auto") { return text; } if (text.StartsWith("zh", StringComparison.Ordinal)) { return "zh"; } int num = text.IndexOf('-'); if (num <= 0) { return text; } return text.Substring(0, num); } public static bool SupportsLanguage(IEnumerable<string> supportedLanguages, string requestedLanguage) { if (supportedLanguages == null) { return false; } string text = NormalizeLanguageCode(requestedLanguage); if (string.IsNullOrEmpty(text) || text == "auto") { return true; } string b = LanguageFamily(text); foreach (string supportedLanguage in supportedLanguages) { string text2 = NormalizeLanguageCode(supportedLanguage); if (!string.IsNullOrEmpty(text2)) { if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(LanguageFamily(text2), b, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } public static string BestLanguageCodeForBackend(IEnumerable<string> supportedLanguages, string requestedLanguage) { if (supportedLanguages == null) { return string.Empty; } string requested = NormalizeLanguageCode(requestedLanguage); if (string.IsNullOrEmpty(requested) || requested == "auto") { return string.Empty; } string requestedFamily = LanguageFamily(requested); string[] source = (from v in supportedLanguages.Select(NormalizeLanguageCode) where !string.IsNullOrEmpty(v) select v).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray(); string text = source.FirstOrDefault((string v) => string.Equals(v, requested, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(text)) { return text; } return source.FirstOrDefault((string v) => string.Equals(LanguageFamily(v), requestedFamily, StringComparison.OrdinalIgnoreCase)) ?? string.Empty; } private static string DetectLatinLanguage(string text) { string text2 = (text ?? string.Empty).ToLowerInvariant(); if (ContainsAny(text2, "ą", "ć", "ę", "ł", "ń", "ś", "ź", "ż")) { return "pl"; } if (ContainsAny(text2, "č", "ď", "ě", "ň", "ř", "š", "ť", "ů", "ž")) { return "cs"; } if (ContainsAny(text2, "ğ", "ı", "ş")) { return "tr"; } if (ContainsAny(text2, "ő", "ű", "ó")) { return "hu"; } if (ContainsAny(text2, "ß")) { return "de"; } if (ContainsAny(text2, "ñ", "¿", "¡")) { return "es"; } if (ContainsAny(text2, "ã", "õ")) { return "pt"; } if (ContainsAny(text2, "œ", "æ")) { return "fr"; } string[] array = Tokenize(text2); if (array.Length == 0) { return string.Empty; } Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); dictionary["en"] = Score(array, "the", "and", "you", "is", "are", "this", "that", "with", "for", "not", "hello", "what"); dictionary["es"] = Score(array, "el", "la", "los", "las", "que", "de", "y", "es", "una", "por", "para", "hola"); dictionary["fr"] = Score(array, "le", "la", "les", "des", "que", "et", "est", "une", "pour", "pas", "bonjour", "avec"); dictionary["de"] = Score(array, "der", "die", "das", "und", "ist", "nicht", "ich", "du", "mit", "für", "ein", "eine"); dictionary["it"] = Score(array, "il", "lo", "la", "gli", "che", "e", "è", "non", "una", "per", "con", "ciao"); dictionary["pt"] = Score(array, "o", "a", "os", "as", "que", "de", "e", "não", "uma", "para", "com", "olá"); dictionary["nl"] = Score(array, "de", "het", "een", "en", "is", "niet", "ik", "je", "met", "voor", "dat", "hallo"); dictionary["tr"] = Score(array, "ve", "bir", "bu", "de", "da", "için", "ile", "ben", "sen", "değil", "merhaba"); dictionary["hu"] = Score(array, "és", "egy", "hogy", "nem", "az", "a", "van", "én", "te", "meg", "szia", "ez", "itt", "ott", "vagy", "vagyok", "volt", "lesz", "mert", "mint", "csak", "igen", "mit", "miért", "jó", "köszönöm", "köszi", "magyar"); dictionary["sv"] = Score(array, "och", "det", "är", "inte", "en", "ett", "jag", "du", "med", "för", "hej"); dictionary["da"] = Score(array, "og", "det", "er", "ikke", "en", "et", "jeg", "du", "med", "for", "hej"); dictionary["no"] = Score(array, "og", "det", "er", "ikke", "en", "et", "jeg", "du", "med", "for", "hei"); dictionary["fi"] = Score(array, "ja", "on", "ei", "että", "se", "minä", "sinä", "kanssa", "hei"); KeyValuePair<string, int>[] array2 = dictionary.OrderByDescending((KeyValuePair<string, int> kv) => kv.Value).ToArray(); if (array2.Length != 0 && array2[0].Value >= 2 && (array2.Length == 1 || array2[0].Value > array2[1].Value)) { return array2[0].Key; } return string.Empty; } private static string[] Tokenize(string text) { char[] array = new char[text.Length]; for (int i = 0; i < text.Length; i++) { char c = text[i]; array[i] = (char.IsLetter(c) ? c : ' '); } return new string(array).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); } private static int Score(string[] words, params string[] markers) { if (words == null || markers == null) { return 0; } HashSet<string> hashSet = new HashSet<string>(words, StringComparer.OrdinalIgnoreCase); int num = 0; foreach (string item in markers) { if (hashSet.Contains(item)) { num++; } } return num; } private static bool ContainsAny(string value, params string[] needles) { if (string.IsNullOrEmpty(value) || needles == null) { return false; } foreach (string value2 in needles) { if (!string.IsNullOrEmpty(value2) && value.IndexOf(value2, StringComparison.Ordinal) >= 0) { return true; } } return false; } } internal sealed class PeakNativeUiRenderer : IDisposable { private sealed class VisualSlot { public GameObject Object; public RectTransform RectTransform; public Component RawImage; } private sealed class TextSlot { public GameObject Object; public RectTransform RectTransform; public Component Text; } private readonly ManualLogSource _log; private readonly List<VisualSlot> _visualSlots = new List<VisualSlot>(); private readonly List<TextSlot> _textSlots = new List<TextSlot>(); private Type _rawImageType; private PropertyInfo _rawImageTextureProperty; private PropertyInfo _rawImageColorProperty; private PropertyInfo _rawImageRaycastProperty; private Type _tmpTextType; private PropertyInfo _tmpTextProperty; private PropertyInfo _tmpFontProperty; private PropertyInfo _tmpFontSharedMaterialProperty; private PropertyInfo _tmpFontSizeProperty; private PropertyInfo _tmpColorProperty; private PropertyInfo _tmpAlignmentProperty; private PropertyInfo _tmpWordWrapProperty; private PropertyInfo _tmpRaycastProperty; private PropertyInfo _tmpRichTextProperty; private PropertyInfo _tmpAutoSizingProperty; private Object _nativeFont; private Material _nativeMaterial; private bool _ownsNativeMaterial; private PropertyInfo _tmpMaterialForRenderingProperty; private GameObject _canvasObject; private Canvas _canvas; private Transform _visualRoot; private Transform _textRoot; private int _visualCursor; private int _textCursor; private float _nextFontScanAt; private bool _loggedFontReady; private bool _loggedMissingUi; internal bool CanRenderVisuals { get { ResolveTypes(); if (_rawImageType != null) { return EnsureCanvas(); } return false; } } internal bool CanRenderNativeText { get { if (CanRenderVisuals && _tmpTextType != null && _nativeFont != (Object)null) { return (Object)(object)_nativeMaterial != (Object)null; } return false; } } internal PeakNativeUiRenderer(ManualLogSource log) { _log = log; ResolveTypes(); } internal void TryDiscoverNativeFont(bool allowScan) { if (CanRenderNativeText || !allowScan || Time.unscaledTime < _nextFontScanAt) { return; } _nextFontScanAt = Time.unscaledTime + 2f; ResolveTypes(); if (_tmpTextType == null) { return; } try { Object[] array = Object.FindObjectsByType(_tmpTextType, (FindObjectsSortMode)0); if (array == null) { return; } Object[] array2 = array; foreach (Object obj in array2) { Component val = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } object value = GetValue(_tmpFontProperty, val); Object val2 = (Object)((value is Object) ? value : null); string text = ((val2 != (Object)null) ? (val2.name ?? string.Empty) : string.Empty); if (text.IndexOf("DarumaDropOne", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Daruma Drop One", StringComparison.OrdinalIgnoreCase) >= 0) { Material val3 = CreateCleanFontMaterial(val2, val); if (!((Object)(object)val3 == (Object)null)) { ReleaseNativeMaterial(); _nativeFont = val2; _nativeMaterial = val3; _ownsNativeMaterial = true; break; } } } if (CanRenderNativeText && !_loggedFontReady) { _loggedFontReady = true; ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("Found PEAK native font '" + _nativeFont.name + "' and created a clean SDF material")); } } } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogDebug((object)("PEAK native Daruma font is not available yet: " + ex.Message)); } } } internal void BeginGuiFrame() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if (Event.current != null && (int)Event.current.type == 7) { _visualCursor = 0; _textCursor = 0; } } internal void EndGuiFrame() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if (Event.current == null || (int)Event.current.type != 7) { return; } for (int i = _visualCursor; i < _visualSlots.Count; i++) { VisualSlot visualSlot = _visualSlots[i]; if ((Object)(object)visualSlot?.Object != (Object)null && visualSlot.Object.activeSelf) { visualSlot.Object.SetActive(false); } } for (int j = _textCursor; j < _textSlots.Count; j++) { TextSlot textSlot = _textSlots[j]; if ((Object)(object)textSlot?.Object != (Object)null && textSlot.Object.activeSelf) { textSlot.Object.SetActive(false); } } } internal bool DrawRect(Rect rect, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture, color); } internal bool DrawTexture(Rect rect, Texture texture, Color color) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!CanRenderVisuals) { return false; } if (Event.current == null || (int)Event.current.type != 7) { return true; } try { VisualSlot visualSlot = GetVisualSlot(_visualCursor++); if (visualSlot == null) { return false; } ConfigureRect(visualSlot.RectTransform, rect); SetValue(_rawImageTextureProperty, visualSlot.RawImage, texture); SetValue(_rawImageColorProperty, visualSlot.RawImage, color); SetValue(_rawImageRaycastProperty, visualSlot.RawImage, false); if (!visualSlot.Object.activeSelf) { visualSlot.Object.SetActive(true); } return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("Canvas visual draw failed: " + ex.Message)); } return false; } } internal bool DrawText(Rect rect, string text, float fontSize, TextAnchor alignment, Color color, bool wordWrap) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (!CanRenderNativeText) { return false; } if (Event.current == null || (int)Event.current.type != 7) { return true; } try { TextSlot textSlot = GetTextSlot(_textCursor++); if (textSlot == null) { return false; } ConfigureRect(textSlot.RectTransform, rect); SetValue(_tmpTextProperty, textSlot.Text, text ?? string.Empty); SetValue(_tmpFontProperty, textSlot.Text, _nativeFont); if (_tmpFontSharedMaterialProperty != null && _tmpFontSharedMaterialProperty.CanWrite) { SetValue(_tmpFontSharedMaterialProperty, textSlot.Text, _nativeMaterial); } SetValue(_tmpFontSizeProperty, textSlot.Text, Mathf.Max(1f, fontSize)); SetValue(_tmpColorProperty, textSlot.Text, color); SetEnumValue(_tmpAlignmentProperty, textSlot.Text, ToTmpAlignment(alignment)); SetValue(_tmpWordWrapProperty, textSlot.Text, wordWrap); SetValue(_tmpRaycastProperty, textSlot.Text, false); SetValue(_tmpRichTextProperty, textSlot.Text, false); SetValue(_tmpAutoSizingProperty, textSlot.Text, false); if (!textSlot.Object.activeSelf) { textSlot.Object.SetActive(true); } return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("Native TMP text draw failed: " + ex.Message)); } return false; } } internal void ResetForScene() { DestroyCanvas(); _nativeFont = null; ReleaseNativeMaterial(); _nativeFont = null; _nextFontScanAt = 0f; _loggedFontReady = false; } public void Dispose() { DestroyCanvas(); _nativeFont = null; ReleaseNativeMaterial(); _nativeFont = null; } private void ResolveTypes() { if (_rawImageType == null) { _rawImageType = Type.GetType("UnityEngine.UI.RawImage, UnityEngine.UI", throwOnError: false); if (_rawImageType != null) { _rawImageTextureProperty = _rawImageType.GetProperty("texture"); _rawImageColorProperty = _rawImageType.GetProperty("color"); _rawImageRaycastProperty = _rawImageType.GetProperty("raycastTarget"); } } if (_tmpTextType == null) { _tmpTextType = Type.GetType("TMPro.TextMeshProUGUI, Unity.TextMeshPro", throwOnError: false); if (_tmpTextType != null) { _tmpTextProperty = _tmpTextType.GetProperty("text"); _tmpFontProperty = _tmpTextType.GetProperty("font"); _tmpFontSharedMaterialProperty = _tmpTextType.GetProperty("fontSharedMaterial"); _tmpFontSizeProperty = _tmpTextType.GetProperty("fontSize"); _tmpColorProperty = _tmpTextType.GetProperty("color"); _tmpAlignmentProperty = _tmpTextType.GetProperty("alignment"); _tmpWordWrapProperty = _tmpTextType.GetProperty("enableWordWrapping"); _tmpRaycastProperty = _tmpTextType.GetProperty("raycastTarget"); _tmpRichTextProperty = _tmpTextType.GetProperty("richText"); _tmpAutoSizingProperty = _tmpTextType.GetProperty("enableAutoSizing"); _tmpMaterialForRenderingProperty = _tmpTextType.GetProperty("materialForRendering"); } } if ((_rawImageType == null || _tmpTextType == null) && !_loggedMissingUi) { _loggedMissingUi = true; ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"Unity UI/TextMeshPro types were not found yet. Using IMGUI fallback until they are available"); } } } private bool EnsureCanvas() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown if ((Object)(object)_canvasObject != (Object)null && (Object)(object)_canvas != (Object)null) { return true; } if (_rawImageType == null) { return false; } try { _canvasObject = new GameObject("PeakTTS.NativeCanvas", new Type[2] { typeof(RectTransform), typeof(Canvas) }) { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_canvasObject); _canvas = _canvasObject.GetComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.pixelPerfect = false; _canvas.overrideSorting = true; _canvas.sortingOrder = 32767; GameObject val = new GameObject("Visuals", new Type[1] { typeof(RectTransform) }) { hideFlags = (HideFlags)61 }; val.transform.SetParent(_canvasObject.transform, false); StretchToParent(val.GetComponent<RectTransform>()); _visualRoot = val.transform; GameObject val2 = new GameObject("Text", new Type[1] { typeof(RectTransform) }) { hideFlags = (HideFlags)61 }; val2.transform.SetParent(_canvasObject.transform, false); StretchToParent(val2.GetComponent<RectTransform>()); _textRoot = val2.transform; _textRoot.SetAsLastSibling(); return true; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("Could not create native Canvas: " + ex.Message)); } DestroyCanvas(); return false; } } private VisualSlot GetVisualSlot(int index) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown while (_visualSlots.Count <= index) { GameObject val = new GameObject("PeakTTS.Visual." + _visualSlots.Count, new Type[1] { typeof(RectTransform) }) { hideFlags = (HideFlags)61 }; val.transform.SetParent(_visualRoot, false); Component val2 = val.AddComponent(_rawImageType); if ((Object)(object)val2 == (Object)null) { Object.Destroy((Object)(object)val); return null; } SetValue(_rawImageRaycastProperty, val2, false); _visualSlots.Add(new VisualSlot { Object = val, RectTransform = val.GetComponent<RectTransform>(), RawImage = val2 }); } return _visualSlots[index]; } private TextSlot GetTextSlot(int index) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown while (_textSlots.Count <= index) { GameObject val = new GameObject("PeakTTS.Text." + _textSlots.Count, new Type[1] { typeof(RectTransform) }) { hideFlags = (HideFlags)61 }; val.transform.SetParent(_textRoot, false); Component val2 = val.AddComponent(_tmpTextType); if ((Object)(object)val2 == (Object)null) { Object.Destroy((Object)(object)val); return null; } SetValue(_tmpRaycastProperty, val2, false); SetValue(_tmpRichTextProperty, val2, false); SetValue(_tmpAutoSizingProperty, val2, false); _textSlots.Add(new TextSlot { Object = val, RectTransform = val.GetComponent<RectTransform>(), Text = val2 }); } return _textSlots[index]; } private Material GetMaterialForRendering(Component component) { if ((Object)(object)component == (Object)null || _tmpTextType == null) { return null; } try { object? obj = _tmpMaterialForRenderingProperty?.GetValue(component, null); return (Material)((obj is Material) ? obj : null); } catch { return null; } } private Material CreateCleanFontMaterial(Object font, Component sourceText) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) if (font == (Object)null) { return null; } Material val = null; try { object? obj = ((object)font).GetType().GetProperty("material", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(font, null); val = (Material)((obj is Material) ? obj : null); } catch { } if ((Object)(object)val == (Object)null) { val = GetMaterialForRendering(sourceText); } if ((Object)(object)val == (Object)null) { return null; } try { Material val2 = new Material(val) { name = "PeakTTS Daruma Clean", hideFlags = (HideFlags)61 }; SetMaterialFloat(val2, "_FaceDilate", 0f); SetMaterialFloat(val2, "_OutlineWidth", 0f); SetMaterialFloat(val2, "_OutlineSoftness", 0f); SetMaterialFloat(val2, "_UnderlayOffsetX", 0f); SetMaterialFloat(val2, "_UnderlayOffsetY", 0f); SetMaterialFloat(val2, "_UnderlayDilate", 0f); SetMaterialFloat(val2, "_UnderlaySoftness", 0f); SetMaterialFloat(val2, "_GlowInner", 0f); SetMaterialFloat(val2, "_GlowOuter", 0f); SetMaterialFloat(val2, "_GlowPower", 0f); if (val2.HasProperty("_FaceColor")) { val2.SetColor("_FaceColor", Color.white); } if (val2.HasProperty("_OutlineColor")) { val2.SetColor("_OutlineColor", Color.clear); } if (val2.HasProperty("_UnderlayColor")) { val2.SetColor("_UnderlayColor", Color.clear); } val2.DisableKeyword("OUTLINE_ON"); val2.DisableKeyword("UNDERLAY_ON"); val2.DisableKeyword("UNDERLAY_INNER"); val2.DisableKeyword("GLOW_ON"); val2.DisableKeyword("BEVEL_ON"); return val2; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("Could not create clean Daruma SDF material: " + ex.Message)); } return null; } } private static void SetMaterialFloat(Material material, string property, float value) { if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetFloat(property, value); } } private void ReleaseNativeMaterial() { if (_ownsNativeMaterial && (Object)(object)_nativeMaterial != (Object)null) { Object.Destroy((Object)(object)_nativeMaterial); } _nativeMaterial = null; _ownsNativeMaterial = false; } private void DestroyCanvas() { _visualSlots.Clear(); _textSlots.Clear(); _visualCursor = 0; _textCursor = 0; _visualRoot = null; _textRoot = null; if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } _canvasObject = null; _canvas = null; } private static void StretchToParent(RectTransform rectTransform) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rectTransform == (Object)null)) { rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; ((Transform)rectTransform).localScale = Vector3.one; ((Transform)rectTransform).localRotation = Quaternion.identity; } } private static void ConfigureRect(RectTransform rectTransform, Rect rect) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rectTransform == (Object)null)) { rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 1f); rectTransform.anchoredPosition = new Vector2(((Rect)(ref rect)).x, 0f - ((Rect)(ref rect)).y); rectTransform.sizeDelta = new Vector2(Mathf.Max(1f, ((Rect)(ref rect)).width), Mathf.Max(1f, ((Rect)(ref rect)).height)); ((Transform)rectTransform).localScale = Vector3.one; ((Transform)rectTransform).localRotation = Quaternion.identity; } } private static object GetValue(PropertyInfo property, object instance) { if (property == null || instance == null) { return null; } try { return property.GetValue(instance, null); } catch { return null; } } private static void SetValue(PropertyInfo property, object instance, object value) { if (property == null || instance == null || !property.CanWrite) { return; } try { property.SetValue(instance, value, null); } catch { } } private static void SetEnumValue(PropertyInfo property, object instance, string name) { if (property == null || instance == null || !property.CanWrite || !property.PropertyType.IsEnum) { return; } try { object value = Enum.Parse(property.PropertyType, name, ignoreCase: true); property.SetValue(instance, value, null); } catch { } } private static string ToTmpAlignment(TextAnchor anchor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected I4, but got Unknown return (int)anchor switch { 0 => "TopLeft", 1 => "Top", 2 => "TopRight", 3 => "MidlineLeft", 4 => "Midline", 5 => "MidlineRight", 6 => "BottomLeft", 7 => "Bottom", 8 => "BottomRight", _ => "MidlineLeft", }; } } internal static class PhotonVoiceBridge { private static readonly object Sync = new object(); private static object _primaryVoice; private static int _sampleRate; private static int _channels; private static int _frameSize; private static int _frameDurationUs; private static Type _sampleType; private static MethodInfo _pushDataAsync; private static PropertyInfo _pushDataAsyncReady; private static object _syntheticTransmitVoice; private static PropertyInfo _syntheticTransmitProperty; private static bool _syntheticTransmitChanged; private static long _lastRealPush; private static long _queueSeenAt; private static double _nextSyntheticFrameAt; private static bool _loggedSyntheticPump; private static bool _loggedEffectiveRate; private static long _retryPumpAt; [ThreadStatic] private static bool _insideSyntheticPush; public static void RefreshPrimaryVoice() { object obj = null; int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; MethodInfo pushDataAsync = null; PropertyInfo pushDataAsyncReady = null; Type type = null; try { object obj2 = FindExistingRecorder(); obj = ((obj2 == null) ? null : AccessTools.Field(obj2.GetType(), "voice")?.GetValue(obj2)); object obj3 = ((obj == null) ? null : AccessTools.Property(obj.GetType(), "Info")?.GetValue(obj, null)); if (obj3 != null) { num = ReadIntProperty(obj3, "SamplingRate"); num2 = ReadIntProperty(obj3, "Channels"); num3 = ReadIntProperty(obj3, "FrameSize"); num4 = ReadIntProperty(obj3, "FrameDurationUs"); if (num4 <= 0) { num4 = CalculateFrameDurationUs(num, num2, num3); } } if (obj != null) { type = FindSampleType(obj.GetType()); if (type == typeof(float) || type == typeof(short)) { pushDataAsync = obj.GetType().GetMethod("PushDataAsync", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type.MakeArrayType() }, null); pushDataAsyncReady = AccessTools.Property(obj.GetType(), "PushDataAsyncReady"); } } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogDebug((object)("Primary voice refresh failed: " + ex.Message)); } } bool flag = obj != null && num > 0 && num2 > 0 && num3 >= num2 && num3 % num2 == 0 && num4 > 0; bool flag2 = false; lock (Sync) { if (!flag) { return; } flag2 = _primaryVoice != obj || _sampleRate != num || _channels != num2 || _frameSize != num3 || _frameDurationUs != num4; bool num5 = _primaryVoice != obj; _primaryVoice = obj; _sampleRate = num; _channels = num2; _frameSize = num3; _frameDurationUs = num4; _sampleType = type; _pushDataAsync = pushDataAsync; _pushDataAsyncReady = pushDataAsyncReady; if (num5) { _lastRealPush = 0L; _queueSeenAt = 0L; _nextSyntheticFrameAt = 0.0; _loggedSyntheticPump = false; _loggedEffectiveRate = false; _retryPumpAt = 0L; } } if (flag2) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogInfo((object)($"Primary encoder format: {num} Hz, " + $"{num2} channels, {num3} values per frame, " + $"{(double)num4 / 1000.0:0.###} ms")); } } } public static void ObservePush(object voice) { if (_insideSyntheticPush) { return; } StopSyntheticTransmit(); bool flag = false; lock (Sync) { if (_primaryVoice == voice) { _lastRealPush = Stopwatch.GetTimestamp(); _nextSyntheticFrameAt = 0.0; flag = true; } } if (flag) { return; } RefreshFromObservedVoice(voice); lock (Sync) { if (_primaryVoice == voice) { _lastRealPush = Stopwatch.GetTimestamp(); _nextSyntheticFrameAt = 0.0; } } } public static void ServiceQueuedSpeech() { if (!TtsMixer.HasAudio || !PhotonVoicePatches.IsInstalled) { StopSyntheticTransmit(); lock (Sync) { _queueSeenAt = 0L; _nextSyntheticFrameAt = 0.0; _loggedSyntheticPump = false; return; } } long timestamp = Stopwatch.GetTimestamp(); long num = Stopwatch.Frequency / 10; object primaryVoice; MethodInfo pushDataAsync; PropertyInfo pushDataAsyncReady; Type sampleType; int sampleRate; int channels; int frameSize; int frameDurationUs; lock (Sync) { if (_primaryVoice == null || _pushDataAsync == null || _sampleType == null) { return; } if (_queueSeenAt == 0L) { _queueSeenAt = timestamp; } if (timestamp - _queueSeenAt < num || (_lastRealPush != 0L && timestamp - _lastRealPush < num)) { _nextSyntheticFrameAt = 0.0; return; } if (timestamp < _retryPumpAt) { return; } primaryVoice = _primaryVoice; pushDataAsync = _pushDataAsync; pushDataAsyncReady = _pushDataAsyncReady; sampleType = _sampleType; sampleRate = _sampleRate; channels = _channels; frameSize = _frameSize; frameDurationUs = _frameDurationUs; } if (primaryVoice == null || sampleRate <= 0 || channels <= 0 || frameSize <= 0) { return; } double num2 = (double)Stopwatch.Frequency * ((double)frameDurationUs / 1000000.0); if (num2 < 1.0) { num2 = (double)Stopwatch.Frequency * ((double)frameSize / (double)(sampleRate * channels)); } if (num2 < 1.0) { return; } lock (Sync) { if (_nextSyntheticFrameAt <= 0.0) { _nextSyntheticFrameAt = timestamp; } if ((double)timestamp < _nextSyntheticFrameAt) { return; } } try { _insideSyntheticPush = true; if (pushDataAsyncReady != null && !Convert.ToBoolean(pushDataAsyncReady.GetValue(primaryVoice, null))) { return; } EnableSyntheticTransmit(primaryVoice); int num3 = 0; while (num3 < 2 && TtsMixer.HasAudio) { lock (Sync) { timestamp = Stopwatch.GetTimestamp(); if (_primaryVoice != primaryVoice || (double)timestamp < _nextSyntheticFrameAt) { break; } goto IL_0238; } IL_0238: Array array = Array.CreateInstance(sampleType, frameSize); pushDataAsync.Invoke(primaryVoice, new object[1] { array }); num3++; lock (Sync) { _nextSyntheticFrameAt += num2; } } if (num3 <= 0) { return; } lock (Sync) { timestamp = Stopwatch.GetTimestamp(); if (_nextSyntheticFrameAt < (double)timestamp - num2) { _nextSyntheticFrameAt = (double)timestamp + num2; } _retryPumpAt = 0L; if (!_loggedSyntheticPump) { _loggedSyntheticPump = true; ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)"Started recorder fallback because no microphone frames were arriving"); } } } } catch (Exception ex) { lock (Sync) { _retryPumpAt = Stopwatch.GetTimestamp() + Stopwatch.Frequency * 2; } Exception arg = ((ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex); ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogError((object)$"Recorder fallback failed: {arg}"); } } finally { _insideSyntheticPush = false; } } private static void RefreshFromObservedVoice(object voice) { if (voice == null) { return; } try { object obj = AccessTools.Property(voice.GetType(), "Info")?.GetValue(voice, null); if (obj == null) { return; } int num = ReadIntProperty(obj, "SamplingRate"); int num2 = ReadIntProperty(obj, "Channels"); int num3 = ReadIntProperty(obj, "FrameSize"); int num4 = ReadIntProperty(obj, "FrameDurationUs"); if (num4 <= 0) { num4 = CalculateFrameDurationUs(num, num2, num3); } Type type = FindSampleType(voice.GetType()); if ((type != typeof(float) && type != typeof(short)) || num <= 0 || num2 <= 0 || num3 < num2 || num3 % num2 != 0 || num4 <= 0) { return; } MethodInfo method = voice.GetType().GetMethod("PushDataAsync", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type.MakeArrayType() }, null); if (method == null) { return; } bool flag = false; lock (Sync) { flag = _primaryVoice != voice || _sampleRate != num || _channels != num2 || _frameSize != num3 || _frameDurationUs != num4; _primaryVoice = voice; _sampleRate = num; _channels = num2; _frameSize = num3; _frameDurationUs = num4; _sampleType = type; _pushDataAsync = method; _pushDataAsyncReady = AccessTools.Property(voice.GetType(), "PushDataAsyncReady"); if (flag) { _queueSeenAt = 0L; _nextSyntheticFrameAt = 0.0; _loggedSyntheticPump = false; _loggedEffectiveRate = false; _retryPumpAt = 0L; } } if (flag) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)($"Captured active Photon voice: {num} Hz, " + $"{num2} channel(s), {num3} values per frame, " + $"{(double)num4 / 1000.0:0.###} ms")); } } } catch (Exception ex) { ManualLogSource logSource2 = Plugin.LogSource; if (logSource2 != null) { logSource2.LogDebug((object)("Observed Photon voice capture failed: " + ex.Message)); } } } private static object FindExistingRecorder() { Type type = AccessTools.TypeByName("VoiceClientHandler"); if (type == null) { return null; } object obj = AccessTools.Field(type, "m_LocalRecorder")?.GetValue(null); if (obj != null) { return obj; } object obj2 = AccessTools.Field(type, "m_VoiceConnection")?.GetValue(null); if (obj2 != null) { return AccessTools.Property(obj2.GetType(), "PrimaryRecorder")?.GetValue(obj2, null); } return null; } private static void EnableSyntheticTransmit(object voice) { if (voice == null) { return; } lock (Sync) { if (_syntheticTransmitVoice == voice) { return; } } StopSyntheticTransmit(); try { PropertyInfo propertyInfo = AccessTools.Property(voice.GetType(), "TransmitEnabled"); if (propertyInfo == null || !propertyInfo.CanRead || !propertyInfo.CanWrite) { return; } bool flag = !Convert.ToBoolean(propertyInfo.GetValue(voice, null)); if (flag) { propertyInfo.SetValue(voice, true, null); } lock (Sync) { _syntheticTransmitVoice = voice; _syntheticTransmitProperty = propertyInfo; _syntheticTransmitChanged = flag; } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogDebug((object)("Transmit override failed: " + ex.Message)); } } } private static void StopSyntheticTransmit() { object syntheticTransmitVoice; PropertyInfo syntheticTransmitProperty; bool syntheticTransmitChanged; lock (Sync) { syntheticTransmitVoice = _syntheticTransmitVoice; syntheticTransmitProperty = _syntheticTransmitProperty; syntheticTransmitChanged = _syntheticTransmitChanged; _syntheticTransmitVoice = null; _syntheticTransmitProperty = null; _syntheticTransmitChanged = false; } if (!syntheticTransmitChanged || syntheticTransmitVoice == null || syntheticTransmitProperty == null) { return; } try { if (Convert.ToBoolean(syntheticTransmitProperty.GetValue(syntheticTransmitVoice, null))) { syntheticTransmitProperty.SetValue(syntheticTransmitVoice, false, null); } } catch { } } private static Type FindSampleType(Type type) { while (type != null) { if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); if (genericTypeDefinition.Namespace == "Photon.Voice" && genericTypeDefinition.Name.StartsWith("LocalVoiceFramed", StringComparison.Ordinal) && genericTypeDefinition.GetGenericArguments().Length == 1) { return type.GetGenericArguments()[0]; } } type = type.BaseType; } return null; } public static bool IsPrimaryVoice(object voice) { lock (Sync) { return _primaryVoice != null && _primaryVoice == voice; } } public static bool TryGetFormat(object voice, int bufferLength, out int sampleRate, out int channels, out int frameSize) { bool flag = false; int num = 0; int num2 = 0; bool flag2 = false; lock (Sync) { num = (sampleRate = _sampleRate); channels = _channels; frameSize = _frameSize; num2 = _frameDurationUs; flag2 = _primaryVoice != null && _primaryVoice == voice; if (flag2 && bufferLength > 0 && channels > 0 && num2 > 0) { int num3 = SnapToVoiceRate((int)Math.Round((double)bufferLength * 1000000.0 / ((double)channels * (double)num2))); if (num3 > 0) { sampleRate = num3; } if (!_loggedEffectiveRate && sampleRate != num) { _loggedEffectiveRate = true; flag = true; } } } if (flag) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)("Outgoing PCM rate corrected: " + $"{bufferLength} values over {(double)num2 / 1000.0:0.###} ms to " + $"{sampleRate} Hz (VoiceInfo {num} Hz)")); } } return flag2; } private static int CalculateFrameDurationUs(int sampleRate, int channels, int frameSize) { if (sampleRate <= 0 || channels <= 0 || frameSize <= 0) { return 0; } return (int)Math.Round((double)frameSize * 1000000.0 / ((double)sampleRate * (double)channels)); } private static int SnapToVoiceRate(int value) { int[] obj = new int[5] { 8000, 12000, 16000, 24000, 48000 }; int num = 0; int num2 = int.MaxValue; int[] array = obj; foreach (int num3 in array) { int num4 = Math.Abs(value - num3); if (num4 < num2) { num = num3; num2 = num4; } } if (num2 <= Math.Max(500, num / 20)) { return num; } if (value < 4000 || value > 96000) { return 0; } return value; } private static int ReadIntProperty(object value, string name) { PropertyInfo propertyInfo = AccessTools.Property(value.GetType(), name); if (!(propertyInfo == null)) { return Convert.ToInt32(propertyInfo.GetValue(value, null)); } return 0; } } internal static class PhotonVoicePatches { private struct TransmitPatchState { public bool Changed; public PropertyInfo Property; public bool PushScopeEntered; } private static int _loggedFormatMismatch; private static int _loggedDuplicateProcessingPass; private static readonly object InstallSync = new object(); private static readonly HashSet<Type> PatchedSampleTypes = new HashSet<Type>(); private static bool _loggedWaitingForPhoton; [ThreadStatic] private static object _activePushVoice; [ThreadStatic] private static int _activePushDepth; [ThreadStatic] private static bool _mixedInActivePush; public static bool IsInstalled { get { lock (InstallSync) { return PatchedSampleTypes.Count == 2; } } } public static int Install(Harmony harmony, ManualLogSource log) { lock (InstallSync) { Type type = FindOpenVoiceType(); if (type == null) { if (!_loggedWaitingForPhoton) { _loggedWaitingForPhoton = true; log.LogInfo((object)"Photon Voice is not loaded yet; will retry"); } return 0; } return PatchSampleType(harmony, type, typeof(float), log) + PatchSampleType(harmony, type, typeof(short), log); } } private static Type FindOpenVoiceType() { string name = "Photon.Voice.LocalVoiceFramed" + '`' + "1"; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType(name, throwOnError: false); if (type != null && type.IsGenericTypeDefinition && type.GetGenericArguments().Length == 1) { return type; } } catch { } } return null; } private static int PatchSampleType(Harmony harmony, Type openType, Type sampleType, ManualLogSource log) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown //IL_0163: Expected O, but got Unknown //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Expected O, but got Unknown if (PatchedSampleTypes.Contains(sampleType)) { return 0; } try { Type type = openType.MakeGenericType(sampleType); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; MethodInfo method = type.GetMethod("PushData", bindingAttr, null, new Type[1] { sampleType.MakeArrayType() }, null); MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(bindingAttr); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "processFrame") && !(methodInfo2.ReturnType != sampleType.MakeArrayType())) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if ((parameters.Length == 1 || (parameters.Length == 3 && parameters[1].ParameterType == typeof(int) && parameters[2].ParameterType == typeof(int))) && parameters[0].ParameterType == sampleType.MakeArrayType() && (methodInfo == null || parameters.Length == 3)) { methodInfo = methodInfo2; } } } if (method == null || methodInfo == null) { log.LogWarning((object)("No compatible PushData/processFrame method found on " + type.FullName)); return 0; } harmony.Patch((MethodBase)method, new HarmonyMethod(PatchMethod("PushPrefix")), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(PatchMethod("PushFinalizer")), (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(PatchMethod((sampleType == typeof(float)) ? "ProcessFramePostfixFloat" : "ProcessFramePostfixShort")), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); log.LogInfo((object)("Patched " + type.FullName + ": TTS after final PCM processing")); PatchedSampleTypes.Add(sampleType); return 1; } catch (Exception arg) { log.LogError((object)$"Could not install safe Photon TTS hook for {sampleType.Name}: {arg}"); return 0; } } private static MethodInfo PatchMethod(string name) { return typeof(PhotonVoicePatches).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic); } private static void PushPrefix(object __instance, ref TransmitPatchState __state) { __state = default(TransmitPatchState); EnterPushScope(__instance); __state.PushScopeEntered = true; PhotonVoiceBridge.ObservePush(__instance); if (!TtsMixer.HasAudio || !PhotonVoiceBridge.IsPrimaryVoice(__instance)) { return; } try { PropertyInfo propertyInfo = AccessTools.Property(__instance.GetType(), "TransmitEnabled"); if (!(propertyInfo == null) && propertyInfo.CanRead && propertyInfo.CanWrite && !Convert.ToBoolean(propertyInfo.GetValue(__instance, null))) { __state.Property = propertyInfo; __state.Changed = true; propertyInfo.SetValue(__instance, true, null); } } catch (Exception ex) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogDebug((object)("Transmit override failed: " + ex.Message)); } } } private static void PushFinalizer(object __instance, TransmitPatchState __state) { try { if (__state.Changed) { __state.Property.SetValue(__instance, false, null); } } catch { } finally { if (__state.PushScopeEntered) { ExitPushScope(); } } } private static void ProcessFramePostfixFloat(object __instance, ref float[] __result) { if (TryGetEncoderFormat(__instance, __result, out var rate, out var channels, out var size)) { if (__result == null) { __result = new float[size]; } TtsMixer.Mix(__result, rate, channels); } } private static void ProcessFramePostfixShort(object __instance, ref short[] __result) { if (TryGetEncoderFormat(__instance, __result, out var rate, out var channels, out var size)) { if (__result == null) { __result = new short[size]; } TtsMixer.Mix(__result, rate, channels); } } private static bool TryGetEncoderFormat(object voice, Array frame, out int rate, out int channels, out int size) { rate = (channels = (size = 0)); int bufferLength = frame?.Length ?? 0; if (!TtsMixer.HasAudio || !PhotonVoiceBridge.TryGetFormat(voice, bufferLength, out rate, out channels, out size)) { return false; } if (frame == null || frame.Length == size) { return TryClaimMixForCurrentPush(voice); } if (Interlocked.Exchange(ref _loggedFormatMismatch, 1) == 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogWarning((object)("Skipped an unexpected final Photon frame: " + $"{frame.Length} values; encoder expects {size} at {rate} Hz / {channels} channels")); } } return false; } private static void EnterPushScope(object voice) { if (_activePushDepth == 0) { _activePushVoice = voice; _mixedInActivePush = false; } _activePushDepth++; } private static void ExitPushScope() { if (_activePushDepth > 0) { _activePushDepth--; } if (_activePushDepth == 0) { _activePushVoice = null; _mixedInActivePush = false; } } private static bool TryClaimMixForCurrentPush(object voice) { if (_activePushDepth <= 0 || _activePushVoice != voice) { return true; } if (!_mixedInActivePush) { _mixedInActivePush = true; return true; } if (Interlocked.Exchange(ref _loggedDuplicateProcessingPass, 1) == 0) { ManualLogSource logSource = Plugin.LogSource; if (logSource != null) { logSource.LogInfo((object)"Suppressed duplicated Photon processing pass for outgoing frame"); } } return false; } } [BepInPlugin("ahun.PeakTTS", "PeakTTS", "1.2.1")] public sealed class Plugin : BaseUnityPlugin { private sealed class SpeechDisplayInfo { public string Text; public string Voice; public string Language; } private enum PeakIconKind { Refresh, Gear } public const string PluginGuid = "ahun.PeakTTS"; public const string PluginName = "PeakTTS"; public const string PluginVersion = "1.2.1"; internal static ManualLogSource LogSource; internal static Plugin Instance; private ConfigEntry<KeyCode> _openKey; private ConfigEntry<float> _ttsGain; private ConfigEntry<float> _transmitGain; private ConfigEntry<bool> _monitorPlayback; private ConfigEntry<float> _monitorVolume; private ConfigEntry<int> _speechRate; private ConfigEntry<string> _voiceName; private ConfigEntry<string> _languageMode; private ConfigEntry<int> _maxCharacters; private ConfigEntry<string> _powershellPath; private ConfigEntry<string> _modelsFolder; private ConfigEntry<string> _piperPath; private ConfigEntry<int> _piperSpeakerId; private ConfigEntry<string> _coquiTtsPath; private ConfigEntry<string> _coquiSpeaker; private ConfigEntry<string> _coquiLanguage; private ConfigEntry<string> _coquiSpeakerWav; private Harmony _harmony; private bool _inputOpen; private string _text = string.Empty; private bool _focusNextFrame; private volatile string _status = "Ready"; private int _pendingJobs; private float _nextVoiceRefreshAt; private float _nextPhotonPatchRetryAt; private bool _photonPatchesInstalled; private int _keyboardHandledFrame = -1; private int _catalogRefreshRunning; private float _nextCatalogRefreshAt; private bool _sapi4HelperPresent; private bool _sapi4Ready; private bool _sapi4SetupLaunched; private ConfigEntry<string> _sapi4HelperPageUrl; private readonly object _monitorSync = new object(); private readonly Queue<SpeechClip> _monitorQueue = new Queue<SpeechClip>(); private AudioSource _monitorSource; private AudioClip _monitorClip; private volatile bool _monitorPlaying; private volatile bool _monitorEnabled; private GUIStyle _boxStyle; private GUIStyle _labelStyle; private GUIStyle _voiceStyle; private GUIStyle _titleStyle; private GUIStyle _smallStyle; private GUIStyle _buttonStyle; private GUIStyle _textAreaStyle; private GUIStyle _speechTextStyle; private GUIStyle _speechVoiceStyle; private Font _contentFont; private Texture2D _refreshIconTexture; private Texture2D _gearIconTexture; private PeakNativeUiRenderer _peakUi; private float _styleScale; private readonly object _speechUiSync = new object(); private readonly Dictionary<SpeechClip, SpeechDisplayInfo> _speechDisplayByClip = new Dictionary<SpeechClip, SpeechDisplayInfo>(); private SpeechDisplayInfo _activeSpeechDisplay; private volatile bool _speechTransmissionFinished = true; private int _speechBannerRevision; private int _observedSpeechBannerRevision; private float _speechBannerShownAt; private float _speechBannerFadeOutAt = -1f; private static readonly Color UiInk = Color32.op_Implicit(new Color32((byte)48, (byte)42, (byte)32, byte.MaxValue)); private static readonly Color UiPaper = Color32.op_Implicit(new Color32((byte)238, (byte)231, (byte)199, byte.MaxValue)); private static readonly Color UiPaperLight = Color32.op_Implicit(new Color32((byte)248, (byte)242, (byte)214, byte.MaxValue)); private static readonly Color UiShadow = Color32.op_Implicit(new Color32((byte)21, (byte)18, (byte)15, (byte)150)); private static readonly Color UiYellow = Color32.op_Implicit(new Color32((byte)232, (byte)191, (byte)48, byte.MaxValue)); private static readonly Color UiGreen = Color32.op_Implicit(new Color32((byte)67, (byte)137, (byte)75, byte.MaxValue)); private static readonly Color UiBlue = Color32.op_Implicit(new Color32((byte)77, (byte)134, (byte)162, byte.MaxValue)); private static readonly Color UiRed = Color32.op_Implicit(new Color32((byte)171, (byte)75, (byte)63, byte.MaxValue)); private static readonly Color UiMuted = Color32.op_Implicit(new Color32((byte)104, (byte)92, (byte)72, byte.MaxValue)); private const string RefreshIconAlpha64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAEcIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABARHwRgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwB8/7oAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQET6/z+QAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAwMDAwIBAAAAAAAAAAMAgP/4/78AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAMAAAAAAAAAAAIEAwAAAAEBFuz+/v3/QgEDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCAAAACRQ+QUFBHQ8AAAABAwEEAHP/+//8/7UAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAChwreHw//////XrvH85AAAABAbW/v3//v72JwECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAARbH4//////39/f3//////8ViCABg//z////8/6EAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAmtv////v8/f7//////v78+/7//9VN0/79/////v34JwECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwBk8P/9/P7//////////////////fz////////////8/6EAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwCY///8/////////vz8/Pz+/////////f3//////////vr0IgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgCa//v+//////77/f////////77/v///////////////////7QCAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwCa//r///////z////wyr6+wev////8/v////////////7/n396BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwBw//r//////v7/8qJOFQAAAAAPOovj///9///////////9/jIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAx/Pv+/////f//nSAAAAABAgICAAAADnzy//z///z9/////P9IBAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIHzv/8/////f/tVgAAAwMBAAAAAAEDBAAAMs7//P3///39/P/+KAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAX//8/////v7sNQAFAgAAAAAAAAAAAAABBQARyf//vN/////5ZQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBNb+/f////z9RAAGAAAAAAAAAAAAAAAAAAAEABPedQAVQUBCJwABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAEL+/f////z/hQAFAAAAAAAAAAAAAAAAAAAAAAEABwABAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwCh//v///3+4A4CAQAAAAAAAAAAAAAAAAAAAAAAAQACAAEDAwMCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEF2P/9///7/38ABAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAK/n+/////vglAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAEH//f///f/OAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABABv//z///v/nQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAg//7///7/3wABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAH7/+///+/+CAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABACF//v///v/fgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAYP/8///8/6sAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAD7//f///f/ZBgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAf9P7+///9/TYBAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAsz//f//+/+aAAQAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABACN//v///797yEBAgAAAAAAAAAAAAAAAAAAAAAAAgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAK/v+/////P+tAAUBAAAAAAAAAAAAAAAAAAAAAgAxAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgC+/vz////8/3AABQEAAAAAAAAAAAAAAAAABQA2/6wZAAQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAQf/8/////vv/ZQABAwEAAAAAAAAAAAADAwA27f3/5VYAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwCq//z////+/f+PBgAABAMCAQEBAwQBAABk8f/9+///kg8AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBFuj+/f////79/9NSBwAAAAAAAAAAADm2///9///+/P/YFwEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwBF//z+//////z//9iLRh4RERQ/dMb///3+//////r/hwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAGX//P3//////f3////27+7w/////vz///////r/mgABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAZf///P///////fv8//////37/P///////fz/mgACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABABi8v/7/v////////7+/v7////////++///jwACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEADLI///7/f////////////////77///lVgACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMABX3o///++/z8/v7+/vz8+/3///meGgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAF3TR////////////////44ssAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwAAAzVvrcDr7u7uyrmAQg0AAAMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAAAAAAADxEQEAAAAAAABAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwQDAgAAAAABAwQDAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; private const string GearIconAlpha64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEAwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAABAAAAAAAAAAAAAAIBJ4o4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAoNAAEAAAAAAAAAAAADAKH//9WfTgUBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEjbyAECAQAAAAAAAAADAD78+f7///oSAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAN7r///+aAAQBAAAAAAAAAwPB//z//PvtEgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMBX/v//f37/48ABAAAAAAAAwBe//z///3/ygQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAEn/+v///vv/ZQAEAAAAAQIP3P79///8/7sAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAR7P/+///+/f9EAAQAAQUAgP/7/////P+/AAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAb7//P////397DYABAAAI/D8/v////z/vQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAB///v//////f/pFwAUEKT//P/////8/8EAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMARv/9///////9/9297e///v//////+/+QAAMAAAAAAAAAAQIDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAB30//7///////7///////////////v/ewMFAAAAAQMEAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECzP/9/////////fz+/v/////////7/4cABQMEAgAAAAADKk4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAJH/+/////////////////////////3zcAAAAAAKNXCu1fr/QAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABABL//z///////////////////////////+oSYG64v/////9/cEDAwAAAAAAAAAAAAAAAAAAAAICAgIDBAQEBQYApv/8//////////////////////////79///////++/z9//z/XAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcP/8//////////////////////////////z8+/z9///////9/OIPAQEAAAAAAAAAAAAAAwBWw7u+vrmEgICAh/z+/v/////////////+/v7/////////////////////////+v/GEwEBAAAAAAAAAAAAAAMBt////////////////////////////vv+/////P3////////////////////+/P+aAwABAAAAAAAAAAAAAAEAEO78+/z8/Pv7+/v7/v///////////P///+7v/P///P/////////////////9/f+AAAABAAAAAAAAAAAAAAADAEv7/P///////////////////////f/pfzYSEipl0//+///////////////9//9lAAMBAAAAAAAAAAAAAAAAAwCe//v9/////////////////////f+4GgAAAAAAAAaP/vz////////////+/+5HAAQAAAAAAAAAAAAAAAAAAAEBIZ7///z+/////////////////P+7AAAEAgEBAgQCAID//P/////////+/tk1AAQAAAAAAAAAAAAAAAAAAAAAAAAAQ8j///v//////////////v7sFwEDAAAAAAAAAQUBwf/8///////+/vIaAAQAAAAAAAAAAAAAAAAAAAAAAAABBAAFben//v3///////////z/fwAEAAAAAAAAAAADAED9/f///////v/uEgIBAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAanv/////////////++y8BAwAAAAAAAAAAAQEI4f/9///////9/kECAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDAABDx/3+///////+/+4RAAEAAAAAAAAAAAACAb3//P///////f84AAYBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEABDv/v7//////v/tDwABAAAAAAAAAAAAAgC7//z///////7+uisAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMT8f/+///////+9iQBAgAAAAAAAAAAAAEF1f/9/////////v/5gxYAAQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAcb//P///////P9kAAQAAAAAAAAAAAIBKfr+///////////8///lYgAAAwEAAAAAAAAAAAAAAAAAAAAAAAAEABLY/v3///////3/1wMEAQAAAAAAAAAGAKH//P////////////77//+8NwAAAAAAAAAAAAAAAAAAAAAAAAAEADXa//7//////////P+PAAMDAQAAAQIGAFT9/P////////////////77//ueIQEBAAAAAAAAAAAAAAAAAAAEAEfu//3////////////9/4AAAAACAgAAAFfu/v7///////////////////z8/54AAwAAAAAAAAAAAAAAAAEDAGX///3///////////////7/vEULAAAELZ3///7//////////////////////PtLAAMAAAAAAAAAAAAAAAEAAID//f3//////////////////P//4r6+1v3//v7///////////z7+/v7/Pz8+/zuEAABAAAAAAAAAAAAAAEAA5r//P7////////////////////8/v///////P////////////3/////////////twEDAAAAAAAAAAAAAAEBE8b/+v/////////////////////////9/Pz9/v////////////z/nn+BgIS5vr67w1YAAwAAAAAAAAAAAAABAQ/i/P3///////38+/z8//////////////////////////////z/rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAXP/8//38+/7///////z///////////////////////////3+xBEDBgQEBAMCAgICAAAAAAAAAAAAAAAAAAAAAwPB/f3/////4rqBSaj////////////////////////////8/0sABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAQP/61a5wNQoAAAAAgPr+////////////////////////+/+RAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFOKgMAAAAAAgQDBACT//v////////////+/v////////3/zAIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwQDAQAAAAUDef/7/////////v39///+///////+//QdAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCAQAAAAAAAAADAJH/+/////////////T0//7///////3/RgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgDB//z//////v7uSUIcI+n//f/////7/38ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAvf/8//////z/awAAAAA27P39/////P++AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAL///P////z/wgAFAwEEAET//f7///7/7BEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgC7//z///78+TIAAgAAAAQAZf/7/v//+v9JAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEyv/9///8/4UABAAAAAAABACP//v9/f/7XwEDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAEu37/P/9/t0OAwEAAAAAAAEEAJr///+6NwABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABABL6///++P9UAAMAAAAAAAAAAQIByNtIAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFTp/V//+rAAQAAAAAAAAAAAABAA0KAAICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAziKJQECAAAAAAAAAAAAAAEAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; internal bool IsInputOpen => _inputOpen; private bool HasMonitorAudio { get { if (_monitorPlaying) { return true; } lock (_monitorSync) { return _monitorQueue.Count > 0; } } } private void Awake() { //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Expected O, but got Unknown Instance = this; LogSource = ((BaseUnityPlugin)this).Logger; _peakUi = new PeakNativeUiRenderer(((BaseUnityPlugin)this).Logger); string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); string path = ((!string.IsNullOrWhiteSpace(directoryName)) ? directoryName : Path.Combine(Paths.PluginPath, "PeakTTS")); string text = Path.Combine(path, "voices"); string text2 = Path.Combine(path, "piper", "piper.exe"); string text3 = Path.Combine(path, "coqui", "tts.exe"); _openKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "OpenKey", (KeyCode)289, "Key used to toggle the TTS chatbox open/close"); _ttsGain = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "TTSGain", 0.85f, "Legacy TTS gain setting retained for compatibility. Local monitor volume is controlled separately; outgoing voice uses TransmitGain"); _transmitGain = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "TransmitGain", 0.22f, "Outgoing TTS gain sent to other players, from 0.0 to 1.0. Lower this if the transmitted voice is too loud"); _monitorPlayback = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "MonitorPlayback", true, "Play your own generated TTS locally so you can hear when it starts and ends"); _monitorVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MonitorVolume", 0.85f, "Local TTS monitoring volume, from 0.0 to 1.0"); _speechRate = ((BaseUnityPlugin)this).Config.Bind<int>("Voice", "SpeechRate", 0, "Windows TTS speaking rate, from -10 to 10. Piper/Coqui models control their own speaking style"); _voiceName = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "VoiceName", string.Empty, "Voice selection. Windows names are matched; the ingame picker writes exact Windows names or SAPI4/Piper/Coqui IDs"); _languageMode = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "Language", "auto", "Speech language. Use auto to detect each message and temporarily switch to a compatible voice when needed, or set a language code such as en, hu or ko"); _sapi4HelperPageUrl = ((BaseUnityPlugin)this).Config.Bind<string>("SAPI4", "HelperPageUrl", "https://github.com/AternosHUN/peaktts-sapi4", "(Do NOT change!) GitHub page containing the optional PeakTTS.Sapi4.exe helper download and setup instructions"); _maxCharacters = ((BaseUnityPlugin)this).Config.Bind<int>("Input", "MaxCharacters", 220, "Maximum text length per TTS message"); _powershellPath = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "PowerShellPath", "powershell.exe", "PowerShell executable used for Windows System.Speech TTS"); _modelsFolder = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "ModelsFolder", text, "Folder scanned for Piper .onnx + .onnx.json voices and Coqui .pth + config.json models"); _piperPath = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "PiperPath", text2, "Path to piper.exe. Required only for Piper .onnx voices"); _piperSpeakerId = ((BaseUnityPlugin)this).Config.Bind<int>("Models", "PiperSpeakerId", -1, "Optional Piper speaker ID for multi-speaker .onnx models. -1 uses Piper's default speaker"); _coquiTtsPath = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "CoquiTtsPath", text3, "Path to the Coqui TTS CLI executable. Required only for .pth models"); _coquiSpeaker = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "CoquiSpeaker", string.Empty, "Optional Coqui --speaker_idx value for multi-speaker .pth models"); _coquiLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "CoquiLanguage", string.Empty, "Optional Coqui --language_idx value for multilingual .pth models"); _coquiSpeakerWav = ((BaseUnityPlugin)this).Config.Bind<string>("Models", "CoquiSpeakerWav", string.Empty, "Optional Coqui --speaker_wav path for XTTS/voice-cloning models. Relative paths are resolved next to the selected .pth model"); try { ((BaseUnityPlugin)this).Config.Save(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not explicitly save config: " + ex.Message)); } EnsureModelFolders(); try { TtsMixer.SetGain(Mathf.Clamp01(_transmitGain.Value)); _monitorEnabled = _monitorPlayback.Value; SetupMonitorAudio(); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Local monitor initialization failed: " + ex2.Message)); } _transmitGain.SettingChanged += delegate { TtsMixer.SetGain(Mathf.Clamp01(_transmitGain.Value)); }; _modelsFolder.SettingChanged += delegate { EnsureModelFolders(); RequestVoiceCatalogRefresh(force: true); }; _powershellPath.SettingChanged += delegate { RequestVoiceCatalogRefresh(force: true); }; try { _harmony = new Harmony("ahun.PeakTTS"); int num = PhotonVoicePatches.Install(_harmony, ((BaseUnityPlugin)this).Logger); _photonPatchesInstalled = PhotonVoicePatches.IsInstalled; bool flag = GameInputBridge.InstallPatch(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Installed {2} Photon Voice patches. Inputblocking: {3}", "PeakTTS", "1.2.1", num, flag)); if (!_photonPatchesInstalled) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Will finish installing its Photon hooks when PEAK initializes voice"); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Harmony/Photon initialization failed: {arg}"); } SceneManager.activeSceneChanged += OnActiveSceneChanged; RefreshSapi4Availability(); RequestVoiceCatalogRefresh(force: true); } private void EnsureModelFolders() { try { string text = _modelsFolder?.Value; if (!string.IsNullOrWhiteSpace(text)) { Directory.CreateDirectory(text); Directory.CreateDirectory(Path.Combine(text, "Piper")); Directory.CreateDirectory(Path.Combine(text, "Coqui")); } string text2 = _piperPath?.Value; if (!string.IsNullOrWhiteSpace(text2)) { string directoryName = Path.GetDirectoryName(text2); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } } string text3 = _coquiTtsPath?.Value; if (!string.IsNullOrWhiteSpace(text3)) { string directoryName2 = Path.GetDirectoryName(text3); if (!string.IsNullOrWhiteSpace(directoryName2)) { Directory.CreateDirectory(directoryName2); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not create model folders: " + ex.Message)); } } private void OnDestroy() { SceneManager.activeSceneChanged -= OnActiveSceneChanged; try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } try { WindowsTts.Shutdown(); } catch { } try { _peakUi?.Dispose(); } catch { } _peakUi = null; if ((Object)(object)_refreshIconTexture != (Object)null) { Object.Destroy((Object)(object)_refreshIconTexture); } if ((Object)(object)_gearIconTexture != (Object)null) { Object.Destroy((Object)(object)_gearIconTexture); } _refreshIconTexture = null; _gearIconTexture = null; ClearMonitorPlayback(); } private void Update() { //IL_0104: Unknown result type (might be due to invalid IL or missing references) PhotonVoiceBridge.ServiceQueuedSpeech(); UpdateMonitorPlayback(); UpdateSpeechBannerState(); bool flag = _inputOpen || Volatile.Read(in _pendingJobs) > 0 || _activeSpeechDisplay != null; _peakUi?.TryDiscoverNativeFont(!IsMainMenuScene() && flag); if (!_photonPatchesInstalled && Time.unscaledTime >= _nextPhotonPatchRetryAt) { _nextPhotonPatchRetryAt = Time.unscaledTime + 1f; try { int num = PhotonVoicePatches.Install(_harmony, ((BaseUnityPlugin)this).Logger); _photonPatchesInstalled = PhotonVoicePatches.IsInstalled; if (num > 0 && _photonPatchesInstalled) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Photon Voice hooks are now ready"); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Delayed Photon hook failed: {arg}"); _nextPhotonPatchRetryAt = Time.unscaledTime + 10f; } } if (Time.unscaledTime >= _nextVoiceRefreshAt) { _nextVoiceRefreshAt = Time.unscaledTime + 2f; PhotonVoiceBridge.RefreshPrimaryVoice(); } if (!Input.GetKeyDown(_openKey.Value)) { return; } if (IsMainMenuScene()) { if (_inputOpen) { SetInputOpen(open: false); } } else { SetInputOpen(!_inputOpen); _keyboardHandledFrame = Time.frameCount; } } private void OnActiveSceneChanged(Scene previous, Scene next) { if (_inputOpen) { _text = string.Empty; SetInputOpen(open: false); } ClearSpeechDisplay(); InvalidateStyles(); _peakUi?.ResetForScene(); } private void OnApplicationFocus(bool hasFocus) { if (hasFocus) { RefreshSapi4Availability(); RequestVoiceCatalogRefresh(force: false); } } private void RefreshSapi4Availability() { string sapi4HelperPath = GetSapi4HelperPath(); _sapi4HelperPresent = File.Exists(sapi4HelperPath); if (!_sapi4HelperPresent) { _sapi4Ready = false; _sapi4SetupLaunched = false; } } private string GetSapi4HelperPath() { string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); if (string.IsNullOrWhiteSpace(text)) { text = Paths.PluginPath; } return Path.Combine(text, "PeakTTS.Sapi4.exe"); } private bool OpenSapi4HelperPage(out string error) { error = null; try { Application.OpenURL((_sapi4HelperPageUrl?.Value ?? string.Empty).Trim()); return true; } catch (Exception ex) { error = "Could not open the SAPI4 helper page: " + ex.Message; return false; } } private static bool IsMainMenuScene() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string a = (((Scene)(ref activeScene)).IsValid() ? (((Scene)(ref activeScene)).name ?? string.Empty) : string.Empty); if (!string.Equals(a, "MainMenu", StringComparison.OrdinalIgnoreCase) && !string.Equals(a, "Title", StringComparison.OrdinalIgnoreCase)) { return string.Equals(a, "Pretitle", StringComparison.OrdinalIgnoreCase); } return true; } private bool OpenConfigFile(out string error) { error = null; try { ((BaseUnityPlugin)this).Config.Save(); string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (string.IsNullOrWhiteSpace(configFilePath)) { throw new InvalidOperationException("BepInEx did not provide a config file path"); } Process.Start(new ProcessStartInfo { FileName = configFilePath, UseShellExecute = true }); return true; } catch (Exception ex) { error = "Could not open config: " + ex.Message; ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogWarning((object)error); } return false; } } private void ReloadConfigFromDisk() { try { ((BaseUnityPlugin)this).Config.Reload(); TtsMixer.SetGain(Mathf.Clamp01(_transmitGain.Value)); _monitorEnabled = _monitorPlayback.Value; if ((Object)(object)_monitorSource != (Object)null) { _monitorSource.volume = Mathf.Clamp01(_monitorVolume.Value); } if (!_monitorEnabled) { ClearMonitorPlayback(); } EnsureModelFolders(); RefreshSapi4Availability(); RequestVoiceCatalogRefresh(force: true); _status = "Config reloaded"; _focusNextFrame = true; ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogInfo((object)"Config reloaded from disk"); } } catch (Exception ex) { _status = "Config reload failed - check LogOutput.log"; ManualLogSource logSource2 = LogSource; if (logSource2 != null) { logSource2.LogWarning((object)("Could not reload config: " + ex)); } } } private void SetInputOpen(bool open) { if ((!open || !IsMainMenuScene()) && _inputOpen != open) { _inputOpen = open; _focusNextFrame = open; ManualLogSource logSource = LogSource; if (logSource != null) { logSource.LogInfo((object)("TTS input overlay " + (open ? "opened" : "closed"))); } if (open) { RefreshSapi4Availability(); } if (open && Time.unscaledTime >= _nextCatalogRefreshAt) { _nextCatalogRefreshAt = Time.unscaledTime + 10f; RequestVoiceCatalogRefresh(force: false); } } } private void OnGUI() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_059e: Unknown result type (might be due to invalid IL or missing references) //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_0658: Unknown result type (might be due to invalid IL or missing references) //IL_075d: Unknown result type (might be due to invalid IL or missing references) //IL_075f: Unknown result type (might be due to invalid IL or missing references) //IL_07a2: Unknown result type (might be due to invalid IL or missing references) //IL_07f0: Unknown result type (might be due to invalid IL or missing references) //IL_07fa: Unknown result type (might be due to invalid IL or missing references) //IL_0901: Unknown result type (might be due to invalid IL or missing references) //IL_085f: Unknown result type (might be due to invalid IL or missing references) //IL_0861: Unknown result type (might be due to invalid IL or missing references) //IL_08a4: Unknown result type (might be due to invalid IL or missing references) //IL_06a8: Unknown result type (might be due to invalid IL or missing references) //IL_06f5: Unknown result type (might be due to invalid IL or missing references) //IL_06ff: Unknown result type (might be due to invalid IL or missing references) EnsureStyles(); _peakUi?.BeginGuiFrame(); try { DrawSpeechOverlay(); if (!_inputOpen) { DrawCompactStatusToast(); return; } Event current = Event.current; if (current != null && (int)current.type == 4 && current.keyCode == _openKe