Decompiled source of VoiceMusicBoost v1.0.1

VoiceMusicBoost-1.0.1.dll

Decompiled 2 days ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Photon.Voice;
using Photon.Voice.Unity;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace VoiceMusicBoost;

[BepInPlugin("com.voicemusicboost.repo", "音乐语音增强 (VoiceMusicBoost)", "1.0.1")]
public sealed class VoiceMusicBoostPlugin : BaseUnityPlugin
{
	public const string Guid = "com.voicemusicboost.repo";

	public const string Version = "1.0.1";

	internal static ManualLogSource L;

	internal static ConfigEntry<bool> CfgEnable;

	internal static ConfigEntry<int> CfgBitrate;

	internal static ConfigEntry<int> CfgSampling;

	internal static ConfigEntry<int> CfgFrameMs;

	internal static ConfigEntry<bool> CfgDisableVad;

	internal static ConfigEntry<float> CfgVadThreshold;

	internal static ConfigEntry<bool> CfgBypassDsp;

	internal static ConfigEntry<bool> CfgReliable;

	internal static ConfigEntry<bool> CfgVerbose;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Expected O, but got Unknown
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Expected O, but got Unknown
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Expected O, but got Unknown
		L = ((BaseUnityPlugin)this).Logger;
		CfgEnable = ((BaseUnityPlugin)this).Config.Bind<bool>("1-编码参数", "启用编码覆盖", true, "是否在每次开启录音时覆盖 Opus 编码参数。\n关闭后本插件的编码覆盖失效,仅保留 VAD 与 WebRTC 音效链的处理开关。");
		CfgBitrate = ((BaseUnityPlugin)this).Config.Bind<int>("1-编码参数", "Opus 码率 (bps)", 96000, new ConfigDescription("6000 ~ 510000。游戏默认 30000(约 30kbps,人声级别),音乐必然发闷断续。\n建议值:64000(够用) / 96000(推荐) / 128000(音质最好,占用上行带宽最高)。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(6000, 510000), Array.Empty<object>()));
		CfgSampling = ((BaseUnityPlugin)this).Config.Bind<int>("1-编码参数", "采样率 (Hz)", 48000, new ConfigDescription("16000 / 24000 / 32000 / 48000。游戏默认 24000,音乐高频会被削掉。\n48000 为音乐首选;若上行带宽紧张可退到 32000。", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 16000, 24000, 32000, 48000 }), Array.Empty<object>()));
		CfgFrameMs = ((BaseUnityPlugin)this).Config.Bind<int>("1-编码参数", "帧长 (毫秒)", 40, new ConfigDescription("20 / 40 / 60。游戏默认 20。\n40 在抗抖动与延迟之间较均衡,音乐推荐;20 延迟最低但更容易被丢包撕碎。", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[3] { 20, 40, 60 }), Array.Empty<object>()));
		CfgDisableVad = ((BaseUnityPlugin)this).Config.Bind<bool>("2-音频处理", "关闭语音检测 (VAD)", true, "游戏的语音检测会把安静段落和低频段落判定为\"没说话\"从而停止发送,音乐会出现频繁断口。\n强烈建议保持开启(关闭 VAD)。");
		CfgVadThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("2-音频处理", "语音检测阈值", 0.001f, new ConfigDescription("仅在未能关闭 VAD 时生效,越低越不容易被切断。范围 0.0001 ~ 0.1。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.0001f, 0.1f), Array.Empty<object>()));
		CfgBypassDsp = ((BaseUnityPlugin)this).Config.Bind<bool>("2-音频处理", "关闭 WebRTC 音效处理", true, "关闭回声消除(AEC) / 噪声抑制(NS) / 自动增益(AGC) / 高通滤波 / WebRTC 自带语音检测。\n这些算法是按人声设计的,会把音乐当成噪声压制,是断续与发闷的主要来源之一。\n副作用:若你外放音箱而非戴耳机,队友可能听到回声。");
		CfgReliable = ((BaseUnityPlugin)this).Config.Bind<bool>("3-传输", "可靠传输", false, "开启后丢包会重传,音乐不易被丢包撕裂,但会增加延迟与带宽占用。\n网络稳定时建议关闭;队友那边持续爆音/断续时可尝试开启。");
		CfgVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("4-诊断", "输出详细日志", true, "在日志中打印每次生效的编码参数与实际麦克风采样率,便于确认是否真的生效。");
		_harmony = new Harmony("com.voicemusicboost.repo");
		TryPatch(typeof(Patch_RecorderCreateVoice), "编码参数");
		TryPatch(typeof(Patch_WebRtcAwake), "WebRTC 音效链");
		TryPatch(typeof(Patch_VoiceCreatedDiagnostics), "诊断日志");
		L.LogInfo((object)("音乐语音增强已加载:码率 " + CfgBitrate.Value + "bps / 采样率 " + CfgSampling.Value + "Hz / 帧长 " + CfgFrameMs.Value + "ms / 关闭VAD=" + CfgDisableVad.Value + " / 关闭WebRTC处理=" + CfgBypassDsp.Value + "(装在本机即生效,房主/客机身份相同)"));
	}

	private void TryPatch(Type patchType, string label)
	{
		try
		{
			_harmony.PatchAll(patchType);
		}
		catch (Exception ex)
		{
			L.LogError((object)("[" + label + "] 补丁失败:" + ex.Message));
		}
	}

	private void OnDestroy()
	{
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
	}

	internal static void LogI(string msg)
	{
		if (L != null)
		{
			L.LogInfo((object)msg);
		}
	}

	internal static void LogV(string msg)
	{
		if (L != null && CfgVerbose != null && CfgVerbose.Value)
		{
			L.LogInfo((object)msg);
		}
	}
}
[HarmonyPatch(typeof(Recorder), "CreateLocalVoiceAudioAndSource")]
internal static class Patch_RecorderCreateVoice
{
	private static readonly FieldInfo FBitrate = AccessTools.Field(typeof(Recorder), "bitrate");

