Decompiled source of Super Soundboard v0.1.4

super_soundboard.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Voice;
using Photon.Voice.Unity;
using UnityEngine;
using UnityEngine.Networking;

[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("sasnews")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+32d474e6f962f181dcdbc32bc77786a062d91c21")]
[assembly: AssemblyProduct("super_soundboard")]
[assembly: AssemblyTitle("super_soundboard")]
[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 super_soundboard
{
	public class AudioStreamAdapter : Stream
	{
		private readonly CircularBuffer _buffer;

		private readonly AutoResetEvent _dataAvailable = new AutoResetEvent(initialState: false);

		private bool _isClosed = false;

		public override bool CanRead => true;

		public override bool CanSeek => false;

		public override bool CanWrite => true;

		public override long Length => _buffer.Count;

		public override long Position
		{
			get
			{
				return 0L;
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		public AudioStreamAdapter(int bufferSize)
		{
			_buffer = new CircularBuffer(bufferSize);
		}

		public override void Flush()
		{
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			int num = 0;
			while (num < count)
			{
				if (_isClosed && _buffer.Count == 0)
				{
					return num;
				}
				int num2 = _buffer.Read(buffer, offset + num, count - num);
				if (num2 == 0)
				{
					if (_isClosed)
					{
						return num;
					}
					_dataAvailable.WaitOne(100);
				}
				else
				{
					num += num2;
				}
			}
			return num;
		}

		public override void Write(byte[] buffer, int offset, int count)
		{
			if (!_isClosed)
			{
				_buffer.Write(buffer, offset, count);
				_dataAvailable.Set();
			}
		}

		public void Write(float[] samples)
		{
			byte[] array = new byte[samples.Length * 2];
			for (int i = 0; i < samples.Length; i++)
			{
				float num = samples[i];
				if (num > 1f)
				{
					num = 1f;
				}
				if (num < -1f)
				{
					num = -1f;
				}
				short num2 = (short)(num * 32767f);
				array[i * 2] = (byte)((uint)num2 & 0xFFu);
				array[i * 2 + 1] = (byte)((uint)(num2 >> 8) & 0xFFu);
			}
			Write(array, 0, array.Length);
		}

		public override void Close()
		{
			_isClosed = true;
			_dataAvailable.Set();
			base.Close();
		}

		public override long Seek(long offset, SeekOrigin origin)
		{
			throw new NotSupportedException();
		}

		public override void SetLength(long value)
		{
			throw new NotSupportedException();
		}
	}
	public class CircularBuffer
	{
		private readonly byte[] _buffer;

		private int _head;

		private int _tail;

		private int _count;

		private readonly object _lock = new object();

		public int Count
		{
			get
			{
				lock (_lock)
				{
					return _count;
				}
			}
		}

		public CircularBuffer(int size)
		{
			_buffer = new byte[size];
		}

		public int Read(byte[] buffer, int offset, int count)
		{
			lock (_lock)
			{
				if (_count == 0)
				{
					return 0;
				}
				int num = Math.Min(count, _count);
				int num2 = Math.Min(_buffer.Length - _head, num);
				Array.Copy(_buffer, _head, buffer, offset, num2);
				Array.Copy(_buffer, 0, buffer, offset + num2, num - num2);
				_head = (_head + num) % _buffer.Length;
				_count -= num;
				return num;
			}
		}

		public void Write(byte[] buffer, int offset, int count)
		{
			lock (_lock)
			{
				if (count <= _buffer.Length - _count)
				{
					int num = Math.Min(_buffer.Length - _tail, count);
					Array.Copy(buffer, offset, _buffer, _tail, num);
					Array.Copy(buffer, offset + num, _buffer, 0, count - num);
					_tail = (_tail + count) % _buffer.Length;
					_count += count;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerController))]
	internal static class ExamplePlayerControllerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch("Start")]
		private static void Start_Prefix(PlayerController __instance)
		{
			super_soundboard.Logger.LogDebug((object)$"{__instance} Start Prefix");
		}

		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void Start_Postfix(PlayerController __instance)
		{
			super_soundboard.Logger.LogDebug((object)$"{__instance} Start Postfix");
		}
	}
	public class SoundboardLoopback
	{
		private int _readIdx;

		private int _writeIdx;

		private int _samplesAvailable;

		private readonly float[] _samples;

		private readonly object _lock = new object();

		public AudioClip Clip { get; }

		public int SamplesAvailable
		{
			get
			{
				lock (_lock)
				{
					return _samplesAvailable;
				}
			}
		}

		public SoundboardLoopback()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_0055: Expected O, but got Unknown
			_samples = new float[24000];
			Clip = AudioClip.Create("SoundboardLoopback", _samples.Length, 1, 48000, true, new PCMReaderCallback(OnRead), new PCMSetPositionCallback(OnSetPosition));
		}

		public void Push(float[] buffer)
		{
			lock (_lock)
			{
				foreach (float num in buffer)
				{
					_samples[_writeIdx++] = num;
					if (_writeIdx >= _samples.Length)
					{
						_writeIdx = 0;
					}
					if (_samplesAvailable < _samples.Length)
					{
						_samplesAvailable++;
					}
					else
					{
						_readIdx = (_readIdx + 1) % _samples.Length;
					}
				}
			}
		}

		private void OnRead(float[] outSamples)
		{
			lock (_lock)
			{
				Array.Clear(outSamples, 0, outSamples.Length);
				int num = Math.Min(outSamples.Length, _samplesAvailable);
				for (int i = 0; i < num; i++)
				{
					outSamples[i] = _samples[_readIdx++];
					if (_readIdx >= _samples.Length)
					{
						_readIdx = 0;
					}
				}
				_samplesAvailable -= num;
			}
		}

		private void OnSetPosition(int position)
		{
		}

		public int ReadInto(float[] buffer, int offset, int count)
		{
			int num = 0;
			lock (_lock)
			{
				for (int i = 0; i < count; i++)
				{
					if (_samplesAvailable == 0)
					{
						break;
					}
					buffer[offset + i] = _samples[_readIdx++];
					if (_readIdx >= _samples.Length)
					{
						_readIdx = 0;
					}
					_samplesAvailable--;
					num++;
				}
			}
			return num;
		}
	}
	public class SoundboardMixer : IAudioReader<float>, IDataReader<float>, IDisposable, IAudioDesc
	{
		private readonly SoundboardLoopback _loopback = super_soundboard.LoopbackInstance;

		private readonly PlayerVoiceChat _vc;

		private AudioClip? _micClip;

		private string? _micDevice;

		private int _lastMicPos;

		private bool _isDisposed = false;

		private int _deviceIndex = 0;

		private int _noProgressCount = 0;

		private readonly int[] _sampleRates = new int[3] { 48000, 44100, 32000 };

		private int _currentSampleRate = 48000;

		private readonly Queue<float> _micBuffer = new Queue<float>();

		private readonly object _bufferLock = new object();

		private const int MAX_BUFFER_SIZE = 96000;

		private IAudioReader<float>? _recorderAudioReader = null;

		private object? _recorderMicInputObject = null;

		private Func<float[], int>? _recorderReadFunc = null;

		public int SamplingRate => 48000;

		public int Channels => 1;

		public string? Error { get; private set; } = null;


		public bool IsDisposed => _isDisposed;

		public bool IsEof => false;

		public SoundboardMixer(PlayerVoiceChat voiceChat, object? micInput)
		{
			object micInput2 = micInput;
			base..ctor();
			_vc = voiceChat;
			if (micInput2 != null)
			{
				try
				{
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Using recorder-provided MicInput of type " + micInput2.GetType().FullName));
					object obj = micInput2;
					AudioClip val = (AudioClip)((obj is AudioClip) ? obj : null);
					if (val != null)
					{
						_micClip = val;
						_lastMicPos = 0;
						super_soundboard.Logger.LogDebug((object)("super_soundboard: Using recorder's AudioClip MicInput ('" + (_micDevice ?? "(unknown)") + "')."));
						return;
					}
					if (micInput2 is IAudioReader<float> recorderAudioReader)
					{
						_micClip = null;
						_recorderAudioReader = recorderAudioReader;
						super_soundboard.Logger.LogDebug((object)"super_soundboard: Using recorder's IAudioReader<float> MicInput.");
						return;
					}
					_recorderMicInputObject = micInput2;
					try
					{
						Type type = micInput2.GetType();
						super_soundboard.Logger.LogDebug((object)("super_soundboard: Recorder MicInput type: " + type.FullName));
						MethodInfo method = type.GetMethod("Read", new Type[1] { typeof(float[]) });
						MethodInfo methodInfo = type.GetMethod("ReadInto", new Type[3]
						{
							typeof(float[]),
							typeof(int),
							typeof(int)
						}) ?? type.GetMethod("ReadInto", new Type[2]
						{
							typeof(float[]),
							typeof(int)
						});
						MethodInfo method2 = type.GetMethod("ReadSamples", new Type[1] { typeof(float[]) });
						MethodInfo method3 = type.GetMethod("Read", new Type[1] { typeof(byte[]) });
						MethodInfo chosen = null;
						if (method != null)
						{
							chosen = method;
						}
						else if (method2 != null)
						{
							chosen = method2;
						}
						else if (methodInfo != null)
						{
							chosen = methodInfo;
						}
						if (chosen != null)
						{
							_recorderReadFunc = delegate(float[] buf)
							{
								try
								{
									object obj2 = chosen.Invoke(micInput2, new object[1] { buf });
									if (obj2 == null)
									{
										return -1;
									}
									if (obj2 is bool flag)
									{
										return flag ? buf.Length : 0;
									}
									if (obj2 is int result)
									{
										return result;
									}
									return -1;
								}
								catch (Exception ex4)
								{
									super_soundboard.Logger.LogDebug((object)("super_soundboard: Recorder MicInput read invocation failed: " + ex4.Message));
									return -1;
								}
							};
							super_soundboard.Logger.LogDebug((object)("super_soundboard: Recorder MicInput supports method '" + chosen.Name + "', using it to read samples."));
						}
						else
						{
							super_soundboard.Logger.LogDebug((object)"super_soundboard: Recorder MicInput does not expose a compatible Read method.");
						}
					}
					catch (Exception ex)
					{
						super_soundboard.Logger.LogDebug((object)("super_soundboard: Error inspecting Recorder MicInput: " + ex.Message));
					}
				}
				catch (Exception ex2)
				{
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Error handling recorder MicInput: " + ex2.Message));
				}
			}
			if (!super_soundboard.AutoStartMicrophone)
			{
				super_soundboard.Logger.LogDebug((object)"super_soundboard: AutoStartMicrophone is disabled; not starting local Microphone.");
				return;
			}
			try
			{
				StartLocalMicInternal();
			}
			catch (Exception ex3)
			{
				super_soundboard.Logger.LogWarning((object)("super_soundboard: Failed to start microphone: " + ex3.Message));
			}
		}

		public void EnsureLocalMicStarted()
		{
			if (_recorderAudioReader != null || _recorderReadFunc != null || (Object)(object)_micClip != (Object)null)
			{
				super_soundboard.Logger.LogDebug((object)"super_soundboard: Local mic start skipped; recorder input already present or mic already started.");
				return;
			}
			try
			{
				StartLocalMicInternal();
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogWarning((object)("super_soundboard: EnsureLocalMicStarted failed: " + ex.Message));
			}
		}

		public void StopLocalMic()
		{
			try
			{
				if (!string.IsNullOrEmpty(_micDevice))
				{
					Microphone.End(_micDevice);
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Stopped microphone '" + _micDevice + "' via StopLocalMic."));
					_micDevice = null;
					_micClip = null;
					_noProgressCount = 0;
				}
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogDebug((object)("super_soundboard: StopLocalMic exception: " + ex.Message));
			}
		}

		private void StartLocalMicInternal()
		{
			string[] devices = Microphone.devices;
			if (devices.Length != 0)
			{
				_deviceIndex = 0;
				_micDevice = devices[_deviceIndex];
				_currentSampleRate = _sampleRates[0];
				_micClip = Microphone.Start(_micDevice, true, 10, _currentSampleRate);
				_lastMicPos = 0;
				super_soundboard.Logger.LogDebug((object)("super_soundboard: Started microphone '" + _micDevice + "' with 10s buffer"));
				if (super_soundboard.AutoSwitchMicrophone)
				{
					super_soundboard.Logger.LogDebug((object)"super_soundboard: Microphone auto-switch enabled; will try other devices if no data is produced.");
				}
			}
			else
			{
				super_soundboard.Logger.LogWarning((object)"super_soundboard: No microphone devices found!");
			}
		}

		public void Update()
		{
			if (_isDisposed)
			{
				return;
			}
			float[] array = null;
			if ((Object)(object)_micClip != (Object)null)
			{
				int position = Microphone.GetPosition(_micDevice);
				int num = ((position < _lastMicPos) ? (_micClip.samples - _lastMicPos + position) : (position - _lastMicPos));
				if (position == _lastMicPos)
				{
					_noProgressCount++;
				}
				else
				{
					_noProgressCount = 0;
				}
				if (super_soundboard.AutoSwitchMicrophone && _noProgressCount > 120)
				{
					super_soundboard.Logger.LogWarning((object)$"SoundboardMixer: No progress on microphone '{_micDevice}' detected; attempting to switch device (attempts: {_noProgressCount}).");
					TrySwitchDevice();
					return;
				}
				if (num > 0)
				{
					array = new float[num];
					if (_lastMicPos + num <= _micClip.samples)
					{
						_micClip.GetData(array, _lastMicPos);
					}
					else
					{
						int num2 = _micClip.samples - _lastMicPos;
						int num3 = num - num2;
						float[] array2 = new float[num2];
						_micClip.GetData(array2, _lastMicPos);
						Array.Copy(array2, 0, array, 0, num2);
						float[] array3 = new float[num3];
						_micClip.GetData(array3, 0);
						Array.Copy(array3, 0, array, num2, num3);
					}
					_lastMicPos = (_lastMicPos + num) % _micClip.samples;
				}
			}
			else
			{
				int num4 = 1024;
				float[] array4 = new float[num4];
				bool flag = false;
				int num5 = 0;
				if (_recorderAudioReader != null)
				{
					try
					{
						if (((IDataReader<float>)(object)_recorderAudioReader).Read(array4))
						{
							flag = true;
							num5 = array4.Length;
						}
					}
					catch (Exception ex)
					{
						super_soundboard.Logger.LogDebug((object)("super_soundboard: Recorder IAudioReader read failed: " + ex.Message));
					}
				}
				else if (_recorderReadFunc != null)
				{
					try
					{
						num5 = _recorderReadFunc(array4);
						if (num5 > 0)
						{
							flag = true;
						}
					}
					catch (Exception ex2)
					{
						super_soundboard.Logger.LogDebug((object)("super_soundboard: Recorder read func failed: " + ex2.Message));
					}
				}
				if (flag && num5 > 0)
				{
					array = new float[num5];
					Array.Copy(array4, 0, array, 0, num5);
				}
			}
			if (array == null || array.Length == 0)
			{
				return;
			}
			if ((Object)(object)super_soundboard.SpeechManagerInstance != (Object)null)
			{
				super_soundboard.SpeechManagerInstance.ProcessAudio(array);
			}
			lock (_bufferLock)
			{
				float[] array5 = array;
				foreach (float item in array5)
				{
					_micBuffer.Enqueue(item);
				}
				while (_micBuffer.Count > 96000)
				{
					_micBuffer.Dequeue();
				}
			}
		}

		public bool Read(float[] buffer)
		{
			if (_isDisposed)
			{
				Array.Clear(buffer, 0, buffer.Length);
				return false;
			}
			bool flag = (Object)(object)_micClip != (Object)null || _recorderAudioReader != null || _recorderReadFunc != null;
			lock (_bufferLock)
			{
				int count = _micBuffer.Count;
				if (flag)
				{
					if (count < buffer.Length)
					{
						return false;
					}
				}
				else if (_loopback.SamplesAvailable == 0)
				{
					return false;
				}
				int num = Math.Min(count, buffer.Length);
				for (int i = 0; i < num; i++)
				{
					buffer[i] = _micBuffer.Dequeue();
				}
				for (int j = num; j < buffer.Length; j++)
				{
					buffer[j] = 0f;
				}
			}
			if (_loopback.SamplesAvailable > 0)
			{
				float[] array = new float[buffer.Length];
				int num2 = _loopback.ReadInto(array, 0, buffer.Length);
				for (int k = 0; k < num2; k++)
				{
					buffer[k] = Math.Max(-1f, Math.Min(1f, buffer[k] + array[k]));
				}
			}
			float masterVolume = super_soundboard.MasterVolume;
			if (Math.Abs(masterVolume - 1f) > 0.0001f)
			{
				for (int l = 0; l < buffer.Length; l++)
				{
					buffer[l] *= masterVolume;
				}
			}
			return true;
		}

		public void Dispose()
		{
			if (_isDisposed)
			{
				return;
			}
			_isDisposed = true;
			try
			{
				if (!string.IsNullOrEmpty(_micDevice))
				{
					Microphone.End(_micDevice);
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Stopped microphone '" + _micDevice + "' on Dispose."));
					_micDevice = null;
					_micClip = null;
				}
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogDebug((object)("super_soundboard: Exception while stopping microphone: " + ex.Message));
			}
		}

		private void TrySwitchDevice()
		{
			try
			{
				string[] devices = Microphone.devices;
				if (devices == null || devices.Length == 0)
				{
					super_soundboard.Logger.LogWarning((object)"super_soundboard: TrySwitchDevice: no microphone devices available.");
					return;
				}
				int num = (_deviceIndex + 1) % devices.Length;
				for (int i = 0; i < devices.Length; i++)
				{
					int num2 = (num + i) % devices.Length;
					string text = devices[num2];
					int[] sampleRates = _sampleRates;
					foreach (int num3 in sampleRates)
					{
						try
						{
							if (!string.IsNullOrEmpty(_micDevice))
							{
								Microphone.End(_micDevice);
							}
						}
						catch
						{
						}
						try
						{
							AudioClip val = Microphone.Start(text, true, 10, num3);
							int position = Microphone.GetPosition(text);
							if (position > 0 && (Object)(object)val != (Object)null)
							{
								_micDevice = text;
								_micClip = val;
								_lastMicPos = position;
								_deviceIndex = num2;
								_currentSampleRate = num3;
								_noProgressCount = 0;
								super_soundboard.Logger.LogDebug((object)$"super_soundboard: Switched to microphone '{text}' at {num3} Hz");
								return;
							}
						}
						catch (Exception ex)
						{
							super_soundboard.Logger.LogDebug((object)("super_soundboard: Try start mic '" + text + "' failed: " + ex.Message));
						}
					}
				}
				string text2 = devices[num];
				try
				{
					if (!string.IsNullOrEmpty(_micDevice))
					{
						Microphone.End(_micDevice);
					}
				}
				catch
				{
				}
				try
				{
					_micDevice = text2;
					_micClip = Microphone.Start(_micDevice, true, 10, _currentSampleRate);
					_lastMicPos = 0;
					_deviceIndex = num;
					_noProgressCount = 0;
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Started microphone '" + _micDevice + "' after switching (no immediate progress)."));
				}
				catch (Exception ex2)
				{
					super_soundboard.Logger.LogDebug((object)("super_soundboard: Failed to start fallback mic '" + text2 + "': " + ex2.Message));
				}
			}
			catch (Exception ex3)
			{
				super_soundboard.Logger.LogDebug((object)("super_soundboard: Exception in TrySwitchDevice: " + ex3.Message));
			}
		}
	}
	public class SpeechManager : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <DownloadAndCache>d__26 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string filename;

			public SpeechManager <>4__this;

			private string <localPath>5__1;

			private string <uri>5__2;

			private Task<byte[]> <task>5__3;

			private Exception <ex>5__4;

			private UnityWebRequest <www>5__5;

			private bool <hasError>5__6;

			private AudioClip <clip>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DownloadAndCache>d__26(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 2)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<localPath>5__1 = null;
				<uri>5__2 = null;
				<task>5__3 = null;
				<ex>5__4 = null;
				<www>5__5 = null;
				<clip>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0285: Unknown result type (might be due to invalid IL or missing references)
				//IL_028b: Invalid comparison between Unknown and I4
				//IL_0238: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						if (<>4__this._soundCache.ContainsKey(filename))
						{
							return false;
						}
						if (!<>4__this.IsValidFilename(filename))
						{
							super_soundboard.Logger.LogError((object)("Invalid filename detected: " + filename + ". Filename contains forbidden characters."));
							return false;
						}
						<localPath>5__1 = Path.Combine(<>4__this._cacheDir, filename);
						if (!File.Exists(<localPath>5__1))
						{
							<task>5__3 = _httpClient.GetByteArrayAsync(<>4__this.ApiBaseUrl + "/api/sounds/" + filename);
							goto IL_0114;
						}
						goto IL_0200;
					case 1:
						<>1__state = -1;
						goto IL_0114;
					case 2:
						{
							<>1__state = -3;
							<hasError>5__6 = false;
							<hasError>5__6 = (int)<www>5__5.result != 1;
							if (<hasError>5__6)
							{
								super_soundboard.Logger.LogError((object)("Failed to load cached sound " + filename + ": " + <www>5__5.error));
							}
							else
							{
								<clip>5__7 = DownloadHandlerAudioClip.GetContent(<www>5__5);
								if ((Object)(object)<clip>5__7 != (Object)null)
								{
									((Object)<clip>5__7).name = filename;
									<>4__this._soundCache[filename] = <clip>5__7;
									super_soundboard.Logger.LogDebug((object)("Cached sound: " + filename));
								}
								<clip>5__7 = null;
							}
							<>m__Finally1();
							<www>5__5 = null;
							return false;
						}
						IL_0114:
						if (!<task>5__3.IsCompleted)
						{
							<>2__current = null;
							<>1__state = 1;
							return true;
						}
						if (<task>5__3.IsFaulted)
						{
							super_soundboard.Logger.LogError((object)("Failed to download sound " + filename + ": " + (<task>5__3.Exception?.InnerException?.Message ?? <task>5__3.Exception?.Message)));
							return false;
						}
						try
						{
							File.WriteAllBytes(<localPath>5__1, <task>5__3.Result);
						}
						catch (Exception ex)
						{
							<ex>5__4 = ex;
							super_soundboard.Logger.LogError((object)("Failed to write sound file " + <localPath>5__1 + ": " + <ex>5__4.Message));
							return false;
						}
						<task>5__3 = null;
						goto IL_0200;
						IL_0200:
						<uri>5__2 = "file://" + <localPath>5__1.Replace("\\", "/");
						<www>5__5 = UnityWebRequestMultimedia.GetAudioClip(<uri>5__2, <>4__this.GetAudioTypeFromExtension(filename));
						<>1__state = -3;
						<>2__current = <www>5__5.SendWebRequest();
						<>1__state = 2;
						return true;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<www>5__5 != null)
				{
					((IDisposable)<www>5__5).Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <DownloadAndCacheAndPlay>d__41 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string filename;

			public float volumePercent;

			public SpeechManager <>4__this;

			private AudioClip <clip>5__1;

			private float <localVolumeScale>5__2;

			private float <remoteVolumeScale>5__3;

			private AudioSource <source>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DownloadAndCacheAndPlay>d__41(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<clip>5__1 = null;
				<source>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = <>4__this.DownloadAndCache(filename);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if (<>4__this._soundCache.TryGetValue(filename, out <clip>5__1))
					{
						super_soundboard.Logger.LogInfo((object)$"Playing sound after download: {filename} (Vol: {volumePercent}%)");
						<localVolumeScale>5__2 = volumePercent / 100f * super_soundboard.LocalVolume * super_soundboard.MasterVolume;
						<remoteVolumeScale>5__3 = volumePercent / 100f * super_soundboard.RemoteVolume * super_soundboard.MasterVolume;
						if ((Object)(object)super_soundboard.Instance != (Object)null)
						{
							<source>5__4 = ((Component)super_soundboard.Instance).GetComponent<AudioSource>();
							if ((Object)(object)<source>5__4 != (Object)null)
							{
								<source>5__4.PlayOneShot(<clip>5__1, <localVolumeScale>5__2);
							}
							<source>5__4 = null;
						}
						((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.PushToLoopback(<clip>5__1, <remoteVolumeScale>5__3));
					}
					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 <FetchConfigAndStart>d__20 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SpeechManager <>4__this;

			private int <retryCount>5__1;

			private Task<string> <task>5__2;

			private string <json>5__3;

			private Mapping[] <>s__4;

			private int <>s__5;

			private Mapping <m>5__6;

			private Exception <ex>5__7;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FetchConfigAndStart>d__20(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<task>5__2 = null;
				<json>5__3 = null;
				<>s__4 = null;
				<m>5__6 = null;
				<ex>5__7 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0151: Unknown result type (might be due to invalid IL or missing references)
				//IL_015b: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<retryCount>5__1 = 0;
					goto IL_03cb;
				case 1:
					<>1__state = -1;
					goto IL_00b0;
				case 2:
					{
						<>1__state = -1;
						goto IL_03cb;
					}
					IL_03cb:
					if (<retryCount>5__1 < 10)
					{
						super_soundboard.Logger.LogDebug((object)$"SpeechManager: Fetching config from {<>4__this.ApiBaseUrl}/api/config (Attempt {<retryCount>5__1 + 1}/{10})...");
						<task>5__2 = _httpClient.GetStringAsync(<>4__this.ApiBaseUrl + "/api/config");
						goto IL_00b0;
					}
					return false;
					IL_00b0:
					if (!<task>5__2.IsCompleted)
					{
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					if (<task>5__2.IsFaulted)
					{
						super_soundboard.Logger.LogWarning((object)("Failed to fetch config: " + (<task>5__2.Exception?.InnerException?.Message ?? <task>5__2.Exception?.Message)));
						<retryCount>5__1++;
						if (<retryCount>5__1 < 10)
						{
							<>2__current = (object)new WaitForSeconds(2f);
							<>1__state = 2;
							return true;
						}
						super_soundboard.Logger.LogError((object)$"SpeechManager: Gave up fetching config after {10} attempts.");
						return false;
					}
					super_soundboard.Logger.LogDebug((object)"SpeechManager: Config response received, parsing...");
					try
					{
						<json>5__3 = <task>5__2.Result;
						super_soundboard.Logger.LogDebug((object)$"SpeechManager: Config JSON length: {<json>5__3.Length}");
						<>4__this._config = new ConfigData();
						<>4__this._config.mappings = <>4__this.ParseMappings(<json>5__3);
						if (<>4__this._config?.mappings != null && <>4__this._config.mappings.Length != 0)
						{
							super_soundboard.Logger.LogDebug((object)$"Loaded {<>4__this._config.mappings.Length} mappings.");
							<>s__4 = <>4__this._config.mappings;
							for (<>s__5 = 0; <>s__5 < <>s__4.Length; <>s__5++)
							{
								<m>5__6 = <>s__4[<>s__5];
								if (<m>5__6.keywords != null && <m>5__6.keywords.Length != 0)
								{
									super_soundboard.Logger.LogDebug((object)string.Format("Mapping: [{0}] -> {1} (Vol: {2})", string.Join(", ", <m>5__6.keywords), <m>5__6.file, <m>5__6.volume));
								}
								<m>5__6 = null;
							}
							<>s__4 = null;
							((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.PreloadAllSounds());
							<>4__this.StartRecognition();
						}
						else
						{
							super_soundboard.Logger.LogError((object)"Failed to parse mappings from config.");
						}
						return false;
					}
					catch (Exception ex)
					{
						<ex>5__7 = ex;
						super_soundboard.Logger.LogError((object)("Error parsing config: " + <ex>5__7.Message));
						super_soundboard.Logger.LogError((object)("Stack trace: " + <ex>5__7.StackTrace));
						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 <PreloadAllSounds>d__25 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public SpeechManager <>4__this;

			private HashSet<string> <uniqueFiles>5__1;

			private Mapping[] <>s__2;

			private int <>s__3;

			private Mapping <mapping>5__4;

			private HashSet<string>.Enumerator <>s__5;

			private string <filename>5__6;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PreloadAllSounds>d__25(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<uniqueFiles>5__1 = null;
				<>s__2 = null;
				<mapping>5__4 = null;
				<>s__5 = default(HashSet<string>.Enumerator);
				<filename>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				if (<>4__this._config == null || <>4__this._config.mappings == null || <>4__this._config.mappings.Length == 0)
				{
					super_soundboard.Logger.LogWarning((object)"No mappings to preload");
					return false;
				}
				<uniqueFiles>5__1 = new HashSet<string>();
				<>s__2 = <>4__this._config.mappings;
				for (<>s__3 = 0; <>s__3 < <>s__2.Length; <>s__3++)
				{
					<mapping>5__4 = <>s__2[<>s__3];
					if (!string.IsNullOrEmpty(<mapping>5__4.file))
					{
						<uniqueFiles>5__1.Add(<mapping>5__4.file);
					}
					<mapping>5__4 = null;
				}
				<>s__2 = null;
				if (<uniqueFiles>5__1.Count > 0)
				{
					<>4__this._cachedSoundList = new string[<uniqueFiles>5__1.Count];
					<uniqueFiles>5__1.CopyTo(<>4__this._cachedSoundList);
					super_soundboard.Logger.LogDebug((object)$"Preloading {<uniqueFiles>5__1.Count} mapped sounds...");
					<>s__5 = <uniqueFiles>5__1.GetEnumerator();
					try
					{
						while (<>s__5.MoveNext())
						{
							<filename>5__6 = <>s__5.Current;
							((MonoBehaviour)<>4__this).StartCoroutine(<>4__this.DownloadAndCache(<filename>5__6));
							<filename>5__6 = null;
						}
					}
					finally
					{
						((IDisposable)<>s__5).Dispose();
					}
					<>s__5 = default(HashSet<string>.Enumerator);
				}
				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 <PushToLoopback>d__42 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public AudioClip clip;

			public float volumeScale;

			public SpeechManager <>4__this;

			private float[] <data>5__1;

			private float[] <monoData>5__2;

			private float[] <resampledData>5__3;

			private int <position>5__4;

			private int <chunkSize>5__5;

			private int <i>5__6;

			private int <i>5__7;

			private float <ratio>5__8;

			private int <newLength>5__9;

			private int <i>5__10;

			private float <srcPos>5__11;

			private int <srcIdx>5__12;

			private float <frac>5__13;

			private int <i>5__14;

			private int <remaining>5__15;

			private int <count>5__16;

			private float[] <chunk>5__17;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PushToLoopback>d__42(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<data>5__1 = null;
				<monoData>5__2 = null;
				<resampledData>5__3 = null;
				<chunk>5__17 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0461: Unknown result type (might be due to invalid IL or missing references)
				//IL_046b: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<data>5__1 = new float[clip.samples * clip.channels];
					clip.GetData(<data>5__1, 0);
					if (clip.channels == 2)
					{
						<monoData>5__2 = new float[clip.samples];
						<i>5__6 = 0;
						while (<i>5__6 < clip.samples)
						{
							<monoData>5__2[<i>5__6] = (<data>5__1[<i>5__6 * 2] + <data>5__1[<i>5__6 * 2 + 1]) * 0.5f;
							<i>5__6++;
						}
					}
					else if (clip.channels == 1)
					{
						<monoData>5__2 = <data>5__1;
					}
					else
					{
						super_soundboard.Logger.LogWarning((object)$"Unsupported channel count: {clip.channels}. Using first channel.");
						<monoData>5__2 = new float[clip.samples];
						<i>5__7 = 0;
						while (<i>5__7 < clip.samples)
						{
							<monoData>5__2[<i>5__7] = <data>5__1[<i>5__7 * clip.channels];
							<i>5__7++;
						}
					}
					if (clip.frequency != 48000)
					{
						<ratio>5__8 = 48000f / (float)clip.frequency;
						<newLength>5__9 = (int)((float)<monoData>5__2.Length * <ratio>5__8);
						<resampledData>5__3 = new float[<newLength>5__9];
						<i>5__10 = 0;
						while (<i>5__10 < <newLength>5__9)
						{
							<srcPos>5__11 = (float)<i>5__10 / <ratio>5__8;
							<srcIdx>5__12 = (int)<srcPos>5__11;
							<frac>5__13 = <srcPos>5__11 - (float)<srcIdx>5__12;
							if (<srcIdx>5__12 + 1 < <monoData>5__2.Length)
							{
								<resampledData>5__3[<i>5__10] = <monoData>5__2[<srcIdx>5__12] * (1f - <frac>5__13) + <monoData>5__2[<srcIdx>5__12 + 1] * <frac>5__13;
							}
							else if (<srcIdx>5__12 < <monoData>5__2.Length)
							{
								<resampledData>5__3[<i>5__10] = <monoData>5__2[<srcIdx>5__12];
							}
							<i>5__10++;
						}
						super_soundboard.Logger.LogDebug((object)$"Resampled audio from {clip.frequency}Hz to 48000Hz ({<monoData>5__2.Length} -> {<newLength>5__9} samples)");
					}
					else
					{
						<resampledData>5__3 = <monoData>5__2;
					}
					if (Math.Abs(volumeScale - 1f) > 0.01f)
					{
						<i>5__14 = 0;
						while (<i>5__14 < <resampledData>5__3.Length)
						{
							<resampledData>5__3[<i>5__14] *= volumeScale;
							<i>5__14++;
						}
					}
					<position>5__4 = 0;
					<chunkSize>5__5 = 4800;
					break;
				case 1:
					<>1__state = -1;
					<chunk>5__17 = null;
					break;
				}
				if (<position>5__4 < <resampledData>5__3.Length)
				{
					<remaining>5__15 = <resampledData>5__3.Length - <position>5__4;
					<count>5__16 = Math.Min(<chunkSize>5__5, <remaining>5__15);
					<chunk>5__17 = new float[<count>5__16];
					Array.Copy(<resampledData>5__3, <position>5__4, <chunk>5__17, 0, <count>5__16);
					super_soundboard.LoopbackInstance?.Push(<chunk>5__17);
					<position>5__4 += <count>5__16;
					<>2__current = (object)new WaitForSeconds(0.1f);
					<>1__state = 1;
					return true;
				}
				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 <SendAudioData>d__32 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public float[] samples;

			public SpeechManager <>4__this;

			private byte[] <pcmData>5__1;

			private string <base64Audio>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SendAudioData>d__32(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<pcmData>5__1 = null;
				<base64Audio>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<pcmData>5__1 = <>4__this.ConvertToPCM16(samples);
					<base64Audio>5__2 = Convert.ToBase64String(<pcmData>5__1);
					<>2__current = <>4__this.SendToGoogleSpeechToText(<base64Audio>5__2);
					<>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 <SendToGoogleSpeechToText>d__34 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string base64Audio;

			public SpeechManager <>4__this;

			private string <apiKey>5__1;

			private string <url>5__2;

			private string <jsonBody>5__3;

			private UnityWebRequest <www>5__4;

			private byte[] <bodyRaw>5__5;

			private string <responseText>5__6;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <SendToGoogleSpeechToText>d__34(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<apiKey>5__1 = null;
				<url>5__2 = null;
				<jsonBody>5__3 = null;
				<www>5__4 = null;
				<bodyRaw>5__5 = null;
				<responseText>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ab: Expected O, but got Unknown
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Expected O, but got Unknown
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f1: Expected O, but got Unknown
				//IL_0166: Unknown result type (might be due to invalid IL or missing references)
				//IL_0181: Unknown result type (might be due to invalid IL or missing references)
				//IL_0187: Invalid comparison between Unknown and I4
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<apiKey>5__1 = super_soundboard.GoogleApiKey;
						if (string.IsNullOrEmpty(<apiKey>5__1))
						{
							super_soundboard.Logger.LogError((object)"SpeechManager: API Key is empty! Cannot send request.");
							return false;
						}
						<url>5__2 = "https://speech.googleapis.com/v1/speech:recognize?key=" + <apiKey>5__1;
						<jsonBody>5__3 = $"\r\n            {{\r\n                \"config\": {{\r\n                    \"encoding\": \"LINEAR16\",\r\n                    \"sampleRateHertz\": {16000},\r\n                    \"languageCode\": \"{super_soundboard.GoogleSpeechLanguage}\",\r\n                    \"model\": \"default\"\r\n                }},\r\n                \"audio\": {{\r\n                    \"content\": \"{base64Audio}\"\r\n                }}\r\n            }}";
						<www>5__4 = new UnityWebRequest(<url>5__2, "POST");
						<>1__state = -3;
						<bodyRaw>5__5 = Encoding.UTF8.GetBytes(<jsonBody>5__3);
						<www>5__4.uploadHandler = (UploadHandler)new UploadHandlerRaw(<bodyRaw>5__5);
						<www>5__4.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
						<www>5__4.SetRequestHeader("Content-Type", "application/json");
						super_soundboard.Logger.LogDebug((object)("SpeechManager: Sending request to Google API... (Key: " + <apiKey>5__1.Substring(0, 5) + "...)"));
						<>2__current = <www>5__4.SendWebRequest();
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -3;
						super_soundboard.Logger.LogDebug((object)$"SpeechManager: Request completed. Result: {<www>5__4.result}");
						if ((int)<www>5__4.result == 1)
						{
							<responseText>5__6 = <www>5__4.downloadHandler.text;
							super_soundboard.Logger.LogDebug((object)("SpeechManager: Google API Response: " + <responseText>5__6));
							<>4__this.ParseGoogleResponse(<responseText>5__6);
							<responseText>5__6 = null;
						}
						else
						{
							super_soundboard.Logger.LogError((object)("Google API Error: " + <www>5__4.error));
							super_soundboard.Logger.LogError((object)$"Response Code: {<www>5__4.responseCode}");
							super_soundboard.Logger.LogError((object)("Response Body: " + <www>5__4.downloadHandler.text));
						}
						<bodyRaw>5__5 = null;
						<>m__Finally1();
						<www>5__4 = null;
						return false;
					}
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<www>5__4 != null)
				{
					((IDisposable)<www>5__4).Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly HttpClient _httpClient = new HttpClient();

		private string _cacheDir = "";

		private const int SAMPLE_RATE = 16000;

		private const float SILENCE_THRESHOLD_TIME = 0.5f;

		private const float MAX_RECORDING_DURATION = 5f;

		private const float VAD_THRESHOLD = 0.001f;

		private float _silenceTimer = 0f;

		private float _recordingDuration = 0f;

		private bool _isSpeaking = false;

		private List<float> _currentUtterance = new List<float>();

		private object _audioLock = new object();

		private int _logThrottle = 0;

		private ConfigData? _config;

		private Dictionary<string, AudioClip> _soundCache = new Dictionary<string, AudioClip>();

		private string[]? _cachedSoundList;

		private readonly Queue<Action> _mainThreadActions = new Queue<Action>();

		private string ApiBaseUrl => super_soundboard.ApiBaseUrl;

		public void Initialize()
		{
			string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".";
			_cacheDir = Path.Combine(path, "cache");
			if (!Directory.Exists(_cacheDir))
			{
				Directory.CreateDirectory(_cacheDir);
			}
			super_soundboard.Logger.LogDebug((object)("SpeechManager: Initializing, starting config fetch from " + ApiBaseUrl + "..."));
			((MonoBehaviour)this).StartCoroutine(FetchConfigAndStart());
		}

		public void OnApiBaseUrlChanged()
		{
			super_soundboard.Logger.LogInfo((object)("SpeechManager: Detected ApiBaseUrl change to " + ApiBaseUrl + ". Refreshing config and clearing caches..."));
			_config = null;
			_cachedSoundList = null;
			try
			{
				foreach (AudioClip value in _soundCache.Values)
				{
					if ((Object)(object)value != (Object)null)
					{
						Object.Destroy((Object)(object)value);
					}
				}
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogWarning((object)("SpeechManager: Error while destroying cached clips: " + ex.Message));
			}
			_soundCache.Clear();
			try
			{
				_httpClient.BaseAddress = new Uri(ApiBaseUrl);
			}
			catch
			{
			}
			((MonoBehaviour)this).StartCoroutine(FetchConfigAndStart());
		}

		[IteratorStateMachine(typeof(<FetchConfigAndStart>d__20))]
		private IEnumerator FetchConfigAndStart()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FetchConfigAndStart>d__20(0)
			{
				<>4__this = this
			};
		}

		private Mapping[] ParseMappings(string json)
		{
			List<Mapping> list = new List<Mapping>();
			try
			{
				Match match = Regex.Match(json, "\"mappings\"\\s*:\\s*\\[");
				if (!match.Success)
				{
					super_soundboard.Logger.LogError((object)"Mappings array not found in JSON");
					return new Mapping[0];
				}
				int num = match.Index + match.Length - 1;
				int num2 = 0;
				int num3 = num;
				for (int i = num; i < json.Length; i++)
				{
					if (json[i] == '[')
					{
						num2++;
					}
					if (json[i] == ']')
					{
						num2--;
						if (num2 == 0)
						{
							num3 = i;
							break;
						}
					}
				}
				if (num3 <= num)
				{
					super_soundboard.Logger.LogError((object)"Could not find end of mappings array");
					return new Mapping[0];
				}
				string text = json.Substring(num + 1, num3 - num - 1);
				List<string> list2 = new List<string>();
				int num4 = 0;
				int num5 = -1;
				for (int j = 0; j < text.Length; j++)
				{
					if (text[j] == '{')
					{
						if (num4 == 0)
						{
							num5 = j;
						}
						num4++;
					}
					else if (text[j] == '}')
					{
						num4--;
						if (num4 == 0 && num5 != -1)
						{
							string item = text.Substring(num5, j - num5 + 1);
							list2.Add(item);
							num5 = -1;
						}
					}
				}
				super_soundboard.Logger.LogDebug((object)$"Found {list2.Count} mapping objects");
				foreach (string item2 in list2)
				{
					Mapping mapping = new Mapping();
					mapping.keywords = ExtractStringArray(item2, "keywords");
					mapping.file = ExtractString(item2, "file");
					mapping.volume = ExtractFloat(item2, "volume");
					if (mapping.keywords != null && mapping.keywords.Length != 0 && !string.IsNullOrEmpty(mapping.file))
					{
						list.Add(mapping);
					}
				}
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogError((object)("Error parsing mappings: " + ex.Message));
			}
			return list.ToArray();
		}

		private string[] ExtractStringArray(string json, string fieldName)
		{
			List<string> list = new List<string>();
			try
			{
				string value = "\"" + fieldName + "\":[";
				int num = json.IndexOf(value);
				if (num == -1)
				{
					return new string[0];
				}
				int num2 = json.IndexOf("[", num);
				int num3 = json.IndexOf("]", num2);
				if (num2 == -1 || num3 == -1)
				{
					return new string[0];
				}
				string text = json.Substring(num2 + 1, num3 - num2 - 1);
				bool flag = false;
				int num4 = -1;
				for (int i = 0; i < text.Length; i++)
				{
					if (text[i] != '"')
					{
						continue;
					}
					if (!flag)
					{
						num4 = i + 1;
						flag = true;
						continue;
					}
					if (num4 != -1)
					{
						string text2 = text.Substring(num4, i - num4);
						if (!string.IsNullOrEmpty(text2))
						{
							list.Add(text2);
						}
					}
					flag = false;
				}
			}
			catch
			{
			}
			return list.ToArray();
		}

		private string ExtractString(string json, string fieldName)
		{
			try
			{
				string text = "\"" + fieldName + "\":\"";
				int num = json.IndexOf(text);
				if (num == -1)
				{
					return "";
				}
				int num2 = num + text.Length;
				int num3 = json.IndexOf("\"", num2);
				if (num3 == -1)
				{
					return "";
				}
				return json.Substring(num2, num3 - num2);
			}
			catch
			{
				return "";
			}
		}

		private float ExtractFloat(string json, string fieldName)
		{
			try
			{
				string text = "\"" + fieldName + "\":";
				int num = json.IndexOf(text);
				if (num == -1)
				{
					return 100f;
				}
				int num2 = num + text.Length;
				int i;
				for (i = num2; i < json.Length && (char.IsDigit(json[i]) || json[i] == '.' || json[i] == '-'); i++)
				{
				}
				if (i > num2)
				{
					string s = json.Substring(num2, i - num2);
					if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						return result;
					}
				}
			}
			catch
			{
			}
			return 100f;
		}

		[IteratorStateMachine(typeof(<PreloadAllSounds>d__25))]
		private IEnumerator PreloadAllSounds()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PreloadAllSounds>d__25(0)
			{
				<>4__this = this
			};
		}

		[IteratorStateMachine(typeof(<DownloadAndCache>d__26))]
		private IEnumerator DownloadAndCache(string filename)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadAndCache>d__26(0)
			{
				<>4__this = this,
				filename = filename
			};
		}

		private AudioType GetAudioTypeFromExtension(string filePath)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			string text = Path.GetExtension(filePath).ToLowerInvariant();
			if (1 == 0)
			{
			}
			AudioType result = (AudioType)(text switch
			{
				".wav" => 20, 
				".ogg" => 14, 
				".mp3" => 13, 
				_ => 20, 
			});
			if (1 == 0)
			{
			}
			return result;
		}

		private bool IsValidFilename(string filename)
		{
			if (string.IsNullOrWhiteSpace(filename))
			{
				return false;
			}
			string fileName = Path.GetFileName(filename);
			if (fileName != filename)
			{
				return false;
			}
			string text = Path.GetExtension(fileName).ToLowerInvariant();
			return text == ".wav" || text == ".ogg" || text == ".mp3";
		}

		private void StartRecognition()
		{
			if (!super_soundboard.SpeechRecognitionEnabled)
			{
				super_soundboard.Logger.LogDebug((object)"Speech recognition is disabled in config.");
			}
			else if (string.IsNullOrEmpty(super_soundboard.GoogleApiKey))
			{
				super_soundboard.Logger.LogWarning((object)"Google API Key is missing. Speech recognition will not work.");
			}
			else
			{
				super_soundboard.Logger.LogDebug((object)"SpeechManager: Ready to receive audio from SoundboardMixer.");
			}
		}

		public void ProcessAudio(float[] buffer)
		{
			if (!super_soundboard.SpeechRecognitionEnabled || string.IsNullOrEmpty(super_soundboard.GoogleApiKey))
			{
				return;
			}
			int num = buffer.Length / 3;
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = buffer[i * 3];
			}
			float num2 = 0f;
			for (int j = 0; j < array.Length; j++)
			{
				if (Mathf.Abs(array[j]) > num2)
				{
					num2 = Mathf.Abs(array[j]);
				}
			}
			if (_logThrottle++ > 100)
			{
				_logThrottle = 0;
				if (num2 > 0f)
				{
					super_soundboard.Logger.LogDebug((object)$"SpeechManager: MaxVol={num2:F4}, IsSpeaking={_isSpeaking}, Duration={_recordingDuration:F2}");
				}
			}
			lock (_audioLock)
			{
				if (num2 > 0.001f)
				{
					_isSpeaking = true;
					_silenceTimer = 0f;
					_recordingDuration += (float)buffer.Length / 48000f;
				}
				else if (_isSpeaking)
				{
					_silenceTimer += (float)buffer.Length / 48000f;
				}
				if (_isSpeaking)
				{
					_currentUtterance.AddRange(array);
				}
				bool flag = false;
				if (_isSpeaking && _silenceTimer >= 0.5f)
				{
					flag = true;
				}
				else if (_recordingDuration >= 5f)
				{
					flag = true;
				}
				if (flag)
				{
					SendCurrentUtterance();
					_isSpeaking = false;
					_silenceTimer = 0f;
					_recordingDuration = 0f;
				}
			}
		}

		private void SendCurrentUtterance()
		{
			if (_currentUtterance.Count != 0)
			{
				float[] samples = _currentUtterance.ToArray();
				_currentUtterance.Clear();
				super_soundboard.Logger.LogDebug((object)$"SpeechManager: Sending utterance of {samples.Length} samples to Google...");
				EnqueueMainThread(delegate
				{
					((MonoBehaviour)this).StartCoroutine(SendAudioData(samples));
				});
			}
		}

		[IteratorStateMachine(typeof(<SendAudioData>d__32))]
		private IEnumerator SendAudioData(float[] samples)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SendAudioData>d__32(0)
			{
				<>4__this = this,
				samples = samples
			};
		}

		private byte[] ConvertToPCM16(float[] samples)
		{
			byte[] array = new byte[samples.Length * 2];
			int num = 0;
			foreach (float num2 in samples)
			{
				short num3 = (short)(Mathf.Clamp(num2, -1f, 1f) * 32767f);
				array[num++] = (byte)((uint)num3 & 0xFFu);
				array[num++] = (byte)((uint)(num3 >> 8) & 0xFFu);
			}
			return array;
		}

		[IteratorStateMachine(typeof(<SendToGoogleSpeechToText>d__34))]
		private IEnumerator SendToGoogleSpeechToText(string base64Audio)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SendToGoogleSpeechToText>d__34(0)
			{
				<>4__this = this,
				base64Audio = base64Audio
			};
		}

		private void ParseGoogleResponse(string json)
		{
			try
			{
				if (string.IsNullOrEmpty(json))
				{
					super_soundboard.Logger.LogWarning((object)"SpeechManager: Empty response from Google.");
					return;
				}
				MatchCollection matchCollection = Regex.Matches(json, "\"transcript\"\\s*:\\s*\"([^\"]+)\"");
				if (matchCollection.Count > 0)
				{
					foreach (Match item in matchCollection)
					{
						if (item.Success && item.Groups.Count > 1)
						{
							string value = item.Groups[1].Value;
							super_soundboard.Logger.LogInfo((object)("Google Recognized: " + value));
							CheckKeywords(value);
						}
					}
					return;
				}
				if (json.Contains("\"results\""))
				{
					super_soundboard.Logger.LogWarning((object)("SpeechManager: No transcripts found in JSON via Regex. (Raw: " + json + ")"));
				}
				else
				{
					super_soundboard.Logger.LogDebug((object)("SpeechManager: No results parsed from JSON (likely silence). (Raw: " + json + ")"));
				}
			}
			catch (Exception ex)
			{
				super_soundboard.Logger.LogError((object)("Error parsing Google response: " + ex.Message));
			}
		}

		private void CheckKeywords(string text)
		{
			if (_config == null || _config.mappings == null)
			{
				super_soundboard.Logger.LogWarning((object)"CheckKeywords: Config is null or has no mappings. Cannot check keywords.");
				return;
			}
			Mapping[] mappings = _config.mappings;
			foreach (Mapping mapping in mappings)
			{
				if (mapping.keywords == null)
				{
					continue;
				}
				string[] keywords = mapping.keywords;
				foreach (string text2 in keywords)
				{
					if (text.Contains(text2))
					{
						super_soundboard.Logger.LogInfo((object)("Keyword matched: '" + text2 + "' -> Playing " + mapping.file));
						if (mapping.file != null)
						{
							PlaySound(mapping.file, mapping.volume);
							return;
						}
					}
				}
			}
		}

		private void EnqueueMainThread(Action action)
		{
			lock (_mainThreadActions)
			{
				_mainThreadActions.Enqueue(action);
			}
		}

		private void Update()
		{
			lock (_mainThreadActions)
			{
				while (_mainThreadActions.Count > 0)
				{
					_mainThreadActions.Dequeue()();
				}
			}
		}

		public void PlayRandomSound()
		{
			if (_cachedSoundList != null && _cachedSoundList.Length != 0)
			{
				string filename = _cachedSoundList[Random.Range(0, _cachedSoundList.Length)];
				PlaySound(filename, 100f);
			}
			else
			{
				super_soundboard.Logger.LogWarning((object)"No sounds available to play randomly.");
			}
		}

		private void PlaySound(string filename, float volumePercent)
		{
			if (_soundCache.TryGetValue(filename, out AudioClip value))
			{
				super_soundboard.Logger.LogInfo((object)$"Playing cached sound: {filename} (Vol: {volumePercent}%)");
				float num = volumePercent / 100f * super_soundboard.LocalVolume * super_soundboard.MasterVolume;
				float volumeScale = volumePercent / 100f * super_soundboard.RemoteVolume * super_soundboard.MasterVolume;
				if ((Object)(object)super_soundboard.Instance != (Object)null)
				{
					AudioSource component = ((Component)super_soundboard.Instance).GetComponent<AudioSource>();
					if ((Object)(object)component != (Object)null)
					{
						component.PlayOneShot(value, num);
					}
				}
				((MonoBehaviour)this).StartCoroutine(PushToLoopback(value, volumeScale));
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(DownloadAndCacheAndPlay(filename, volumePercent));
			}
		}

		[IteratorStateMachine(typeof(<DownloadAndCacheAndPlay>d__41))]
		private IEnumerator DownloadAndCacheAndPlay(string filename, float volumePercent)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DownloadAndCacheAndPlay>d__41(0)
			{
				<>4__this = this,
				filename = filename,
				volumePercent = volumePercent
			};
		}

		[IteratorStateMachine(typeof(<PushToLoopback>d__42))]
		private IEnumerator PushToLoopback(AudioClip clip, float volumeScale)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PushToLoopback>d__42(0)
			{
				<>4__this = this,
				clip = clip,
				volumeScale = volumeScale
			};
		}

		private void OnDestroy()
		{
		}
	}
	[Serializable]
	public class ConfigResponse
	{
		public bool success;

		public ConfigData? data;
	}
	[Serializable]
	public class ConfigData
	{
		public Mapping[]? mappings;

		public int cooldownMs;

		public string? lang;

		public int wsPort;
	}
	[Serializable]
	public class Mapping
	{
		public string[]? keywords;

		public string? file;

		public float volume;
	}
	[Serializable]
	public class SoundListResponse
	{
		public bool success;

		public string[]? data;
	}
	[BepInPlugin("sasnews.super_soundboard", "super_soundboard", "1.0")]
	public class super_soundboard : BaseUnityPlugin
	{
		private AudioSource? _source;

		private static ConfigEntry<bool>? _speechRecognitionEnabled;

		private static ConfigEntry<float>? _localVolume;

		private static ConfigEntry<float>? _remoteVolume;

		private static ConfigEntry<string>? _apiBaseUrl;

		private static ConfigEntry<string>? _googleApiKey;

		private static ConfigEntry<string>? _googleSpeechLanguage;

		private static ConfigEntry<KeyCode>? _debugHotkey;

		private static ConfigEntry<bool>? _autoStartMicrophone;

		private static ConfigEntry<bool>? _autoSwitchMicrophone;

		private SoundboardMixer? _soundboardMixer;

		private PlayerVoiceChat? _vc;

		private PlayerVoiceChat? _lastVc;

		private int _initRetryCount = 0;

		private const int MAX_INIT_RETRIES = 10;

		private float _nextRetryTime = 0f;

		internal static super_soundboard Instance { get; private set; }

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal static ManualLogSource Logger => Instance._logger;

		internal static bool SpeechRecognitionEnabled => _speechRecognitionEnabled?.Value ?? true;

		internal static float LocalVolume => _localVolume?.Value ?? 1f;

		internal static float RemoteVolume => _remoteVolume?.Value ?? 1f;

		internal static string ApiBaseUrl
		{
			get
			{
				string text = _apiBaseUrl?.Value ?? "http://localhost:3211";
				return text.TrimEnd('/');
			}
		}

		internal static string GoogleApiKey => _googleApiKey?.Value ?? "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw";

		internal static string GoogleSpeechLanguage => _googleSpeechLanguage?.Value ?? "ja-JP";

		internal static bool AutoStartMicrophone => _autoStartMicrophone?.Value ?? false;

		internal static bool AutoSwitchMicrophone => _autoSwitchMicrophone?.Value ?? false;

		internal static float MasterVolume => 0.5f;

		internal static SoundboardLoopback? LoopbackInstance { get; private set; }

		internal static SpeechManager? SpeechManagerInstance { get; private set; }

		private void Awake()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			Instance = this;
			_googleApiKey = ((BaseUnityPlugin)this).Config.Bind<string>("SpeechRecognition", "GoogleApiKey", "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw", "API Key for Google Cloud Speech-to-Text. If empty, speech recognition may not work.");
			_googleSpeechLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("SpeechRecognition", "GoogleSpeechLanguage", "ja-JP", "Language code for speech recognition (e.g. ja-JP, en-US)");
			_speechRecognitionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("SpeechRecognition", "Enabled", true, "Enable/disable automatic speech recognition");
			_localVolume = ((BaseUnityPlugin)this).Config.Bind<float>("SpeechRecognition", "LocalVolume", 1f, new ConfigDescription("Volume for sounds you hear locally (0.0 = mute, 1.0 = normal, 2.0 = double)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			_remoteVolume = ((BaseUnityPlugin)this).Config.Bind<float>("SpeechRecognition", "RemoteVolume", 1f, new ConfigDescription("Volume for sounds sent to voice chat (0.0 = mute, 1.0 = normal, 2.0 = double)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			_apiBaseUrl = ((BaseUnityPlugin)this).Config.Bind<string>("SpeechRecognition", "ApiBaseUrl", "http://localhost:3211", "Base URL for the soundboard API server");
			_apiBaseUrl.SettingChanged += ApiBaseUrlSettingChanged;
			_debugHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "DebugPlayRandomSound", (KeyCode)290, "Key to play a random sound (selectable). Use any Unity KeyCode value.");
			_autoStartMicrophone = ((BaseUnityPlugin)this).Config.Bind<bool>("Microphone", "AutoStart", true, "Automatically start a microphone if the Recorder does not provide an input (set true to allow auto-start). Default: true");
			_autoSwitchMicrophone = ((BaseUnityPlugin)this).Config.Bind<bool>("Microphone", "AutoSwitch", false, "Automatically switch microphone device if the current device produces no samples. Default: false");
			_autoStartMicrophone.SettingChanged += AutoStartSettingChanged;
			_autoSwitchMicrophone.SettingChanged += AutoSwitchSettingChanged;
			SpeechManagerInstance = ((Component)this).gameObject.AddComponent<SpeechManager>();
			SpeechManagerInstance.Initialize();
			_source = ((Component)this).gameObject.AddComponent<AudioSource>();
			_source.playOnAwake = false;
			_source.loop = false;
			_source.spatialBlend = 0f;
			_source.dopplerLevel = 0f;
			_source.priority = 128;
			LoopbackInstance = new SoundboardLoopback();
			Logger.LogDebug((object)"super_soundboard: Awake finished.");
		}

		private void Update()
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_vc == (Object)null)
			{
				_vc = Object.FindObjectOfType<PlayerVoiceChat>();
				if ((Object)(object)_vc != (Object)null)
				{
					Logger.LogDebug((object)"super_soundboard: Found PlayerVoiceChat. Initializing soundboard recorder bridge...");
					_lastVc = _vc;
					InitSoundboard();
				}
			}
			else if ((Object)(object)_lastVc != (Object)(object)_vc)
			{
				Logger.LogDebug((object)"super_soundboard: PlayerVoiceChat instance changed (scene reload detected). Resetting...");
				_soundboardMixer?.Dispose();
				_soundboardMixer = null;
				_lastVc = _vc;
				_initRetryCount = 0;
				_nextRetryTime = 0f;
				InitSoundboard();
			}
			else if (_soundboardMixer == null && _initRetryCount < 10 && Time.time >= _nextRetryTime)
			{
				Logger.LogDebug((object)$"super_soundboard: Retrying initialization (attempt {_initRetryCount + 1}/{10})...");
				InitSoundboard();
				_initRetryCount++;
				_nextRetryTime = Time.time + 1f;
			}
			ConfigEntry<KeyCode>? debugHotkey = _debugHotkey;
			if (Input.GetKeyDown((KeyCode)((debugHotkey == null) ? 290 : ((int)debugHotkey.Value))))
			{
				SpeechManagerInstance?.PlayRandomSound();
			}
			_soundboardMixer?.Update();
		}

		private void InitSoundboard()
		{
			if ((Object)(object)_vc == (Object)null)
			{
				return;
			}
			try
			{
				Recorder val = _vc.recorder;
				if ((Object)(object)val == (Object)null)
				{
					Logger.LogDebug((object)"super_soundboard: PlayerVoiceChat.recorder is null — attempting fallback search for Recorder in scene...");
					Recorder val2 = Object.FindObjectOfType<Recorder>();
					if (!((Object)(object)val2 != (Object)null))
					{
						if (_initRetryCount == 0 || _initRetryCount >= 9)
						{
							Logger.LogWarning((object)"========================================");
							Logger.LogWarning((object)"super_soundboard: Recorder not found on PlayerVoiceChat.");
							if (_initRetryCount >= 9)
							{
								Logger.LogWarning((object)"Voice chat integration is not available.");
								Logger.LogWarning((object)"Sounds will only play locally (you can hear them).");
								Logger.LogWarning((object)"Others in voice chat will NOT hear the soundboard.");
							}
							else
							{
								Logger.LogWarning((object)"Will retry in a moment...");
							}
							Logger.LogWarning((object)"========================================");
						}
						return;
					}
					Logger.LogDebug((object)("super_soundboard: Found Recorder on GameObject '" + ((Object)((Component)val2).gameObject).name + "' (type " + ((object)val2).GetType().FullName + "). Will use it."));
					val = val2;
					try
					{
						PropertyInfo property = ((object)_vc).GetType().GetProperty("recorder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (property != null && property.CanWrite)
						{
							property.SetValue(_vc, val);
							Logger.LogDebug((object)"super_soundboard: Assigned found Recorder to PlayerVoiceChat.recorder property.");
						}
						else
						{
							FieldInfo field = ((object)_vc).GetType().GetField("recorder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (field != null)
							{
								field.SetValue(_vc, val);
								Logger.LogDebug((object)"super_soundboard: Assigned found Recorder to PlayerVoiceChat.recorder field.");
							}
							else
							{
								Logger.LogDebug((object)"super_soundboard: Could not assign Recorder to PlayerVoiceChat (no accessible property/field).");
							}
						}
					}
					catch (Exception ex)
					{
						Logger.LogDebug((object)("super_soundboard: Exception while assigning Recorder to PlayerVoiceChat: " + ex.Message));
					}
				}
				try
				{
					PropertyInfo property2 = ((object)val).GetType().GetProperty("enabled");
					if (property2 != null && property2.PropertyType == typeof(bool))
					{
						if (!(bool)property2.GetValue(val))
						{
							property2.SetValue(val, true);
							Logger.LogDebug((object)"super_soundboard: Enabled the Recorder component.");
						}
					}
					else
					{
						Behaviour val3 = (Behaviour)(object)val;
						if ((Object)(object)val3 != (Object)null && !val3.enabled)
						{
							val3.enabled = true;
							Logger.LogDebug((object)"super_soundboard: Enabled the Recorder component (cast to Behaviour).");
						}
					}
				}
				catch (Exception ex2)
				{
					Logger.LogDebug((object)("super_soundboard: Could not enable Recorder: " + ex2.Message));
				}
				try
				{
					Component val4 = (Component)(object)val;
					string text = (((Object)(object)val4 != (Object)null) ? ((Object)val4.gameObject).name : "(unknown)");
					Logger.LogDebug((object)("super_soundboard: Using Recorder type " + ((object)val).GetType().FullName + " on GameObject '" + text + "'"));
					Logger.LogDebug((object)string.Format("super_soundboard: Recorder has InputFactory? {0}", ((object)val).GetType().GetProperty("InputFactory") != null));
					Logger.LogDebug((object)string.Format("super_soundboard: Recorder has SourceType? {0}", ((object)val).GetType().GetProperty("SourceType") != null));
				}
				catch (Exception ex3)
				{
					Logger.LogDebug((object)("super_soundboard: Error inspecting Recorder: " + ex3.Message));
				}
				try
				{
					string[] devices = Microphone.devices;
					if (devices == null || devices.Length == 0)
					{
						Logger.LogWarning((object)"super_soundboard: No microphone devices found on this machine (Microphone.devices is empty).");
					}
					else
					{
						Logger.LogDebug((object)string.Format("super_soundboard: Microphone devices ({0}): {1}", devices.Length, string.Join(", ", devices)));
					}
				}
				catch (Exception ex4)
				{
					Logger.LogDebug((object)("super_soundboard: Could not query Microphone.devices: " + ex4.Message));
				}
				if (_soundboardMixer == null)
				{
					object obj = null;
					try
					{
						PropertyInfo property3 = ((object)val).GetType().GetProperty("MicInput", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (property3 != null)
						{
							obj = property3.GetValue(val);
							Logger.LogDebug((object)$"super_soundboard: Found MicInput property: {obj != null}");
						}
					}
					catch (Exception ex5)
					{
						Logger.LogDebug((object)("super_soundboard: Could not access MicInput: " + ex5.Message));
					}
					_soundboardMixer = new SoundboardMixer(_vc, obj);
				}
				try
				{
					PropertyInfo property4 = ((object)val).GetType().GetProperty("TransmitEnabled");
					if (property4 != null && property4.PropertyType == typeof(bool))
					{
						try
						{
							bool flag = (bool)property4.GetValue(val);
							Logger.LogDebug((object)$"super_soundboard: TransmitEnabled property exists, current value: {flag}");
						}
						catch (Exception ex6)
						{
							Logger.LogDebug((object)("super_soundboard: Could not read TransmitEnabled value: " + ex6.Message));
						}
						property4.SetValue(val, true);
						Logger.LogDebug((object)"super_soundboard: TransmitEnabled set to true");
					}
					else
					{
						Logger.LogDebug((object)"super_soundboard: TransmitEnabled property not found on Recorder.");
					}
					PropertyInfo property5 = ((object)val).GetType().GetProperty("RecordingEnabled");
					if (property5 != null && property5.PropertyType == typeof(bool))
					{
						try
						{
							bool flag2 = (bool)property5.GetValue(val);
							Logger.LogDebug((object)$"super_soundboard: RecordingEnabled property exists, current value: {flag2}");
						}
						catch (Exception ex7)
						{
							Logger.LogDebug((object)("super_soundboard: Could not read RecordingEnabled value: " + ex7.Message));
						}
						property5.SetValue(val, true);
						Logger.LogDebug((object)"super_soundboard: RecordingEnabled set to true");
					}
					else
					{
						Logger.LogDebug((object)"super_soundboard: RecordingEnabled property not found on Recorder.");
					}
				}
				catch (Exception arg)
				{
					Logger.LogWarning((object)$"super_soundboard: Failed to enable recording/transmit: {arg}");
				}
				try
				{
					PropertyInfo property6 = ((object)val).GetType().GetProperty("InputFactory");
					if (property6 != null)
					{
						Func<IAudioReader<float>> value = CreateMixerFactory;
						property6.SetValue(val, value);
						Logger.LogDebug((object)"super_soundboard: InputFactory set.");
					}
					else
					{
						Logger.LogError((object)("super_soundboard: InputFactory property not found on Recorder (recorder type: " + ((object)val).GetType().FullName + "). This may indicate a Photon Voice version mismatch."));
					}
				}
				catch (Exception arg2)
				{
					Logger.LogError((object)$"super_soundboard: Failed to set InputFactory: {arg2}");
				}
				try
				{
					PropertyInfo property7 = ((object)val).GetType().GetProperty("SourceType");
					if (property7 != null && property7.PropertyType.IsEnum)
					{
						object obj2 = Enum.ToObject(property7.PropertyType, 2);
						property7.SetValue(val, obj2);
						Logger.LogDebug((object)$"super_soundboard: SourceType set to {obj2} (Factory)");
					}
				}
				catch (Exception arg3)
				{
					Logger.LogWarning((object)$"super_soundboard: Failed to set SourceType: {arg3}");
				}
				try
				{
					Component[] array = Object.FindObjectsOfType<Component>();
					Component[] array2 = array;
					foreach (Component val5 in array2)
					{
						Type type = ((object)val5).GetType();
						if (!(type.Name == "PhotonVoiceView") && (type.FullName == null || !type.FullName.Contains("Photon.Voice")))
						{
							continue;
						}
						try
						{
							string text2 = "(unknown)";
							if (val5 != null)
							{
								Component val6 = val5;
								if (true)
								{
									text2 = ((Object)val6.gameObject).name;
								}
							}
							Logger.LogDebug((object)("super_soundboard: Found PhotonVoiceView on '" + text2 + "' (type " + type.FullName + ")"));
							object obj3 = null;
							PropertyInfo property8 = type.GetProperty("RecorderInUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (property8 != null)
							{
								obj3 = property8.GetValue(val5);
								if (obj3 == null)
								{
									property8.SetValue(val5, val);
									obj3 = val;
									Logger.LogDebug((object)"super_soundboard: Assigned Recorder to PhotonVoiceView.RecorderInUse");
								}
							}
							else
							{
								FieldInfo field2 = type.GetField("recorderInUse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
								if (field2 != null)
								{
									obj3 = field2.GetValue(val5);
									if (obj3 == null)
									{
										field2.SetValue(val5, val);
										obj3 = val;
										Logger.LogDebug((object)"super_soundboard: Assigned Recorder to PhotonVoiceView.recorderInUse");
									}
								}
								else
								{
									Logger.LogDebug((object)"super_soundboard: PhotonVoiceView has no accessible RecorderInUse property/field.");
								}
							}
							if (obj3 == null)
							{
								continue;
							}
							try
							{
								PropertyInfo property9 = obj3.GetType().GetProperty("TransmitEnabled");
								if (property9 != null && property9.PropertyType == typeof(bool))
								{
									try
									{
										bool flag3 = (bool)property9.GetValue(obj3);
										Logger.LogDebug((object)$"super_soundboard: PhotonVoiceView recorder TransmitEnabled current: {flag3}");
									}
									catch
									{
									}
									property9.SetValue(obj3, true);
									Logger.LogDebug((object)"super_soundboard: Set PhotonVoiceView recorder TransmitEnabled = true");
								}
							}
							catch (Exception ex8)
							{
								Logger.LogDebug((object)("super_soundboard: Could not set PhotonVoiceView recorder TransmitEnabled: " + ex8.Message));
							}
						}
						catch (Exception ex9)
						{
							Logger.LogDebug((object)("super_soundboard: Error handling PhotonVoiceView instance: " + ex9.Message));
						}
					}
				}
				catch (Exception ex10)
				{
					Logger.LogDebug((object)("super_soundboard: Error searching for PhotonVoiceView: " + ex10.Message));
				}
				try
				{
					MethodInfo method = ((object)val).GetType().GetMethod("RestartRecording");
					if (method != null)
					{
						method.Invoke(val, null);
					}
					else
					{
						((object)val).GetType().GetMethod("StartRecording")?.Invoke(val, null);
					}
				}
				catch (Exception arg4)
				{
					Logger.LogWarning((object)$"super_soundboard: Failed to restart recording: {arg4}");
				}
				Logger.LogDebug((object)"super_soundboard: Recorder hooked with SoundboardMixer (Factory mode).");
				_initRetryCount = 10;
			}
			catch (Exception arg5)
			{
				Logger.LogError((object)$"super_soundboard: InitSoundboard failed: {arg5}");
			}
		}

		private IAudioReader<float> CreateMixerFactory()
		{
			if (_soundboardMixer == null || _soundboardMixer.IsDisposed)
			{
				SoundboardMixer? soundboardMixer = _soundboardMixer;
				if (soundboardMixer != null && soundboardMixer.IsDisposed)
				{
					Logger.LogDebug((object)"super_soundboard: Previous SoundboardMixer was disposed, creating new instance in CreateMixerFactory.");
				}
				else
				{
					Logger.LogDebug((object)"super_soundboard: Creating new SoundboardMixer instance in CreateMixerFactory.");
				}
				object micInput = null;
				try
				{
					Recorder val = _vc?.recorder;
					if ((Object)(object)val != (Object)null)
					{
						PropertyInfo property = ((object)val).GetType().GetProperty("MicInput", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (property != null)
						{
							micInput = property.GetValue(val);
						}
					}
				}
				catch (Exception ex)
				{
					Logger.LogDebug((object)("super_soundboard: Could not access MicInput in CreateMixerFactory: " + ex.Message));
				}
				_soundboardMixer = new SoundboardMixer(_vc, micInput);
			}
			return (IAudioReader<float>)(object)_soundboardMixer;
		}

		private void OnDestroy()
		{
			_soundboardMixer?.Dispose();
			try
			{
				if (_autoStartMicrophone != null)
				{
					_autoStartMicrophone.SettingChanged -= AutoStartSettingChanged;
				}
				if (_autoSwitchMicrophone != null)
				{
					_autoSwitchMicrophone.SettingChanged -= AutoSwitchSettingChanged;
				}
				if (_apiBaseUrl != null)
				{
					_apiBaseUrl.SettingChanged -= ApiBaseUrlSettingChanged;
				}
			}
			catch
			{
			}
		}

		private void OnAutoStartChanged()
		{
			Logger.LogInfo((object)$"super_soundboard: AutoStartMicrophone changed => {AutoStartMicrophone}");
			if (_soundboardMixer != null)
			{
				if (AutoStartMicrophone)
				{
					_soundboardMixer.EnsureLocalMicStarted();
				}
				else
				{
					_soundboardMixer.StopLocalMic();
				}
			}
		}

		private void OnAutoSwitchChanged()
		{
			Logger.LogInfo((object)$"super_soundboard: AutoSwitchMicrophone changed => {AutoSwitchMicrophone}");
		}

		private void AutoStartSettingChanged(object? sender, EventArgs e)
		{
			OnAutoStartChanged();
		}

		private void AutoSwitchSettingChanged(object? sender, EventArgs e)
		{
			OnAutoSwitchChanged();
		}

		private void ApiBaseUrlSettingChanged(object? sender, EventArgs e)
		{
			OnApiBaseUrlChanged();
		}

		private void OnApiBaseUrlChanged()
		{
			string apiBaseUrl = ApiBaseUrl;
			try
			{
				if (_apiBaseUrl != null && _apiBaseUrl.Value != apiBaseUrl)
				{
					_apiBaseUrl.Value = apiBaseUrl;
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("super_soundboard: Could not persist normalized ApiBaseUrl: " + ex.Message));
			}
			Logger.LogInfo((object)("super_soundboard: ApiBaseUrl changed => " + apiBaseUrl));
			try
			{
				SpeechManagerInstance?.OnApiBaseUrlChanged();
			}
			catch (Exception ex2)
			{
				Logger.LogWarning((object)("super_soundboard: Error notifying SpeechManager of ApiBaseUrl change: " + ex2.Message));
			}
		}
	}
}