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 MultisyllableCurse v1.4.5
MultisyllableCurse/MultisyllableCurse.dll
Decompiled a week agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Photon.Voice.Unity; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("MultisyllableCurse")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Org")] [assembly: AssemblyProduct("MultisyllableCurse")] [assembly: AssemblyCopyright("Copyright © Org 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e1cf5710-aeff-4084-9894-a4eeac8fb5ef")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace MultisyllableCurse; [BepInPlugin("twin.multisyllablecurse", "Multisyllable Curse", "1.4.5")] public class Plugin : BaseUnityPlugin { private class LocalVoiceSession { public IntPtr Recognizer; public int InputSampleRate; public int InputChannels; public volatile bool Removed; public bool IsSpeaking; public int SilenceFrames; public object AudioSource; public EventInfo AudioEvent; public Delegate AudioCallback; public readonly Queue<short[]> PreRoll = new Queue<short[]>(); public readonly List<short> AudioBuffer = new List<short>(); public LocalVoiceSession(IntPtr recognizer, int sampleRate, int channels) { Recognizer = recognizer; InputSampleRate = sampleRate; InputChannels = channels; } } private class AudioPacket { public LocalVoiceSession Session; public float[] Samples; public int Channels; public bool Ended; public AudioPacket(LocalVoiceSession session, float[] samples, int channels, bool ended) { Session = session; Samples = samples; Channels = channels; Ended = ended; } } internal static ManualLogSource Log; internal static Plugin Instance; private static IntPtr VoskModel = IntPtr.Zero; private const int TargetSampleRate = 16000; private const float SpeechThreshold = 0.02f; private const int PreRollFrames = 5; private const int SilenceFramesThreshold = 25; private const int MinimumSpeechSamples = 16000; private readonly ConcurrentQueue<AudioPacket> audioQueue = new ConcurrentQueue<AudioPacket>(); private LocalVoiceSession localSession; private Harmony harmony; private bool micHookLogged; private volatile bool gameplayEnabled; private void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; harmony = new Harmony("twin.multisyllablecurse"); harmony.PatchAll(); Log.LogInfo((object)"Multisyllable Curse v1.4.5 loaded!"); try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = Path.Combine(directoryName, "libvosk.dll"); string text2 = Path.Combine(directoryName, "model", "vosk-model-small-en-us-0.15"); string[] array = new string[4] { "libvosk.dll", "libgcc_s_seh-1.dll", "libstdc++-6.dll", "libwinpthread-1.dll" }; string[] array2 = array; foreach (string text3 in array2) { string text4 = Path.Combine(directoryName, text3); if (!File.Exists(text4)) { throw new FileNotFoundException("Required Vosk file missing: " + text3, text4); } } if (!Directory.Exists(text2)) { throw new DirectoryNotFoundException("Vosk model folder not found: " + text2); } if (!SetDllDirectory(directoryName)) { throw new Exception("Could not set native DLL directory."); } Log.LogInfo((object)("Loading Vosk from: " + text)); VoskNative.Load(text); Log.LogInfo((object)"Vosk native library loaded!"); VoskNative.vosk_set_log_level(-1); VoskModel = VoskNative.vosk_model_new(text2); if (VoskModel == IntPtr.Zero) { throw new Exception("Vosk model failed to load!"); } Log.LogInfo((object)"Vosk model loaded successfully!"); Log.LogInfo((object)("Vosk loaded from: " + directoryName)); } catch (Exception ex) { Log.LogError((object)("Vosk initialization failed: " + ex)); } } private bool IsGameplayAllowed() { try { return SemiFunc.RunIsLevel(); } catch (Exception ex) { Log.LogError((object)("Could not check current game area: " + ex)); return false; } } private void ResetAudioState() { AudioPacket result; while (audioQueue.TryDequeue(out result)) { } LocalVoiceSession localVoiceSession = localSession; if (localVoiceSession != null) { localVoiceSession.IsSpeaking = false; localVoiceSession.SilenceFrames = 0; localVoiceSession.PreRoll.Clear(); localVoiceSession.AudioBuffer.Clear(); } } private void UpdateGameplayState() { bool flag = IsGameplayAllowed(); if (flag != gameplayEnabled) { gameplayEnabled = flag; ResetAudioState(); if (flag) { Log.LogInfo((object)"Speech detection enabled: normal level."); } else { Log.LogInfo((object)"Speech detection paused: outside normal gameplay."); } } } internal void RegisterLocalRecorder(GameObject recorderObject) { if ((Object)(object)recorderObject == (Object)null || VoskModel == IntPtr.Zero) { return; } try { LocalRecorderListener localRecorderListener = recorderObject.GetComponent<LocalRecorderListener>(); if ((Object)(object)localRecorderListener == (Object)null) { localRecorderListener = recorderObject.AddComponent<LocalRecorderListener>(); } localRecorderListener.Initialize(this); } catch (Exception ex) { Log.LogError((object)("Could not register local recorder: " + ex)); } } internal void RegisterMicrophone(object payload) { if (payload == null || VoskModel == IntPtr.Zero) { return; } try { PropertyInfo property = payload.GetType().GetProperty("AudioDesc"); if (property == null) { Log.LogWarning((object)"PhotonVoiceCreated had no AudioDesc property."); return; } object value = property.GetValue(payload, null); if (value == null) { Log.LogWarning((object)"Photon Voice supplied a null audio source."); return; } Type type = value.GetType(); PropertyInfo property2 = type.GetProperty("SamplingRate"); PropertyInfo property3 = type.GetProperty("Channels"); if (property2 == null || property3 == null) { Log.LogWarning((object)("Audio source has no SamplingRate or Channels property: " + type.FullName)); return; } int num = Convert.ToInt32(property2.GetValue(value, null)); int num2 = Convert.ToInt32(property3.GetValue(value, null)); if (num <= 0 || num2 <= 0) { Log.LogWarning((object)("Invalid microphone format. Rate: " + num + ", channels: " + num2)); return; } if (value is MicWrapper) { RegisterMicWrapper(value, num, num2); return; } EventInfo eventInfo = type.GetEvent("OnAudioFrame", BindingFlags.Instance | BindingFlags.Public); if (eventInfo == null) { Log.LogWarning((object)("Unsupported microphone source type: " + type.FullName)); return; } RemoveLocalSession(); IntPtr intPtr = CreateRecognizer(); if (intPtr == IntPtr.Zero) { throw new Exception("Could not create local recognizer."); } LocalVoiceSession session = new LocalVoiceSession(intPtr, num, num2); Action<float[], int> action = delegate(float[] samples, int frameChannels) { if (!gameplayEnabled || session.Removed || samples == null || samples.Length == 0) { return; } try { float[] array = new float[samples.Length]; Array.Copy(samples, array, samples.Length); audioQueue.Enqueue(new AudioPacket(session, array, frameChannels, ended: false)); } catch { } }; eventInfo.AddEventHandler(value, action); session.AudioSource = value; session.AudioEvent = eventInfo; session.AudioCallback = action; localSession = session; Log.LogInfo((object)("Local microphone connected through audio event! Sample rate: " + num)); } catch (Exception ex) { Log.LogError((object)("Microphone registration failed: " + ex)); } } private void RegisterMicWrapper(object audioSource, int sampleRate, int channels) { RemoveLocalSession(); IntPtr intPtr = CreateRecognizer(); if (intPtr == IntPtr.Zero) { throw new Exception("Could not create MicWrapper recognizer."); } LocalVoiceSession localVoiceSession = new LocalVoiceSession(intPtr, sampleRate, channels); localVoiceSession.AudioSource = audioSource; localSession = localVoiceSession; Log.LogInfo((object)("MicWrapper detected! Read() capture registered. Sample rate: " + sampleRate + ", channels: " + channels)); } internal void OnMicWrapperRead(object source, float[] samples, bool readSucceeded) { if (!gameplayEnabled || !readSucceeded || source == null || samples == null || samples.Length == 0) { return; } try { LocalVoiceSession localVoiceSession = localSession; if (localVoiceSession != null && !localVoiceSession.Removed && localVoiceSession.AudioSource == source) { float[] array = new float[samples.Length]; Array.Copy(samples, array, samples.Length); audioQueue.Enqueue(new AudioPacket(localVoiceSession, array, localVoiceSession.InputChannels, ended: false)); if (!micHookLogged) { Log.LogInfo((object)"MicWrapper.Read() is successfully supplying audio samples!"); micHookLogged = true; } } } catch { } } private IntPtr CreateRecognizer() { return VoskNative.vosk_recognizer_new(VoskModel, 16000f); } private short[] ConvertToPcm16(float[] input, int sampleRate, int channels) { if (channels <= 0 || input.Length < channels) { return new short[0]; } int num = input.Length / channels; if (num == 0) { return new short[0]; } float[] array = new float[num]; for (int i = 0; i < num; i++) { float num2 = 0f; for (int j = 0; j < channels; j++) { num2 += input[i * channels + j]; } array[i] = num2 / (float)channels; } int num3 = Math.Max(1, (int)((long)num * 16000L / sampleRate)); short[] array2 = new short[num3]; for (int k = 0; k < num3; k++) { double num4 = (double)k * (double)sampleRate / 16000.0; int num5 = (int)num4; int num6 = Math.Min(num5 + 1, num - 1); float num7 = (float)(num4 - (double)num5); float val = array[num5] + (array[num6] - array[num5]) * num7; val = Math.Max(-1f, Math.Min(1f, val)); array2[k] = (short)(val * 32767f); } return array2; } private float CalculateRms(float[] samples) { if (samples.Length == 0) { return 0f; } double num = 0.0; foreach (float num2 in samples) { num += (double)(num2 * num2); } return (float)Math.Sqrt(num / (double)samples.Length); } private void ProcessAudioFrame(LocalVoiceSession session, float[] samples, int channels) { if (!gameplayEnabled || session.Removed || session.Recognizer == IntPtr.Zero) { return; } float num = CalculateRms(samples); short[] array = ConvertToPcm16(samples, session.InputSampleRate, channels); if (array.Length == 0) { return; } if (!session.IsSpeaking) { if (num > 0.02f) { session.IsSpeaking = true; while (session.PreRoll.Count > 0) { session.AudioBuffer.AddRange(session.PreRoll.Dequeue()); } session.AudioBuffer.AddRange(array); session.SilenceFrames = 0; } else { short[] array2 = new short[array.Length]; Array.Copy(array, array2, array.Length); session.PreRoll.Enqueue(array2); while (session.PreRoll.Count > 5) { session.PreRoll.Dequeue(); } } return; } session.AudioBuffer.AddRange(array); if (num > 0.02f) { session.SilenceFrames = 0; return; } session.SilenceFrames++; if (session.SilenceFrames > 25) { FinishSpeech(session); } } private void FinishSpeech(LocalVoiceSession session) { if (gameplayEnabled && session.IsSpeaking) { session.IsSpeaking = false; session.SilenceFrames = 0; session.PreRoll.Clear(); if (session.AudioBuffer.Count >= 16000) { ProcessSpeech(session, session.AudioBuffer.ToArray()); } session.AudioBuffer.Clear(); } } private void ProcessSpeech(LocalVoiceSession session, short[] audioData) { if (!gameplayEnabled || VoskModel == IntPtr.Zero || session.Removed) { return; } try { byte[] array = new byte[audioData.Length * 2]; Buffer.BlockCopy(audioData, 0, array, 0, array.Length); IntPtr ptr = ((VoskNative.vosk_recognizer_accept_waveform(session.Recognizer, array, array.Length) != 0) ? VoskNative.vosk_recognizer_result(session.Recognizer) : VoskNative.vosk_recognizer_final_result(session.Recognizer)); string json = Marshal.PtrToStringAnsi(ptr); LogRecognizedText(json); } catch (Exception ex) { Log.LogError((object)("Speech processing error: " + ex)); } } private void LogRecognizedText(string json) { if (gameplayEnabled && !string.IsNullOrEmpty(json)) { Log.LogInfo((object)("Local recognition: " + json)); string text = ExtractRecognizedText(json); if (!string.IsNullOrWhiteSpace(text)) { DetectMultisyllableWords(text); } } } private string ExtractRecognizedText(string json) { Match match = Regex.Match(json, "\"text\"\\s*:\\s*\"((?:\\\\.|[^\"\\\\])*)\""); if (!match.Success) { return ""; } return DecodeJsonString(match.Groups[1].Value); } private string DecodeJsonString(string value) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < value.Length; i++) { char c = value[i]; if (c != '\\' || i + 1 >= value.Length) { stringBuilder.Append(c); continue; } i++; char c2 = value[i]; switch (c2) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (i + 4 < value.Length && int.TryParse(value.Substring(i + 1, 4), NumberStyles.HexNumber, null, out var result)) { stringBuilder.Append((char)result); i += 4; } break; } default: stringBuilder.Append(c2); break; } } return stringBuilder.ToString(); } private void DetectMultisyllableWords(string text) { if (!gameplayEnabled) { return; } MatchCollection matchCollection = Regex.Matches(text.ToLowerInvariant(), "[a-z]+(?:'[a-z]+)?"); foreach (Match item in matchCollection) { if (!gameplayEnabled) { break; } string value = item.Value; int num = CountSyllables(value); if (num > 1) { Log.LogWarning((object)("[MULTISYLLABLE DETECTED] You said \"" + value + "\" (" + num + " estimated syllables)")); ApplyInstantDeath(value); break; } } } private void ApplyInstantDeath(string word) { if (!gameplayEnabled) { return; } try { if ((Object)(object)GameDirector.instance == (Object)null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || (Object)(object)player.photonView == (Object)null || !player.photonView.IsMine) { continue; } if (!((Object)(object)player.playerHealth == (Object)null)) { Log.LogWarning((object)("[PUNISHMENT] Killing your character for saying \"" + word + "\"!")); player.PlayerDeath(-1); } return; } Log.LogWarning((object)"Could not find your local PlayerAvatar."); } catch (Exception ex) { Log.LogError((object)("Instant death failed: " + ex)); } } private int CountSyllables(string word) { if (string.IsNullOrEmpty(word)) { return 0; } word = word.ToLowerInvariant().Replace("'", ""); if (word.Length <= 2) { return 1; } int num = 0; bool flag = false; for (int i = 0; i < word.Length; i++) { bool flag2 = IsVowel(word[i]); if (flag2 && !flag) { num++; } flag = flag2; } if (word.EndsWith("e") && !word.EndsWith("le") && num > 1) { num--; } if (word.EndsWith("le") && word.Length > 2 && !IsVowel(word[word.Length - 3])) { num++; } if (word.EndsWith("ed") && word.Length > 2 && word[word.Length - 3] != 't' && word[word.Length - 3] != 'd' && num > 1) { num--; } return Math.Max(1, num); } private bool IsVowel(char c) { return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y'; } private void Update() { UpdateGameplayState(); if (!gameplayEnabled) { AudioPacket result; while (audioQueue.TryDequeue(out result)) { } return; } int num = 0; AudioPacket result2; while (num < 100 && audioQueue.TryDequeue(out result2)) { num++; if (!gameplayEnabled) { ResetAudioState(); break; } if (result2.Session == null || result2.Session.Removed) { continue; } try { if (result2.Ended) { FinishSpeech(result2.Session); } else { ProcessAudioFrame(result2.Session, result2.Samples, result2.Channels); } } catch (Exception ex) { Log.LogError((object)("Audio processing error: " + ex)); } } } internal void RemoveLocalSession() { if (localSession == null) { return; } LocalVoiceSession localVoiceSession = localSession; localSession = null; localVoiceSession.Removed = true; try { if (localVoiceSession.AudioSource != null && localVoiceSession.AudioEvent != null && (object)localVoiceSession.AudioCallback != null) { localVoiceSession.AudioEvent.RemoveEventHandler(localVoiceSession.AudioSource, localVoiceSession.AudioCallback); } } catch (Exception ex) { Log.LogWarning((object)("Could not detach microphone callback: " + ex)); } if (localVoiceSession.Recognizer != IntPtr.Zero) { VoskNative.vosk_recognizer_free(localVoiceSession.Recognizer); localVoiceSession.Recognizer = IntPtr.Zero; } } private void OnDestroy() { gameplayEnabled = false; RemoveLocalSession(); AudioPacket result; while (audioQueue.TryDequeue(out result)) { } if (harmony != null) { harmony.UnpatchSelf(); } if (VoskModel != IntPtr.Zero) { VoskNative.vosk_model_free(VoskModel); VoskModel = IntPtr.Zero; } VoskNative.Unload(); Instance = null; } [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern bool SetDllDirectory(string path); } public class LocalRecorderListener : MonoBehaviour { private Plugin plugin; internal void Initialize(Plugin owner) { plugin = owner; } public void PhotonVoiceCreated(object payload) { if ((Object)(object)plugin != (Object)null) { plugin.RegisterMicrophone(payload); } } private void OnDestroy() { if ((Object)(object)plugin != (Object)null) { plugin.RemoveLocalSession(); } } } [HarmonyPatch] public static class LocalRecorderPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Recorder), "SendPhotonVoiceCreatedMessage", (Type[])null, (Type[])null); } private static void Prefix(Recorder __instance) { if ((Object)(object)Plugin.Instance != (Object)null && (Object)(object)__instance != (Object)null) { Plugin.Instance.RegisterLocalRecorder(((Component)__instance).gameObject); } } } [HarmonyPatch] public static class MicWrapperReadPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(MicWrapper), "Read", new Type[1] { typeof(float[]) }, (Type[])null); } private static void Postfix(MicWrapper __instance, float[] __0, bool __result) { if (!((Object)(object)Plugin.Instance == (Object)null)) { Plugin.Instance.OnMicWrapperRead(__instance, __0, __result); } } } internal static class VoskNative { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void SetLogLevelDelegate(int level); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr ModelNewDelegate([MarshalAs(UnmanagedType.LPStr)] string modelPath); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void ModelFreeDelegate(IntPtr model); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr RecognizerNewDelegate(IntPtr model, float sampleRate); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void RecognizerFreeDelegate(IntPtr recognizer); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int AcceptWaveformDelegate(IntPtr recognizer, [In] byte[] data, int length); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr RecognizerResultDelegate(IntPtr recognizer); private static IntPtr libraryHandle = IntPtr.Zero; private static SetLogLevelDelegate setLogLevel; private static ModelNewDelegate modelNew; private static ModelFreeDelegate modelFree; private static RecognizerNewDelegate recognizerNew; private static RecognizerFreeDelegate recognizerFree; private static AcceptWaveformDelegate acceptWaveform; private static RecognizerResultDelegate recognizerResult; private static RecognizerResultDelegate recognizerFinalResult; [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern IntPtr LoadLibraryW(string fileName); [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr module, string procedureName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr module); internal static void Load(string fullPath) { if (libraryHandle != IntPtr.Zero) { throw new InvalidOperationException("Vosk is already loaded."); } libraryHandle = LoadLibraryW(fullPath); if (libraryHandle == IntPtr.Zero) { int lastWin32Error = Marshal.GetLastWin32Error(); throw new Exception("Windows could not load libvosk.dll. Error " + lastWin32Error + ": " + new Win32Exception(lastWin32Error).Message); } try { setLogLevel = GetFunction<SetLogLevelDelegate>("vosk_set_log_level"); modelNew = GetFunction<ModelNewDelegate>("vosk_model_new"); modelFree = GetFunction<ModelFreeDelegate>("vosk_model_free"); recognizerNew = GetFunction<RecognizerNewDelegate>("vosk_recognizer_new"); recognizerFree = GetFunction<RecognizerFreeDelegate>("vosk_recognizer_free"); acceptWaveform = GetFunction<AcceptWaveformDelegate>("vosk_recognizer_accept_waveform"); recognizerResult = GetFunction<RecognizerResultDelegate>("vosk_recognizer_result"); recognizerFinalResult = GetFunction<RecognizerResultDelegate>("vosk_recognizer_final_result"); } catch { Unload(); throw; } } private static T GetFunction<T>(string name) where T : class { IntPtr procAddress = GetProcAddress(libraryHandle, name); if (procAddress == IntPtr.Zero) { throw new Exception("Could not find Vosk function " + name + ". Windows error: " + Marshal.GetLastWin32Error()); } return Marshal.GetDelegateForFunctionPointer(procAddress, typeof(T)) as T; } internal static void vosk_set_log_level(int level) { EnsureLoaded(); setLogLevel(level); } internal static IntPtr vosk_model_new(string modelPath) { EnsureLoaded(); return modelNew(modelPath); } internal static void vosk_model_free(IntPtr model) { EnsureLoaded(); modelFree(model); } internal static IntPtr vosk_recognizer_new(IntPtr model, float sampleRate) { EnsureLoaded(); return recognizerNew(model, sampleRate); } internal static void vosk_recognizer_free(IntPtr recognizer) { EnsureLoaded(); recognizerFree(recognizer); } internal static int vosk_recognizer_accept_waveform(IntPtr recognizer, byte[] data, int length) { EnsureLoaded(); return acceptWaveform(recognizer, data, length); } internal static IntPtr vosk_recognizer_result(IntPtr recognizer) { EnsureLoaded(); return recognizerResult(recognizer); } internal static IntPtr vosk_recognizer_final_result(IntPtr recognizer) { EnsureLoaded(); return recognizerFinalResult(recognizer); } private static void EnsureLoaded() { if (libraryHandle == IntPtr.Zero) { throw new InvalidOperationException("Vosk native library is not loaded."); } } internal static void Unload() { if (!(libraryHandle == IntPtr.Zero)) { setLogLevel = null; modelNew = null; modelFree = null; recognizerNew = null; recognizerFree = null; acceptWaveform = null; recognizerResult = null; recognizerFinalResult = null; FreeLibrary(libraryHandle); libraryHandle = IntPtr.Zero; } } }