	private static readonly FieldInfo FSampling = AccessTools.Field(typeof(Recorder), "samplingRate");

	private static readonly FieldInfo FFrame = AccessTools.Field(typeof(Recorder), "frameDuration");

	private static readonly FieldInfo FVoiceDetection = AccessTools.Field(typeof(Recorder), "voiceDetection");

	private static readonly FieldInfo FThreshold = AccessTools.Field(typeof(Recorder), "voiceDetectionThreshold");

	private static readonly FieldInfo FReliable = AccessTools.Field(typeof(Recorder), "reliableMode");

	private static void Prefix(Recorder __instance)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			if (VoiceMusicBoostPlugin.CfgEnable.Value)
			{
				int num = Mathf.Clamp(VoiceMusicBoostPlugin.CfgBitrate.Value, 6000, 510000);
				FBitrate?.SetValue(__instance, num);
				int value = VoiceMusicBoostPlugin.CfgSampling.Value;
				if (FSampling != null && Enum.IsDefined(FSampling.FieldType, value))
				{
					FSampling.SetValue(__instance, Enum.ToObject(FSampling.FieldType, value));
				}
				else
				{
					VoiceMusicBoostPlugin.LogI("[编码] 采样率 " + value + "Hz 不被 Opus 封装枚举支持,保持游戏默认值。");
				}
				int value2 = VoiceMusicBoostPlugin.CfgFrameMs.Value;
				if (FFrame != null && Enum.IsDefined(FFrame.FieldType, value2 * 1000))
				{
					FFrame.SetValue(__instance, Enum.ToObject(FFrame.FieldType, value2 * 1000));
				}
				else
				{
					VoiceMusicBoostPlugin.LogI("[编码] 帧长 " + value2 + "ms 不被 Opus 封装枚举支持,保持游戏默认值。");
				}
			}
			if (VoiceMusicBoostPlugin.CfgDisableVad.Value)
			{
				FVoiceDetection?.SetValue(__instance, false);
			}
			else
			{
				FThreshold?.SetValue(__instance, VoiceMusicBoostPlugin.CfgVadThreshold.Value);
			}
			FReliable?.SetValue(__instance, VoiceMusicBoostPlugin.CfgReliable.Value);
			VoiceMusicBoostPlugin.LogV("[编码] 已应用:bitrate=" + (FBitrate?.GetValue(__instance))?.ToString() + " sampling=" + (FSampling?.GetValue(__instance))?.ToString() + " frame=" + (FFrame?.GetValue(__instance))?.ToString() + " voiceDetection=" + (FVoiceDetection?.GetValue(__instance))?.ToString() + " reliable=" + FReliable?.GetValue(__instance));
		}
		catch (Exception ex)
		{
			VoiceMusicBoostPlugin.LogI("[编码] 应用参数异常:" + ex.Message);
		}
	}
}
[HarmonyPatch(typeof(WebRtcAudioDsp), "Awake")]
internal static class Patch_WebRtcAwake
{
	private static void Postfix(WebRtcAudioDsp __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && VoiceMusicBoostPlugin.CfgBypassDsp.Value)
			{
				__instance.AEC = false;
				__instance.AecHighPass = false;
				__instance.NoiseSuppression = false;
				__instance.HighPass = false;
				__instance.AGC = false;
				__instance.VAD = false;
				__instance.Bypass = true;
				VoiceMusicBoostPlugin.LogI("[音效] 已关闭 AEC / 噪声抑制 / AGC / 高通滤波 / WebRTC VAD,并旁路处理链。");
			}
		}
		catch (Exception ex)
		{
			VoiceMusicBoostPlugin.LogI("[音效] 关闭处理链失败:" + ex.Message);
		}
	}
}
[HarmonyPatch]
internal static class Patch_VoiceCreatedDiagnostics
{
	private static FieldInfo _fRecorder;

