Decompiled source of Bardheim v0.7.0

BepInEx\plugins\Bardheim\Bardheim.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Bardheim.Assets;
using Bardheim.Audio;
using Bardheim.Config;
using Bardheim.Instruments;
using Bardheim.Items;
using Bardheim.Network;
using Bardheim.Notes;
using Bardheim.PlayMode;
using Bardheim.Songs;
using Bardheim.UI;
using Bardheim.Visuals;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Bardheim")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.7.0.0")]
[assembly: AssemblyInformationalVersion("0.7.0+bbb433bb04fd122a08b7d7af9f7876c4021e01ca")]
[assembly: AssemblyProduct("Bardheim")]
[assembly: AssemblyTitle("Bardheim")]
[assembly: AssemblyVersion("0.7.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 Bardheim
{
	[BepInPlugin("com.valheimmodlab.bardheim", "Bardheim", "0.7.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[DefaultExecutionOrder(-1000)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.valheimmodlab.bardheim";

		public const string PluginName = "Bardheim";

		public const string PluginVersion = "0.7.0";

		private LyreConfig? _config;

		private LyreItemRegistration? _lyreItemRegistration;

		private DrumItemRegistration? _drumItemRegistration;

		private FluteItemRegistration? _fluteItemRegistration;

		private LyrePlayMode? _playMode;

		private LyreInputRouter? _inputRouter;

		private LocalNotePlayback? _playback;

		private LocalDrumPlayback? _drumPlayback;

		private LocalFlutePlayback? _flutePlayback;

		private RemoteNotePlayback? _remotePlayback;

		private RemoteDrumPlayback? _remoteDrumPlayback;

		private RemoteFlutePlayback? _remoteFlutePlayback;

		private ILyreNetworkNoteEmitter? _noteEmitter;

		private ValheimRoutedLyreNetwork? _network;

		private MidiSongController? _songController;

		private PlayerFeedback? _feedback;

		private readonly HudVisibilityState _hudVisibility = new HudVisibilityState();

		private void Awake()
		{
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			_config = LyreConfig.Bind(((BaseUnityPlugin)this).Config);
			_feedback = new PlayerFeedback(((BaseUnityPlugin)this).Logger);
			_playMode = new LyrePlayMode();
			LyreNoteClipCache clipCache = new LyreNoteClipCache(((BaseUnityPlugin)this).Logger);
			_playback = new LocalNotePlayback(((BaseUnityPlugin)this).Logger, clipCache);
			_drumPlayback = new LocalDrumPlayback(((BaseUnityPlugin)this).Logger);
			_flutePlayback = new LocalFlutePlayback(((BaseUnityPlugin)this).Logger);
			_remotePlayback = new RemoteNotePlayback(((BaseUnityPlugin)this).Logger, clipCache);
			_remoteDrumPlayback = new RemoteDrumPlayback(((BaseUnityPlugin)this).Logger);
			_remoteFlutePlayback = new RemoteFlutePlayback(((BaseUnityPlugin)this).Logger);
			_network = new ValheimRoutedLyreNetwork(_remotePlayback, _remoteDrumPlayback, _remoteFlutePlayback, ((BaseUnityPlugin)this).Logger, _config.EnableMultiplayerAudio.Value, _config.RemoteNoteVolume.Value, _config.RemoteMaxSimultaneousNotes.Value, _config.RemoteMaxNoteEventsPerSecond.Value, _config.RemoteAudibleDistance.Value);
			_noteEmitter = _network;
			_songController = new MidiSongController(PathForSongs(), _playback, _drumPlayback, _flutePlayback, _noteEmitter, _feedback, ((BaseUnityPlugin)this).Logger);
			_inputRouter = new LyreInputRouter(new NoteSetSelector(NoteSetCatalog.CreateDefaultSets()), _playMode, _playback, _drumPlayback, _flutePlayback, _noteEmitter, _feedback, ((BaseUnityPlugin)this).Logger);
			_lyreItemRegistration = new LyreItemRegistration(((BaseUnityPlugin)this).Logger);
			_drumItemRegistration = new DrumItemRegistration(((BaseUnityPlugin)this).Logger);
			_fluteItemRegistration = new FluteItemRegistration(((BaseUnityPlugin)this).Logger);
			_inputRouter.RegisterButtons();
			_network.RegisterReceiver();
			_lyreItemRegistration.Register();
			_drumItemRegistration.Register();
			_fluteItemRegistration.Register();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Equip the lyre, drum, or flute and press {2} to toggle play mode.", "Bardheim", "0.7.0", _config.PlayModeToggle.Value));
		}

		private void Update()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			_hudVisibility.Update(IsControlHeld(), Input.GetKeyDown((KeyCode)284));
			if (_config == null || _playMode == null || _inputRouter == null || _feedback == null || _songController == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			string equippedInstrumentId = GetEquippedInstrumentId(localPlayer);
			bool canPlay = equippedInstrumentId != null;
			if (_playMode.UpdateEligibility(canPlay))
			{
				_songController.StopIfPlaying();
				_feedback.Center("Instrument play mode off.");
			}
			KeyboardShortcut value = _config.PlayModeToggle.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				if (_playMode.Toggle(canPlay))
				{
					if (!_playMode.IsActive)
					{
						_songController.StopIfPlaying();
					}
					_feedback.Center(_playMode.IsActive ? (GetInstrumentDisplayName(equippedInstrumentId) + " play mode on.") : "Instrument play mode off.");
				}
				else
				{
					_feedback.Center("Equip the lyre, drum, or flute to play.");
				}
			}
			_inputRouter.Update(localPlayer, equippedInstrumentId, _config.MaxSimultaneousNotes.Value, _config.NoteVolume.Value);
			if (_playMode.IsActive && localPlayer != null)
			{
				_songController.Update(localPlayer, equippedInstrumentId, _config.MaxSimultaneousMidiNotes.Value, _config.MaxFluteMidiNotesPerSecond.Value, _config.NoteVolume.Value);
			}
			LyrePlayModeVisualPose.UpdateCalibration(_config.EnablePlayPoseCalibration.Value, ((BaseUnityPlugin)this).Logger);
			DrumPlayModeVisualPose.UpdateCalibration(_config.EnablePlayPoseCalibration.Value, equippedInstrumentId, ((BaseUnityPlugin)this).Logger);
			FlutePlayModeVisualPose.UpdateCalibration(_config.EnablePlayPoseCalibration.Value, equippedInstrumentId, ((BaseUnityPlugin)this).Logger);
		}

		private void OnGUI()
		{
			MidiSongController? songController = _songController;
			if (songController != null)
			{
				LyrePlayMode? playMode = _playMode;
				songController.DrawHud(playMode != null && playMode.IsActive && !_hudVisibility.IsHidden);
			}
		}

		private static bool IsControlHeld()
		{
			return Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
		}

		private static string? GetEquippedInstrumentId(Player? player)
		{
			object obj;
			if (player == null)
			{
				obj = null;
			}
			else
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				obj = ((inventory != null) ? inventory.GetEquippedItems() : null);
			}
			List<ItemData> list = (List<ItemData>)obj;
			if (list == null)
			{
				return null;
			}
			foreach (ItemData item in list)
			{
				if (item?.m_dropPrefab != null)
				{
					if (((Object)item.m_dropPrefab).name == "Bardheim_Lyre")
					{
						return "lyre";
					}
					if (((Object)item.m_dropPrefab).name == "Bardheim_Drum")
					{
						return "drums";
					}
					if (((Object)item.m_dropPrefab).name == "Bardheim_Flute")
					{
						return "flute";
					}
				}
			}
			return null;
		}

		private static string GetInstrumentDisplayName(string? instrumentId)
		{
			if (1 == 0)
			{
			}
			string result = instrumentId switch
			{
				"lyre" => "Lyre", 
				"drums" => "Drum", 
				"flute" => "Flute", 
				_ => "Instrument", 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static string PathForSongs()
		{
			return Path.Combine(Paths.PluginPath, "Bardheim", "songs");
		}
	}
}
namespace Bardheim.Visuals
{
	internal sealed class DrumPlayModeVisualPose : MonoBehaviour
	{
		private static readonly HashSet<DrumPlayModeVisualPose> Instances = new HashSet<DrumPlayModeVisualPose>();

		private static readonly Vector3 DefaultCarryLocalPosition = new Vector3(0.05f, 0.1f, 0.3f);

		private static readonly Vector3 DefaultCarryLocalEuler = new Vector3(-15f, -55f, 85f);

		private const float PositionStep = 0.05f;

		private const float RotationStep = 5f;

		private const string PoseDirectoryName = "Bardheim";

		private const string PoseFilename = "drum-hand-visual-pose.txt";

		private Vector3 _baseLocalPosition;

		private Quaternion _baseLocalRotation;

		private Vector3 _baseLocalScale;

		private Vector3 _carryLocalPosition = DefaultCarryLocalPosition;

		private Vector3 _carryLocalEuler = DefaultCarryLocalEuler;

		private bool _capturedBasePose;

		private bool _loadedPose;

		public static void UpdateCalibration(bool enabled, string? activeInstrumentId, ManualLogSource logger)
		{
			if (!enabled || activeInstrumentId != "drums")
			{
				return;
			}
			foreach (DrumPlayModeVisualPose instance in Instances)
			{
				if (instance != null && instance.CanApplyPose())
				{
					instance.UpdateCalibrationInput(logger);
				}
			}
		}

		private void Awake()
		{
			CaptureBasePose();
			LoadPoseIfNeeded();
			ApplyCarryPose();
		}

		private void OnEnable()
		{
			CaptureBasePose();
			LoadPoseIfNeeded();
			ApplyCarryPose();
			Instances.Add(this);
		}

		private void OnDisable()
		{
			Instances.Remove(this);
		}

		private bool CanApplyPose()
		{
			return ((Component)this).gameObject.activeInHierarchy && ((Component)this).transform.root != null && !((Object)((Component)this).transform.root).name.Equals("Bardheim_Drum", StringComparison.Ordinal);
		}

		private void CaptureBasePose()
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			if (!_capturedBasePose)
			{
				_baseLocalPosition = ((Component)this).transform.localPosition;
				_baseLocalRotation = ((Component)this).transform.localRotation;
				_baseLocalScale = ((Component)this).transform.localScale;
				_capturedBasePose = true;
			}
		}

		private void LoadPoseIfNeeded()
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (_loadedPose)
			{
				return;
			}
			_loadedPose = true;
			string path = PosePath();
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				foreach (string text in array)
				{
					if (text.StartsWith("position=", StringComparison.Ordinal))
					{
						_carryLocalPosition = ParseVector3(text.Substring("position=".Length), _carryLocalPosition);
					}
					else if (text.StartsWith("euler=", StringComparison.Ordinal))
					{
						_carryLocalEuler = ParseVector3(text.Substring("euler=".Length), _carryLocalEuler);
					}
				}
			}
			catch (Exception)
			{
				_carryLocalPosition = DefaultCarryLocalPosition;
				_carryLocalEuler = DefaultCarryLocalEuler;
			}
		}

		private void ApplyCarryPose()
		{
			//IL_0018: 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_0023: 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)
			//IL_0040: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (_capturedBasePose)
			{
				((Component)this).transform.localPosition = _baseLocalPosition + _carryLocalPosition;
				((Component)this).transform.localRotation = _baseLocalRotation * Quaternion.Euler(_carryLocalEuler);
				((Component)this).transform.localScale = _baseLocalScale;
			}
		}

		private void UpdateCalibrationInput(ManualLogSource logger)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 zero = Vector3.zero;
			Vector3 zero2 = Vector3.zero;
			bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			if (Input.GetKeyDown((KeyCode)260))
			{
				if (flag)
				{
					zero2.y -= 5f;
				}
				else
				{
					zero.x -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)262))
			{
				if (flag)
				{
					zero2.y += 5f;
				}
				else
				{
					zero.x += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)264))
			{
				if (flag)
				{
					zero2.x -= 5f;
				}
				else
				{
					zero.y += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)258))
			{
				if (flag)
				{
					zero2.x += 5f;
				}
				else
				{
					zero.y -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)263))
			{
				if (flag)
				{
					zero2.z -= 5f;
				}
				else
				{
					zero.z -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)265))
			{
				if (flag)
				{
					zero2.z += 5f;
				}
				else
				{
					zero.z += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)91))
			{
				zero2.y -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)93))
			{
				zero2.y += 5f;
			}
			if (Input.GetKeyDown((KeyCode)59))
			{
				zero2.z -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)39))
			{
				zero2.z += 5f;
			}
			if (Input.GetKeyDown((KeyCode)44))
			{
				zero2.x -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)46))
			{
				zero2.x += 5f;
			}
			if (!(zero == Vector3.zero) || !(zero2 == Vector3.zero))
			{
				_carryLocalPosition += zero;
				_carryLocalEuler += zero2;
				ApplyCarryPose();
				SavePose(logger);
			}
		}

		private void SavePose(ManualLogSource logger)
		{
			//IL_0022: 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)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			string text = PosePath();
			Directory.CreateDirectory(Path.GetDirectoryName(text));
			File.WriteAllLines(text, new string[2]
			{
				"position=" + FormatVector(_carryLocalPosition),
				"euler=" + FormatVector(_carryLocalEuler)
			});
			logger.LogInfo((object)("Drum visual pose saved: position=" + FormatVector(_carryLocalPosition) + " euler=" + FormatVector(_carryLocalEuler) + " file=" + text));
		}

		private static string PosePath()
		{
			return Path.Combine(Paths.ConfigPath, "Bardheim", "drum-hand-visual-pose.txt");
		}

		private static string FormatVector(Vector3 value)
		{
			return string.Join(",", value.x.ToString("F3", CultureInfo.InvariantCulture), value.y.ToString("F3", CultureInfo.InvariantCulture), value.z.ToString("F3", CultureInfo.InvariantCulture));
		}

		private static Vector3 ParseVector3(string value, Vector3 fallback)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			string[] array = value.Split(new char[1] { ',' });
			if (array.Length != 3)
			{
				return fallback;
			}
			float result;
			float result2;
			float result3;
			return (Vector3)((float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out result2) && float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) ? new Vector3(result, result2, result3) : fallback);
		}
	}
	internal sealed class FlutePlayModeVisualPose : MonoBehaviour
	{
		private static readonly HashSet<FlutePlayModeVisualPose> Instances = new HashSet<FlutePlayModeVisualPose>();

		private static readonly Vector3 DefaultCarryLocalPosition = new Vector3(0.03f, 0.03f, 0.08f);

		private static readonly Vector3 DefaultCarryLocalEuler = new Vector3(0f, -70f, 90f);

		private const float PositionStep = 0.05f;

		private const float RotationStep = 5f;

		private const string PoseDirectoryName = "Bardheim";

		private const string PoseFilename = "flute-hand-visual-pose.txt";

		private Vector3 _baseLocalPosition;

		private Quaternion _baseLocalRotation;

		private Vector3 _baseLocalScale;

		private Vector3 _carryLocalPosition = DefaultCarryLocalPosition;

		private Vector3 _carryLocalEuler = DefaultCarryLocalEuler;

		private bool _capturedBasePose;

		private bool _loadedPose;

		public static void UpdateCalibration(bool enabled, string? activeInstrumentId, ManualLogSource logger)
		{
			if (!enabled || activeInstrumentId != "flute")
			{
				return;
			}
			foreach (FlutePlayModeVisualPose instance in Instances)
			{
				if (instance != null && instance.CanApplyPose())
				{
					instance.UpdateCalibrationInput(logger);
				}
			}
		}

		private void Awake()
		{
			CaptureBasePose();
			LoadPoseIfNeeded();
			ApplyCarryPose();
		}

		private void OnEnable()
		{
			CaptureBasePose();
			LoadPoseIfNeeded();
			ApplyCarryPose();
			Instances.Add(this);
		}

		private void OnDisable()
		{
			Instances.Remove(this);
		}

		private bool CanApplyPose()
		{
			return ((Component)this).gameObject.activeInHierarchy && ((Component)this).transform.root != null && !((Object)((Component)this).transform.root).name.Equals("Bardheim_Flute", StringComparison.Ordinal);
		}

		private void CaptureBasePose()
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			if (!_capturedBasePose)
			{
				_baseLocalPosition = ((Component)this).transform.localPosition;
				_baseLocalRotation = ((Component)this).transform.localRotation;
				_baseLocalScale = ((Component)this).transform.localScale;
				_capturedBasePose = true;
			}
		}

		private void LoadPoseIfNeeded()
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			if (_loadedPose)
			{
				return;
			}
			_loadedPose = true;
			string path = PosePath();
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(path);
				foreach (string text in array)
				{
					if (text.StartsWith("position=", StringComparison.Ordinal))
					{
						_carryLocalPosition = ParseVector3(text.Substring("position=".Length), _carryLocalPosition);
					}
					else if (text.StartsWith("euler=", StringComparison.Ordinal))
					{
						_carryLocalEuler = ParseVector3(text.Substring("euler=".Length), _carryLocalEuler);
					}
				}
			}
			catch (Exception)
			{
				_carryLocalPosition = DefaultCarryLocalPosition;
				_carryLocalEuler = DefaultCarryLocalEuler;
			}
		}

		private void ApplyCarryPose()
		{
			//IL_0018: 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_0023: 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)
			//IL_0040: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (_capturedBasePose)
			{
				((Component)this).transform.localPosition = _baseLocalPosition + _carryLocalPosition;
				((Component)this).transform.localRotation = _baseLocalRotation * Quaternion.Euler(_carryLocalEuler);
				((Component)this).transform.localScale = _baseLocalScale;
			}
		}

		private void UpdateCalibrationInput(ManualLogSource logger)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 zero = Vector3.zero;
			Vector3 zero2 = Vector3.zero;
			bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			if (Input.GetKeyDown((KeyCode)260))
			{
				if (flag)
				{
					zero2.y -= 5f;
				}
				else
				{
					zero.x -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)262))
			{
				if (flag)
				{
					zero2.y += 5f;
				}
				else
				{
					zero.x += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)264))
			{
				if (flag)
				{
					zero2.x -= 5f;
				}
				else
				{
					zero.y += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)258))
			{
				if (flag)
				{
					zero2.x += 5f;
				}
				else
				{
					zero.y -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)263))
			{
				if (flag)
				{
					zero2.z -= 5f;
				}
				else
				{
					zero.z -= 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)265))
			{
				if (flag)
				{
					zero2.z += 5f;
				}
				else
				{
					zero.z += 0.05f;
				}
			}
			if (Input.GetKeyDown((KeyCode)91))
			{
				zero2.y -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)93))
			{
				zero2.y += 5f;
			}
			if (Input.GetKeyDown((KeyCode)59))
			{
				zero2.z -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)39))
			{
				zero2.z += 5f;
			}
			if (Input.GetKeyDown((KeyCode)44))
			{
				zero2.x -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)46))
			{
				zero2.x += 5f;
			}
			if (!(zero == Vector3.zero) || !(zero2 == Vector3.zero))
			{
				_carryLocalPosition += zero;
				_carryLocalEuler += zero2;
				ApplyCarryPose();
				SavePose(logger);
			}
		}

		private void SavePose(ManualLogSource logger)
		{
			//IL_0022: 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)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			string text = PosePath();
			Directory.CreateDirectory(Path.GetDirectoryName(text));
			File.WriteAllLines(text, new string[2]
			{
				"position=" + FormatVector(_carryLocalPosition),
				"euler=" + FormatVector(_carryLocalEuler)
			});
			logger.LogInfo((object)("Flute visual pose saved: position=" + FormatVector(_carryLocalPosition) + " euler=" + FormatVector(_carryLocalEuler) + " file=" + text));
		}

		private static string PosePath()
		{
			return Path.Combine(Paths.ConfigPath, "Bardheim", "flute-hand-visual-pose.txt");
		}

		private static string FormatVector(Vector3 value)
		{
			return string.Join(",", value.x.ToString("F3", CultureInfo.InvariantCulture), value.y.ToString("F3", CultureInfo.InvariantCulture), value.z.ToString("F3", CultureInfo.InvariantCulture));
		}

		private static Vector3 ParseVector3(string value, Vector3 fallback)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			string[] array = value.Split(new char[1] { ',' });
			if (array.Length != 3)
			{
				return fallback;
			}
			float result;
			float result2;
			float result3;
			return (Vector3)((float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out result2) && float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) ? new Vector3(result, result2, result3) : fallback);
		}
	}
	internal sealed class LyrePlayModeVisualPose : MonoBehaviour
	{
		private static readonly HashSet<LyrePlayModeVisualPose> Instances = new HashSet<LyrePlayModeVisualPose>();

		private static readonly Vector3 CarryLocalPosition = new Vector3(0.05f, 0f, -0.05f);

		private static readonly Vector3 CarryLocalEuler = new Vector3(10f, -15f, 10f);

		private const float PositionStep = 0.05f;

		private const float RotationStep = 5f;

		private Vector3 _baseLocalPosition;

		private Quaternion _baseLocalRotation;

		private Vector3 _baseLocalScale;

		private Vector3 _carryLocalPosition = CarryLocalPosition;

		private Vector3 _carryLocalEuler = CarryLocalEuler;

		private bool _capturedBasePose;

		public static void UpdateCalibration(bool enabled, ManualLogSource logger)
		{
			if (!enabled)
			{
				return;
			}
			foreach (LyrePlayModeVisualPose instance in Instances)
			{
				if (instance != null && instance.CanApplyPose())
				{
					instance.UpdateCalibrationInput(logger);
				}
			}
		}

		private void Awake()
		{
			CaptureBasePose();
			ApplyCarryPose();
		}

		private void OnEnable()
		{
			CaptureBasePose();
			ApplyCarryPose();
			Instances.Add(this);
		}

		private void OnDisable()
		{
			Instances.Remove(this);
		}

		private bool CanApplyPose()
		{
			return ((Component)this).gameObject.activeInHierarchy && ((Component)this).transform.root != null && !((Object)((Component)this).transform.root).name.Equals("Bardheim_Lyre", StringComparison.Ordinal);
		}

		private void ApplyCarryPose()
		{
			//IL_0018: 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_0023: 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)
			//IL_0040: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (_capturedBasePose)
			{
				((Component)this).transform.localPosition = _baseLocalPosition + _carryLocalPosition;
				((Component)this).transform.localRotation = _baseLocalRotation * Quaternion.Euler(_carryLocalEuler);
				((Component)this).transform.localScale = _baseLocalScale;
			}
		}

		private void CaptureBasePose()
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			if (!_capturedBasePose)
			{
				_baseLocalPosition = ((Component)this).transform.localPosition;
				_baseLocalRotation = ((Component)this).transform.localRotation;
				_baseLocalScale = ((Component)this).transform.localScale;
				_capturedBasePose = true;
			}
		}

		private void UpdateCalibrationInput(ManualLogSource logger)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			Vector3 zero = Vector3.zero;
			Vector3 zero2 = Vector3.zero;
			if (Input.GetKeyDown((KeyCode)260))
			{
				zero.x -= 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)262))
			{
				zero.x += 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)264))
			{
				zero.y += 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)258))
			{
				zero.y -= 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)263))
			{
				zero.z -= 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)265))
			{
				zero.z += 0.05f;
			}
			if (Input.GetKeyDown((KeyCode)91))
			{
				zero2.y -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)93))
			{
				zero2.y += 5f;
			}
			if (Input.GetKeyDown((KeyCode)59))
			{
				zero2.z -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)39))
			{
				zero2.z += 5f;
			}
			if (Input.GetKeyDown((KeyCode)44))
			{
				zero2.x -= 5f;
			}
			if (Input.GetKeyDown((KeyCode)46))
			{
				zero2.x += 5f;
			}
			if (!(zero == Vector3.zero) || !(zero2 == Vector3.zero))
			{
				_carryLocalPosition += zero;
				_carryLocalEuler += zero2;
				ApplyCarryPose();
				LogCalibrationConstants(logger);
			}
		}

		private void LogCalibrationConstants(ManualLogSource logger)
		{
			logger.LogInfo((object)("Lyre carry pose calibration constants: " + $"CarryLocalPosition = new Vector3({_carryLocalPosition.x:F2}f, {_carryLocalPosition.y:F2}f, {_carryLocalPosition.z:F2}f); " + $"CarryLocalEuler = new Vector3({_carryLocalEuler.x:F1}f, {_carryLocalEuler.y:F1}f, {_carryLocalEuler.z:F1}f)"));
		}
	}
}
namespace Bardheim.UI
{
	public sealed class HudVisibilityState
	{
		public bool IsHidden { get; private set; }

