Decompiled source of RepoSoundboard v1.0.2

Audio.dll

Decompiled 3 weeks ago
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Audio.Wav;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Audio")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+af3062504208f895f9297509e19f0bf4d754990a")]
[assembly: AssemblyProduct("Audio")]
[assembly: AssemblyTitle("Audio")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 Audio
{
	public readonly struct AudioFormat : IEquatable<AudioFormat>
	{
		public readonly int SampleRate;

		public readonly int Channels;

		public AudioFormat(int sampleRate, int channels)
		{
			SampleRate = sampleRate;
			Channels = channels;
		}

		public override bool Equals(object? obj)
		{
			if (obj is AudioFormat other)
			{
				return Equals(other);
			}
			return false;
		}

		public bool Equals(AudioFormat other)
		{
			if (SampleRate == other.SampleRate)
			{
				return Channels == other.Channels;
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (SampleRate * 397) ^ Channels;
		}

		public static bool operator ==(AudioFormat left, AudioFormat right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(AudioFormat left, AudioFormat right)
		{
			return !(left == right);
		}
	}
	public static class AudioProviderFactory
	{
		public static ISamplesProvider FromFile(FileInfo file, AudioFormat format)
		{
			ISamplesProvider samplesProvider = null;
			if (file.Extension.ToLower() == ".wav")
			{
				samplesProvider = FromWavFile(file);
				if (samplesProvider.Format != format)
				{
					samplesProvider = new ResampledAudioProvider(samplesProvider, format, disposeSource: true);
				}
				return samplesProvider;
			}
			throw new NotImplementedException($"Support for \"{file.Exists}\" missing");
		}

		private static WaveSamplesProvider FromWavFile(FileInfo file)
		{
			return new WaveSamplesProvider(file.OpenRead(), leaveOpen: false);
		}
	}
	public interface ISamplesProvider : IDisposable
	{
		AudioFormat Format { get; }

		TimeSpan Duration { get; }

		int TotalSamples { get; }

		TimeSpan Position { get; set; }

		int SamplePosition { get; set; }

		int Read(float[] samples, int offset, int count);
	}
	public class ResampledAudioProvider : ISamplesProvider, IDisposable
	{
		private readonly float[] _srcChunk;

		private readonly float[] _resampledChunk;

		private int _resampledChunkLen;

		private int _resampledChunkReadIdx;

		private TimeSpan _pos;

		private int _samplePos;

		public ISamplesProvider Source { get; }

		public AudioFormat SourceFormat => Source.Format;

		public bool DisposeSource { get; }

		public AudioFormat Format { get; }

		public double Ratio { get; }

		public TimeSpan Duration => Source.Duration;

		public TimeSpan Position
		{
			get
			{
				return _pos;
			}
			set
			{
				SamplePosition = (int)(value.TotalSeconds * (double)(Format.SampleRate * Format.Channels));
			}
		}

		public int TotalSamples { get; }

		public int SamplePosition
		{
			get
			{
				return _samplePos;
			}
			set
			{
				_samplePos = Math.Clamp(value, 0, TotalSamples);
				_pos = TimeSpan.FromSeconds((double)_samplePos / (double)(Format.SampleRate * Format.Channels));
				_resampledChunkLen = 0;
				Source.SamplePosition = (int)Math.Floor((double)value / Ratio);
			}
		}

		public int Read(float[] samples, int offset, int count)
		{
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				if (_resampledChunkReadIdx >= _resampledChunkLen && !ResampleChunk())
				{
					break;
				}
				samples[offset++] = _resampledChunk[_resampledChunkReadIdx++];
				num++;
			}
			return num;
		}

		public void Dispose()
		{
			if (DisposeSource)
			{
				Source.Dispose();
			}
		}

		private bool ResampleChunk()
		{
			int num = Source.Read(_srcChunk, 0, _srcChunk.Length);
			if (num == 0)
			{
				_resampledChunkLen = 0;
				_resampledChunkReadIdx = 0;
				return false;
			}
			if (SourceFormat.Channels == 2 && Format.Channels == 1)
			{
				int num2 = 0;
				for (int i = 0; i < num; i += 2)
				{
					_srcChunk[num2++] = _srcChunk[i];
				}
				num /= 2;
			}
			int num3 = 0;
			double num4;
			for (num4 = 0.0; num4 < (double)num; num4 += Ratio)
			{
				if (num3 >= _resampledChunk.Length)
				{
					break;
				}
				_resampledChunk[num3++] = _srcChunk[(int)Math.Floor(num4)];
			}
			_resampledChunkLen = num3;
			_resampledChunkReadIdx = 0;
			Source.SamplePosition -= num - (int)Math.Floor(num4);
			return true;
		}

		public ResampledAudioProvider(ISamplesProvider source, AudioFormat format, bool disposeSource)
		{
			Source = source;
			Format = format;
			Ratio = (double)SourceFormat.SampleRate / (double)Format.SampleRate;
			DisposeSource = disposeSource;
			_srcChunk = new float[Format.SampleRate];
			_resampledChunk = new float[Format.SampleRate];
			TotalSamples = (int)((double)Source.TotalSamples / Ratio);
		}
	}
}
namespace Audio.Wav
{
	public readonly struct WaveChunk
	{
		public readonly WaveChunkId Id;

		public readonly int Length;

		public WaveChunk(WaveChunkId id, int length)
		{
			Id = id;
			Length = length;
		}
	}
	public enum WaveChunkId
	{
		RIFF = 1179011410,
		FMT = 544501094,
		DATA = 1635017060
	}
	public readonly struct WaveInfo
	{
		public readonly WaveFormat Format;

		public readonly int Channels;

		public readonly int SampleRate;

		public readonly int BitRate;

		public readonly int BlockAlign;

		public readonly int BitsPerSample;

		public const int SIG_WAVE = 1163280727;

		public WaveInfo(WaveFormat format, int channels, int sampleRate, int bitRate, int blockAlign, int bitsPerSample)
		{
			Format = format;
			Channels = channels;
			SampleRate = sampleRate;
			BitRate = bitRate;
			BlockAlign = blockAlign;
			BitsPerSample = bitsPerSample;
		}
	}
	public enum WaveFormat : short
	{
		PCM = 1
	}
	public class WaveSamplesProvider : ISamplesProvider, IDisposable
	{
		private readonly struct MappedWaveChunk
		{
			public readonly WaveChunk Chunk;

			public readonly long Position;

			public MappedWaveChunk(WaveChunk chunk, long position)
			{
				Chunk = chunk;
				Position = position;
			}
		}

		private delegate bool WaveSampleReader(out float s);

		private readonly Dictionary<WaveChunkId, List<MappedWaveChunk>> _chunkMap = new Dictionary<WaveChunkId, List<MappedWaveChunk>>();

		private readonly WaveSampleReader _sampleReader;

		private int _bytePos;

		private int _samplePos;

		private TimeSpan _timePos;

		private readonly int _dataChunkCount;

		private int _currentDataChunkSz = -1;

		private int _currentDataChunkByteIdx;

		private int _currentDataChunkIdx;

		private readonly byte[] _nextSamples = new byte[4];

		public WaveInfo Info { get; }

		public Stream Stream { get; }

		public bool LeaveOpen { get; }

		public AudioFormat Format { get; }

		public int TotalSamples { get; }

		public int SamplePosition
		{
			get
			{
				return _samplePos;
			}
			set
			{
				SetSamplePos(value);
			}
		}

		public int BytesLength { get; }

		public int BytePosition
		{
			get
			{
				return _bytePos;
			}
			set
			{
				SetBytePos(value);
			}
		}

		public TimeSpan Duration { get; }

		public TimeSpan Position
		{
			get
			{
				return _timePos;
			}
			set
			{
				SetTimePos(value);
			}
		}

		public int Read(float[] samples, int offset, int count)
		{
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				if (!_sampleReader(out var s))
				{
					break;
				}
				samples[offset++] = s;
				num++;
			}
			_samplePos += num;
			_timePos = TimeSpan.FromSeconds((double)_samplePos / (double)(Format.SampleRate * Format.Channels));
			return num;
		}

		public void Dispose()
		{
			if (!LeaveOpen)
			{
				Stream.Dispose();
			}
		}

		private bool SampleI8(out float s)
		{
			if (!GetBytesForSample(1))
			{
				s = 0f;
				return false;
			}
			s = (float)(int)_nextSamples[0] / 127f;
			return true;
		}

		private bool SampleI16(out float s)
		{
			if (!GetBytesForSample(2))
			{
				s = 0f;
				return false;
			}
			float num = 32767f;
			s = (float)BinaryPrimitives.ReadInt16LittleEndian(_nextSamples) / num;
			return true;
		}

		private bool SampleI24(out float s)
		{
			if (!GetBytesForSample(3))
			{
				s = 0f;
				return false;
			}
			float num = 8388607f;
			int num2 = _nextSamples[0] | (_nextSamples[1] << 8) | (_nextSamples[2] << 16);
			if (((uint)num2 & 0x800000u) != 0)
			{
				num2 |= -16777216;
			}
			s = (float)num2 / num;
			return true;
		}

		private bool SampleI32(out float s)
		{
			if (!GetBytesForSample(4))
			{
				s = 0f;
				return false;
			}
			float num = 2.1474836E+09f;
			s = (float)BinaryPrimitives.ReadInt32LittleEndian(_nextSamples) / num;
			return true;
		}

		private bool GetBytesForSample(int count)
		{
			if (_currentDataChunkByteIdx >= _currentDataChunkSz && !NextDataChunk())
			{
				return false;
			}
			if (Stream.Read(_nextSamples, 0, count) == count)
			{
				return true;
			}
			return false;
		}

		private bool NextDataChunk()
		{
			if (_currentDataChunkIdx >= _dataChunkCount)
			{
				return false;
			}
			WaveChunk waveChunk = SeekToChunk(WaveChunkId.DATA, _currentDataChunkIdx++);
			_currentDataChunkByteIdx = 0;
			_currentDataChunkSz = waveChunk.Length;
			return true;
		}

		private void SetSamplePos(int sample)
		{
			_samplePos = Math.Clamp(sample, 0, TotalSamples);
			_bytePos = _samplePos * (Info.BitsPerSample / 8);
			_timePos = TimeSpan.FromSeconds((double)_samplePos / (double)(Format.SampleRate * Format.Channels));
			SeekToDataIdx(_bytePos);
		}

		private void SetBytePos(int idx)
		{
			_bytePos = Math.Clamp(idx, 0, BytesLength);
			_samplePos = _bytePos / (Info.BitsPerSample / 8);
			_timePos = TimeSpan.FromSeconds((double)_samplePos / (double)(Format.SampleRate * Format.Channels));
			SeekToDataIdx(_bytePos);
		}

		private void SetTimePos(TimeSpan ts)
		{
			_samplePos = Math.Clamp((int)(ts.TotalSeconds * (double)(Format.SampleRate * Format.Channels)), 0, TotalSamples);
			_bytePos = _samplePos * (Info.BitsPerSample / 8);
			_timePos = TimeSpan.FromSeconds((double)_samplePos / (double)(Format.SampleRate * Format.Channels));
			SeekToDataIdx(_bytePos);
		}

		private void SeekToDataIdx(int bp)
		{
			int num = 0;
			for (int i = 0; i < _dataChunkCount; i++)
			{
				WaveChunk waveChunk = SeekToChunk(WaveChunkId.DATA, i);
				if (bp - num >= waveChunk.Length)
				{
					num += waveChunk.Length;
					continue;
				}
				_currentDataChunkIdx = i;
				_currentDataChunkSz = waveChunk.Length;
				_currentDataChunkByteIdx = bp - num;
				Stream.Position += _currentDataChunkByteIdx;
				return;
			}
			_currentDataChunkIdx = _dataChunkCount;
			_currentDataChunkByteIdx = 0;
			_currentDataChunkSz = 0;
		}

		private byte[] ReadAll(int count, out int br)
		{
			int i = 0;
			byte[] array = new byte[count];
			for (; i < count; i += br)
			{
				br = Stream.Read(array, i, count - i);
				if (br == 0)
				{
					break;
				}
			}
			br = i;
			return array;
		}

		private byte[] ReadAllOrException(int count)
		{
			int br;
			byte[] result = ReadAll(count, out br);
			if (br < count)
			{
				throw new EndOfStreamException();
			}
			return result;
		}

		private short ReadI16()
		{
			return BinaryPrimitives.ReadInt16LittleEndian(ReadAllOrException(2));
		}

		private int ReadI32()
		{
			return BinaryPrimitives.ReadInt32LittleEndian(ReadAllOrException(4));
		}

		private WaveChunk ReadChunk()
		{
			return new WaveChunk((WaveChunkId)ReadI32(), ReadI32());
		}

		private void LoadChunks()
		{
			if (ReadChunk().Id != WaveChunkId.RIFF)
			{
				throw new FormatException("Expected RIFF signature");
			}
			if (ReadI32() != 1163280727)
			{
				throw new FormatException("Expected WAVE format");
			}
			while (Stream.Position < Stream.Length)
			{
				WaveChunk chunk = ReadChunk();
				if (!_chunkMap.TryGetValue(chunk.Id, out List<MappedWaveChunk> value))
				{
					value = new List<MappedWaveChunk>();
					_chunkMap.Add(chunk.Id, value);
				}
				else
				{
					WaveChunkId id = chunk.Id;
					if (id == WaveChunkId.FMT || id == WaveChunkId.RIFF)
					{
						throw new FormatException($"Repeating chunk {chunk.Id}");
					}
				}
				value.Add(new MappedWaveChunk(chunk, Stream.Position - 8));
				Stream.Position += chunk.Length;
			}
		}

		private WaveChunk SeekToChunk(WaveChunkId id, int index, bool seekToContent = true)
		{
			if (!_chunkMap.TryGetValue(id, out List<MappedWaveChunk> value))
			{
				throw new KeyNotFoundException($"Could not find chunk {id}");
			}
			Stream.Position = value[index].Position;
			if (seekToContent)
			{
				Stream.Position += 8L;
			}
			return value[index].Chunk;
		}

		private int ChunkCount(WaveChunkId id)
		{
			if (!_chunkMap.TryGetValue(id, out List<MappedWaveChunk> value))
			{
				return 0;
			}
			return value.Count;
		}

		private WaveInfo LoadInfo()
		{
			WaveChunk waveChunk = SeekToChunk(WaveChunkId.FMT, 0);
			if (waveChunk.Length != 16)
			{
				throw new FormatException($"Format chunk has length of {waveChunk.Length}. Expected 16");
			}
			short num = ReadI16();
			int channels = ReadI16();
			int sampleRate = ReadI32();
			int bitRate = ReadI32() * 8;
			int blockAlign = ReadI16();
			int bitsPerSample = ReadI16();
			if (num != 1)
			{
				throw new NotSupportedException("Expected PCM audio format");
			}
			return new WaveInfo((WaveFormat)num, channels, sampleRate, bitRate, blockAlign, bitsPerSample);
		}

		private void LoadDataInfo(out int bytes, out int samples)
		{
			bytes = 0;
			samples = 0;
			for (int i = 0; i < _dataChunkCount; i++)
			{
				WaveChunk waveChunk = SeekToChunk(WaveChunkId.DATA, i, seekToContent: false);
				bytes += waveChunk.Length;
				samples += waveChunk.Length / (Info.BitsPerSample / 8);
			}
		}

		public WaveSamplesProvider(Stream stream, bool leaveOpen)
		{
			Stream = stream;
			LeaveOpen = leaveOpen;
			LoadChunks();
			Info = LoadInfo();
			Format = new AudioFormat(Info.SampleRate, Info.Channels);
			_dataChunkCount = ChunkCount(WaveChunkId.DATA);
			LoadDataInfo(out var bytes, out var samples);
			BytesLength = bytes;
			TotalSamples = samples;
			Duration = TimeSpan.FromSeconds((double)TotalSamples / (double)(Info.SampleRate * Info.Channels));
			switch (Info.BitsPerSample)
			{
			case 8:
				_sampleReader = SampleI8;
				break;
			case 16:
				_sampleReader = SampleI16;
				break;
			case 24:
				_sampleReader = SampleI24;
				break;
			case 32:
				_sampleReader = SampleI32;
				break;
			default:
				throw new NotSupportedException($"Unsupported bits per sample {Info.BitsPerSample}");
			}
		}
	}
	public class WaveWriter : IDisposable
	{
		private readonly MemoryStream _ms = new MemoryStream();

		public void AddSamples(float[] samples, int offset, int count)
		{
			byte[] array = new byte[2];
			for (int i = 0; i < count; i++)
			{
				BinaryPrimitives.WriteInt16LittleEndian(array, (short)(samples[offset++] * 32767f));
				_ms.Write(array, 0, array.Length);
			}
		}

		public void ToWave(Stream stream, int sampleRate)
		{
			WriteChunk(stream, new WaveChunk(WaveChunkId.RIFF, 44 + (int)_ms.Length));
			WriteI32(stream, 1163280727);
			WriteChunk(stream, new WaveChunk(WaveChunkId.FMT, 16));
			WriteI16(stream, 1, 1);
			WriteI32(stream, sampleRate, 96000);
			WriteI16(stream, 2, 16);
			_ms.Position = 0L;
			WriteChunk(stream, new WaveChunk(WaveChunkId.DATA, (int)_ms.Length));
			_ms.CopyTo(stream);
		}

		public void Dispose()
		{
			_ms.Dispose();
		}

		private void WriteI16(Stream s, params short[] value)
		{
			byte[] array = new byte[2];
			foreach (short value2 in value)
			{
				BinaryPrimitives.WriteInt16LittleEndian(array, value2);
				s.Write(array, 0, array.Length);
			}
		}

		private void WriteI32(Stream s, params int[] value)
		{
			byte[] array = new byte[4];
			foreach (int value2 in value)
			{
				BinaryPrimitives.WriteInt32LittleEndian(array, value2);
				s.Write(array, 0, array.Length);
			}
		}

		private void WriteChunk(Stream s, WaveChunk chk)
		{
			WriteI32(s, (int)chk.Id, chk.Length);
		}
	}
}

RepoSoundboard.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Audio;
using BepInEx;
using BepInEx.Logging;
using MenuLib;
using MenuLib.MonoBehaviors;
using MenuLib.Structs;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Photon.Voice;
using Photon.Voice.Unity;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("NatanM")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+4f8309492bba3d4fb682253d577fbb5dbba8997f")]
[assembly: AssemblyProduct("RepoSoundboard")]
[assembly: AssemblyTitle("RepoSoundboard")]
[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 RepoSoundboard
{
	[BepInPlugin("NatanM.RepoSoundboard", "RepoSoundboard", "1.0.2")]
	public class RepoSoundboard : BaseUnityPlugin
	{
		private PlayerVoiceChat? _vc;

		internal static RepoSoundboard Instance { get; private set; } = null;


		internal static ManualLogSource Logger => Instance._logger;

		internal static AudioFormat AudioFormat { get; } = new AudioFormat(48000, 1);


		internal static SoundboardLoopback Loopback { get; } = new SoundboardLoopback();


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

		private void Awake()
		{
			Instance = this;
			SoundboardPool.Load();
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			RepoSoundboardMenu.Init();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		private void Update()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_vc == (Object)null)
			{
				_vc = Object.FindAnyObjectByType<PlayerVoiceChat>();
			}
			else if ((Object)(object)_vc.recorder != (Object)null && (int)_vc.recorder.SourceType == 0)
			{
				Logger.LogInfo((object)"Detected native audio source. Overriding...");
				InitSoundboard();
			}
		}

		private void OnGUI()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			Event current = Event.current;
			if ((int)current.type == 4)
			{
				if (RepoSoundboardMenu.IsCapturingHotKey)
				{
					RepoSoundboardMenu.DispatchHotKey(current.keyCode);
				}
				else if ((Object)(object)_vc != (Object)null && (int)_vc.recorder.SourceType == 2)
				{
					SoundboardPool.DispatchHotKey(current.keyCode);
				}
			}
		}

		private void InitSoundboard()
		{
			AudioSource val = ((Component)this).gameObject.AddComponent<AudioSource>();
			val.clip = Loopback.Clip;
			val.loop = true;
			val.volume = 0.35f;
			val.Play();
			Recorder recorder = _vc.recorder;
			recorder.RecordingEnabled = false;
			recorder.SourceType = (InputSourceType)2;
			recorder.InputFactory = () => (IAudioDesc)(object)new SoundboardMixer(_vc);
			Logger.LogInfo((object)"Started recording.");
			recorder.RestartRecording();
		}
	}
	public static class RepoSoundboardMenu
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static BuilderDelegate <>9__0_0;

			public static ShouldCloseMenuDelegate <>9__1_2;

			public static ShouldCloseMenuDelegate <>9__4_8;

			internal void <Init>b__0_0(Transform p)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				MenuAPI.CreateREPOButton("Soundboard", (Action)SpawnSoundboardMenu, p, new Vector2(120f, 90f));
			}

			internal bool <SpawnSoundboardMenu>b__1_2()
			{
				return true;
			}

			internal bool <CreateEntryLayout>b__4_8()
			{
				SoundboardPool.Save();
				return true;
			}
		}

		private static SoundboardObject? capturingHotKeyObj;

		private static REPOButton? capturingHotKeyBtn;

		private static readonly List<REPOElement> entryLayoutElements = new List<REPOElement>();

		public static bool IsCapturingHotKey { get; set; }

		public static void Init()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				BuilderDelegate val = delegate(Transform p)
				{
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					MenuAPI.CreateREPOButton("Soundboard", (Action)SpawnSoundboardMenu, p, new Vector2(120f, 90f));
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			MenuAPI.AddElementToSettingsMenu((BuilderDelegate)obj);
		}

		private static void SpawnSoundboardMenu()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			SoundboardPool.Save();
			if (IsCapturingHotKey)
			{
				RepoSoundboard.Logger.LogWarning((object)"IsCapturingHotKey didn't properly reset.");
				IsCapturingHotKey = false;
			}
			REPOPopupPage popup = MenuAPI.CreateREPOPopupPage("Soundboard", (PresetSide)0, false, true, 0f);
			popup.scrollView.scrollSpeed = 3f;
			REPOPopupPage obj = popup;
			Padding maskPadding = popup.maskPadding;
			maskPadding.top = 35f;
			obj.maskPadding = maskPadding;
			REPOPopupPage obj2 = popup;
			ShouldCloseMenuDelegate onEscapePressed = obj2.onEscapePressed;
			object obj3 = <>c.<>9__1_2;
			if (obj3 == null)
			{
				ShouldCloseMenuDelegate val = () => true;
				<>c.<>9__1_2 = val;
				obj3 = (object)val;
			}
			obj2.onEscapePressed = (ShouldCloseMenuDelegate)Delegate.Combine((Delegate?)(object)onEscapePressed, (Delegate?)obj3);
			CreateCfgLayout(popup);
			popup.AddElement((BuilderDelegate)delegate(Transform p)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				MenuAPI.CreateREPOButton("Back", (Action)delegate
				{
					popup.ClosePage(true);
				}, p, new Vector2(66f, 10f));
			});
			popup.AddElement((BuilderDelegate)delegate(Transform p)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				MenuAPI.CreateREPOButton("Add", (Action)delegate
				{
					SoundboardObject entry = SoundboardPool.Add(new SoundboardObject
					{
						Name = "New Sound"
					});
					popup.ClosePage(true);
					OpenMenuForEntry(entry);
				}, p, new Vector2(150f, 10f));
			});
			popup.OpenPage(false);
		}

		private static void CreateCfgLayout(REPOPopupPage popup)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			REPOPopupPage popup2 = popup;
			for (int i = 0; i < SoundboardPool.Count; i++)
			{
				SoundboardObject entry = SoundboardPool.Get(i);
				popup2.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform p)
				{
					//IL_002d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0033: Unknown result type (might be due to invalid IL or missing references)
					REPOButton val = MenuAPI.CreateREPOButton(entry.Name, (Action)delegate
					{
						popup2.ClosePage(false);
						OpenMenuForEntry(entry);
					}, p, default(Vector2));
					return ((REPOElement)val).rectTransform;
				}, 0f, 0f);
			}
		}

		private static void OpenMenuForEntry(SoundboardObject entry)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			REPOPopupPage popup = MenuAPI.CreateREPOPopupPage("Entry Settings", (PresetSide)0, false, true, 0f);
			popup.scrollView.scrollSpeed = 3f;
			REPOPopupPage obj = popup;
			Padding maskPadding = popup.maskPadding;
			maskPadding.top = 35f;
			obj.maskPadding = maskPadding;
			REPOPopupPage obj2 = popup;
			obj2.onEscapePressed = (ShouldCloseMenuDelegate)Delegate.Combine((Delegate?)(object)obj2.onEscapePressed, (Delegate?)(ShouldCloseMenuDelegate)delegate
			{
				if (IsCapturingHotKey)
				{
					return false;
				}
				SpawnSoundboardMenu();
				popup.ClosePage(true);
				return false;
			});
			CreateEntryLayout(popup, entry);
			popup.AddElement((BuilderDelegate)delegate(Transform p)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				REPOButton item = MenuAPI.CreateREPOButton("Back", (Action)delegate
				{
					popup.ClosePage(true);
					SpawnSoundboardMenu();
				}, p, new Vector2(66f, 10f));
				entryLayoutElements.Add((REPOElement)(object)item);
			});
			popup.OpenPage(false);
		}

		private static void CreateEntryLayout(REPOPopupPage popup, SoundboardObject entry)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			SoundboardObject entry2 = entry;
			REPOPopupPage popup2 = popup;
			entryLayoutElements.Clear();
			entry2.Stop();
			popup2.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform p)
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				REPOInputField val4 = MenuAPI.CreateREPOInputField("Name", (Action<string>)delegate(string s)
				{
					entry2.Name = s;
				}, p, default(Vector2), false, "", "");
				val4.inputStringSystem.SetValue(entry2.Name, false);
				entryLayoutElements.Add((REPOElement)(object)val4);
				return ((REPOElement)val4).rectTransform;
			}, 0f, 0f);
			popup2.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform p)
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				REPOInputField val3 = MenuAPI.CreateREPOInputField("Path", (Action<string>)delegate(string s)
				{
					entry2.Path = s;
				}, p, default(Vector2), false, "", "");
				val3.inputStringSystem.SetValue(entry2.Path, false);
				entryLayoutElements.Add((REPOElement)(object)val3);
				return ((REPOElement)val3).rectTransform;
			}, 0f, 0f);
			popup2.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform p)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				KeyCode hotKey = entry2.HotKey;
				REPOButton button = MenuAPI.CreateREPOButton("HotKey: " + ((object)(KeyCode)(ref hotKey)).ToString(), (Action)null, p, default(Vector2));
				button.onClick = delegate
				{
					((TMP_Text)button.labelTMP).text = "HotKey: ...";
					capturingHotKeyObj = entry2;
					capturingHotKeyBtn = button;
					IsCapturingHotKey = true;
					RepoSoundboard.Logger.LogDebug((object)$"IsCapturingHotKey: {IsCapturingHotKey}");
					foreach (REPOElement entryLayoutElement in entryLayoutElements)
					{
						((Behaviour)entryLayoutElement).enabled = false;
					}
				};
				entryLayoutElements.Add((REPOElement)(object)button);
				return ((REPOElement)button).rectTransform;
			}, 0f, 0f);
			popup2.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform p)
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				REPOButton val2 = MenuAPI.CreateREPOButton("Delete", (Action)delegate
				{
					popup2.ClosePage(false);
					SoundboardPool.Remove(entry2);
					SpawnSoundboardMenu();
				}, p, default(Vector2));
				RectTransform rectTransform = ((REPOElement)val2).rectTransform;
				((Transform)rectTransform).position = ((Transform)rectTransform).position + new Vector3(0f, 10f);
				((Graphic)val2.labelTMP).color = new Color(0.8f, 0.25f, 0.25f, 1f);
				entryLayoutElements.Add((REPOElement)(object)val2);
				return ((REPOElement)val2).rectTransform;
			}, 0f, 0f);
			REPOPopupPage obj = popup2;
			ShouldCloseMenuDelegate onEscapePressed = obj.onEscapePressed;
			object obj2 = <>c.<>9__4_8;
			if (obj2 == null)
			{
				ShouldCloseMenuDelegate val = delegate
				{
					SoundboardPool.Save();
					return true;
				};
				<>c.<>9__4_8 = val;
				obj2 = (object)val;
			}
			obj.onEscapePressed = (ShouldCloseMenuDelegate)Delegate.Combine((Delegate?)(object)onEscapePressed, (Delegate?)obj2);
		}

		public static void DispatchHotKey(KeyCode keyCode)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if ((int)keyCode == 27)
			{
				RepoSoundboard.Logger.LogDebug((object)$"Ignore set new key code: {keyCode}");
				RestoreAfterHotKey();
				return;
			}
			if ((int)keyCode == 8)
			{
				keyCode = (KeyCode)0;
			}
			RepoSoundboard.Logger.LogDebug((object)$"Set new HotKey: {keyCode}");
			if (capturingHotKeyObj == null)
			{
				RestoreAfterHotKey();
				return;
			}
			RepoSoundboard.Logger.LogDebug((object)(capturingHotKeyObj.UpdateHotKey(keyCode) ? "Success." : "Failed."));
			RestoreAfterHotKey();
		}

		private static void RestoreAfterHotKey()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			IsCapturingHotKey = false;
			foreach (REPOElement entryLayoutElement in entryLayoutElements)
			{
				((Behaviour)entryLayoutElement).enabled = true;
			}
			TextMeshProUGUI labelTMP = capturingHotKeyBtn.labelTMP;
			KeyCode hotKey = capturingHotKeyObj.HotKey;
			((TMP_Text)labelTMP).text = "HotKey: " + ((object)(KeyCode)(ref hotKey)).ToString();
		}
	}
	public static class SoundboardAudioProvider
	{
		private static readonly List<SoundboardObject> _active = new List<SoundboardObject>();

		public static void Push(SoundboardObject obj)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			lock (_active)
			{
				ISamplesProvider? provider = obj.Provider;
				ResampledAudioProvider val = (ResampledAudioProvider)(object)((provider is ResampledAudioProvider) ? provider : null);
				if (val != null)
				{
					RepoSoundboard.Logger.LogDebug((object)$"Resampling from {val.SourceFormat.SampleRate} to {val.Format.SampleRate}");
				}
				_active.Add(obj);
			}
		}

		public static void Remove(SoundboardObject obj)
		{
			lock (_active)
			{
				_active.Remove(obj);
			}
		}

		public static void Mix(float[] samples, float[] soundboardLoopback)
		{
			float[] array = new float[samples.Length];
			lock (_active)
			{
				for (int num = _active.Count - 1; num >= 0; num--)
				{
					SoundboardObject soundboardObject = _active[num];
					if (soundboardObject.Finished)
					{
						soundboardObject.Stop();
					}
					int num2 = soundboardObject.Read(array, 0, array.Length);
					if (num2 == 0)
					{
						soundboardObject.Stop();
					}
					for (int i = 0; i < num2; i++)
					{
						samples[i] += array[i];
						soundboardLoopback[i] += array[i];
					}
				}
			}
		}
	}
	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 SoundboardLoopback()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0068: Expected O, but got Unknown
			_samples = new float[RepoSoundboard.AudioFormat.SampleRate / 2];
			Clip = AudioClip.Create("SoundboardLoopback", _samples.Length, RepoSoundboard.AudioFormat.Channels, RepoSoundboard.AudioFormat.SampleRate, true, new PCMReaderCallback(OnRead), new PCMSetPositionCallback(OnSetPosition));
		}

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

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

		private void OnSetPosition(int position)
		{
		}
	}
	public class SoundboardMixer : IAudioReader<float>, IDataReader<float>, IDisposable, IAudioDesc
	{
		private int _lastMicPos;

		public PlayerVoiceChat VoiceChat { get; }

		public DeviceInfo MicrophoneDevice { get; set; }

		public AudioClip? MicrophoneClip { get; private set; }

		public int SamplingRate => RepoSoundboard.AudioFormat.SampleRate;

		public int Channels => RepoSoundboard.AudioFormat.Channels;

		public string? Error { get; private set; }

		public SoundboardMixer(PlayerVoiceChat voiceChat)
		{
			VoiceChat = voiceChat;
			base..ctor();
		}

		private void EnsureAudioDevice()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			DeviceInfo microphoneDevice = VoiceChat.recorder.MicrophoneDevice;
			string iDString = ((DeviceInfo)(ref microphoneDevice)).IDString;
			microphoneDevice = MicrophoneDevice;
			if (iDString != ((DeviceInfo)(ref microphoneDevice)).IDString)
			{
				_lastMicPos = 0;
				MicrophoneDevice = VoiceChat.recorder.MicrophoneDevice;
				microphoneDevice = MicrophoneDevice;
				MicrophoneClip = Microphone.Start(((DeviceInfo)(ref microphoneDevice)).Name, true, 1, SamplingRate);
				RepoSoundboard.Logger.LogInfo((object)"Microphone clip updated.");
			}
		}

		public bool Read(float[] buffer)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			EnsureAudioDevice();
			if (!((Object)(object)MicrophoneClip == (Object)null))
			{
				DeviceInfo microphoneDevice = MicrophoneDevice;
				if (Microphone.IsRecording(((DeviceInfo)(ref microphoneDevice)).Name))
				{
					microphoneDevice = MicrophoneDevice;
					int position = Microphone.GetPosition(((DeviceInfo)(ref microphoneDevice)).Name);
					int num = position - _lastMicPos;
					if (num < 0)
					{
						num += MicrophoneClip.samples;
					}
					if (num < buffer.Length)
					{
						return false;
					}
					Array.Clear(buffer, 0, buffer.Length);
					MicrophoneClip.GetData(buffer, _lastMicPos);
					float[] array = new float[buffer.Length];
					SoundboardAudioProvider.Mix(buffer, array);
					RepoSoundboard.Loopback.Push(array);
					_lastMicPos = (_lastMicPos + buffer.Length) % MicrophoneClip.samples;
					return true;
				}
			}
			return false;
		}

		public void Dispose()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			DeviceInfo microphoneDevice = MicrophoneDevice;
			if (Microphone.IsRecording(((DeviceInfo)(ref microphoneDevice)).Name))
			{
				microphoneDevice = MicrophoneDevice;
				Microphone.End(((DeviceInfo)(ref microphoneDevice)).Name);
			}
			AudioClip? microphoneClip = MicrophoneClip;
			if (microphoneClip != null)
			{
				microphoneClip.UnloadAudioData();
			}
		}
	}
	public class SoundboardObject
	{
		[JsonIgnore]
		private readonly object _lock = new object();

		private KeyCode _hk;

		public string Name { get; set; } = string.Empty;


		public string Path { get; set; } = string.Empty;


		public KeyCode HotKey
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return _hk;
			}
			set
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				UpdateHotKey(value);
			}
		}

		[JsonIgnore]
		public bool Finished
		{
			get
			{
				if (Provider != null)
				{
					return Provider.SamplePosition >= Provider.TotalSamples;
				}
				return true;
			}
		}

		[JsonIgnore]
		public ISamplesProvider? Provider { get; private set; }

		public void Play()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				lock (_lock)
				{
					if (Provider != null)
					{
						RepoSoundboard.Logger.LogDebug((object)("Sound " + Name + " restarted."));
						Restart();
						return;
					}
					RepoSoundboard.Logger.LogInfo((object)("Trying to play " + Name + " from file " + Path + "..."));
					FileInfo fileInfo = new FileInfo(Path);
					if (!fileInfo.Exists)
					{
						throw new FileNotFoundException(Path);
					}
					Provider = AudioProviderFactory.FromFile(fileInfo, RepoSoundboard.AudioFormat);
					SoundboardAudioProvider.Push(this);
				}
			}
			catch (Exception arg)
			{
				RepoSoundboard.Logger.LogWarning((object)$"Could not play \"{Name}\": {arg}");
			}
		}

		public void Stop()
		{
			lock (_lock)
			{
				SoundboardAudioProvider.Remove(this);
				((IDisposable)Provider)?.Dispose();
				Provider = null;
				RepoSoundboard.Logger.LogDebug((object)("Sound " + Name + " stopped."));
			}
		}

		public void Restart()
		{
			lock (_lock)
			{
				Provider.Position = TimeSpan.Zero;
			}
		}

		public int Read(float[] buffer, int offset, int samples)
		{
			lock (_lock)
			{
				if (Provider == null)
				{
					return 0;
				}
				return Provider.Read(buffer, offset, samples);
			}
		}

		public bool UpdateHotKey(KeyCode newHk)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (SoundboardPool.HasObject(this))
			{
				if (!SoundboardPool.IsHotKeyAvailableFor(newHk, this))
				{
					return false;
				}
				SoundboardPool.UpdateHk(this, HotKey, newHk);
			}
			_hk = newHk;
			return true;
		}
	}
	public static class SoundboardPool
	{
		private static readonly List<SoundboardObject> _objects = new List<SoundboardObject>();

		private static readonly Dictionary<KeyCode, SoundboardObject> _keyMap = new Dictionary<KeyCode, SoundboardObject>();

		public static int Count => _objects.Count;

		public static SoundboardObject Add(SoundboardObject obj)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!IsHotKeyAvailable(obj.HotKey))
			{
				RepoSoundboard.Logger.LogWarning((object)$"Found object with repeating HotKey: {obj.HotKey}");
				obj.UpdateHotKey((KeyCode)0);
			}
			_objects.Add(obj);
			if ((int)obj.HotKey != 0)
			{
				_keyMap.Add(obj.HotKey, obj);
			}
			return obj;
		}

		public static void Remove(SoundboardObject obj)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			_objects.Remove(obj);
			_keyMap.Remove(obj.HotKey, out SoundboardObject _);
		}

		public static bool HasObject(SoundboardObject obj)
		{
			return _objects.Contains(obj);
		}

		public static void Save()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			try
			{
				FileInfo settingsFile = GetSettingsFile();
				RepoSoundboard.Logger.LogDebug((object)("Saving settings to \"" + settingsFile.FullName + "\"..."));
				File.WriteAllText(settingsFile.FullName, JsonConvert.SerializeObject((object)_objects, new JsonSerializerSettings
				{
					Converters = { (JsonConverter)(object)new HotKeyConverter() },
					Formatting = (Formatting)1
				}));
				RepoSoundboard.Logger.LogDebug((object)"Settings saved.");
			}
			catch (Exception arg)
			{
				RepoSoundboard.Logger.LogError((object)$"Failed to write settings: {arg}");
			}
		}

		public static void Load()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			try
			{
				RepoSoundboard.Logger.LogDebug((object)"Loading settings...");
				_objects.Clear();
				_keyMap.Clear();
				FileInfo settingsFile = GetSettingsFile();
				string text = File.ReadAllText(settingsFile.FullName);
				List<SoundboardObject> list = JsonConvert.DeserializeObject<List<SoundboardObject>>(text, new JsonSerializerSettings
				{
					Converters = { (JsonConverter)(object)new HotKeyConverter() }
				});
				if (list != null)
				{
					foreach (SoundboardObject item in list)
					{
						Add(item);
					}
				}
				RepoSoundboard.Logger.LogDebug((object)"Settings loaded.");
			}
			catch (Exception arg)
			{
				RepoSoundboard.Logger.LogError((object)$"Failed to read settings: {arg}");
			}
		}

		public static SoundboardObject Get(int idx)
		{
			return _objects[idx];
		}

		public static void DispatchHotKey(KeyCode key)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (_keyMap.TryGetValue(key, out SoundboardObject value))
			{
				value.Play();
			}
		}

		public static bool IsHotKeyAvailable(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key != 0)
			{
				return !_keyMap.ContainsKey(key);
			}
			return true;
		}

		public static bool IsHotKeyAvailableFor(KeyCode key, SoundboardObject obj)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key == 0 || !_keyMap.TryGetValue(key, out SoundboardObject value))
			{
				return true;
			}
			return obj == value;
		}

		internal static void UpdateHk(SoundboardObject obj, KeyCode old, KeyCode newValue)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			_keyMap.Remove(old);
			_keyMap.Add(newValue, obj);
		}

		internal static FileInfo GetSettingsFile()
		{
			try
			{
				Process currentProcess = Process.GetCurrentProcess();
				FileInfo fileInfo = new FileInfo(currentProcess.MainModule.FileName);
				return new FileInfo(Path.Combine(fileInfo.Directory.FullName, "soundboard_settings.json"));
			}
			catch (Exception arg)
			{
				RepoSoundboard.Logger.LogError((object)$"Failed to get settings file path. Defaulting to current working directory. Reason: {arg}");
				return new FileInfo("soundboard_settings.json");
			}
		}
	}
	internal class HotKeyConverter : JsonConverter
	{
		public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!(value is KeyCode val))
			{
				writer.WriteNull();
			}
			else
			{
				writer.WriteValue(((object)(KeyCode)(ref val)).ToString());
			}
		}

		public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!(reader.Value is string value) || !Enum.TryParse<KeyCode>(value, out KeyCode result))
			{
				return (object)(KeyCode)0;
			}
			return result;
		}

		public override bool CanConvert(Type objectType)
		{
			return objectType == typeof(KeyCode);
		}
	}
}