using 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()
{
}
}
}