		public void Update(bool controlHeld, bool f3Pressed)
		{
			if (controlHeld && f3Pressed)
			{
				IsHidden = !IsHidden;
			}
		}
	}
	public sealed class PlayerFeedback
	{
		private readonly ManualLogSource _logger;

		public PlayerFeedback(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void Center(string message)
		{
			_logger.LogInfo((object)message);
			if (MessageHud.instance != null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
	}
}
namespace Bardheim.Songs
{
	public static class DrumMidiMapper
	{
		private const int PercussionChannel = 9;

		public static bool TryMap(MidiNoteEvent note, out string? hitId)
		{
			hitId = null;
			if (note.Channel != 9)
			{
				return false;
			}
			int midiNote = note.MidiNote;
			if (1 == 0)
			{
			}
			string text;
			switch (midiNote)
			{
			case 27:
			case 28:
			case 29:
			case 30:
			case 31:
			case 32:
			case 33:
			case 34:
				text = "rim";
				break;
			case 35:
				text = "low_kick";
				break;
			case 36:
				text = "kick";
				break;
			case 38:
				text = "snare";
				break;
			case 40:
				text = "electric_snare";
				break;
			case 37:
				text = "rim";
				break;
			case 39:
				text = "clap";
				break;
			case 42:
				text = "muted";
				break;
			case 44:
				text = "pedal_hat";
				break;
			case 46:
				text = "pedal_hat";
				break;
			case 49:
			case 52:
			case 55:
			case 57:
				text = "crash";
				break;
			case 51:
			case 53:
			case 59:
			case 73:
				text = "ride";
				break;
			case 41:
			case 43:
				text = "low_tom";
				break;
			case 45:
			case 47:
				text = "mid_tom";
				break;
			case 48:
			case 50:
				text = "high_tom";
				break;
			case 54:
			case 70:
			case 72:
			case 74:
			case 83:
				text = "tambourine";
				break;
			case 56:
			case 58:
			case 84:
				text = "cowbell";
				break;
			case 60:
			case 65:
			case 67:
			case 81:
				text = "high_tom";
				break;
			case 61:
			case 64:
			case 68:
			case 77:
			case 80:
				text = "low_tom";
				break;
			case 62:
			case 63:
			case 78:
			case 79:
			case 82:
				text = "muted";
				break;
			case 66:
				text = "mid_tom";
				break;
			case 69:
				text = "clap";
				break;
			case 71:
				text = "pedal_hat";
				break;
			case 75:
			case 76:
				text = "rim";
				break;
			case 85:
			case 86:
			case 87:
				text = "open";
				break;
			default:
				text = null;
				break;
			}
			if (1 == 0)
			{
			}
			hitId = text;
			return hitId != null;
		}

		public static bool HasMappedPercussion(IEnumerable<MidiNoteEvent> events)
		{
			foreach (MidiNoteEvent @event in events)
			{
				if (TryMap(@event, out string _))
				{
					return true;
				}
			}
			return false;
		}

		public static bool TryMapMelodicFallback(MidiNoteEvent note, out string hitId)
		{
			int midiNote = note.MidiNote;
			if (1 == 0)
			{
			}
			string text = ((midiNote < 54) ? ((midiNote < 40) ? ((midiNote >= 30) ? "kick" : "low_kick") : ((midiNote >= 47) ? "mid_tom" : "low_tom")) : ((midiNote < 69) ? ((midiNote >= 61) ? "high_tom" : "snare") : ((midiNote >= 78) ? "tambourine" : "rim")));
			if (1 == 0)
			{
			}
			hitId = text;
			return true;
		}
	}
	public static class FluteMidiArranger
	{
		private const double MelodyBucketSeconds = 0.05;

		public static IEnumerable<MidiNoteEvent> SelectPlayableEvents(IEnumerable<MidiNoteEvent> dueNotes, MidiSongProfile profile)
		{
			MidiSongProfile profile2 = profile;
			MidiNoteEvent[] array = dueNotes.Where(profile2.Allows).ToArray();
			if (profile2.ArrangementMode == MidiArrangementMode.Full)
			{
				return array;
			}
			return (from note in array
				group note by Math.Floor(note.StartSeconds / 0.05) into @group
				orderby @group.Min((MidiNoteEvent note) => note.StartSeconds)
				select @group).SelectMany((IGrouping<double, MidiNoteEvent> group) => (from note in @group
				orderby profile2.ApplyOctaveOffset(note) descending, note.Velocity descending
				select note).Take(profile2.MaxSimultaneousMelodyNotes));
		}
	}
	public static class FluteMidiMapper
	{
		private static readonly (int MidiNote, string Name)[] FluteBank = new(int, string)[22]
		{
			(62, "D4"),
			(63, "D#4"),
			(64, "E4"),
			(65, "F4"),
			(66, "F#4"),
			(67, "G4"),
			(68, "G#4"),
			(69, "A4"),
			(70, "Bb4"),
			(71, "B4"),
			(72, "C5"),
			(73, "C#5"),
			(74, "D5"),
			(75, "D#5"),
			(76, "E5"),
			(77, "F5"),
			(78, "F#5"),
			(79, "G5"),
			(80, "G#5"),
			(81, "A5"),
			(82, "Bb5"),
			(83, "B5")
		};

		public static NoteDefinition MapToFluteNote(int midiNote)
		{
			int i;
			for (i = midiNote; i < FluteBank[0].MidiNote; i += 12)
			{
			}
			while (i > FluteBank[FluteBank.Length - 1].MidiNote)
			{
				i -= 12;
			}
			(int, string) tuple = FluteBank[0];
			int num = Math.Abs(i - tuple.Item1);
			(int, string)[] fluteBank = FluteBank;
			for (int j = 0; j < fluteBank.Length; j++)
			{
				(int, string) tuple2 = fluteBank[j];
				int num2 = Math.Abs(i - tuple2.Item1);
				if (num2 < num)
				{
					tuple = tuple2;
					num = num2;
				}
			}
			return new NoteDefinition(0, tuple.Item2, GetFrequency(tuple.Item1));
		}

		private static float GetFrequency(int midiNote)
		{
			return (float)(440.0 * Math.Pow(2.0, (double)(midiNote - 69) / 12.0));
		}
	}
	public sealed class FluteMidiRateLimiter
	{
		[CompilerGenerated]
		private sealed class <SelectPlayableEvents>d__2 : IEnumerable<MidiNoteEvent>, IEnumerable, IEnumerator<MidiNoteEvent>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private MidiNoteEvent <>2__current;

			private int <>l__initialThreadId;

			private IEnumerable<MidiNoteEvent> dueNotes;

			public IEnumerable<MidiNoteEvent> <>3__dueNotes;

			private MidiSongProfile profile;

			public MidiSongProfile <>3__profile;

			private int maxNoteStartsPerSecond;

			public int <>3__maxNoteStartsPerSecond;

			public FluteMidiRateLimiter <>4__this;

			private IEnumerator<MidiNoteEvent> <>s__1;

			private MidiNoteEvent <note>5__2;

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

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

			[DebuggerHidden]
			public <SelectPlayableEvents>d__2(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>s__1 = null;
				<note>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>s__1 = dueNotes.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						<note>5__2 = null;
						break;
					}
					while (<>s__1.MoveNext())
					{
						<note>5__2 = <>s__1.Current;
						if (!profile.Allows(<note>5__2) || (<>4__this._plannedEvents != null && !<>4__this._plannedEvents.Contains(<note>5__2)))
						{
							continue;
						}
						<>2__current = <note>5__2;
						<>1__state = 1;
						return true;
					}
					<>m__Finally1();
					<>s__1 = 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 (<>s__1 != null)
				{
					<>s__1.Dispose();
				}
			}

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

			[DebuggerHidden]
			IEnumerator<MidiNoteEvent> IEnumerable<MidiNoteEvent>.GetEnumerator()
			{
				<SelectPlayableEvents>d__2 <SelectPlayableEvents>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<SelectPlayableEvents>d__ = this;
				}
				else
				{
					<SelectPlayableEvents>d__ = new <SelectPlayableEvents>d__2(0)
					{
						<>4__this = <>4__this
					};
				}
				<SelectPlayableEvents>d__.dueNotes = <>3__dueNotes;
				<SelectPlayableEvents>d__.profile = <>3__profile;
				<SelectPlayableEvents>d__.maxNoteStartsPerSecond = <>3__maxNoteStartsPerSecond;
				return <SelectPlayableEvents>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<MidiNoteEvent>)this).GetEnumerator();
			}
		}

		private HashSet<MidiNoteEvent>? _plannedEvents;

		public void Prepare(IEnumerable<MidiNoteEvent> songEvents, MidiSongProfile profile, int maxNoteStartsPerSecond)
		{
			MidiSongProfile profile2 = profile;
			int maxStarts = Math.Max(1, maxNoteStartsPerSecond);
			_plannedEvents = (from note in songEvents.Where(profile2.Allows)
				group note by (int)Math.Floor(note.StartSeconds)).SelectMany((IGrouping<int, MidiNoteEvent> group) => from note in @group.OrderByDescending(profile2.ApplyOctaveOffset).ThenByDescending((MidiNoteEvent note) => note.Velocity).ThenBy((MidiNoteEvent note) => note.StartSeconds)
					.Take(maxStarts)
				orderby note.StartSeconds
				select note).ToHashSet();
		}

		[IteratorStateMachine(typeof(<SelectPlayableEvents>d__2))]
		public IEnumerable<MidiNoteEvent> SelectPlayableEvents(IEnumerable<MidiNoteEvent> dueNotes, MidiSongProfile profile, int maxNoteStartsPerSecond)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <SelectPlayableEvents>d__2(-2)
			{
				<>4__this = this,
				<>3__dueNotes = dueNotes,
				<>3__profile = profile,
				<>3__maxNoteStartsPerSecond = maxNoteStartsPerSecond
			};
		}

		public void Reset()
		{
			_plannedEvents = null;
		}
	}
	public enum MidiArrangementMode
	{
		Full,
		Melody
	}
	public static class MidiFileParser
	{
		private sealed class TimedMidiEvent
		{
			public int Tick { get; }

			public TimedMidiEventKind Kind { get; }

			public int Channel { get; }

			public int Note { get; }

			public int Velocity { get; }

			public bool IsNoteOn { get; }

			public int TempoMicrosecondsPerQuarterNote { get; }

			public int SortOrder => (Kind != 0) ? ((!IsNoteOn) ? 1 : 2) : 0;

			private TimedMidiEvent(int tick, TimedMidiEventKind kind, int channel, int note, int velocity, bool isNoteOn, int tempo)
			{
				Tick = tick;
				Kind = kind;
				Channel = channel;
				Note = note;
				Velocity = velocity;
				IsNoteOn = isNoteOn;
				TempoMicrosecondsPerQuarterNote = tempo;
			}

			public static TimedMidiEvent Tempo(int tick, int tempo)
			{
				return new TimedMidiEvent(tick, TimedMidiEventKind.Tempo, 0, 0, 0, isNoteOn: false, tempo);
			}

			public static TimedMidiEvent NoteEvent(int tick, bool isNoteOn, int channel, int note, int velocity)
			{
				return new TimedMidiEvent(tick, TimedMidiEventKind.Note, channel, note, velocity, isNoteOn, 0);
			}
		}

		private enum TimedMidiEventKind
		{
			Tempo,
			Note
		}

		private const int DefaultMicrosecondsPerQuarterNote = 500000;

		public static MidiSong Parse(string displayName, byte[] data, string? sourcePath = null)
		{
			using MemoryStream memoryStream = new MemoryStream(data);
			using BinaryReader reader = new BinaryReader(memoryStream, Encoding.ASCII);
			Require(ReadAscii(reader, 4) == "MThd", "Missing MIDI header.");
			Require(ReadInt32(reader) == 6, "Unsupported MIDI header length.");
			int num = ReadInt16(reader);
			int num2 = ReadInt16(reader);
			int num3 = ReadInt16(reader);
			Require(num == 0 || num == 1, $"Unsupported MIDI format {num}.");
			Require(num2 > 0, "MIDI file has no tracks.");
			Require(num3 > 0 && (num3 & 0x8000) == 0, "SMPTE time division is not supported.");
			List<TimedMidiEvent> events = new List<TimedMidiEvent>();
			for (int i = 0; i < num2; i++)
			{
				Require(ReadAscii(reader, 4) == "MTrk", "Missing MIDI track header.");
				int num4 = ReadInt32(reader);
				long num5 = memoryStream.Position + num4;
				ParseTrack(reader, num5, events);
				memoryStream.Position = num5;
			}
			return new MidiSong(displayName, BuildNoteEvents(events, num3), sourcePath);
		}

		private static void ParseTrack(BinaryReader reader, long trackEnd, List<TimedMidiEvent> events)
		{
			int num = 0;
			byte b = 0;
			while (reader.BaseStream.Position < trackEnd)
			{
				num += ReadVariableLength(reader);
				byte b2 = reader.ReadByte();
				byte b3;
				byte b4;
				if ((b2 & 0x80) == 0)
				{
					Require(b != 0, "Running status appeared before any status byte.");
					b3 = b;
					b4 = b2;
				}
				else
				{
					b3 = b2;
					b4 = 0;
					if (b3 < 240)
					{
						b = b3;
					}
				}
				switch (b3)
				{
				case byte.MaxValue:
				{
					byte b6 = reader.ReadByte();
					int count = ReadVariableLength(reader);
					byte[] array = reader.ReadBytes(count);
					if (b6 == 81 && array.Length == 3)
					{
						int tempo = (array[0] << 16) | (array[1] << 8) | array[2];
						events.Add(TimedMidiEvent.Tempo(num, tempo));
					}
					if (b6 == 47)
					{
						return;
					}
					break;
				}
				default:
					if (b3 != 247)
					{
						int num2 = b3 & 0xF0;
						int channel = b3 & 0xF;
						byte note = (((b2 & 0x80) == 0) ? b4 : reader.ReadByte());
						if (num2 != 192 && num2 != 208)
						{
							byte b5 = reader.ReadByte();
							if (num2 == 144 || num2 == 128)
							{
								events.Add(TimedMidiEvent.NoteEvent(num, num2 == 144 && b5 > 0, channel, note, b5));
							}
						}
						break;
					}
					goto case 240;
				case 240:
				{
					int num3 = ReadVariableLength(reader);
					reader.BaseStream.Position += num3;
					break;
				}
				}
			}
		}

		private static IEnumerable<MidiNoteEvent> BuildNoteEvents(List<TimedMidiEvent> events, int ticksPerQuarterNote)
		{
			events.Sort((TimedMidiEvent left, TimedMidiEvent right) => (left.Tick == right.Tick) ? left.SortOrder.CompareTo(right.SortOrder) : left.Tick.CompareTo(right.Tick));
			int num = 500000;
			int num2 = 0;
			double num3 = 0.0;
			Dictionary<(int, int), Queue<(double, int)>> dictionary = new Dictionary<(int, int), Queue<(double, int)>>();
			List<MidiNoteEvent> list = new List<MidiNoteEvent>();
			foreach (TimedMidiEvent @event in events)
			{
				int num4 = @event.Tick - num2;
				num3 += (double)num4 * ((double)num / 1000000.0) / (double)ticksPerQuarterNote;
				num2 = @event.Tick;
				if (@event.Kind == TimedMidiEventKind.Tempo)
				{
					num = @event.TempoMicrosecondsPerQuarterNote;
					continue;
				}
				(int, int) key = (@event.Channel, @event.Note);
				Queue<(double, int)> value2;
				if (@event.IsNoteOn)
				{
					if (!dictionary.TryGetValue(key, out var value))
					{
						value = new Queue<(double, int)>();
						dictionary.Add(key, value);
					}
					value.Enqueue((num3, @event.Velocity));
				}
				else if (dictionary.TryGetValue(key, out value2) && value2.Count > 0)
				{
					(double, int) tuple = value2.Dequeue();
					list.Add(new MidiNoteEvent(@event.Note, @event.Channel, tuple.Item1, Math.Max(0.03, num3 - tuple.Item1), tuple.Item2));
				}
			}
			return from item in list
				orderby item.StartSeconds, item.Velocity descending
				select item;
		}

		private static int ReadVariableLength(BinaryReader reader)
		{
			int num = 0;
			for (int i = 0; i < 4; i++)
			{
				byte b = reader.ReadByte();
				num = (num << 7) | (b & 0x7F);
				if ((b & 0x80) == 0)
				{
					return num;
				}
			}
			throw new InvalidDataException("Invalid MIDI variable-length value.");
		}

		private static string ReadAscii(BinaryReader reader, int length)
		{
			return Encoding.ASCII.GetString(reader.ReadBytes(length));
		}

		private static int ReadInt16(BinaryReader reader)
		{
			byte[] array = reader.ReadBytes(2);
			Require(array.Length == 2, "Unexpected end of MIDI file.");
			return (array[0] << 8) | array[1];
		}

		private static int ReadInt32(BinaryReader reader)
		{
			byte[] array = reader.ReadBytes(4);
			Require(array.Length == 4, "Unexpected end of MIDI file.");
			return (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3];
		}

		private static void Require(bool condition, string message)
		{
			if (!condition)
			{
				throw new InvalidDataException(message);
			}
		}
	}
	public sealed class MidiNoteEvent
	{
		public int MidiNote { get; }

		public int Channel { get; }

		public double StartSeconds { get; }

		public double DurationSeconds { get; }

		public int Velocity { get; }

		public MidiNoteEvent(int midiNote, int channel, double startSeconds, double durationSeconds, int velocity)
		{
			MidiNote = midiNote;
			Channel = channel;
			StartSeconds = startSeconds;
			DurationSeconds = durationSeconds;
			Velocity = velocity;
		}
	}
	public static class MidiNoteMapper
	{
		private static readonly string[] NoteNames = new string[12]
		{
			"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A",
			"A#", "B"
		};

		public static NoteDefinition MapToLyreNote(int midiNote)
		{
			int i;
			for (i = midiNote; i < 50; i += 12)
			{
			}
			while (i > 74)
			{
				i -= 12;
			}
			i = Math.Max(50, Math.Min(74, i));
			int num = i / 12 - 1;
			string name = $"{NoteNames[i % 12]}{num}";
			return new NoteDefinition(0, name, GetFrequency(i));
		}

		private static float GetFrequency(int midiNote)
		{
			return (float)(440.0 * Math.Pow(2.0, (double)(midiNote - 69) / 12.0));
		}
	}
	public sealed class MidiPlaybackSession
	{
		private const double MinSpeedMultiplier = 0.25;

		private const double MaxSpeedMultiplier = 3.0;

		private MidiSong? _song;

		private double _startTimeSeconds;

		private double _baseSongSeconds;

		private int _nextEventIndex;

		public bool IsPlaying => _song != null;

		public MidiSong? CurrentSong => _song;

		public double SpeedMultiplier { get; private set; } = 1.0;


		public double ElapsedSongSeconds { get; private set; }

		public bool LoopEnabled { get; private set; }

		public void Start(MidiSong song, double startTimeSeconds)
		{
			_song = song;
			_startTimeSeconds = startTimeSeconds;
			_baseSongSeconds = 0.0;
			ElapsedSongSeconds = 0.0;
			_nextEventIndex = 0;
		}

		public void Stop()
		{
			_song = null;
			_nextEventIndex = 0;
			_startTimeSeconds = 0.0;
			_baseSongSeconds = 0.0;
			ElapsedSongSeconds = 0.0;
		}

		public IReadOnlyList<MidiNoteEvent> CollectDueNotes(double nowSeconds)
		{
			if (_song == null)
			{
				return Array.Empty<MidiNoteEvent>();
			}
			double num = GetElapsedSongSeconds(nowSeconds);
			if (_song.LengthSeconds > 0.0 && LoopEnabled)
			{
				while (num > _song.LengthSeconds)
				{
					num -= _song.LengthSeconds;
					_baseSongSeconds -= _song.LengthSeconds;
					_nextEventIndex = 0;
				}
			}
			ElapsedSongSeconds = num;
			List<MidiNoteEvent> list = new List<MidiNoteEvent>();
			while (_nextEventIndex < _song.Events.Count && _song.Events[_nextEventIndex].StartSeconds <= num)
			{
				list.Add(_song.Events[_nextEventIndex]);
				_nextEventIndex++;
			}
			if (_nextEventIndex >= _song.Events.Count && num > _song.LengthSeconds)
			{
				Stop();
			}
			return list;
		}

		public void SetSpeed(double speedMultiplier, double? nowSeconds = null)
		{
			double speedMultiplier2 = Math.Max(0.25, Math.Min(3.0, speedMultiplier));
			if (_song != null && nowSeconds.HasValue)
			{
				_baseSongSeconds = GetElapsedSongSeconds(nowSeconds.Value);
				_startTimeSeconds = nowSeconds.Value;
				ElapsedSongSeconds = _baseSongSeconds;
			}
			SpeedMultiplier = speedMultiplier2;
		}

		public void SetLoopEnabled(bool loopEnabled)
		{
			LoopEnabled = loopEnabled;
		}

		public bool ToggleLoopEnabled()
		{
			LoopEnabled = !LoopEnabled;
			return LoopEnabled;
		}

		private double GetElapsedSongSeconds(double nowSeconds)
		{
			return _baseSongSeconds + (nowSeconds - _startTimeSeconds) * SpeedMultiplier;
		}
	}
	public sealed class MidiSong
	{
		public string DisplayName { get; }

		public string? SourcePath { get; }

		public IReadOnlyList<MidiNoteEvent> Events { get; }

		public double LengthSeconds { get; }

		public MidiSong(string displayName, IEnumerable<MidiNoteEvent> events, string? sourcePath = null)
		{
			DisplayName = displayName;
			SourcePath = sourcePath;
			Events = events.OrderBy((MidiNoteEvent item) => item.StartSeconds).ToArray();
			LengthSeconds = ((Events.Count == 0) ? 0.0 : Events.Max((MidiNoteEvent item) => item.StartSeconds + item.DurationSeconds));
		}
	}
	public sealed class MidiSongController
	{
		private readonly MidiSongLibrary _library;

		private readonly MidiPlaybackSession _session = new MidiPlaybackSession();

		private readonly LocalNotePlayback _playback;

		private readonly LocalDrumPlayback _drumPlayback;

		private readonly LocalFlutePlayback _flutePlayback;

		private readonly FluteMidiRateLimiter _fluteMidiRateLimiter = new FluteMidiRateLimiter();

		private readonly ILyreNetworkNoteEmitter _noteEmitter;

		private readonly PlayerFeedback _feedback;

		private readonly ManualLogSource _logger;

		private readonly GUIStyle _mutedStyle = new GUIStyle();

		private readonly GUIStyle _selectedStyle = new GUIStyle();

		private readonly GUIStyle _playingStyle = new GUIStyle();

		private MidiSongProfile _profile = MidiSongProfile.Default;

		private int _lastLoadedCount = -1;

		private string _activeInstrumentId = "lyre";

		private MidiSong? _drumAnalysisSong;

		private bool _drumAnalysisHasMappedPercussion;

		private bool _stylesInitialized;

		public bool IsPlaying => _session.IsPlaying;

		public MidiSong? CurrentSong => _session.CurrentSong;

		public MidiSongLibrary Library => _library;

		public MidiSongController(LocalNotePlayback playback, PlayerFeedback feedback, ManualLogSource logger)
			: this(Path.Combine(Paths.PluginPath, "Bardheim", "songs"), playback, new LocalDrumPlayback(logger), new LocalFlutePlayback(logger), DisabledLyreNetworkNoteEmitter.Instance, feedback, logger)
		{
		}

		public MidiSongController(string songsDirectory, LocalNotePlayback playback, LocalDrumPlayback drumPlayback, LocalFlutePlayback flutePlayback, ILyreNetworkNoteEmitter noteEmitter, PlayerFeedback feedback, ManualLogSource logger)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			_library = new MidiSongLibrary(songsDirectory);
			_playback = playback;
			_drumPlayback = drumPlayback;
			_flutePlayback = flutePlayback;
			_noteEmitter = noteEmitter;
			_feedback = feedback;
			_logger = logger;
			RefreshSongs();
		}

		public void Update(Player player, string? activeInstrumentId, int maxSimultaneousNotes, int maxFluteMidiNotesPerSecond, float volume)
		{
			_activeInstrumentId = activeInstrumentId ?? "lyre";
			if (Input.GetKeyDown((KeyCode)111))
			{
				TogglePlayback(maxFluteMidiNotesPerSecond);
			}
			if (Input.GetKeyDown((KeyCode)91))
			{
				SelectPrevious();
			}
			if (Input.GetKeyDown((KeyCode)93))
			{
				SelectNext();
			}
			if (Input.GetKeyDown((KeyCode)45))
			{
				ChangeSpeed(-0.25);
			}
			if (Input.GetKeyDown((KeyCode)61))
			{
				ChangeSpeed(0.25);
			}
			if (Input.GetKeyDown((KeyCode)48))
			{
				ResetSpeed();
			}
			if (Input.GetKeyDown((KeyCode)108))
			{
				ToggleLoop();
			}
			IReadOnlyList<MidiNoteEvent> readOnlyList = _session.CollectDueNotes(Time.timeAsDouble);
			if (activeInstrumentId == "flute")
			{
				IEnumerable<MidiNoteEvent> dueNotes = FluteMidiArranger.SelectPlayableEvents(readOnlyList, _profile);
				{
					foreach (MidiNoteEvent item in _fluteMidiRateLimiter.SelectPlayableEvents(dueNotes, _profile, maxFluteMidiNotesPerSecond))
					{
						PlayFluteMidiEvent(player, item, maxSimultaneousNotes, volume);
					}
					return;
				}
			}
			foreach (MidiNoteEvent item2 in readOnlyList)
			{
				if (activeInstrumentId == "lyre")
				{
					if (_profile.Allows(item2))
					{
						PlayLyreMidiEvent(player, item2, maxSimultaneousNotes, volume);
					}
				}
				else if (activeInstrumentId == "drums")
				{
					PlayDrumMidiEvent(player, item2, maxSimultaneousNotes, volume);
				}
			}
		}

		public void StopIfPlaying()
		{
			if (_session.IsPlaying)
			{
				_session.Stop();
				_fluteMidiRateLimiter.Reset();
				_feedback.Center(GetSongLabel() + " stopped.");
			}
		}

		public void DrawHud(bool visible)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!visible)
			{
				return;
			}
			EnsureStyles();
			int num = Mathf.Min(10, Mathf.Max(1, _library.Songs.Count));
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(24f, 100f, 520f, 142f + (float)num * 24f);
			GUI.Box(val, string.Empty);
			GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 14f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 28f, ((Rect)(ref val)).height - 20f));
			GUILayout.Label(GetHudTitle(), _playingStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(GetStatusLine(), Array.Empty<GUILayoutOption>());
			GUILayout.Label("O play/stop    [ previous    ] next    - slower    = faster    0 reset    L loop", _mutedStyle, Array.Empty<GUILayoutOption>());
			GUILayout.Space(4f);
			if (_library.Songs.Count == 0)
			{
				GUILayout.Label("No .mid files in Bardheim/songs for " + GetInstrumentName().ToLowerInvariant(), _mutedStyle, Array.Empty<GUILayoutOption>());
			}
			else
			{
				int num2 = Mathf.Max(0, Mathf.Min(_library.SelectedIndex - 4, Mathf.Max(0, _library.Songs.Count - num)));
				int num3 = Mathf.Min(_library.Songs.Count, num2 + num);
				for (int i = num2; i < num3; i++)
				{
					MidiSong midiSong = _library.Songs[i];
					bool flag = i == _library.SelectedIndex;
					bool flag2 = _session.CurrentSong == midiSong && _session.IsPlaying;
					string arg = (flag2 ? ">" : (flag ? "*" : " "));
					GUIStyle val2 = (GUIStyle)(flag2 ? _playingStyle : (flag ? ((object)_selectedStyle) : ((object)GUI.skin.label)));
					GUILayout.Label($"{arg} {i + 1:00}. {midiSong.DisplayName}", val2, Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.EndArea();
		}

		private void TogglePlayback(int maxFluteMidiNotesPerSecond)
		{
			RefreshSongs();
			if (_session.IsPlaying)
			{
				_session.Stop();
				_fluteMidiRateLimiter.Reset();
				_feedback.Center(GetSongLabel() + " stopped.");
				return;
			}
			MidiSong selectedSong = _library.SelectedSong;
			if (selectedSong == null)
			{
				_feedback.Center("No " + GetInstrumentName().ToLowerInvariant() + " songs found.");
				return;
			}
			_profile = MidiSongProfile.LoadFor(selectedSong, _activeInstrumentId);
			if (_activeInstrumentId == "flute")
			{
				_logger.LogInfo((object)("Flute MIDI profile for " + selectedSong.DisplayName + ": " + _profile.DescribeChannelSelection()));
			}
			_session.Start(selectedSong, Time.timeAsDouble);
			_fluteMidiRateLimiter.Prepare(FluteMidiArranger.SelectPlayableEvents(selectedSong.Events, _profile), _profile, maxFluteMidiNotesPerSecond);
			_feedback.Center(GetSongLabel() + ": " + selectedSong.DisplayName);
		}

		private void SelectNext()
		{
			RefreshSongs();
			MidiSong song = _library.SelectNext();
			AnnounceSelection(song);
		}

		private void SelectPrevious()
		{
			RefreshSongs();
			MidiSong song = _library.SelectPrevious();
			AnnounceSelection(song);
		}

		private void AnnounceSelection(MidiSong? song)
		{
			if (song == null)
			{
				_feedback.Center("No " + GetInstrumentName().ToLowerInvariant() + " songs found.");
				return;
			}
			if (_session.IsPlaying)
			{
				_session.Stop();
				_fluteMidiRateLimiter.Reset();
			}
			_feedback.Center("Selected " + GetSongLabel().ToLowerInvariant() + ": " + song.DisplayName);
		}

		private void RefreshSongs()
		{
			_library.Refresh();
			if (_library.Songs.Count != _lastLoadedCount)
			{
				_lastLoadedCount = _library.Songs.Count;
				_logger.LogInfo((object)$"Loaded {_library.Songs.Count} MIDI song(s).");
			}
			foreach (string skippedFile in _library.SkippedFiles)
			{
				_logger.LogWarning((object)("Skipped unsupported MIDI file: " + Path.GetFileName(skippedFile)));
			}
		}

		private void ChangeSpeed(double delta)
		{
			_session.SetSpeed(_session.SpeedMultiplier + delta, Time.timeAsDouble);
			_feedback.Center($"{GetSongLabel()} speed: {_session.SpeedMultiplier:0.00}x");
		}

		private void ResetSpeed()
		{
			_session.SetSpeed(1.0, Time.timeAsDouble);
			_feedback.Center(GetSongLabel() + " speed: 1.00x");
		}

		private void ToggleLoop()
		{
			bool flag = _session.ToggleLoopEnabled();
			_feedback.Center(flag ? (GetSongLabel() + " loop on.") : (GetSongLabel() + " loop off."));
		}

		private void PlayLyreMidiEvent(Player player, MidiNoteEvent noteEvent, int maxSimultaneousNotes, float volume)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			NoteDefinition note = MidiNoteMapper.MapToLyreNote(_profile.ApplyOctaveOffset(noteEvent));
			NoteRequest request = new NoteRequest(note, ((Component)player).transform.position);
			if (_playback.Play(request, maxSimultaneousNotes, volume))
			{
				_noteEmitter.Emit(request, LyreNetworkNoteSource.Midi, volume);
			}
		}

		private void PlayDrumMidiEvent(Player player, MidiNoteEvent noteEvent, int maxSimultaneousHits, float volume)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if (((DrumMidiMapper.TryMap(noteEvent, out string hitId) && hitId != null) || (!CurrentDrumSongHasMappedPercussion() && DrumMidiMapper.TryMapMelodicFallback(noteEvent, out hitId))) && hitId != null)
			{
				float volume2 = volume * Mathf.Clamp01((float)noteEvent.Velocity / 127f);
				if (_drumPlayback.Play(hitId, ((Component)player).transform.position, maxSimultaneousHits, volume2))
				{
					_noteEmitter.Emit("drums", hitId, ((Component)player).transform.position, LyreNetworkNoteSource.Midi, volume2);
				}
			}
		}

		private void PlayFluteMidiEvent(Player player, MidiNoteEvent noteEvent, int maxSimultaneousNotes, float volume)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			NoteDefinition noteDefinition = FluteMidiMapper.MapToFluteNote(_profile.ApplyOctaveOffset(noteEvent));
			NoteRequest request = new NoteRequest(noteDefinition, ((Component)player).transform.position);
			if (_flutePlayback.Play(request, maxSimultaneousNotes, volume))
			{
				_noteEmitter.Emit("flute", noteDefinition.Name, ((Component)player).transform.position, LyreNetworkNoteSource.Midi, volume);
			}
		}

		private bool CurrentDrumSongHasMappedPercussion()
		{
			MidiSong currentSong = _session.CurrentSong;
			if (currentSong == null)
			{
				return false;
			}
			if (currentSong != _drumAnalysisSong)
			{
				_drumAnalysisSong = currentSong;
				_drumAnalysisHasMappedPercussion = DrumMidiMapper.HasMappedPercussion(currentSong.Events);
			}
			return _drumAnalysisHasMappedPercussion;
		}

		private string GetStatusLine()
		{
			string text = _library.SelectedSong?.DisplayName ?? "No song";
			string text2 = (_session.IsPlaying ? "Playing" : "Selected");
			string text3 = string.Empty;
			if (_session.CurrentSong != null && _session.CurrentSong.LengthSeconds > 0.0)
			{
				text3 = "  " + FormatSeconds(_session.ElapsedSongSeconds) + " / " + FormatSeconds(_session.CurrentSong.LengthSeconds);
			}
			string text4 = (_session.LoopEnabled ? "On" : "Off");
			return $"{text2}: {text}  Songs: {_library.Songs.Count}  Speed: {_session.SpeedMultiplier:0.00}x  Loop: {text4}{text3}";
		}

		private string GetHudTitle()
		{
			string activeInstrumentId = _activeInstrumentId;
			if (1 == 0)
			{
			}
			string result = ((activeInstrumentId == "drums") ? "DRUM SONGS" : ((!(activeInstrumentId == "flute")) ? "LYRE SONGS" : "FLUTE SONGS"));
			if (1 == 0)
			{
			}
			return result;
		}

		private string GetSongLabel()
		{
			string activeInstrumentId = _activeInstrumentId;
			if (1 == 0)
			{
			}
			string result = ((activeInstrumentId == "drums") ? "Drum song" : ((!(activeInstrumentId == "flute")) ? "Lyre song" : "Flute song"));
			if (1 == 0)
			{
			}
			return result;
		}

		private string GetInstrumentName()
		{
			string activeInstrumentId = _activeInstrumentId;
			if (1 == 0)
			{
			}
			string result = ((activeInstrumentId == "drums") ? "Drum" : ((!(activeInstrumentId == "flute")) ? "Lyre" : "Flute"));
			if (1 == 0)
			{
			}
			return result;
		}

		private void EnsureStyles()
		{
			//IL_0028: 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)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (!_stylesInitialized)
			{
				_mutedStyle.normal.textColor = new Color(0.72f, 0.72f, 0.72f);
				_selectedStyle.normal.textColor = new Color(1f, 0.88f, 0.48f);
				_playingStyle.normal.textColor = new Color(0.48f, 1f, 0.62f);
				_stylesInitialized = true;
			}
		}

		private static string FormatSeconds(double seconds)
		{
			int num = Mathf.Max(0, Mathf.FloorToInt((float)seconds));
			return $"{num / 60:00}:{num % 60:00}";
		}
	}
	public sealed class MidiSongLibrary
	{
		private readonly string _songsDirectory;

		private readonly List<MidiSong> _songs = new List<MidiSong>();

		private readonly List<string> _skippedFiles = new List<string>();

		public IReadOnlyList<MidiSong> Songs => _songs;

		public IReadOnlyList<string> SkippedFiles => _skippedFiles;

		public int SelectedIndex { get; private set; }

		public MidiSong? SelectedSong => (_songs.Count == 0) ? null : _songs[SelectedIndex];

		public MidiSongLibrary(string songsDirectory)
		{
			_songsDirectory = songsDirectory;
		}

		public void Refresh()
		{
			Directory.CreateDirectory(_songsDirectory);
			string selectedName = SelectedSong?.DisplayName;
			_songs.Clear();
			_skippedFiles.Clear();
			foreach (string item in Directory.GetFiles(_songsDirectory, "*.mid").OrderBy<string, string>(Path.GetFileName, StringComparer.OrdinalIgnoreCase))
			{
				try
				{
					_songs.Add(MidiFileParser.Parse(Path.GetFileName(item), File.ReadAllBytes(item), item));
				}
				catch
				{
					_skippedFiles.Add(item);
				}
			}
			if (_songs.Count == 0)
			{
				SelectedIndex = 0;
				return;
			}
			int num = ((selectedName == null) ? (-1) : _songs.FindIndex((MidiSong song) => string.Equals(song.DisplayName, selectedName, StringComparison.OrdinalIgnoreCase)));
			SelectedIndex = ((num >= 0) ? num : Math.Min(SelectedIndex, _songs.Count - 1));
		}

		public MidiSong? SelectNext()
		{
			if (_songs.Count == 0)
			{
				return null;
			}
			SelectedIndex = (SelectedIndex + 1) % _songs.Count;
			return SelectedSong;
		}

		public MidiSong? SelectPrevious()
		{
			if (_songs.Count == 0)
			{
				return null;
			}
			SelectedIndex = (SelectedIndex + _songs.Count - 1) % _songs.Count;
			return SelectedSong;
		}
	}
	public sealed class MidiSongProfile
	{
		private readonly HashSet<int> _activeChannels;

		private readonly HashSet<int> _mutedChannels;

		private readonly Dictionary<int, int> _channelOctaveOffsets;

		public static MidiSongProfile Default { get; } = new MidiSongProfile(new HashSet<int>(), new HashSet<int> { 10 }, new Dictionary<int, int>(), MidiArrangementMode.Full, int.MaxValue);


		public MidiArrangementMode ArrangementMode { get; }

		public int MaxSimultaneousMelodyNotes { get; }

		private MidiSongProfile(HashSet<int> activeChannels, HashSet<int> mutedChannels, Dictionary<int, int> channelOctaveOffsets, MidiArrangementMode arrangementMode, int maxSimultaneousMelodyNotes)
		{
			_activeChannels = activeChannels;
			_mutedChannels = mutedChannels;
			_channelOctaveOffsets = channelOctaveOffsets;
			ArrangementMode = arrangementMode;
			MaxSimultaneousMelodyNotes = maxSimultaneousMelodyNotes;
		}

		public string DescribeChannelSelection()
		{
			string text = ((_activeChannels.Count == 0) ? "all" : string.Join(",", _activeChannels.OrderBy((int channel) => channel)));
			string text2 = ((_mutedChannels.Count == 0) ? "none" : string.Join(",", _mutedChannels.OrderBy((int channel) => channel)));
			return $"activeChannels={text}; mutedChannels={text2}; arrangementMode={ArrangementMode}; maxSimultaneousMelodyNotes={MaxSimultaneousMelodyNotes}";
		}

		public static MidiSongProfile ForInstrument(string instrumentId)
		{
			return Default;
		}

		public static MidiSongProfile LoadFor(MidiSong song)
		{
			return LoadFor(song, "lyre");
		}

		public static MidiSongProfile LoadFor(MidiSong song, string instrumentId)
		{
			if (song.SourcePath == null)
			{
				return ForSong(instrumentId, song);
			}
			string path = song.SourcePath + "." + instrumentId + "-profile.json";
			if (!File.Exists(path))
			{
				return ForSong(instrumentId, song);
			}
			string json = File.ReadAllText(path);
			if (!ProfileMatchesInstrument(json, instrumentId))
			{
				return ForSong(instrumentId, song);
			}
			MidiSongProfile midiSongProfile = ForInstrument(instrumentId);
			return new MidiSongProfile(ParseChannelSet(json, "activeChannels", midiSongProfile._activeChannels), ParseChannelSet(json, "mutedChannels", midiSongProfile._mutedChannels), ParseOctaveOffsets(json), ParseArrangementMode(json, midiSongProfile.ArrangementMode), ParseInt(json, "maxSimultaneousMelodyNotes", midiSongProfile.MaxSimultaneousMelodyNotes));
		}

		private static MidiSongProfile ForSong(string instrumentId, MidiSong song)
		{
			return ForInstrument(instrumentId);
		}

		public bool Allows(MidiNoteEvent note)
		{
			int item = note.Channel + 1;
			if (_mutedChannels.Contains(item))
			{
				return false;
			}
			return _activeChannels.Count == 0 || _activeChannels.Contains(item);
		}

		public int ApplyOctaveOffset(MidiNoteEvent note)
		{
			int key = note.Channel + 1;
			int value;
			return note.MidiNote + (_channelOctaveOffsets.TryGetValue(key, out value) ? (value * 12) : 0);
		}

		private static HashSet<int> ParseChannelSet(string json, string propertyName, HashSet<int> fallback)
		{
			HashSet<int> hashSet = new HashSet<int>();
			Match match = Regex.Match(json, "\"" + propertyName + "\"\\s*:\\s*\\[(?<values>[^\\]]*)\\]");
			if (!match.Success)
			{
				return new HashSet<int>(fallback);
			}
			foreach (Match item in Regex.Matches(match.Groups["values"].Value, "\\d+"))
			{
				hashSet.Add(int.Parse(item.Value));
			}
			return hashSet;
		}

		private static Dictionary<int, int> ParseOctaveOffsets(string json)
		{
			Dictionary<int, int> dictionary = new Dictionary<int, int>();
			Match match = Regex.Match(json, "\"channelOctaveOffsets\"\\s*:\\s*\\{(?<values>[^}]*)\\}");
			if (!match.Success)
			{
				return dictionary;
			}
			foreach (Match item in Regex.Matches(match.Groups["values"].Value, "\"(?<channel>\\d+)\"\\s*:\\s*(?<offset>-?\\d+)"))
			{
				dictionary[int.Parse(item.Groups["channel"].Value)] = int.Parse(item.Groups["offset"].Value);
			}
			return dictionary;
		}

		private static MidiArrangementMode ParseArrangementMode(string json, MidiArrangementMode fallback)
		{
			Match match = Regex.Match(json, "\"arrangementMode\"\\s*:\\s*\"(?<value>[^\"]+)\"");
			if (!match.Success)
			{
				return fallback;
			}
			return (!(match.Groups["value"].Value == "Full")) ? MidiArrangementMode.Melody : MidiArrangementMode.Full;
		}

		private static int ParseInt(string json, string propertyName, int fallback)
		{
			Match match = Regex.Match(json, "\"" + propertyName + "\"\\s*:\\s*(?<value>\\d+)");
			return match.Success ? int.Parse(match.Groups["value"].Value) : fallback;
		}

		private static bool ProfileMatchesInstrument(string json, string instrumentId)
		{
			Match match = Regex.Match(json, "\"instrumentId\"\\s*:\\s*\"(?<value>[^\"]+)\"");
			return !match.Success || match.Groups["value"].Value == instrumentId;
		}
	}
}
namespace Bardheim.PlayMode
{
	public sealed class LyreInputRouter
	{
		private const string CycleButtonName = "Bardheim_CycleNoteSet";

