Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions AquaMai.Mods/GameSystem/HeadphoneVolumeOverride.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using HarmonyLib;

using AquaMai.Config.Attributes;
using UnityEngine;

namespace AquaMai.Mods.GameSystem;

[ConfigSection(
name: "耳机音量覆盖",
en: "Override 1P/2P headphone volume to a fixed value, ignoring the user's in-game setting. Set to -1 to disable (use user setting). Range: 0–20.",
zh: "将 1P/2P 耳机音量强制覆盖为固定值,忽略用户的游戏内设置。设为 -1 则禁用(使用用户设置)。范围:0–20。")]
public static class HeadphoneVolumeOverride
{
[ConfigEntry(
name: "1P 音量覆盖",
en: "1P headphone volume override. -1 = use user setting, 0–20 = fixed volume (20 is max).",
zh: "1P 耳机音量覆盖值。-1 = 使用用户设置,0–20 = 固定音量(20 为最大值)。")]
private static readonly float p1 = -1f;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since this field is intended to be modified by the config loader at runtime, it should not be marked as readonly. In some .NET runtimes or under certain JIT optimization levels, static readonly fields of primitive types can be inlined as constants by the JIT compiler, which would prevent any runtime config updates from taking effect. Removing readonly ensures correctness and avoids potential JIT inlining issues.

    private static float p1 = -1f;


[ConfigEntry(
name: "2P 音量覆盖",
en: "2P headphone volume override. -1 = use user setting, 0–20 = fixed volume (20 is max).",
zh: "2P 耳机音量覆盖值。-1 = 使用用户设置,0–20 = 固定音量(20 为最大值)。")]
private static readonly float p2 = -1f;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since this field is intended to be modified by the config loader at runtime, it should not be marked as readonly. In some .NET runtimes or under certain JIT optimization levels, static readonly fields of primitive types can be inlined as constants by the JIT compiler, which would prevent any runtime config updates from taking effect. Removing readonly ensures correctness and avoids potential JIT inlining issues.

    private static float p2 = -1f;


[HarmonyPrefix]
[HarmonyPatch(typeof(CriAtomExPlayer), "SetAisacControl", [typeof(uint), typeof(float)])]
public static void PreSetAisacControl(uint controlId, ref float value)
{
if (controlId == 2 && p1 >= 0f)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Replace magic AISAC control IDs 2 and 3 with named constants (e.g., const uint Headphone1AisacControlId = 2;) to clarify their meaning and prevent accidental misuse.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At AquaMai.Mods/GameSystem/HeadphoneVolumeOverride.cs, line 30:

<comment>Replace magic AISAC control IDs `2` and `3` with named constants (e.g., `const uint Headphone1AisacControlId = 2;`) to clarify their meaning and prevent accidental misuse.</comment>

<file context>
@@ -0,0 +1,39 @@
+    [HarmonyPatch(typeof(CriAtomExPlayer), "SetAisacControl", [typeof(uint), typeof(float)])]
+    public static void PreSetAisacControl(uint controlId, ref float value)
+    {
+        if (controlId == 2 && p1 >= 0f)
+        {
+            value = Mathf.Clamp(p1 / 20f, 0f, 1f);
</file context>

{
value = Mathf.Clamp(p1 / 20f, 0f, 1f);
}
else if (controlId == 3 && p2 >= 0f)
Comment on lines +30 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 考虑将魔法 AISAC 控制 ID 替换为具名常量或枚举。

直接使用像 23 这样的原始数值会掩盖它们的含义,并且让理解依赖于外部上下文。通过定义具名常量(例如 const uint Headphone1ControlId = 2;)或枚举,可以更清晰地表达意图,并帮助防止在其他地方错误地复用或修改这些 ID。

建议实现方式:

    [ConfigEntry(
        name: "2P 音量覆盖",
        en: "2P headphone volume override. -1 = use user setting, 0–20 = fixed volume (20 is max).",
        zh: "2P 耳机音量覆盖值。-1 = 使用用户设置,0–20 = 固定音量(20 为最大值)。")]
    private static readonly float p2 = -1f;

    private const uint Headphone1AisacControlId = 2;
    private const uint Headphone2AisacControlId = 3;

    [HarmonyPrefix]
    public static void PreSetAisacControl(uint controlId, ref float value)
    {
        if (controlId == Headphone1AisacControlId && p1 >= 0f)
        {
            value = Mathf.Clamp(p1 / 20f, 0f, 1f);
        }
        else if (controlId == Headphone2AisacControlId && p2 >= 0f)
        {
            value = Mathf.Clamp(p2 / 20f, 0f, 1f);
        }
    }
Original comment in English

suggestion: Consider replacing magic AISAC control IDs with named constants or an enum.

Using raw values like 2 and 3 obscures their meaning and couples understanding to external context. Defining named constants (e.g., const uint Headphone1ControlId = 2;) or an enum will clarify intent and help prevent incorrect reuse or modification of these IDs elsewhere.

Suggested implementation:

    [ConfigEntry(
        name: "2P 音量覆盖",
        en: "2P headphone volume override. -1 = use user setting, 0–20 = fixed volume (20 is max).",
        zh: "2P 耳机音量覆盖值。-1 = 使用用户设置,0–20 = 固定音量(20 为最大值)。")]
    private static readonly float p2 = -1f;

    private const uint Headphone1AisacControlId = 2;
    private const uint Headphone2AisacControlId = 3;

    [HarmonyPrefix]
    public static void PreSetAisacControl(uint controlId, ref float value)
    {
        if (controlId == Headphone1AisacControlId && p1 >= 0f)
        {
            value = Mathf.Clamp(p1 / 20f, 0f, 1f);
        }
        else if (controlId == Headphone2AisacControlId && p2 >= 0f)
        {
            value = Mathf.Clamp(p2 / 20f, 0f, 1f);
        }
    }

{
value = Mathf.Clamp(p2 / 20f, 0f, 1f);
}
}
}
Loading