Decompiled source of NeverTooLoud v1.0.0

BepInEx/plugins/NeverTooLoud/NeverTooLoud.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")]
[assembly: AssemblyCompany("NeverTooLoud")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d973d8148fc7ce7511b752cce80f941095a571f8")]
[assembly: AssemblyProduct("NeverTooLoud")]
[assembly: AssemblyTitle("NeverTooLoud")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace NeverTooLoud;

public static class Api
{
	public const int Version = 1;

	public static bool Ready => Plugin.Enabled != null;

	public static bool Enabled
	{
		get
		{
			if (Plugin.Enabled != null)
			{
				return Plugin.Enabled.Value;
			}
			return false;
		}
		set
		{
			if (Plugin.Enabled != null)
			{
				Plugin.Enabled.Value = value;
			}
		}
	}

	public static float GunShotBoostDb
	{
		get
		{
			if (Plugin.GunShotBoostDb == null)
			{
				return 0f;
			}
			return Plugin.GunShotBoostDb.Value;
		}
		set
		{
			if (Plugin.GunShotBoostDb != null)
			{
				Plugin.GunShotBoostDb.Value = value;
			}
		}
	}

	public static float TailBoostDb
	{
		get
		{
			if (Plugin.TailBoostDb == null)
			{
				return 0f;
			}
			return Plugin.TailBoostDb.Value;
		}
		set
		{
			if (Plugin.TailBoostDb != null)
			{
				Plugin.TailBoostDb.Value = value;
			}
		}
	}

	public static float ClipThresholdDb
	{
		get
		{
			return 0f;
		}
		set
		{
		}
	}
}
internal static class ChainParams
{
	public static volatile bool Enabled;

	public static volatile bool Limiter = true;

	public static volatile bool Metering;

	public static volatile float ShotGain = 1f;

	public static volatile float TailGain = 1f;

	public static volatile float ReverbGain = 1f;

	public static volatile int SampleRate = 48000;
}
internal static class ChainMeters
{
	public static volatile float BlockInPeak;

	public static volatile float BlockOutPeak;

	public static volatile int ClippedTotal;

	public static volatile int Blocks;
}
internal sealed class SourceBoost : MonoBehaviour
{
	public const int KindShot = 0;

	public const int KindTail = 1;

	public const int KindReverb = 2;

	public volatile int Kind;

	private float m_gain = -1f;

	private void OnAudioFilterRead(float[] data, int channels)
	{
		float num = 1f;
		if (ChainParams.Enabled)
		{
			num = ((Kind != 2) ? ((Kind == 1) ? ChainParams.TailGain : ChainParams.ShotGain) : ChainParams.ReverbGain);
		}
		if (m_gain < 0f)
		{
			m_gain = num;
		}
		float num2 = m_gain;
		int num3 = data.Length;
		if (num2 == num)
		{
			if (num2 != 1f)
			{
				for (int i = 0; i < num3; i++)
				{
					data[i] *= num2;
				}
			}
			return;
		}
		int num4 = ((channels > 0) ? (num3 / channels) : num3);
		float num5 = ((num4 > 0) ? ((num - num2) / (float)num4) : 0f);
		int num6 = 0;
		for (int j = 0; j < num4; j++)
		{
			num2 += num5;
			for (int k = 0; k < channels; k++)
			{
				data[num6++] *= num2;
			}
		}
		m_gain = num;
	}
}
internal static class PeakControl
{
	internal const float Knee = 0.7079458f;

	internal const float Ceiling = 0.9772372f;

	internal static float Gain(float peak)
	{
		if (peak <= 0.7079458f)
		{
			return 1f;
		}
		float num = peak - 0.7079458f;
		float num2 = 0.2692914f;
		return (0.7079458f + num2 * (num / (num2 + num))) / peak;
	}
}
internal sealed class PeakMeter : MonoBehaviour
{
	internal const float FullScale = 1f;

	private void OnAudioFilterRead(float[] data, int channels)
	{
		if (channels <= 0 || !ChainParams.Enabled || (!ChainParams.Limiter && !ChainParams.Metering))
		{
			return;
		}
		bool metering = ChainParams.Metering;
		float num = 0f;
		float num2 = 0f;
		int num3 = 0;
		for (int i = 0; i < data.Length; i += channels)
		{
			float num4 = 0f;
			int num5 = Math.Min(data.Length, i + channels);
			for (int j = i; j < num5; j++)
			{
				float num6 = Math.Abs(data[j]);
				if (num6 > num4)
				{
					num4 = num6;
				}
				if (metering && num6 > 1f)
				{
					num3++;
				}
			}
			float num7 = (ChainParams.Limiter ? PeakControl.Gain(num4) : 1f);
			if (num7 < 1f)
			{
				for (int k = i; k < num5; k++)
				{
					data[k] *= num7;
				}
			}
			if (metering)
			{
				num = Math.Max(num, num4);
				num2 = Math.Max(num2, num4 * num7);
			}
		}
		if (metering)
		{
			ChainMeters.BlockInPeak = num;
			ChainMeters.BlockOutPeak = num2;
			if (num3 > 0)
			{
				ChainMeters.ClippedTotal += num3;
			}
			ChainMeters.Blocks++;
		}
	}
}
internal static class ConfigMigration
{
	public static readonly ConfigDefinition NewShot = new ConfigDefinition("Loudness", "Gun Shot Boost (dB)");

	public static readonly ConfigDefinition NewTail = new ConfigDefinition("Loudness", "Tail Boost (dB)");

	private static readonly ConfigDefinition OldMixerVolume = new ConfigDefinition("Mixer", "Volume (dB)");

	private static readonly ConfigDefinition OldGunMult = new ConfigDefinition("Volume", "Gun Dry Volume Multiplier");

	private static readonly ConfigDefinition OldTailMult = new ConfigDefinition("Volume", "Reverb Tail Multiplier");

	private static readonly ConfigDefinition[] Dead = (ConfigDefinition[])(object)new ConfigDefinition[18]
	{
		new ConfigDefinition("Mixer", "Volume (dB)"),
		new ConfigDefinition("Mixer", "Compressor Threshold (dB)"),
		new ConfigDefinition("Mixer", "Compressor Attack (ms)"),
		new ConfigDefinition("Mixer", "Compressor Release (ms)"),
		new ConfigDefinition("Mixer", "Compressor Make-up Gain (dB)"),
		new ConfigDefinition("Mixer", "Distortion Level"),
		new ConfigDefinition("Mixer", "Lowpass Cutoff (Hz)"),
		new ConfigDefinition("Internal", "Mixer Bundle Signature"),
		new ConfigDefinition("Volume", "Gun Dry Volume Multiplier"),
		new ConfigDefinition("Volume", "Reverb Tail Multiplier"),
		new ConfigDefinition("Volume", "Post-Clamp Hard Clip (fallback only)"),
		new ConfigDefinition("Volume", "Post-Clamp Hard Clip"),
		new ConfigDefinition("Volume", "Indoor Reverb Tail Multiplier"),
		new ConfigDefinition("Volume", "Outdoor Reverb Tail Multiplier"),
		new ConfigDefinition("Camera Mic", "Recording Effect"),
		new ConfigDefinition("Camera Mic", "Clip Threshold (dBFS)"),
		new ConfigDefinition("Camera Mic", "Mic Gain (dB)"),
		new ConfigDefinition("Camera Mic", "AGC Duck (dB)")
	};

	public static bool HasShot;

	public static float ShotDb;

	public static bool HasTail;

	public static float TailDb;

	public static void Read(ConfigFile cfg)
	{
		Dictionary<ConfigDefinition, string> orphans = GetOrphans(cfg);
		if (orphans == null)
		{
			Plugin.Log.LogWarning((object)"[Migrate] Could not read the cfg's unbound entries; old values not carried over.");
			return;
		}
		if (orphans.ContainsKey(OldMixerVolume) || orphans.ContainsKey(OldGunMult) || orphans.ContainsKey(OldTailMult))
		{
			Plugin.Log.LogInfo((object)"[Migrate] 1.0.0 loudness keys found: not carried over, Gun Shot Boost / Tail Boost start at defaults.");
		}
		int num = 0;
		for (int i = 0; i < Dead.Length; i++)
		{
			if (orphans.Remove(Dead[i]))
			{
				num++;
			}
		}
		if (num > 0)
		{
			Plugin.Log.LogInfo((object)$"[Migrate] Removed {num} old config keys (1.0.0 mixer, camera-mic distortion).");
		}
	}

	public static void Apply(ConfigEntry<float> shot, ConfigEntry<float> tail, ConfigFile cfg)
	{
		if (HasShot)
		{
			shot.Value = ((((ConfigEntryBase)shot).Description.AcceptableValues != null) ? ((float)((ConfigEntryBase)shot).Description.AcceptableValues.Clamp((object)ShotDb)) : ShotDb);
		}
		if (HasTail)
		{
			tail.Value = ((((ConfigEntryBase)tail).Description.AcceptableValues != null) ? ((float)((ConfigEntryBase)tail).Description.AcceptableValues.Clamp((object)TailDb)) : TailDb);
		}
		cfg.Save();
	}

	private static Dictionary<ConfigDefinition, string> GetOrphans(ConfigFile cfg)
	{
		PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries");
		if ((object)propertyInfo == null)
		{
			return null;
		}
		return propertyInfo.GetValue(cfg, null) as Dictionary<ConfigDefinition, string>;
	}

	private static bool TryFloat(Dictionary<ConfigDefinition, string> orphans, ConfigDefinition def, out float value)
	{
		value = 0f;
		if (!orphans.TryGetValue(def, out var value2) || value2 == null)
		{
			return false;
		}
		return float.TryParse(value2.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
	}
}
[HarmonyPatch(typeof(ReverbSettings), "UpdateMixerParams")]
internal static class EnvironmentResponse
{
	private static ReverbSettings s_current;

	private static readonly FieldInfo s_settingsField = AccessTools.Field(typeof(SM), "m_reverbSettings");

	private static FVRSoundEnvironment s_lastEnvironment = (FVRSoundEnvironment)0;

	private static bool Active
	{
		get
		{
			if (Plugin.Enabled != null && Plugin.Enabled.Value && Plugin.EnvironmentResponseEnabled != null)
			{
				return Plugin.EnvironmentResponseEnabled.Value;
			}
			return false;
		}
	}

	private static void Postfix(ReverbSettings __instance)
	{
		s_current = __instance;
		Refresh();
	}

	internal static float GainAt(Vector3 point)
	{
		return 1f;
	}

	private static float MinimumDecay(FVRSoundEnvironment environment)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected I4, but got Unknown
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Invalid comparison between Unknown and I4
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Invalid comparison between Unknown and I4
		switch (environment - 1)
		{
		default:
			if ((int)environment != 30)
			{
				if ((int)environment != 31)
				{
					break;
				}
				return 2f;
			}
			goto case 20;
		case 11:
		case 16:
			return 3.2f;
		case 13:
			return 2.8f;
		case 14:
		case 15:
			return 1.8f;
		case 9:
		case 10:
		case 12:
			return 0.9f;
		case 20:
		case 21:
			return 4.5f;
		case 19:
			return 2.2f;
		case 0:
			return 1.6f;
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 17:
		case 18:
			break;
		}
		return 0f;
	}

	internal unsafe static void Refresh()
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		if ((s_current == null || (Object)(object)s_current.MasterMixer == (Object)null) && (Object)(object)ManagerSingleton<SM>.Instance != (Object)null && (object)s_settingsField != null)
		{
			object? value = s_settingsField.GetValue(ManagerSingleton<SM>.Instance);
			s_current = (ReverbSettings)((value is ReverbSettings) ? value : null);
		}
		ReverbSettings val = s_current;
		if (val == null || (Object)(object)val.MasterMixer == (Object)null)
		{
			return;
		}
		FVRSoundEnvironment val2 = (((Object)(object)GM.CurrentPlayerBody != (Object)null) ? GM.CurrentPlayerBody.GetCurrentSoundEnvironment() : val.Environment);
		float num = (Active ? MinimumDecay(val2) : 0f);
		float num2 = ((Active && num > 0f) ? Mathf.Clamp(Mathf.Max(num, val.DecayTime * Plugin.TailLength.Value), 0.1f, 10f) : val.DecayTime);
		val.MasterMixer.SetFloat("MReverb_DecayTime", num2);
		val.MasterMixer.SetFloat("MReverb_Reverb", val.Reverb);
		if (Active && val2 != s_lastEnvironment)
		{
			s_lastEnvironment = val2;
			if (Plugin.Verbose)
			{
				Plugin.Log.LogInfo((object)("[Reverb] " + ((object)(*(FVRSoundEnvironment*)(&val2))/*cast due to .constrained prefix*/).ToString() + ": late decay " + num2.ToString("0.0") + " s; game wet level kept"));
			}
		}
	}
}
internal static class ChainMonitor
{
	private const float HoldSeconds = 1.5f;

	private const float BurstGapSeconds = 0.5f;

	public static float InPeakHold;

	public static float OutPeakHold;

	public static float DuckDbHold;

	public static int ClippedLastSecond;

	private static float s_inHoldT;

	private static float s_outHoldT;

	private static float s_duckHoldT;

	private static int s_lastClipped;

	private static int s_secondStartClipped;

	private static float s_secondStart;

	private static bool s_burst;

	private static int s_burstSamples;

	private static float s_burstStart;

	private static float s_burstLast;

	private static float s_burstMaxIn;

	private static float s_burstMaxDuck;

	public static void Tick()
	{
		float unscaledTime = Time.unscaledTime;
		float blockInPeak = ChainMeters.BlockInPeak;
		float blockOutPeak = ChainMeters.BlockOutPeak;
		float num = 0f;
		if (blockInPeak >= InPeakHold || unscaledTime - s_inHoldT > 1.5f)
		{
			InPeakHold = blockInPeak;
			s_inHoldT = unscaledTime;
		}
		if (blockOutPeak >= OutPeakHold || unscaledTime - s_outHoldT > 1.5f)
		{
			OutPeakHold = blockOutPeak;
			s_outHoldT = unscaledTime;
		}
		if (num >= DuckDbHold || unscaledTime - s_duckHoldT > 1.5f)
		{
			DuckDbHold = num;
			s_duckHoldT = unscaledTime;
		}
		int clippedTotal = ChainMeters.ClippedTotal;
		if (unscaledTime - s_secondStart >= 1f)
		{
			ClippedLastSecond = clippedTotal - s_secondStartClipped;
			s_secondStartClipped = clippedTotal;
			s_secondStart = unscaledTime;
		}
		int num2 = clippedTotal - s_lastClipped;
		s_lastClipped = clippedTotal;
		if (num2 > 0)
		{
			if (!s_burst)
			{
				s_burst = true;
				s_burstSamples = 0;
				s_burstStart = unscaledTime;
				s_burstMaxIn = 0f;
				s_burstMaxDuck = 0f;
			}
			s_burstSamples += num2;
			s_burstLast = unscaledTime;
		}
		if (!s_burst)
		{
			return;
		}
		if (blockInPeak > s_burstMaxIn)
		{
			s_burstMaxIn = blockInPeak;
		}
		if (num > s_burstMaxDuck)
		{
			s_burstMaxDuck = num;
		}
		if (unscaledTime - s_burstLast > 0.5f)
		{
			s_burst = false;
			float num3 = LinToDb(s_burstMaxIn);
			if (Plugin.Verbose)
			{
				Plugin.Log.LogInfo((object)$"[Mix] {s_burstSamples} input samples over full scale in {(s_burstLast - s_burstStart) * 1000f:F0} ms, input peak {num3:F1} dBFS; peak limiter={ChainParams.Limiter}, no recovery ducking.");
			}
		}
	}

	public static float LinToDb(float v)
	{
		if (!(v > 1E-06f))
		{
			return -120f;
		}
		return 20f * Mathf.Log10(v);
	}
}
internal static class MeterOverlay
{
	public static bool Visible;

	private static GUIStyle s_style;

	public static void Draw()
	{
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		if (Visible)
		{
			if (s_style == null)
			{
				s_style = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14
				};
				s_style.normal.textColor = Color.white;
			}
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(20f, 20f, 380f, 150f);
			GUI.Box(val, GUIContent.none);
			GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 6f, ((Rect)(ref val)).width - 20f, ((Rect)(ref val)).height - 12f), "Never Too Loud  " + (ChainParams.Enabled ? "ON" : "OFF") + "  no limit\nListener: " + Plugin.ListenerName + $"\nMix in: {ChainMonitor.LinToDb(ChainMonitor.InPeakHold):F1} dBFS" + $"\nOut: {ChainMonitor.LinToDb(ChainMonitor.OutPeakHold):F1} dBFS" + "\nSamples over full scale/s: " + ChainMonitor.ClippedLastSecond + $"\nTagged sources: {Tagger.TaggedCount}   audio blocks: {ChainMeters.Blocks}", s_style);
		}
	}
}
internal static class Tagger
{
	private const int LogFirst = 25;