		private const string HotbarButtonPrefix = "Hotbar";

		private static readonly KeyCode[] NoteKeys;

		private readonly NoteSetSelector _noteSetSelector;

		private readonly LyrePlayMode _playMode;

		private readonly LocalNotePlayback _playback;

		private readonly LocalDrumPlayback _drumPlayback;

		private readonly LocalFlutePlayback _flutePlayback;

		private readonly ILyreNetworkNoteEmitter _noteEmitter;

		private readonly PlayerFeedback _feedback;

		private readonly ManualLogSource _logger;

		private bool _registered;

		public LyreInputRouter(NoteSetSelector noteSetSelector, LyrePlayMode playMode, LocalNotePlayback playback, LocalDrumPlayback drumPlayback, LocalFlutePlayback flutePlayback, ILyreNetworkNoteEmitter noteEmitter, PlayerFeedback feedback, ManualLogSource logger)
		{
			_noteSetSelector = noteSetSelector;
			_playMode = playMode;
			_playback = playback;
			_drumPlayback = drumPlayback;
			_flutePlayback = flutePlayback;
			_noteEmitter = noteEmitter;
			_feedback = feedback;
			_logger = logger;
		}

		public void RegisterButtons()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			if (!_registered)
			{
				for (int i = 0; i < NoteKeys.Length; i++)
				{
					int num = i + 1;
					InputManager.Instance.AddButton("com.valheimmodlab.bardheim", new ButtonConfig
					{
						Name = GetButtonName(num),
						Key = NoteKeys[i],
						Hint = $"Lyre note {num}",
						BlockOtherInputs = true,
						ActiveInGUI = false
					});
				}
				InputManager.Instance.AddButton("com.valheimmodlab.bardheim", new ButtonConfig
				{
					Name = "Bardheim_CycleNoteSet",
					Key = (KeyCode)57,
					Hint = "Cycle lyre tuning",
					BlockOtherInputs = true,
					ActiveInGUI = false
				});
				_registered = true;
			}
		}