	private static FieldInfo _fMicSource;

	private static MethodBase TargetMethod()
	{
		Type type = AccessTools.TypeByName("PlayerVoiceChat");
		if (!(type == null))
		{
			return AccessTools.Method(type, "PhotonVoiceCreated", (Type[])null, (Type[])null);
		}
		return null;
	}

	private static void Postfix(object __instance, PhotonVoiceCreatedParams p)
	{
		//IL_002d: 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_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (p == null || !VoiceMusicBoostPlugin.CfgVerbose.Value)
			{
				return;
			}
			VoiceInfo val = (VoiceInfo)((p.Voice != null) ? p.Voice.Info : default(VoiceInfo));
			string text = ((p.Voice == null) ? "?" : ((object)p.Voice).GetType().Name);
			string text2 = "[实际语音流] 采样率=" + ((VoiceInfo)(ref val)).SamplingRate + "Hz 声道=" + ((VoiceInfo)(ref val)).Channels + " 帧长=" + (float)((VoiceInfo)(ref val)).FrameDurationUs / 1000f + "ms 码率=" + ((VoiceInfo)(ref val)).Bitrate + "bps 采样管道=" + text;
			if (__instance != null)
			{
				if (_fRecorder == null)
				{
					_fRecorder = AccessTools.Field(__instance.GetType(), "recorder");
				}
				object? obj = _fRecorder?.GetValue(__instance);
				Recorder val2 = (Recorder)((obj is Recorder) ? obj : null);
				if ((Object)(object)val2 != (Object)null)
				{
					text2 = text2 + " | Recorder: " + ((object)val2.SamplingRate/*cast due to .constrained prefix*/).ToString() + " / " + ((object)val2.FrameDuration/*cast due to .constrained prefix*/).ToString() + " / " + val2.Bitrate + "bps / VAD=" + val2.VoiceDetection + " / Reliable=" + val2.ReliableMode;
				}
				if (_fMicSource == null)
				{
					_fMicSource = AccessTools.Field(__instance.GetType(), "microphoneSource");
				}
				object? obj2 = _fMicSource?.GetValue(__instance);
				IAudioDesc val3 = (IAudioDesc)((obj2 is IAudioDesc) ? obj2 : null);
				if (val3 != null)
				{
					text2 = text2 + " | 麦克风输入: " + val3.SamplingRate + "Hz x" + val3.Channels;
					if (val3.SamplingRate < 32000)
					{
						text2 += "  ← 偏低!系统或设备把输入压到了电话级别,请检查声卡/虚拟声卡的格式与\"通信设备\"设置。";
					}
				}
			}
			VoiceMusicBoostPlugin.LogI(text2);
		}
		catch (Exception ex)
		{
			VoiceMusicBoostPlugin.LogI("[诊断] 读取语音流参数失败:" + ex.Message);
		}
	}
}