Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Speech2TTS v1.4.1
plugins/NocturnalLyfe-Speech2TTS/Speech2TTS.dll
Decompiled 7 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Photon.Voice; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("NocturnalLyfe")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.3.1.0")] [assembly: AssemblyInformationalVersion("1.3.1")] [assembly: AssemblyProduct("Speech2TTS")] [assembly: AssemblyTitle("Speech2TTS")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 Speech2TTS { [HarmonyPatch(typeof(PlayerController))] internal static class ExamplePlayerControllerPatch { [HarmonyPrefix] [HarmonyPatch("Start")] private static void Start_Prefix(PlayerController __instance) { Speech2TTS.Logger.LogDebug((object)$"{__instance} Start Prefix"); } [HarmonyPostfix] [HarmonyPatch("Start")] private static void Start_Postfix(PlayerController __instance) { Speech2TTS.Logger.LogDebug((object)$"{__instance} Start Postfix"); } } [BepInPlugin("NocturnalLyfe.Speech2TTS", "Speech2TTS", "1.4.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Speech2TTS : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<string, bool> <>9__30_0; public static Action<bool> <>9__36_8; public static ScrollViewBuilderDelegate <>9__36_0; public static Action <>9__36_13; public static ScrollViewBuilderDelegate <>9__36_5; internal bool <ScanAvailableModels>b__30_0(string dir) { return Directory.Exists(Path.Combine(dir, "am")) && Directory.Exists(Path.Combine(dir, "graph")) && Directory.Exists(Path.Combine(dir, "ivector")) && Directory.Exists(Path.Combine(dir, "conf")); } internal RectTransform <OpenSTTMenu>b__36_0(Transform scrollView) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) REPOToggle val = MenuAPI.CreateREPOToggle("Speech-2-TTS", (Action<bool>)delegate(bool enabled) { sttEnabled = enabled; Logger.LogInfo((object)("STT Status: " + (enabled ? "ENABLED" : "DISABLED"))); if (!enabled) { audioProcessor?.ClearBuffer(); } }, scrollView, Vector2.zero, "Enabled", "Disabled", sttEnabled); return ((REPOElement)val).rectTransform; } internal void <OpenSTTMenu>b__36_8(bool enabled) { sttEnabled = enabled; Logger.LogInfo((object)("STT Status: " + (enabled ? "ENABLED" : "DISABLED"))); if (!enabled) { audioProcessor?.ClearBuffer(); } } internal RectTransform <OpenSTTMenu>b__36_5(Transform scrollView) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) REPOButton val = MenuAPI.CreateREPOButton("Download Models", (Action)delegate { Process.Start("https://alphacephei.com/vosk/models"); }, scrollView, Vector2.zero); return ((REPOElement)val).rectTransform; } internal void <OpenSTTMenu>b__36_13() { Process.Start("https://alphacephei.com/vosk/models"); } } [CompilerGenerated] private sealed class <>c__DisplayClass32_0 { public string modelPath; internal VoskRecognizer <LoadModelAsync>b__0() { try { return new VoskRecognizer(modelPath, 48000f); } catch (Exception ex) { Logger.LogError((object)("Model loading error: " + ex.Message)); return null; } } } [CompilerGenerated] private sealed class <InitializeVoskAsync>d__31 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Speech2TTS <>4__this; private string <pluginPath>5__1; private string[] <requiredDlls>5__2; private string <modelToLoad>5__3; private string <modelPath>5__4; private string[] <>s__5; private int <>s__6; private string <dll>5__7; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitializeVoskAsync>d__31(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <pluginPath>5__1 = null; <requiredDlls>5__2 = null; <modelToLoad>5__3 = null; <modelPath>5__4 = null; <>s__5 = null; <dll>5__7 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <pluginPath>5__1 = Path.GetDirectoryName(((BaseUnityPlugin)<>4__this).Info.Location) ?? ""; if (string.IsNullOrEmpty(<pluginPath>5__1)) { return false; } <requiredDlls>5__2 = new string[4] { "libvosk.dll", "libgcc_s_seh-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll" }; <>s__5 = <requiredDlls>5__2; for (<>s__6 = 0; <>s__6 < <>s__5.Length; <>s__6++) { <dll>5__7 = <>s__5[<>s__6]; if (!File.Exists(Path.Combine(<pluginPath>5__1, <dll>5__7))) { Logger.LogError((object)("Missing DLL: " + <dll>5__7)); return false; } <dll>5__7 = null; } <>s__5 = null; SetDllDirectory(<pluginPath>5__1); <modelToLoad>5__3 = ""; if (!string.IsNullOrEmpty(<>4__this.selectedModelName.Value) && <>4__this.availableModels.Contains(<>4__this.selectedModelName.Value)) { <modelToLoad>5__3 = <>4__this.selectedModelName.Value; } else { if (<>4__this.availableModels.Count <= 0) { Logger.LogError((object)"No models available to load!"); return false; } <modelToLoad>5__3 = <>4__this.availableModels[0]; <>4__this.selectedModelName.Value = <modelToLoad>5__3; } <modelPath>5__4 = Path.Combine(<pluginPath>5__1, "model", <modelToLoad>5__3); <>2__current = ((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.LoadModelAsync(<modelPath>5__4, <modelToLoad>5__3, showUI: false)); <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <LoadModelAsync>d__32 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string modelPath; public string modelName; public bool showUI; public Speech2TTS <>4__this; private <>c__DisplayClass32_0 <>8__1; private Task<VoskRecognizer?> <loadTask>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <LoadModelAsync>d__32(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>8__1 = null; <loadTask>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>8__1 = new <>c__DisplayClass32_0(); <>8__1.modelPath = modelPath; if (<>4__this.isLoadingModel) { return false; } <>4__this.isLoadingModel = true; if (!Directory.Exists(<>8__1.modelPath)) { Logger.LogError((object)("Model path does not exist: " + <>8__1.modelPath)); <>4__this.isLoadingModel = false; return false; } if (<>4__this.isMenuOpen && (Object)(object)<>4__this.sttMenuPage != (Object)null) { <>4__this.sttMenuPage.ClosePage(false); <>4__this.isMenuOpen = false; } if (showUI) { <>4__this.ShowLoadingPopup(modelName); } audioProcessor?.ClearBuffer(); voskRecognizer?.Dispose(); voskRecognizer = null; <loadTask>5__2 = Task.Run(delegate { try { return new VoskRecognizer(<>8__1.modelPath, 48000f); } catch (Exception ex) { Logger.LogError((object)("Model loading error: " + ex.Message)); return null; } }); goto IL_0175; case 1: <>1__state = -1; goto IL_0175; case 2: { <>1__state = -1; <>4__this.loadingPopup.ClosePage(false); <>4__this.loadingPopup = null; <>4__this.loadingTimerLabel = null; break; } IL_0175: if (!<loadTask>5__2.IsCompleted) { <>2__current = null; <>1__state = 1; return true; } voskRecognizer = <loadTask>5__2.Result; if (voskRecognizer != null) { Logger.LogInfo((object)("Loaded Vosk Model: " + modelName)); <>4__this.selectedModelName.Value = modelName; } else { Logger.LogError((object)("Failed to load model: " + modelName)); } if (showUI && (Object)(object)<>4__this.loadingPopup != (Object)null) { <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 2; return true; } break; } <>4__this.isLoadingModel = false; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } internal static AudioProcessor? audioProcessor; internal static VoskRecognizer? voskRecognizer; internal static bool sttEnabled = true; private ConfigEntry<string>? selectedModelName; private ConfigEntry<bool>? enableSTTOnStartup; private ConfigEntry<float>? speechThreshold; private ConfigEntry<int>? preRollFrames; private List<string> availableModels = new List<string>(); private REPOPopupPage? sttMenuPage; private REPOPopupPage? loadingPopup; private REPOLabel? loadingTimerLabel; private bool isMenuOpen = false; private bool isLoadingModel = false; private float loadingStartTime = 0f; internal static Speech2TTS Instance { get; private set; } = null; internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; SetupConfig(); ScanAvailableModels(); ((MonoBehaviour)this).StartCoroutine(InitializeVoskAsync()); Patch(); Logger.LogInfo((object)$"Speech2TTS Mod v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded!"); Logger.LogInfo((object)"Press F7 to open STT Settings menu"); Logger.LogInfo((object)"Press F8 to quickly toggle STT on/off"); Logger.LogInfo((object)$"Found {availableModels.Count} Vosk model(s)"); } private void SetupConfig() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown selectedModelName = ((BaseUnityPlugin)this).Config.Bind<string>("Speech Recognition", "SelectedModel", "", "Name of the currently selected Vosk model folder"); enableSTTOnStartup = ((BaseUnityPlugin)this).Config.Bind<bool>("Speech Recognition", "EnableOnStartup", true, "Enable STT when the mod loads"); speechThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Speech Recognition", "SpeechThreshold", 0.02f, new ConfigDescription("RMS threshold for speech detection (lower = more sensitive)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 0.1f), Array.Empty<object>())); preRollFrames = ((BaseUnityPlugin)this).Config.Bind<int>("Speech Recognition", "PreRollFrames", 5, new ConfigDescription("Number of audio frames to capture before speech detection (higher = captures more at start)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); sttEnabled = enableSTTOnStartup.Value; } public float GetSpeechThreshold() { return speechThreshold?.Value ?? 0.02f; } public int GetPreRollFrames() { return preRollFrames?.Value ?? 5; } private void ScanAvailableModels() { availableModels.Clear(); string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? ""; if (string.IsNullOrEmpty(text)) { return; } string text2 = Path.Combine(text, "model"); if (!Directory.Exists(text2)) { Directory.CreateDirectory(text2); Logger.LogWarning((object)("Models Directory created at: " + text2)); Logger.LogWarning((object)"Please download a Vosk model and place it in the 'model' folder"); return; } List<string> collection = (from dir in Directory.GetDirectories(text2) where Directory.Exists(Path.Combine(dir, "am")) && Directory.Exists(Path.Combine(dir, "graph")) && Directory.Exists(Path.Combine(dir, "ivector")) && Directory.Exists(Path.Combine(dir, "conf")) select dir).Select(Path.GetFileName).ToList(); availableModels.AddRange(collection); if (availableModels.Count == 0) { Logger.LogWarning((object)"No Vosk models found in model folder!"); Logger.LogWarning((object)"Download a Vosk model from: https://alphacephei.com/vosk/models"); } } [IteratorStateMachine(typeof(<InitializeVoskAsync>d__31))] private IEnumerator InitializeVoskAsync() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InitializeVoskAsync>d__31(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<LoadModelAsync>d__32))] private IEnumerator LoadModelAsync(string modelPath, string modelName, bool showUI = true) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <LoadModelAsync>d__32(0) { <>4__this = this, modelPath = modelPath, modelName = modelName, showUI = showUI }; } private void ShowLoadingPopup(string modelName) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown string displayName = GetShortModelName(modelName); loadingPopup = MenuAPI.CreateREPOPopupPage("Loading...", (PresetSide)1, false, true, 1.5f); loadingPopup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) REPOLabel val2 = MenuAPI.CreateREPOLabel(displayName, scrollView, Vector2.zero); return ((REPOElement)val2).rectTransform; }, 0f, 0f); loadingPopup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) loadingTimerLabel = MenuAPI.CreateREPOLabel("Elapsed: 0s", scrollView, Vector2.zero); return ((REPOElement)loadingTimerLabel).rectTransform; }, 0f, 0f); loadingPopup.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) REPOButton val = MenuAPI.CreateREPOButton("Exit", (Action)delegate { loadingPopup.ClosePage(false); }, scrollView, Vector2.zero); return ((REPOElement)val).rectTransform; }, 0f, 0f); loadingPopup.OpenPage(false); loadingStartTime = Time.time; } private string GetShortModelName(string modelName) { if (modelName.Contains("small")) { return "Small Model"; } if (modelName.Contains("0.22")) { return "Standard Model"; } if (modelName.Contains("gigaspeech")) { return "Gigaspeech Model"; } if (modelName.Length > 20) { return modelName.Substring(0, 20) + "..."; } return modelName; } public void SwitchModel(string modelName) { if (!availableModels.Contains(modelName)) { Logger.LogError((object)("Model not found: " + modelName)); } else if (!isLoadingModel && !(selectedModelName.Value == modelName)) { string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? ""; if (!string.IsNullOrEmpty(text)) { string modelPath = Path.Combine(text, "model", modelName); ((MonoBehaviour)this).StartCoroutine(LoadModelAsync(modelPath, modelName)); } } } private void OpenSTTMenu() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_017e: 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_0189: Expected O, but got Unknown if (isLoadingModel && !isMenuOpen) { Logger.LogWarning((object)"Loading Model, Cannot Access Settings Menu."); return; } if (isMenuOpen && (Object)(object)sttMenuPage != (Object)null) { sttMenuPage.ClosePage(false); isMenuOpen = false; return; } sttMenuPage = MenuAPI.CreateREPOPopupPage("Speech-2-TTS Settings", (PresetSide)0, true, true, 1.5f); REPOPopupPage? obj = sttMenuPage; object obj2 = <>c.<>9__36_0; if (obj2 == null) { ScrollViewBuilderDelegate val = delegate(Transform scrollView) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) REPOToggle val10 = MenuAPI.CreateREPOToggle("Speech-2-TTS", (Action<bool>)delegate(bool enabled) { sttEnabled = enabled; Logger.LogInfo((object)("STT Status: " + (enabled ? "ENABLED" : "DISABLED"))); if (!enabled) { audioProcessor?.ClearBuffer(); } }, scrollView, Vector2.zero, "Enabled", "Disabled", sttEnabled); return ((REPOElement)val10).rectTransform; }; <>c.<>9__36_0 = val; obj2 = (object)val; } obj.AddElementToScrollView((ScrollViewBuilderDelegate)obj2, 0f, 0f); if (availableModels.Count <= 0) { Logger.LogError((object)"No Vosk Models found!\nDownload from alphacephei.com/vosk/models"); } else { sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) REPOSlider val9 = MenuAPI.CreateREPOSlider("Model Selection", GetModelDescription(selectedModelName.Value), (Action<string>)delegate(string modelName) { if (!isLoadingModel && modelName != selectedModelName.Value) { SwitchModel(modelName); } }, scrollView, availableModels.ToArray(), selectedModelName.Value, Vector2.zero, "", "", (BarBehavior)0); return ((REPOElement)val9).rectTransform; }, 0f, 0f); } sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) REPOSlider val8 = MenuAPI.CreateREPOSlider("Speech Sensitivity", "Lower = More Sensitive", (Action<float>)delegate(float value) { speechThreshold.Value = value; audioProcessor?.SetThreshold(value); }, scrollView, Vector2.zero, 0.001f, 0.1f, 3, speechThreshold.Value, "", "", (BarBehavior)0); return ((REPOElement)val8).rectTransform; }, 0f, 0f); sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) REPOSlider val7 = MenuAPI.CreateREPOSlider("Pre-Roll Capture", "Higher = Better Stability", (Action<float>)delegate(float value) { preRollFrames.Value = (int)value; audioProcessor?.SetPreRollFrames((int)value); }, scrollView, Vector2.zero, 1f, 10f, 0, (float)preRollFrames.Value, "", " frames", (BarBehavior)0); return ((REPOElement)val7).rectTransform; }, 0f, 0f); sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) REPOButton val6 = MenuAPI.CreateREPOButton("Open Model Folder", (Action)delegate { string text = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? ""; if (!string.IsNullOrEmpty(text)) { string text2 = Path.Combine(text, "model"); if (Directory.Exists(text2)) { Process.Start("explorer.exe", text2); } } }, scrollView, Vector2.zero); return ((REPOElement)val6).rectTransform; }, 0f, 0f); REPOPopupPage? obj3 = sttMenuPage; object obj4 = <>c.<>9__36_5; if (obj4 == null) { ScrollViewBuilderDelegate val2 = delegate(Transform scrollView) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) REPOButton val5 = MenuAPI.CreateREPOButton("Download Models", (Action)delegate { Process.Start("https://alphacephei.com/vosk/models"); }, scrollView, Vector2.zero); return ((REPOElement)val5).rectTransform; }; <>c.<>9__36_5 = val2; obj4 = (object)val2; } obj3.AddElementToScrollView((ScrollViewBuilderDelegate)obj4, 0f, 0f); sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) REPOButton val4 = MenuAPI.CreateREPOButton("Refresh Models", (Action)delegate { ScanAvailableModels(); Logger.LogInfo((object)$"Refreshed Models: Found {availableModels.Count} model(s)"); sttMenuPage.ClosePage(false); isMenuOpen = false; OpenSTTMenu(); }, scrollView, Vector2.zero); return ((REPOElement)val4).rectTransform; }, 0f, 0f); sttMenuPage.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform scrollView) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) REPOButton val3 = MenuAPI.CreateREPOButton("Exit", (Action)delegate { sttMenuPage.ClosePage(false); isMenuOpen = false; }, scrollView, Vector2.zero); return ((REPOElement)val3).rectTransform; }, 0f, 0f); sttMenuPage.OpenPage(false); isMenuOpen = true; } private string GetModelDescription(string modelName) { if (string.IsNullOrEmpty(modelName)) { return "No model selected"; } if (modelName.Contains("vosk-model-small-en-us-0.15")) { return "Small (40MB) - Fast, lightweight"; } if (modelName.Contains("vosk-model-en-us-0.22")) { return "Standard (1.8GB) - Best accuracy"; } if (modelName.Contains("vosk-model-en-us-0.42-gigaspeech")) { return "Gigaspeech (2.3GB) - Podcasts optimized"; } return modelName; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetDllDirectory(string lpPathName); internal void Patch() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0026: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } try { Type type = AccessTools.TypeByName("Photon.Voice.Unity.Recorder"); if (type == null) { Logger.LogError((object)"Could not find Photon Voice Recorder"); return; } MethodInfo methodInfo = AccessTools.Method(type, "SendPhotonVoiceCreatedMessage", (Type[])null, (Type[])null); if (methodInfo != null) { Harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(RecorderPatches), "SendPhotonVoiceCreatedPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Harmony.PatchAll(); } catch (Exception ex) { Logger.LogError((object)("Patching failed: " + ex.Message)); } } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void Update() { if (isLoadingModel && (Object)(object)loadingTimerLabel != (Object)null) { float num = Time.time - loadingStartTime; ((TMP_Text)loadingTimerLabel.labelTMP).text = $"Elapsed: {num:F1}s"; } if (Input.GetKeyDown((KeyCode)288)) { OpenSTTMenu(); } if (!Input.GetKeyDown((KeyCode)289)) { return; } if (isMenuOpen) { Logger.LogInfo((object)"Cannot Update STT Status while in Settings Menu."); return; } sttEnabled = !sttEnabled; Logger.LogInfo((object)("STT Status: " + (sttEnabled ? "ENABLED" : "DISABLED"))); if (!sttEnabled) { audioProcessor?.ClearBuffer(); } } private void OnDestroy() { voskRecognizer?.Dispose(); } } public class VoskRecognizer { private IntPtr model; private IntPtr recognizer; [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private static extern IntPtr vosk_model_new([MarshalAs(UnmanagedType.LPStr)] string model_path); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern void vosk_model_free(IntPtr model); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr vosk_recognizer_new(IntPtr model, float sample_rate); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern void vosk_recognizer_free(IntPtr recognizer); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern int vosk_recognizer_accept_waveform(IntPtr recognizer, byte[] data, int length); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr vosk_recognizer_result(IntPtr recognizer); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr vosk_recognizer_final_result(IntPtr recognizer); [DllImport("libvosk", CallingConvention = CallingConvention.Cdecl)] private static extern void vosk_set_log_level(int level); public VoskRecognizer(string modelPath, float sampleRate) { vosk_set_log_level(-1); modelPath = modelPath.Replace('\\', '/'); model = vosk_model_new(modelPath); if (model == IntPtr.Zero) { throw new Exception("Failed to load Vosk model from: " + modelPath); } recognizer = vosk_recognizer_new(model, sampleRate); if (recognizer == IntPtr.Zero) { vosk_model_free(model); throw new Exception("Failed to create Vosk recognizer"); } } public bool AcceptWaveform(byte[] data) { return vosk_recognizer_accept_waveform(recognizer, data, data.Length) != 0; } public string GetResult() { IntPtr ptr = vosk_recognizer_result(recognizer); return Marshal.PtrToStringAnsi(ptr) ?? ""; } public string GetFinalResult() { IntPtr ptr = vosk_recognizer_final_result(recognizer); return Marshal.PtrToStringAnsi(ptr) ?? ""; } public void Dispose() { if (recognizer != IntPtr.Zero) { vosk_recognizer_free(recognizer); recognizer = IntPtr.Zero; } if (model != IntPtr.Zero) { vosk_model_free(model); model = IntPtr.Zero; } } } public class AudioProcessor { [CompilerGenerated] private sealed class <SendToChat>d__15 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string text; public AudioProcessor <>4__this; private Type <chatManagerType>5__1; private Object <chatManager>5__2; private MethodInfo <sendMethod>5__3; private Exception <e>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SendToChat>d__15(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <chatManagerType>5__1 = null; <chatManager>5__2 = null; <sendMethod>5__3 = null; <e>5__4 = null; <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.1f); <>1__state = 1; return true; case 1: <>1__state = -1; try { <chatManagerType>5__1 = AccessTools.TypeByName("ChatManager"); if (<chatManagerType>5__1 == null) { return false; } <chatManager>5__2 = Object.FindObjectOfType(<chatManagerType>5__1); if (<chatManager>5__2 == (Object)null) { return false; } <sendMethod>5__3 = <chatManagerType>5__1.GetMethod("ForceSendMessage", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (<sendMethod>5__3 == null) { return false; } <sendMethod>5__3.Invoke(<chatManager>5__2, new object[1] { text }); <chatManagerType>5__1 = null; <chatManager>5__2 = null; <sendMethod>5__3 = null; } catch (Exception ex) { <e>5__4 = ex; Speech2TTS.Logger.LogError((object)("Chat error: " + <e>5__4.Message)); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private List<short> audioBuffer = new List<short>(); private Queue<short[]> preRollBuffer = new Queue<short[]>(); private bool isSpeaking = false; private int silenceFrames = 0; private float speechThreshold; private int preRollFrames; private const int SILENCE_FRAMES_THRESHOLD = 25; private const int MIN_SPEECH_FRAMES = 50; public AudioProcessor() { speechThreshold = Speech2TTS.Instance.GetSpeechThreshold(); preRollFrames = Speech2TTS.Instance.GetPreRollFrames(); Speech2TTS.Logger.LogInfo((object)$"AudioProcessor initialized - Threshold: {speechThreshold:F3}, Pre-roll: {preRollFrames} frames"); } public void SetThreshold(float threshold) { speechThreshold = threshold; Speech2TTS.Logger.LogInfo((object)$"Speech threshold updated to: {threshold:F3}"); } public void SetPreRollFrames(int frames) { preRollFrames = frames; Speech2TTS.Logger.LogInfo((object)$"Pre-roll frames updated to: {frames} (~{frames * 20}ms)"); } public void ClearBuffer() { audioBuffer.Clear(); preRollBuffer.Clear(); isSpeaking = false; silenceFrames = 0; } public void ProcessAudioFrame(short[] data) { float num = 0f; for (int i = 0; i < data.Length; i++) { float num2 = (float)data[i] / 32768f; num += num2 * num2; } float num3 = Mathf.Sqrt(num / (float)data.Length); if (!Speech2TTS.sttEnabled) { preRollBuffer.Clear(); return; } if (!isSpeaking) { short[] array = new short[data.Length]; Array.Copy(data, array, data.Length); preRollBuffer.Enqueue(array); if (preRollBuffer.Count > preRollFrames) { preRollBuffer.Dequeue(); } } if (num3 > speechThreshold) { if (!isSpeaking) { isSpeaking = true; audioBuffer.Clear(); while (preRollBuffer.Count > 0) { audioBuffer.AddRange(preRollBuffer.Dequeue()); } } audioBuffer.AddRange(data); silenceFrames = 0; } else if (isSpeaking) { audioBuffer.AddRange(data); silenceFrames++; if (silenceFrames > 25 && audioBuffer.Count > 48000) { ProcessSpeech(audioBuffer.ToArray()); audioBuffer.Clear(); isSpeaking = false; silenceFrames = 0; preRollBuffer.Clear(); } } } private void ProcessSpeech(short[] audioData) { if (Speech2TTS.voskRecognizer == null) { return; } try { byte[] array = new byte[audioData.Length * 2]; Buffer.BlockCopy(audioData, 0, array, 0, array.Length); string json = (Speech2TTS.voskRecognizer.AcceptWaveform(array) ? Speech2TTS.voskRecognizer.GetResult() : Speech2TTS.voskRecognizer.GetFinalResult()); string text = ParseVoskResult(json); if (!string.IsNullOrEmpty(text)) { ((MonoBehaviour)Speech2TTS.Instance).StartCoroutine(SendToChat(text)); } } catch (Exception ex) { Speech2TTS.Logger.LogError((object)("STT Error: " + ex.Message)); } } private string ParseVoskResult(string json) { try { int num = json.IndexOf("\"text\""); if (num == -1) { return ""; } int startIndex = json.IndexOf(":", num); int num2 = json.IndexOf("\"", startIndex); int num3 = json.IndexOf("\"", num2 + 1); if (num2 != -1 && num3 != -1) { return json.Substring(num2 + 1, num3 - num2 - 1).Trim(); } } catch { } return ""; } [IteratorStateMachine(typeof(<SendToChat>d__15))] private IEnumerator SendToChat(string text) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SendToChat>d__15(0) { <>4__this = this, text = text }; } } [HarmonyPatch] public static class RecorderPatches { public static void SendPhotonVoiceCreatedPrefix(object __instance) { try { Type type = __instance.GetType(); PropertyInfo propertyInfo = AccessTools.Property(type, "voiceAudio"); if (propertyInfo == null) { return; } object value = propertyInfo.GetValue(__instance); if (value == null) { return; } Type type2 = value.GetType(); if (type2.FullName.Contains("LocalVoiceAudioShort")) { if (Speech2TTS.audioProcessor == null) { Speech2TTS.audioProcessor = new AudioProcessor(); } AudioProcessorShortWrapper value2 = new AudioProcessorShortWrapper(Speech2TTS.audioProcessor); Array array = Array.CreateInstance(typeof(IProcessor<>).MakeGenericType(typeof(short)), 1); array.SetValue(value2, 0); AccessTools.Method(type2, "AddPreProcessor", (Type[])null, (Type[])null)?.Invoke(value, new object[1] { array }); } } catch { } } } public class AudioProcessorShortWrapper : IProcessor<short>, IDisposable { private AudioProcessor processor; public AudioProcessorShortWrapper(AudioProcessor processor) { this.processor = processor; } public short[] Process(short[] buf) { processor.ProcessAudioFrame(buf); return Speech2TTS.sttEnabled ? new short[buf.Length] : buf; } public void Dispose() { } } }