		public void Update(Player player, string? activeInstrumentId, int maxSimultaneousNotes, float volume)
		{
			if (!_playMode.IsActive || player == null || (activeInstrumentId == "lyre" && TryCycleNoteSet()))
			{
				return;
			}
			for (int i = 1; i <= NoteKeys.Length; i++)
			{
				bool flag = ZInput.GetButtonDown(GetButtonName(i)) || ZInput.GetButtonDown(GetHotbarButtonName(i)) || ZInput.GetKeyDown(NoteKeys[i - 1], false);
				ZInput.ResetButtonStatus(GetHotbarButtonName(i));
				if (flag)
				{
					switch (activeInstrumentId)
					{
					case "lyre":
						PlayLyreSlot(player, i, maxSimultaneousNotes, volume);
						break;
					case "drums":
						PlayDrumSlot(player, i, maxSimultaneousNotes, volume);
						break;
					case "flute":
						PlayFluteSlot(player, i, maxSimultaneousNotes, volume);
						break;
					}
				}
			}
		}

		private void PlayLyreSlot(Player player, int slot, int maxSimultaneousNotes, float volume)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (_noteSetSelector.Current.Map.TryGet(slot, out NoteDefinition note))
			{
				NoteRequest request = new NoteRequest(note, ((Component)player).transform.position);
				if (_playback.Play(request, maxSimultaneousNotes, volume))
				{
					_noteEmitter.Emit(request, LyreNetworkNoteSource.Manual, volume);
					_logger.LogInfo((object)$"Played lyre note {note.Name} from slot {slot}.");
				}
			}
		}

		private void PlayDrumSlot(Player player, int slot, int maxSimultaneousHits, float volume)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			if (1 == 0)
			{
			}
			string text = slot switch
			{
				1 => "kick", 
				2 => "snare", 
				3 => "rim", 
				4 => "clap", 
				5 => "muted", 
				6 => "open", 
				7 => "low_tom", 
				8 => "high_tom", 
				_ => null, 
			};
			if (1 == 0)
			{
			}
			string text2 = text;
			if (text2 != null && _drumPlayback.Play(text2, ((Component)player).transform.position, maxSimultaneousHits, volume))
			{
				_noteEmitter.Emit("drums", text2, ((Component)player).transform.position, LyreNetworkNoteSource.Manual, volume);
				_logger.LogInfo((object)$"Played drum hit {text2} from slot {slot}.");
			}
		}

		private void PlayFluteSlot(Player player, int slot, int maxSimultaneousNotes, float volume)
		{
			//IL_003b: 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)
			InstrumentDefinition instrumentDefinition = InstrumentCatalog.CreateDefaultInstruments()[2];
			NoteSet noteSet = instrumentDefinition.NoteSets[0];
			if (noteSet.Map.TryGet(slot, out NoteDefinition note))
			{
				NoteRequest request = new NoteRequest(note, ((Component)player).transform.position);
				if (_flutePlayback.Play(request, maxSimultaneousNotes, volume))
				{
					_noteEmitter.Emit("flute", note.Name, ((Component)player).transform.position, LyreNetworkNoteSource.Manual, volume);
					_logger.LogInfo((object)$"Played flute note {note.Name} from slot {slot}.");
				}
			}
		}

		private bool TryCycleNoteSet()
		{
			bool flag = ZInput.GetButtonDown("Bardheim_CycleNoteSet") || ZInput.GetButtonDown(GetHotbarButtonName(9)) || ZInput.GetKeyDown((KeyCode)57, false);
			ZInput.ResetButtonStatus(GetHotbarButtonName(9));
			if (!flag)
			{
				return false;
			}
			NoteSet noteSet = _noteSetSelector.CycleNext();
			_feedback.Center("Lyre tuning: " + noteSet.Name);
			return true;
		}

		private static string GetButtonName(int slot)
		{
			return $"Bardheim_Note{slot}";
		}

		private static string GetHotbarButtonName(int slot)
		{
			return string.Format("{0}{1}", "Hotbar", slot);
		}

		static LyreInputRouter()
		{
			KeyCode[] array = new KeyCode[8];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			NoteKeys = (KeyCode[])(object)array;
		}
	}
	public sealed class LyrePlayMode
	{
		public bool IsActive { get; private set; }

		public bool Toggle(bool canPlay)
		{
			if (!canPlay)
			{
				return DisableIfActive();
			}
			IsActive = !IsActive;
			return true;
		}

		public bool UpdateEligibility(bool canPlay)
		{
			return !canPlay && DisableIfActive();
		}

		public bool DisableIfActive()
		{
			if (!IsActive)
			{
				return false;
			}
			IsActive = false;
			return true;
		}
	}
}
namespace Bardheim.Notes
{
	public sealed class NoteDefinition
	{
		public int Slot { get; }

