Decompiled source of SpeechRecognitionAPI v1.0.0

BepInEx/plugins/Vosk.dll

Decompiled 2 months ago
using 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 ago
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 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();
		}
	}
}