	private static int s_tagged;

	public static int TaggedCount => s_tagged;

	public static int KindFor(FVRPooledAudioType t)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected I4, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Invalid comparison between Unknown and I4
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Invalid comparison between Unknown and I4
		switch (t - 1)
		{
		default:
			if ((int)t != 30)
			{
				if ((int)t != 31)
				{
					break;
				}
				return 1;
			}
			goto case 0;
		case 0:
		case 4:
			return 0;
		case 1:
		case 5:
			return 2;
		case 2:
		case 3:
			break;
		}
		return -1;
	}

	public static void TagPool(AudioSourcePool pool)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		if (pool == null)
		{
			return;
		}
		int num = KindFor(pool.Type);
		if (num < 0)
		{
			return;
		}
		if (pool.SourceQueue_Disabled != null)
		{
			foreach (FVRPooledAudioSource item in pool.SourceQueue_Disabled)
			{
				Tag(item, num, pool.Type);
			}
		}
		if (pool.ActiveSources != null)
		{
			for (int i = 0; i < pool.ActiveSources.Count; i++)
			{
				Tag(pool.ActiveSources[i], num, pool.Type);
			}
		}
	}

	public static void TagAllExistingPools(string reason)
	{
		if ((Object)(object)ManagerSingleton<SM>.Instance == (Object)null)
		{
			return;
		}
		List<AudioSourcePool> activePools = SM.ActivePools;
		if (activePools != null)
		{
			int num = s_tagged;
			for (int i = 0; i < activePools.Count; i++)
			{
				TagPool(activePools[i]);
			}
			if (Plugin.Verbose)
			{
				Plugin.Log.LogInfo((object)$"[Tag] Scan ({reason}): {activePools.Count} pools, {s_tagged - num} new sources tagged, {s_tagged} total.");
			}
		}
	}

	public static void TagSource(AudioSource src, int kind)
	{
		GameObject gameObject = ((Component)src).gameObject;
		SourceBoost sourceBoost = gameObject.GetComponent<SourceBoost>();
		if ((Object)(object)sourceBoost == (Object)null)
		{
			sourceBoost = gameObject.AddComponent<SourceBoost>();
			s_tagged++;
		}
		sourceBoost.Kind = kind;
	}

	private static void Tag(FVRPooledAudioSource s, int kind, FVRPooledAudioType type)
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)s == (Object)null) && !((Object)(object)s.Source == (Object)null))
		{
			int num = s_tagged;
			TagSource(s.Source, kind);
			if (s_tagged != num && (s_tagged <= 25 || s_tagged % 100 == 0) && Plugin.Verbose)
			{
				Plugin.Log.LogInfo((object)string.Format("[Tag] {0} pool={1} kind={2} (#{3})", ((Object)s).name, type, kind switch
				{
					1 => "distant", 
					2 => "reverb", 
					_ => "shot", 
				}, s_tagged));
			}
		}
	}
}
[HarmonyPatch(typeof(AudioSourcePool), "InstantiateAndEnqueue")]
internal static class AudioSourcePool_InstantiateAndEnqueue_Tag
{
	private static void Postfix(AudioSourcePool __instance)
	{
		Tagger.TagPool(__instance);
	}
}
[HarmonyPatch(typeof(FVRFireArm), "Awake")]
internal static class FVRFireArm_Awake_ShotClips
{
	internal static readonly HashSet<AudioClip> ShotClips = new HashSet<AudioClip>();