		public string Name { get; }

		public float FrequencyHz { get; }

		public NoteDefinition(int slot, string name, float frequencyHz)
		{
			Slot = slot;
			Name = name;
			FrequencyHz = frequencyHz;
		}
	}
	public sealed class NoteMap
	{
		private readonly Dictionary<int, NoteDefinition> _notesBySlot;

		public IReadOnlyList<NoteDefinition> Notes { get; }

		public NoteMap(IEnumerable<NoteDefinition> notes)
		{
			_notesBySlot = new Dictionary<int, NoteDefinition>();
			List<NoteDefinition> list = new List<NoteDefinition>();
			foreach (NoteDefinition note in notes)
			{
				if (note.Slot < 1)
				{
					throw new ArgumentOutOfRangeException("notes", "Note slots must be positive.");
				}
				_notesBySlot.Add(note.Slot, note);
				list.Add(note);
			}
			Notes = new ReadOnlyCollection<NoteDefinition>(list);
		}

		public bool TryGet(int slot, out NoteDefinition note)
		{
			return _notesBySlot.TryGetValue(slot, out note);
		}

		public NoteDefinition GetRequired(int slot)
		{
			if (!TryGet(slot, out NoteDefinition note))
			{
				throw new KeyNotFoundException($"No lyre note is mapped to slot {slot}.");
			}
			return note;
		}
	}
	public sealed class NoteRequest
	{
		public NoteDefinition Note { get; }

