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 SpeechRecognitionAPI v1.0.0
BepInEx/plugins/Vosk.dll
Decompiled 2 months agousing System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace Vosk; public class Model : IDisposable { private HandleRef handle; internal Model(IntPtr cPtr) { handle = new HandleRef(this, cPtr); } public Model(string model_path) : this(VoskPINVOKE.new_Model(model_path)) { } internal static HandleRef getCPtr(Model obj) { return obj?.handle ?? new HandleRef(null, IntPtr.Zero); } ~Model() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { lock (this) { if (handle.Handle != IntPtr.Zero) { VoskPINVOKE.delete_Model(handle); handle = new HandleRef(null, IntPtr.Zero); } } } public int vosk_model_find_word(string word) { return VoskPINVOKE.Model_vosk_model_find_word(handle, word); } } public class SpkModel : IDisposable { private HandleRef handle; internal SpkModel(IntPtr cPtr) { handle = new HandleRef(this, cPtr); } public SpkModel(string model_path) : this(VoskPINVOKE.new_SpkModel(model_path)) { } internal static HandleRef getCPtr(SpkModel obj) { return obj?.handle ?? new HandleRef(null, IntPtr.Zero); } ~SpkModel() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { lock (this) { if (handle.Handle != IntPtr.Zero) { VoskPINVOKE.delete_SpkModel(handle); handle = new HandleRef(null, IntPtr.Zero); } } } } public class Vosk { public static void SetLogLevel(int level) { VoskPINVOKE.SetLogLevel(level); } public static void GpuInit() { VoskPINVOKE.GpuInit(); } public static void GpuThreadInit() { VoskPINVOKE.GpuThreadInit(); } } internal class VoskPINVOKE { static VoskPINVOKE() { } [DllImport("libvosk", EntryPoint = "vosk_model_new")] public static extern IntPtr new_Model(string jarg1); [DllImport("libvosk", EntryPoint = "vosk_model_free")] public static extern void delete_Model(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_model_find_word")] public static extern int Model_vosk_model_find_word(HandleRef jarg1, string jarg2); [DllImport("libvosk", EntryPoint = "vosk_spk_model_new")] public static extern IntPtr new_SpkModel(string jarg1); [DllImport("libvosk", EntryPoint = "vosk_spk_model_free")] public static extern void delete_SpkModel(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_recognizer_new")] public static extern IntPtr new_VoskRecognizer(HandleRef jarg1, float jarg2); [DllImport("libvosk", EntryPoint = "vosk_recognizer_new_spk")] public static extern IntPtr new_VoskRecognizerSpk(HandleRef jarg1, float jarg2, HandleRef jarg3); [DllImport("libvosk", EntryPoint = "vosk_recognizer_new_grm")] public static extern IntPtr new_VoskRecognizerGrm(HandleRef jarg1, float jarg2, string jarg3); [DllImport("libvosk", EntryPoint = "vosk_recognizer_free")] public static extern void delete_VoskRecognizer(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_recognizer_set_max_alternatives")] public static extern void VoskRecognizer_SetMaxAlternatives(HandleRef jarg1, int jarg2); [DllImport("libvosk", EntryPoint = "vosk_recognizer_set_words")] public static extern void VoskRecognizer_SetWords(HandleRef jarg1, int jarg2); [DllImport("libvosk", EntryPoint = "vosk_recognizer_set_spk_model")] public static extern void VoskRecognizer_SetSpkModel(HandleRef jarg1, HandleRef jarg2); [DllImport("libvosk", EntryPoint = "vosk_recognizer_accept_waveform")] public static extern bool VoskRecognizer_AcceptWaveform(HandleRef jarg1, [In][MarshalAs(UnmanagedType.LPArray)] byte[] jarg2, int jarg3); [DllImport("libvosk", EntryPoint = "vosk_recognizer_accept_waveform_s")] public static extern bool VoskRecognizer_AcceptWaveformShort(HandleRef jarg1, [In][MarshalAs(UnmanagedType.LPArray)] short[] jarg2, int jarg3); [DllImport("libvosk", EntryPoint = "vosk_recognizer_accept_waveform_f")] public static extern bool VoskRecognizer_AcceptWaveformFloat(HandleRef jarg1, [In][MarshalAs(UnmanagedType.LPArray)] float[] jarg2, int jarg3); [DllImport("libvosk", EntryPoint = "vosk_recognizer_result")] public static extern IntPtr VoskRecognizer_Result(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_recognizer_partial_result")] public static extern IntPtr VoskRecognizer_PartialResult(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_recognizer_final_result")] public static extern IntPtr VoskRecognizer_FinalResult(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_recognizer_reset")] public static extern void VoskRecognizer_Reset(HandleRef jarg1); [DllImport("libvosk", EntryPoint = "vosk_set_log_level")] public static extern void SetLogLevel(int jarg1); [DllImport("libvosk", EntryPoint = "vosk_gpu_init")] public static extern void GpuInit(); [DllImport("libvosk", EntryPoint = "vosk_gpu_thread_init")] public static extern void GpuThreadInit(); } public class VoskRecognizer : IDisposable { private HandleRef handle; internal VoskRecognizer(IntPtr cPtr) { handle = new HandleRef(this, cPtr); } public VoskRecognizer(Model model, float sample_rate) : this(VoskPINVOKE.new_VoskRecognizer(Model.getCPtr(model), sample_rate)) { } public VoskRecognizer(Model model, float sample_rate, SpkModel spk_model) : this(VoskPINVOKE.new_VoskRecognizerSpk(Model.getCPtr(model), sample_rate, SpkModel.getCPtr(spk_model))) { } public VoskRecognizer(Model model, float sample_rate, string grammar) : this(VoskPINVOKE.new_VoskRecognizerGrm(Model.getCPtr(model), sample_rate, grammar)) { } internal static HandleRef getCPtr(VoskRecognizer obj) { return obj?.handle ?? new HandleRef(null, IntPtr.Zero); } ~VoskRecognizer() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { lock (this) { if (handle.Handle != IntPtr.Zero) { VoskPINVOKE.delete_VoskRecognizer(handle); handle = new HandleRef(null, IntPtr.Zero); } } } public void SetMaxAlternatives(int max_alternatives) { VoskPINVOKE.VoskRecognizer_SetMaxAlternatives(handle, max_alternatives); } public void SetWords(bool words) { VoskPINVOKE.VoskRecognizer_SetWords(handle, words ? 1 : 0); } public void SetSpkModel(SpkModel spk_model) { VoskPINVOKE.VoskRecognizer_SetSpkModel(handle, SpkModel.getCPtr(spk_model)); } public bool AcceptWaveform(byte[] data, int len) { return VoskPINVOKE.VoskRecognizer_AcceptWaveform(handle, data, len); } public bool AcceptWaveform(short[] sdata, int len) { return VoskPINVOKE.VoskRecognizer_AcceptWaveformShort(handle, sdata, len); } public bool AcceptWaveform(float[] fdata, int len) { return VoskPINVOKE.VoskRecognizer_AcceptWaveformFloat(handle, fdata, len); } private static string PtrToStringUTF8(IntPtr ptr) { int i; for (i = 0; Marshal.ReadByte(ptr, i) != 0; i++) { } byte[] array = new byte[i]; Marshal.Copy(ptr, array, 0, i); return Encoding.UTF8.GetString(array); } public string Result() { return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_Result(handle)); } public string PartialResult() { return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_PartialResult(handle)); } public string FinalResult() { return PtrToStringUTF8(VoskPINVOKE.VoskRecognizer_FinalResult(handle)); } public void Reset() { VoskPINVOKE.VoskRecognizer_Reset(handle); } }
BepInEx/plugins/SpeechRecognitionAPI.dll
Decompiled 2 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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Dissonance; using Dissonance.Audio.Capture; using HarmonyLib; using Microsoft.CodeAnalysis; using NAudio.Wave; using Newtonsoft.Json; using SpeechRecognitionAPI.Patches; using UnityEngine; using Vosk; [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: AssemblyCompany("JS03")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+9248eb8c7794f7211b32ff17374b70fbdc2ac5fe")] [assembly: AssemblyProduct("WhisperLC")] [assembly: AssemblyTitle("SpeechRecognitionAPI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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 SpeechRecognitionAPI { public class Engine : MonoBehaviour { [CompilerGenerated] private sealed class <WaitForDissonance>d__10 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Engine <>4__this; private DissonanceComms <dissonance>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForDissonance>d__10(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <dissonance>5__1 = null; <>1__state = -2; } private bool MoveNext() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <dissonance>5__1 = Object.FindObjectOfType<DissonanceComms>(); break; case 1: <>1__state = -1; break; } if (!Object.op_Implicit((Object)(object)<dissonance>5__1) || <dissonance>5__1.MicrophoneCapture == null) { <dissonance>5__1 = Object.FindObjectOfType<DissonanceComms>(); <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; } <>4__this._micCapture = new MicrophoneCapture(); <dissonance>5__1.MicrophoneCapture.Subscribe((IMicrophoneSubscriber)(object)<>4__this._micCapture); Plugin.mls.LogInfo((object)"Subscribed to Dissonance microphone stream"); 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 string[] models = new string[2] { "vosk-model-small-en-us-0.15", "vosk-model-small-es-0.42" }; private VoskRecognizer _recognizer; private Model _model; private MicrophoneCapture _micCapture; private int _micSampleRate; public static event EventHandler<SpeechEventArgs> SpeechRecognized; public void StartEngine() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown Object.DontDestroyOnLoad((Object)(object)this); string text = Path.Combine(Plugin.pluginDir, "models", models[(int)Plugin.language.Value]); _model = new Model(text); _recognizer = new VoskRecognizer(_model, 16000f); Plugin.mls.LogInfo((object)"Vosk ASR ready."); } public void StartMicCapture() { ((MonoBehaviour)this).StartCoroutine(WaitForDissonance()); } [IteratorStateMachine(typeof(<WaitForDissonance>d__10))] private IEnumerator WaitForDissonance() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForDissonance>d__10(0) { <>4__this = this }; } public void StopMicCapture() { DissonanceComms val = Object.FindObjectOfType<DissonanceComms>(); if ((Object)(object)val == (Object)null) { Plugin.mls.LogError((object)"DissonanceComms not found"); } else { val.MicrophoneCapture.Unsubscribe((IMicrophoneSubscriber)(object)_micCapture); } } private float[] Resample(float[] input, int inputRate, int outputRate) { if (inputRate == outputRate) { return input; } int num = (int)((long)input.Length * (long)outputRate / inputRate); float[] array = new float[num]; float num2 = (float)input.Length / (float)num; for (int i = 0; i < num; i++) { float num3 = (float)i * num2; int num4 = (int)num3; float num5 = num3 - (float)num4; float num6 = input[num4]; float num7 = ((num4 + 1 < input.Length) ? input[num4 + 1] : num6); array[i] = num6 + (num7 - num6) * num5; } return array; } public void ReceiveAudio(ArraySegment<float> buffer, int sampleRate) { float[] array = new float[buffer.Count]; if (buffer.Array == null) { return; } Array.Copy(buffer.Array, buffer.Offset, array, 0, buffer.Count); float[] array2 = Resample(array, sampleRate, 16000); byte[] array3 = new byte[array2.Length * 2]; for (int i = 0; i < array2.Length; i++) { short num = (short)(array2[i] * 32767f); array3[i * 2] = (byte)((uint)num & 0xFFu); array3[i * 2 + 1] = (byte)(num >> 8); } if (!_recognizer.AcceptWaveform(array3, array3.Length)) { return; } string text = _recognizer.Result(); string text2 = JsonConvert.DeserializeObject<VoskResult>(text)?.text; if (!string.IsNullOrEmpty(text2)) { if (Plugin.logging.Value) { Plugin.mls.LogInfo((object)("Recognized: " + text2)); } if (Speech.phrases.Count > 0) { Speech.GetBestMatch(text2); } Engine.SpeechRecognized?.Invoke(this, new SpeechEventArgs(text2)); } } } public enum Languages { English, Spanish } public class MicrophoneCapture : IMicrophoneSubscriber { public void ReceiveMicrophoneData(ArraySegment<float> buffer, WaveFormat format) { Plugin.SpeechEngine.ReceiveAudio(buffer, format.SampleRate); } public void Reset() { } } [BepInPlugin("JS03.SpeechRecognitionAPI", "SpeechRecognitionAPI", "1.0.0")] public class Plugin : BaseUnityPlugin { private const string modGUID = "JS03.SpeechRecognitionAPI"; private const string modName = "SpeechRecognitionAPI"; private const string modVersion = "1.0.0"; public static Engine SpeechEngine; private static readonly string[] _libraries = new string[5] { "libgcc_s_seh-1", "libstdc++-6", "libvosk", "libwinpthread-1", "Vosk" }; internal static string? pluginDir; private readonly Harmony harmony = new Harmony("JS03.SpeechRecognitionAPI"); public static Plugin Instance; internal static ManualLogSource mls; public static ConfigEntry<bool> logging; public static ConfigEntry<Languages> language; [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr LoadLibrary(string lpFileName); private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } mls = Logger.CreateLogSource("JS03.SpeechRecognitionAPI"); mls.LogInfo((object)"Starting SpeechRecognitionAPI"); language = ((BaseUnityPlugin)this).Config.Bind<Languages>("General", "Language", Languages.English, "Language to be used for speech recognition"); logging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Log recognized speech", true, "Shows the speech recognition output"); Speech.phrases = new List<string>(); pluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); Environment.SetEnvironmentVariable("PATH", pluginDir + ";" + Environment.GetEnvironmentVariable("PATH")); string[] libraries = _libraries; foreach (string text in libraries) { string text2 = Path.Combine(pluginDir, text + ".dll"); if (File.Exists(text2)) { LoadLibrary(text2); } else { mls.LogError((object)(text + ".dll not found at: " + text2)); } } harmony.PatchAll(typeof(GameNetworkManagerPatch)); harmony.PatchAll(typeof(StartOfRoundPatch)); } } public class Speech { internal static List<string> phrases; public static string bestMatch; private static double bestScore; internal static float GetSimilarity(string phrase, string recognized) { if (string.IsNullOrEmpty(phrase) || string.IsNullOrEmpty(recognized)) { return 0f; } int num = Math.Max(phrase.Length, recognized.Length); if (num == 0) { return 1f; } int num2 = LevenshteinDistance(phrase, recognized); return (float)Math.Round(1.0 - (double)num2 / (double)num, 2); } internal static void GetBestMatch(string recognized) { float num = float.MinValue; foreach (string phrase in phrases) { float similarity = GetSimilarity(phrase, recognized); if (similarity > num) { num = similarity; bestMatch = phrase; } } bestScore = num; if (Plugin.logging.Value) { Plugin.mls.LogDebug((object)("Best match: " + bestMatch)); Plugin.mls.LogDebug((object)$"Best similarity score: {bestScore}"); } } public static bool IsAboveThreshold(string[] phrases, double similarityThreshold) { return phrases.Contains(bestMatch) && bestScore >= similarityThreshold; } public static void RegisterPhrases(string[] phrases) { Speech.phrases.AddRange(phrases); } public static EventHandler<SpeechEventArgs> RegisterCustomHandler(EventHandler<SpeechEventArgs> callback) { Engine.SpeechRecognized += callback; return callback; } private static int LevenshteinDistance(string s1, string s2) { s1 = s1.ToLower(); s2 = s2.ToLower(); int[] array = new int[s2.Length + 1]; int[] array2 = new int[s2.Length + 1]; for (int i = 0; i <= s2.Length; i++) { array[i] = i; } for (int j = 1; j <= s1.Length; j++) { array2[0] = j; for (int k = 1; k <= s2.Length; k++) { int num = ((s1[j - 1] != s2[k - 1]) ? 1 : 0); array2[k] = Math.Min(Math.Min(array[k] + 1, array2[k - 1] + 1), array[k - 1] + num); } int[] array3 = array2; array2 = array; array = array3; } return array[s2.Length]; } } public class SpeechEventArgs : EventArgs { public string Text { get; } public SpeechEventArgs(string text) { Text = text; } } internal class VoskResult { public string text; } } namespace SpeechRecognitionAPI.Patches { [HarmonyPatch(typeof(GameNetworkManager))] internal class GameNetworkManagerPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartModel() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (Plugin.pluginDir == null) { Debug.LogError((object)"[SpeechRecognitionAPI] Plugin did not initialize correctly, skipping engine start."); return; } GameObject val = new GameObject("SpeechRecognitionAPIEngine"); Plugin.SpeechEngine = val.AddComponent<Engine>(); Plugin.SpeechEngine.StartEngine(); } [HarmonyPatch("Disconnect")] [HarmonyPostfix] private static void StopMicCapture() { Plugin.SpeechEngine.StopMicCapture(); } } [HarmonyPatch(typeof(StartOfRound))] public class StartOfRoundPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartMicCapture() { Plugin.SpeechEngine.StartMicCapture(); } } }