	private static void Postfix(FVRFireArm __instance)
	{
		FVRFirearmAudioSet audioClipSet = __instance.AudioClipSet;
		if (!((Object)(object)audioClipSet == (Object)null))
		{
			Add(audioClipSet.Shots_Main);
			Add(audioClipSet.Shots_Suppressed);
			Add(audioClipSet.Shots_LowPressure);
		}
	}

	private static void Add(AudioEvent e)
	{
		if (e == null || e.Clips == null)
		{
			return;
		}
		for (int i = 0; i < e.Clips.Count; i++)
		{
			AudioClip val = e.Clips[i];
			if ((Object)(object)val != (Object)null)
			{
				ShotClips.Add(val);
			}
		}
	}
}
internal static class OneShotTagger
{
	private static readonly HashSet<string> LoggedClips = new HashSet<string>();

	internal static void Prefix(AudioSource src)
	{
		if ((Object)(object)src == (Object)null)
		{
			return;
		}
		AudioClip clip = src.clip;
		if (!((Object)(object)clip == (Object)null) && FVRFireArm_Awake_ShotClips.ShotClips.Contains(clip) && !((Object)(object)((Component)src).gameObject.GetComponent<SourceBoost>() != (Object)null))
		{
			Tagger.TagSource(src, 0);
			if (Plugin.Verbose && LoggedClips.Add(((Object)clip).name) && Plugin.Verbose)
			{
				Plugin.Log.LogInfo((object)("[Tag/OneShot] " + ((Object)((Component)src).gameObject).name + " clip=" + ((Object)clip).name + " kind=shot (non-pooled shot, e.g. Audio Balancing)"));
			}
		}
	}
}
[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
internal static class AudioSource_Play_OneShot
{
	private static void Prefix(AudioSource __instance)
	{
		OneShotTagger.Prefix(__instance);
	}
}
[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { typeof(ulong) })]
internal static class AudioSource_PlayDelayed_OneShot
{
	private static void Prefix(AudioSource __instance)
	{
		OneShotTagger.Prefix(__instance);
	}
}
[BepInPlugin("h3vr.invent60.nevertooloud", "Never Too Loud", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	public static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> LimiterEnabled;

	internal static ConfigEntry<bool> DebugLog;

	internal static ConfigEntry<float> GunShotBoostDb;

	internal static ConfigEntry<float> TailBoostDb;

	internal static ConfigEntry<float> ReverbBoostDb;

	internal static ConfigEntry<bool> EnvironmentResponseEnabled;

	internal static ConfigEntry<float> TailLength;

	internal static ConfigEntry<KeyCode> ToggleMeterKey;

	internal static string ListenerName = "(none)";

	private const float ListenerCheckSeconds = 1f;

	private static readonly object[] NoTags = new object[0];

	private AudioListener m_listener;

	private float m_nextListenerCheck;

	internal static bool Verbose
	{
		get
		{
			if (DebugLog != null)
			{
				return DebugLog.Value;
			}
			return false;
		}
	}

	private void Awake()
	{
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Expected O, but got Unknown
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Expected O, but got Unknown
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Expected O, but got Unknown
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Expected O, but got Unknown
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		ConfigMigration.Read(((BaseUnityPlugin)this).Config);
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for louder shots and long echoes.\nExamples: true = on (default), false = vanilla game audio.\nActionCam can flip this from its panel; without ActionCam just edit it here.");
		LimiterEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Loudness", "Peak Limiter", true, "Smooth overloaded peaks without a lingering volume dip after the shot. Example: true = on, false = unrestricted output.");
		DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log", false, "Extra audio/source diagnostics. Example: true = log details, false = quiet (default).");
		GunShotBoostDb = ((BaseUnityPlugin)this).Config.Bind<float>("Loudness", "Gun Shot Boost (dB)", 12f, new ConfigDescription("Extra loudness for gunshots (player guns, sosig guns up close) and explosions, in dB. Applied after the game's own volume, so it can go past what the game allows.\nExamples: 0 = game volume, 6 = twice as loud, 12 = four times (default), 20 = ten times.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-12f, 30f), NoTags));
		TailBoostDb = ((BaseUnityPlugin)this).Config.Bind<float>("Loudness", "Tail Boost (dB)", 6f, new ConfigDescription("Base loudness boost for gunshot echo tails, explosion tails and distant sosig gunfire, in dB. Extra Reverb Boost adds more only to the echo tails.\nExamples: 0 = game volume, 6 = twice as loud (default), 12 = four times.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-12f, 30f), NoTags));
		ReverbBoostDb = ((BaseUnityPlugin)this).Config.Bind<float>("Loudness", "Extra Reverb Boost (dB)", 12f, new ConfigDescription("Additional loudness only for gunshot and explosion echo/reverb tails, on top of Tail Boost. Dry shots and distant sosig gunfire keep their existing boost.\nExamples: 0 = base tail boost, 12 = four times the base tail amplitude (default). Tail Boost + Extra Reverb Boost is applied up to +30 dB, independently of the dry-shot boost.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 24f), NoTags));
		EnvironmentResponseEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Reverb", "Environment Response", true, "Let the game's acoustic environment extend late reverberation; the game's wet/dry balance is kept. Uses authored environment types, not terrain reconstruction.");
		TailLength = ((BaseUnityPlugin)this).Config.Bind<float>("Reverb", "Tail Length Multiplier", 1.8f, new ConfigDescription("Late room decay multiplier with environment-dependent minimum duration, clamped to 10 seconds. Outdoor enclosed/range environments retain longer reflections. Example: 1.8 = default, 1 = shorter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 4f), NoTags));
		ToggleMeterKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Monitor", "Toggle Live Monitor Key", (KeyCode)287, "Desktop-only on-screen readout of the mix (peak level, samples over digital full scale). Read only, the sound is not changed. Not shown in the headset.\nExamples: F6 (default), F7, None = disabled.");
		ConfigMigration.Apply(GunShotBoostDb, TailBoostDb, ((BaseUnityPlugin)this).Config);
		Enabled.SettingChanged += delegate
		{
			OnSettingChanged("Enabled");
		};
		LimiterEnabled.SettingChanged += delegate
		{
			OnSettingChanged("Peak Limiter");
		};
		GunShotBoostDb.SettingChanged += delegate
		{
			OnSettingChanged("Gun Shot Boost");
		};
		TailBoostDb.SettingChanged += delegate
		{
			OnSettingChanged("Tail Boost");
		};
		ReverbBoostDb.SettingChanged += delegate
		{
			OnSettingChanged("Extra Reverb Boost");
		};
		EnvironmentResponseEnabled.SettingChanged += delegate
		{
			OnSettingChanged("Environment Response");
		};
		TailLength.SettingChanged += delegate
		{
			OnSettingChanged("Tail Length");
		};
		PushParams();
		LogParams("Loaded");
		new Harmony("h3vr.invent60.nevertooloud").PatchAll();
		SceneManager.sceneLoaded += OnSceneLoaded;
		if (Verbose)
		{
			Log.LogInfo((object)"Never Too Loud loaded. Drowning out the sosigs in your head.");
		}
	}

	private void Start()
	{
		Tagger.TagAllExistingPools("start");
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		Tagger.TagAllExistingPools("scene " + ((Scene)(ref scene)).name);
		m_nextListenerCheck = 0f;
	}

	private static void OnSettingChanged(string what)
	{
		PushParams();
		LogParams("Changed " + what);
	}

	internal static float DbToLin(float db)
	{
		return Mathf.Pow(10f, db / 20f);
	}

	internal static void PushParams()
	{
		ChainParams.ShotGain = DbToLin(GunShotBoostDb.Value);
		ChainParams.TailGain = DbToLin(TailBoostDb.Value);
		ChainParams.ReverbGain = DbToLin(Mathf.Clamp(TailBoostDb.Value + ReverbBoostDb.Value, -12f, 30f));
		ChainParams.SampleRate = AudioSettings.outputSampleRate;
		ChainParams.Enabled = Enabled.Value;
		ChainParams.Limiter = LimiterEnabled.Value;
		ChainParams.Metering = Verbose || MeterOverlay.Visible;
		EnvironmentResponse.Refresh();
	}

	private static void LogParams(string why)
	{
		if (Verbose)
		{
			float num = Mathf.Clamp(TailBoostDb.Value + ReverbBoostDb.Value, -12f, 30f);
			if (Verbose)
			{
				Log.LogInfo((object)$"[Config] {why}: Enabled={Enabled.Value} GunShotBoost={GunShotBoostDb.Value:F1}dB TailBoost={TailBoostDb.Value:F1}dB ExtraReverbBoost={ReverbBoostDb.Value:F1}dB EffectiveReverbBoost={num:F1}dB Limiter={LimiterEnabled.Value} EnvironmentResponse={EnvironmentResponseEnabled.Value} TailLength={TailLength.Value:F1}x SampleRate={ChainParams.SampleRate}");
			}
		}
	}

	private void Update()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		if ((int)ToggleMeterKey.Value != 0 && Input.GetKeyDown(ToggleMeterKey.Value))
		{
			MeterOverlay.Visible = !MeterOverlay.Visible;
		}
		if (Time.unscaledTime >= m_nextListenerCheck)
		{
			m_nextListenerCheck = Time.unscaledTime + 1f;
			EnvironmentResponse.Refresh();
			EnsureMeter();
		}
		ChainParams.Metering = Verbose || MeterOverlay.Visible;
		if (ChainParams.Metering)
		{
			ChainMonitor.Tick();
		}
	}

	private void EnsureMeter()
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Expected O, but got Unknown
		int outputSampleRate = AudioSettings.outputSampleRate;
		if (outputSampleRate != ChainParams.SampleRate)
		{
			if (Verbose)
			{
				Log.LogInfo((object)$"[Cam] Output sample rate {ChainParams.SampleRate} -> {outputSampleRate}");
			}
			ChainParams.SampleRate = outputSampleRate;
		}
		if ((Object)(object)m_listener != (Object)null && ((Behaviour)m_listener).isActiveAndEnabled)
		{
			return;
		}
		AudioListener val = (AudioListener)Object.FindObjectOfType(typeof(AudioListener));
		if ((Object)(object)val == (Object)null)
		{
			if ((Object)(object)m_listener != (Object)null && Verbose)
			{
				Log.LogInfo((object)"[Cam] Listener lost, none active right now.");
			}
			m_listener = null;
			ListenerName = "(none)";
			return;
		}
		m_listener = val;
		ListenerName = ((Object)((Component)val).gameObject).name;
		bool flag = false;
		if ((Object)(object)((Component)val).gameObject.GetComponent<PeakMeter>() == (Object)null)
		{
			((Component)val).gameObject.AddComponent<PeakMeter>();
			flag = true;
		}
		if (Verbose)
		{
			Log.LogInfo((object)("[Output] Peak control " + (flag ? "attached to" : "already on") + " listener '" + ListenerName + "'."));
		}
	}

	private void OnGUI()
	{
		MeterOverlay.Draw();
	}
}