		public Vector3 Position { get; }

		public NoteRequest(NoteDefinition note, Vector3 position)
		{
			//IL_0010: 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)
			Note = note;
			Position = position;
		}
	}
	public sealed class NoteSet
	{
		public string Name { get; }

		public NoteMap Map { get; }

		public NoteSet(string name, NoteMap map)
		{
			Name = name;
			Map = map;
		}
	}
	public static class NoteSetCatalog
	{
		public static IReadOnlyList<NoteSet> CreateDefaultSets()
		{
			return new NoteSet[3]
			{
				Create("D minor pentatonic", ("D3", 146.83f), ("F3", 174.61f), ("G3", 196f), ("A3", 220f), ("C4", 261.63f), ("D4", 293.66f), ("F4", 349.23f), ("G4", 392f)),
				Create("D natural minor", ("D3", 146.83f), ("E3", 164.81f), ("F3", 174.61f), ("G3", 196f), ("A3", 220f), ("Bb3", 233.08f), ("C4", 261.63f), ("D4", 293.66f)),
				Create("D folk/dorian", ("D3", 146.83f), ("E3", 164.81f), ("F3", 174.61f), ("G3", 196f), ("A3", 220f), ("C4", 261.63f), ("D4", 293.66f), ("E4", 329.63f))
			};
		}

		public static NoteSet Create(string name, params (string Name, float FrequencyHz)[] notes)
		{
			NoteDefinition[] array = new NoteDefinition[notes.Length];
			for (int i = 0; i < notes.Length; i++)
			{
				array[i] = new NoteDefinition(i + 1, notes[i].Name, notes[i].FrequencyHz);
			}
			return new NoteSet(name, new NoteMap(array));
		}
	}
	public sealed class NoteSetSelector
	{
		private readonly IReadOnlyList<NoteSet> _sets;

		private int _index;

		public NoteSet Current { get; private set; }

		public NoteSetSelector(IReadOnlyList<NoteSet> sets)
		{
			if (sets.Count == 0)
			{
				throw new ArgumentException("At least one note set is required.", "sets");
			}
			_sets = sets;
			Current = _sets[0];
		}

		public NoteSet CycleNext()
		{
			_index = (_index + 1) % _sets.Count;
			Current = _sets[_index];
			return Current;
		}
	}
}
namespace Bardheim.Network
{
	public sealed class DisabledLyreNetworkNoteEmitter : ILyreNetworkNoteEmitter
	{
		public static readonly DisabledLyreNetworkNoteEmitter Instance = new DisabledLyreNetworkNoteEmitter();

		private DisabledLyreNetworkNoteEmitter()
		{
		}

		public void Emit(NoteRequest request, LyreNetworkNoteSource source, float volume)
		{
		}

		public void Emit(string instrumentId, string eventName, Vector3 position, LyreNetworkNoteSource source, float volume)
		{
		}
	}
	public interface ILyreNetworkNoteEmitter
	{
		void Emit(NoteRequest request, LyreNetworkNoteSource source, float volume);

		void Emit(string instrumentId, string eventName, Vector3 position, LyreNetworkNoteSource source, float volume);
	}
	public interface ILyreNetworkNoteTransport
	{
		void Send(byte[] payload);
	}
	public sealed class LyreNetworkNoteEmitter : ILyreNetworkNoteEmitter
	{
		private readonly ILyreNetworkNoteTransport _transport;

		private long _nextEventId;

		public LyreNetworkNoteEmitter(ILyreNetworkNoteTransport transport)
		{
			_transport = transport;
		}

		public void Emit(NoteRequest request, LyreNetworkNoteSource source, float volume)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			Emit("lyre", request.Note.Name, request.Position, source, volume);
		}

		public void Emit(string instrumentId, string eventName, Vector3 position, LyreNetworkNoteSource source, float volume)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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)
			LyreNetworkNoteEvent noteEvent = new LyreNetworkNoteEvent(Interlocked.Increment(ref _nextEventId), instrumentId, eventName, new LyreNetworkPosition(position.x, position.y, position.z), volume, source);
			byte[] payload = LyreNetworkNoteSerializer.Serialize(noteEvent);
			_transport.Send(payload);
		}
	}
	public sealed class LyreNetworkNoteEvent
	{
		public long EventId { get; }

		public string InstrumentId { get; }

		public string NoteName { get; }

		public LyreNetworkPosition Position { get; }

		public float Volume { get; }

		public LyreNetworkNoteSource Source { get; }

		public LyreNetworkNoteEvent(long eventId, string noteName, LyreNetworkPosition position, float volume, LyreNetworkNoteSource source)
			: this(eventId, "lyre", noteName, position, volume, source)
		{
		}

		public LyreNetworkNoteEvent(long eventId, string instrumentId, string noteName, LyreNetworkPosition position, float volume, LyreNetworkNoteSource source)
		{
			EventId = eventId;
			InstrumentId = instrumentId ?? throw new ArgumentNullException("instrumentId");
			NoteName = noteName ?? throw new ArgumentNullException("noteName");
			Position = position;
			Volume = volume;
			Source = source;
		}
	}
	public enum LyreNetworkNoteRejectReason
	{
		None,
		SelfOriginated,
		UnknownNote,
		InvalidPayload,
		Duplicate,
		RateLimited
	}
	public static class LyreNetworkNoteSerializer
	{
		public const int CurrentVersion = 2;

		public static byte[] Serialize(LyreNetworkNoteEvent noteEvent)
		{
			if (noteEvent == null)
			{
				throw new ArgumentNullException("noteEvent");
			}
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8);
			binaryWriter.Write(2);
			binaryWriter.Write(noteEvent.EventId);
			binaryWriter.Write(noteEvent.InstrumentId);
			binaryWriter.Write(noteEvent.NoteName);
			binaryWriter.Write(noteEvent.Position.X);
			binaryWriter.Write(noteEvent.Position.Y);
			binaryWriter.Write(noteEvent.Position.Z);
			binaryWriter.Write(noteEvent.Volume);
			binaryWriter.Write((byte)noteEvent.Source);
			binaryWriter.Flush();
			return memoryStream.ToArray();
		}

		public static bool TryDeserialize(byte[]? bytes, out LyreNetworkNoteEvent noteEvent)
		{
			noteEvent = new LyreNetworkNoteEvent(0L, "lyre", string.Empty, LyreNetworkPosition.Zero, 0f, LyreNetworkNoteSource.Manual);
			if (bytes == null || bytes.Length == 0)
			{
				return false;
			}
			try
			{
				using MemoryStream input = new MemoryStream(bytes);
				using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8);
				switch (binaryReader.ReadInt32())
				{
				case 1:
					return TryReadVersionOne(binaryReader, out noteEvent);
				default:
					return false;
				case 2:
				{
					long eventId = binaryReader.ReadInt64();
					string text = binaryReader.ReadString();
					string text2 = binaryReader.ReadString();
					LyreNetworkPosition position = new LyreNetworkPosition(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
					float volume = binaryReader.ReadSingle();
					LyreNetworkNoteSource source = (LyreNetworkNoteSource)binaryReader.ReadByte();
					if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(text2))
					{
						return false;
					}
					noteEvent = new LyreNetworkNoteEvent(eventId, text, text2, position, volume, source);
					return true;
				}
				}
			}
			catch (EndOfStreamException)
			{
				return false;
			}
			catch (IOException)
			{
				return false;
			}
		}

		private static bool TryReadVersionOne(BinaryReader reader, out LyreNetworkNoteEvent noteEvent)
		{
			long eventId = reader.ReadInt64();
			string text = reader.ReadString();
			LyreNetworkPosition position = new LyreNetworkPosition(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
			float volume = reader.ReadSingle();
			LyreNetworkNoteSource source = (LyreNetworkNoteSource)reader.ReadByte();
			if (string.IsNullOrWhiteSpace(text))
			{
				noteEvent = new LyreNetworkNoteEvent(0L, "lyre", string.Empty, LyreNetworkPosition.Zero, 0f, LyreNetworkNoteSource.Manual);
				return false;
			}
			noteEvent = new LyreNetworkNoteEvent(eventId, "lyre", text, position, volume, source);
			return true;
		}
	}
	public enum LyreNetworkNoteSource : byte
	{
		Manual = 1,
		Midi
	}
	public static class LyreNetworkNoteValidator
	{
		public static bool TryValidate(LyreNetworkNoteEvent noteEvent, long senderPeerId, long localPeerId, ISet<string> validNoteNames, out LyreNetworkNoteRejectReason reason)
		{
			Dictionary<string, ISet<string>> validEventNamesByInstrument = new Dictionary<string, ISet<string>>(StringComparer.Ordinal) { ["lyre"] = validNoteNames };
			return TryValidate(noteEvent, senderPeerId, localPeerId, validEventNamesByInstrument, out reason);
		}

		public static bool TryValidate(LyreNetworkNoteEvent noteEvent, long senderPeerId, long localPeerId, IReadOnlyDictionary<string, ISet<string>> validEventNamesByInstrument, out LyreNetworkNoteRejectReason reason)
		{
			if (senderPeerId == localPeerId)
			{
				reason = LyreNetworkNoteRejectReason.SelfOriginated;
				return false;
			}
			if (validEventNamesByInstrument == null || !validEventNamesByInstrument.TryGetValue(noteEvent.InstrumentId, out ISet<string> value) || value == null || !value.Contains(noteEvent.NoteName))
			{
				reason = LyreNetworkNoteRejectReason.UnknownNote;
				return false;
			}
			if (noteEvent.EventId <= 0 || noteEvent.Volume < 0f || float.IsNaN(noteEvent.Volume) || float.IsInfinity(noteEvent.Volume) || !Enum.IsDefined(typeof(LyreNetworkNoteSource), noteEvent.Source))
			{
				reason = LyreNetworkNoteRejectReason.InvalidPayload;
				return false;
			}
			reason = LyreNetworkNoteRejectReason.None;
			return true;
		}
	}
	public readonly struct LyreNetworkPosition
	{
		public static readonly LyreNetworkPosition Zero = new LyreNetworkPosition(0f, 0f, 0f);

		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public LyreNetworkPosition(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}
	public sealed class LyreRemoteNoteIntakeGate
	{
		private sealed class SenderState
		{
			public HashSet<long> SeenEventIds { get; } = new HashSet<long>();


			public Queue<double> RecentEventTimes { get; } = new Queue<double>();

		}

		private const double RateWindowSeconds = 1.0;

		private readonly Dictionary<long, SenderState> _statesBySender = new Dictionary<long, SenderState>();

		private readonly int _maxEventsPerSecond;

		public LyreRemoteNoteIntakeGate(int maxEventsPerSecond)
		{
			if (maxEventsPerSecond < 1)
			{
				throw new ArgumentOutOfRangeException("maxEventsPerSecond", "Remote rate limit must allow at least one event per second.");
			}
			_maxEventsPerSecond = maxEventsPerSecond;
		}

		public bool TryAccept(long senderPeerId, LyreNetworkNoteEvent noteEvent, double nowSeconds, out LyreNetworkNoteRejectReason reason)
		{
			SenderState senderState = GetSenderState(senderPeerId);
			if (senderState.SeenEventIds.Contains(noteEvent.EventId))
			{
				reason = LyreNetworkNoteRejectReason.Duplicate;
				return false;
			}
			PruneOldEvents(senderState, nowSeconds);
			if (senderState.RecentEventTimes.Count >= _maxEventsPerSecond)
			{
				reason = LyreNetworkNoteRejectReason.RateLimited;
				return false;
			}
			senderState.SeenEventIds.Add(noteEvent.EventId);
			senderState.RecentEventTimes.Enqueue(nowSeconds);
			reason = LyreNetworkNoteRejectReason.None;
			return true;
		}

		private SenderState GetSenderState(long senderPeerId)
		{
			if (_statesBySender.TryGetValue(senderPeerId, out SenderState value))
			{
				return value;
			}
			value = new SenderState();
			_statesBySender.Add(senderPeerId, value);
			return value;
		}

		private static void PruneOldEvents(SenderState state, double nowSeconds)
		{
			while (state.RecentEventTimes.Count > 0 && state.RecentEventTimes.Peek() <= nowSeconds - 1.0)
			{
				state.RecentEventTimes.Dequeue();
			}
		}
	}
	public sealed class ValheimRoutedLyreNetwork : ILyreNetworkNoteEmitter, ILyreNetworkNoteTransport
	{
		private const string RpcName = "Bardheim_NoteEvent";

		private readonly LyreNetworkNoteEmitter _emitter;

		private readonly Dictionary<string, ISet<string>> _validEventNamesByInstrument;

		private readonly LyreRemoteNoteIntakeGate _intakeGate;

		private readonly RemoteNotePlayback _remotePlayback;

		private readonly RemoteDrumPlayback _remoteDrumPlayback;

		private readonly RemoteFlutePlayback _remoteFlutePlayback;

		private readonly ManualLogSource _logger;

		private readonly bool _multiplayerEnabled;

		private readonly float _remoteVolume;

		private readonly int _remoteMaxSimultaneousNotes;

		private readonly float _remoteAudibleDistance;

		private bool _registered;

		public ValheimRoutedLyreNetwork(RemoteNotePlayback remotePlayback, RemoteDrumPlayback remoteDrumPlayback, RemoteFlutePlayback remoteFlutePlayback, ManualLogSource logger, bool multiplayerEnabled, float remoteVolume, int remoteMaxSimultaneousNotes, int remoteMaxNoteEventsPerSecond, float remoteAudibleDistance)
		{
			_emitter = new LyreNetworkNoteEmitter(this);
			_validEventNamesByInstrument = BuildValidEventNamesByInstrument();
			_intakeGate = new LyreRemoteNoteIntakeGate(remoteMaxNoteEventsPerSecond);
			_remotePlayback = remotePlayback;
			_remoteDrumPlayback = remoteDrumPlayback;
			_remoteFlutePlayback = remoteFlutePlayback;
			_logger = logger;
			_multiplayerEnabled = multiplayerEnabled;
			_remoteVolume = remoteVolume;
			_remoteMaxSimultaneousNotes = remoteMaxSimultaneousNotes;
			_remoteAudibleDistance = remoteAudibleDistance;
		}

		public void RegisterReceiver()
		{
			if (!_multiplayerEnabled)
			{
				_logger.LogInfo((object)"Lyre multiplayer audio is disabled by config.");
			}
			else if (!_registered)
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance == null)
				{
					_logger.LogWarning((object)"Lyre multiplayer audio RPC registration skipped because ZRoutedRpc is not ready.");
					return;
				}
				instance.Register<ZPackage>("Bardheim_NoteEvent", (Action<long, ZPackage>)OnRoutedNote);
				_registered = true;
				_logger.LogInfo((object)"Lyre multiplayer audio RPC receiver registered.");
			}
		}

		public void Emit(NoteRequest request, LyreNetworkNoteSource source, float volume)
		{
			_emitter.Emit(request, source, volume);
		}

		public void Emit(string instrumentId, string eventName, Vector3 position, LyreNetworkNoteSource source, float volume)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			_emitter.Emit(instrumentId, eventName, position, source, volume);
		}

		public void Send(byte[] payload)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (_multiplayerEnabled)
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					ZPackage val = new ZPackage();
					val.Write(payload);
					instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "Bardheim_NoteEvent", new object[1] { val });
				}
			}
		}

		private void OnRoutedNote(long senderPeerId, ZPackage package)
		{
			if (!_multiplayerEnabled || package == null)
			{
				return;
			}
			byte[] bytes = package.ReadByteArray();
			if (!LyreNetworkNoteSerializer.TryDeserialize(bytes, out LyreNetworkNoteEvent noteEvent))
			{
				_logger.LogDebug((object)"Skipped lyre multiplayer note because the payload was invalid.");
				return;
			}
			long uID = ZNet.GetUID();
			if (!LyreNetworkNoteValidator.TryValidate(noteEvent, senderPeerId, uID, _validEventNamesByInstrument, out var reason))
			{
				LogRejected(reason);
			}
			else if (!_intakeGate.TryAccept(senderPeerId, noteEvent, Time.timeAsDouble, out reason))
			{
				LogRejected(reason);
			}
			else if (noteEvent.InstrumentId == "lyre")
			{
				TryPlayRemoteLyreEvent(noteEvent);
			}
			else if (noteEvent.InstrumentId == "drums")
			{
				TryPlayRemoteDrumEvent(noteEvent);
			}
			else if (noteEvent.InstrumentId == "flute")
			{
				TryPlayRemoteFluteEvent(noteEvent);
			}
		}

		private void TryPlayRemoteLyreEvent(LyreNetworkNoteEvent noteEvent)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (!TryFindNote(noteEvent.NoteName, o