From ae5facdb7b7b87c7d48c40b78b493a8b6e67828b Mon Sep 17 00:00:00 2001 From: LostLuma Date: Fri, 31 May 2024 18:21:29 +0200 Subject: [PATCH 1/2] Add smooth volume transition --- .../java/dynamic_fps/impl/DynamicFPSMod.java | 8 +++ .../dynamic_fps/impl/compat/ClothConfig.java | 47 ++++++++++++++++ .../impl/config/DynamicFPSConfig.java | 39 +++++++++++++- .../impl/config/Serialization.java | 13 +++++ .../impl/mixin/SoundEngineMixin.java | 6 +-- .../impl/util/SmoothVolumeHandler.java | 53 +++++++++++++++++++ .../assets/dynamic_fps/lang/en_us.json | 5 ++ .../fabric/src/main/resources/fabric.mod.json | 1 + .../src/main/resources/META-INF/mods.toml | 1 + .../resources/META-INF/neoforge.mods.toml | 1 + .../quilt/src/main/resources/quilt.mod.json | 1 + 11 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 platforms/common/src/main/java/dynamic_fps/impl/util/SmoothVolumeHandler.java diff --git a/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java b/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java index ad4659d1..8e1f3609 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java @@ -10,6 +10,7 @@ import dynamic_fps.impl.util.Logging; import dynamic_fps.impl.util.OptionsHolder; import dynamic_fps.impl.util.Version; +import dynamic_fps.impl.util.SmoothVolumeHandler; import dynamic_fps.impl.util.duck.DuckLoadingOverlay; import dynamic_fps.impl.util.duck.DuckSoundEngine; import dynamic_fps.impl.util.event.WindowObserver; @@ -52,6 +53,7 @@ public class DynamicFPSMod { public static void init() { IdleHandler.init(); + SmoothVolumeHandler.init(); Platform platform = Platform.getInstance(); Version version = platform.getModVersion(Constants.MOD_ID).orElseThrow(); @@ -97,6 +99,7 @@ public static void toggleDisabled() { public static void onConfigChanged() { modConfig.save(); IdleHandler.init(); + SmoothVolumeHandler.init(); } public static Screen getConfigScreen(Screen parent) { @@ -159,6 +162,10 @@ public static float volumeMultiplier(SoundSource source) { return config.volumeMultiplier(source); } + public static DynamicFPSConfig.VolumeTransitionSpeed volumeTransitionSpeed() { + return modConfig.volumeTransitionSpeed(); + } + public static boolean shouldShowToasts() { return config.showToasts(); } @@ -189,6 +196,7 @@ public static void handleStateChange(PowerState previous, PowerState current) { System.gc(); } + // Update volume of current sounds for users not using smooth volume transition for (SoundSource source : SoundSource.values()) { ((DuckSoundEngine) minecraft.getSoundManager().soundEngine).dynamic_fps$updateVolume(source); } diff --git a/platforms/common/src/main/java/dynamic_fps/impl/compat/ClothConfig.java b/platforms/common/src/main/java/dynamic_fps/impl/compat/ClothConfig.java index a89e0041..074d814c 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/compat/ClothConfig.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/compat/ClothConfig.java @@ -72,6 +72,34 @@ public static Screen genConfigScreen(Screen parent) { .build() ); + VariableStepTransformer volumeTransformer = getVolumeStepTransformer(); + + general.addEntry( + entryBuilder.startIntSlider( + localized("config", "volume_transition_speed_up"), + volumeTransformer.toStep((int) (DynamicFPSMod.volumeTransitionSpeed().getUp() * 10)), + 1, 31 + ) + .setDefaultValue(volumeTransformer.toStep((int) (1.0f * 10))) + .setSaveConsumer(step -> DynamicFPSMod.volumeTransitionSpeed().setUp((float) volumeTransformer.toValue(step) / 10)) + .setTextGetter(ClothConfig::volumeTransitionMessage) + .setTooltip(localized("config", "volume_transition_speed_tooltip")) + .build() + ); + + general.addEntry( + entryBuilder.startIntSlider( + localized("config", "volume_transition_speed_down"), + volumeTransformer.toStep((int) (DynamicFPSMod.volumeTransitionSpeed().getDown() * 10)), + 1, 31 + ) + .setDefaultValue(volumeTransformer.toStep((int) (0.5f * 10))) + .setSaveConsumer(step -> DynamicFPSMod.volumeTransitionSpeed().setDown((float) volumeTransformer.toValue(step) / 10)) + .setTextGetter(ClothConfig::volumeTransitionMessage) + .setTooltip(localized("config", "volume_transition_speed_tooltip")) + .build() + ); + // Used for each state's frame rate target slider below VariableStepTransformer fpsTransformer = getFpsTransformer(); @@ -169,6 +197,25 @@ private static Component idleTimeMessage(int value) { } } + private static VariableStepTransformer getVolumeStepTransformer() { + VariableStepTransformer transformer = new VariableStepTransformer(); + + transformer.addStep(1, 30); + transformer.addStep(970, 1000); + + return transformer; + } + + private static Component volumeTransitionMessage(int step) { + float value = (float) getVolumeStepTransformer().toValue(step) / 10; + + if (value < 100.0f) { + return Component.literal(value + "%"); + } else { + return localized("config", "volume_transition_speed_instant"); + } + } + private static VariableStepTransformer getFpsTransformer() { VariableStepTransformer transformer = new VariableStepTransformer(); diff --git a/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java b/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java index 715fc37c..1f9ebad0 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java @@ -11,15 +11,17 @@ public final class DynamicFPSConfig { private int idleTime; // Seconds private boolean detectIdleMovement; private boolean uncapMenuFrameRate; + private VolumeTransitionSpeed volumeTransitionSpeed; @SerializedName("states") private final Map configs; - private DynamicFPSConfig(boolean enabled, int abandonTime, boolean detectIdleMovement, boolean uncapMenuFrameRate, Map configs) { + private DynamicFPSConfig(boolean enabled, int abandonTime, boolean detectIdleMovement, boolean uncapMenuFrameRate, VolumeTransitionSpeed volumeTransitionSpeed, Map configs) { this.enabled = enabled; this.idleTime = abandonTime; this.detectIdleMovement = detectIdleMovement; this.uncapMenuFrameRate = uncapMenuFrameRate; + this.volumeTransitionSpeed = volumeTransitionSpeed; this.configs = new EnumMap<>(configs); @@ -36,6 +38,7 @@ public static DynamicFPSConfig createDefault() { 0, true, false, + new VolumeTransitionSpeed(1.0f, 0.5f), new EnumMap<>(PowerState.class) ); @@ -67,6 +70,10 @@ public void setIdleTime(int value) { this.idleTime = value; } + public VolumeTransitionSpeed volumeTransitionSpeed() { + return this.volumeTransitionSpeed; + } + public boolean detectIdleMovement() { return this.detectIdleMovement; } @@ -96,4 +103,34 @@ public static DynamicFPSConfig load() { public void save() { Serialization.save(this); } + + public static class VolumeTransitionSpeed { + private float up; + private float down; + + protected VolumeTransitionSpeed(float up, float down) { + this.up = up; + this.down = down; + } + + public float getUp() { + return this.up; + } + + public void setUp(float value) { + this.up = value; + } + + public float getDown() { + return this.down; + } + + public void setDown(float value) { + this.down = value; + } + + public boolean isActive() { + return this.up != 100.0f || this.down != 100.0f; + } + } } diff --git a/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java b/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java index e5fd09ff..be8b2dbe 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java @@ -100,6 +100,7 @@ private static void upgradeConfig(JsonObject root) { addUncapMenuFrameRate(root); addEnabled(root); addDetectIdleMovement(root); + addVolumeTransitionSpeed(root); } private static void addIdleTime(JsonObject root) { @@ -181,6 +182,18 @@ private static void addDetectIdleMovement(JsonObject root) { } } + private static void addVolumeTransitionSpeed(JsonObject root) { + // Add volume_transition_speed object if it's missing + if (!root.has("volume_transition_speed")) { + JsonObject object = new JsonObject(); + + object.addProperty("up", 1.0f); + object.addProperty("down", 0.5f); + + root.add("volume_transition_speed", object); + } + } + private static @Nullable JsonObject getStatesAsObject(JsonObject root) { if (!root.has("states")) { return null; diff --git a/platforms/common/src/main/java/dynamic_fps/impl/mixin/SoundEngineMixin.java b/platforms/common/src/main/java/dynamic_fps/impl/mixin/SoundEngineMixin.java index c472ed1e..cd9e83b3 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/mixin/SoundEngineMixin.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/mixin/SoundEngineMixin.java @@ -2,6 +2,7 @@ import java.util.Map; +import dynamic_fps.impl.util.SmoothVolumeHandler; import net.minecraft.client.Minecraft; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; @@ -16,7 +17,6 @@ import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.audio.Listener; -import dynamic_fps.impl.DynamicFPSMod; import dynamic_fps.impl.util.duck.DuckSoundEngine; import net.minecraft.client.Options; import net.minecraft.client.resources.sounds.SoundInstance; @@ -104,7 +104,7 @@ private float calculateVolume(SoundInstance instance) { */ @Inject(method = { "play", "playDelayed" }, at = @At("HEAD"), cancellable = true) private void play(SoundInstance instance, CallbackInfo callbackInfo) { - if (DynamicFPSMod.volumeMultiplier(instance.getSource()) == 0.0f) { + if (SmoothVolumeHandler.volumeMultiplier(instance.getSource()) == 0.0f) { callbackInfo.cancel(); } } @@ -125,6 +125,6 @@ private float adjustVolume(float value, @Nullable SoundSource source) { source = SoundSource.MASTER; } - return value * DynamicFPSMod.volumeMultiplier(source); + return value * SmoothVolumeHandler.volumeMultiplier(source); } } diff --git a/platforms/common/src/main/java/dynamic_fps/impl/util/SmoothVolumeHandler.java b/platforms/common/src/main/java/dynamic_fps/impl/util/SmoothVolumeHandler.java new file mode 100644 index 00000000..36f4b8ec --- /dev/null +++ b/platforms/common/src/main/java/dynamic_fps/impl/util/SmoothVolumeHandler.java @@ -0,0 +1,53 @@ +package dynamic_fps.impl.util; + +import dynamic_fps.impl.DynamicFPSMod; +import dynamic_fps.impl.service.Platform; +import dynamic_fps.impl.util.duck.DuckSoundEngine; +import net.minecraft.client.Minecraft; +import net.minecraft.sounds.SoundSource; + +import java.util.HashMap; +import java.util.Map; + +public class SmoothVolumeHandler { + private static boolean active = false; + private static final Minecraft minecraft = Minecraft.getInstance(); + private static final Map currentOverrides = new HashMap<>(); + + public static void init() { + if (active || !DynamicFPSMod.volumeTransitionSpeed().isActive()) { + return; + } + + active = true; + Platform.getInstance().registerStartTickEvent(SmoothVolumeHandler::tickVolumes); + } + + public static float volumeMultiplier(SoundSource source) { + if (!active) { + return DynamicFPSMod.volumeMultiplier(source); + } else { + return currentOverrides.getOrDefault(source, 1.0f); + } + } + + private static void tickVolumes() { + for (SoundSource source : SoundSource.values()) { + float desired = DynamicFPSMod.volumeMultiplier(source); + float current = currentOverrides.getOrDefault(source, 1.0f); + + if (current != desired) { + if (current < desired) { + float up = DynamicFPSMod.volumeTransitionSpeed().getUp(); + currentOverrides.put(source, Math.min(desired, current + up / 20.0f)); + } else { + float down = DynamicFPSMod.volumeTransitionSpeed().getDown(); + currentOverrides.put(source, Math.max(desired, current - down / 20.0f)); + } + + // Update volume of currently playing sounds + ((DuckSoundEngine) minecraft.getSoundManager().soundEngine).dynamic_fps$updateVolume(source); + } + } + } +} diff --git a/platforms/common/src/main/resources/assets/dynamic_fps/lang/en_us.json b/platforms/common/src/main/resources/assets/dynamic_fps/lang/en_us.json index add43273..8fe0eed4 100644 --- a/platforms/common/src/main/resources/assets/dynamic_fps/lang/en_us.json +++ b/platforms/common/src/main/resources/assets/dynamic_fps/lang/en_us.json @@ -21,6 +21,11 @@ "config.dynamic_fps.uncap_menu_frame_rate": "Uncap Menu FPS", "config.dynamic_fps.uncap_menu_frame_rate_tooltip": "Remove the 60 FPS limit in the main menu.", + "config.dynamic_fps.volume_transition_speed_up": "Upward Volume Transition Speed", + "config.dynamic_fps.volume_transition_speed_down": "Downward Volume Transition Speed", + "config.dynamic_fps.volume_transition_speed_instant": "Immediate", + "config.dynamic_fps.volume_transition_speed_tooltip": "Set the speed at which the game volume will be adjusted per second.", + "config.dynamic_fps.frame_rate_target": "Frame Rate Target", "config.dynamic_fps.volume_multiplier": "Volume Multiplier", diff --git a/platforms/fabric/src/main/resources/fabric.mod.json b/platforms/fabric/src/main/resources/fabric.mod.json index ffbc008b..7be16a7d 100644 --- a/platforms/fabric/src/main/resources/fabric.mod.json +++ b/platforms/fabric/src/main/resources/fabric.mod.json @@ -28,6 +28,7 @@ "LotuxPunk", "Madis0", "mpustovoi", + "N4TH4NOT", "notlin4", "parly", "Pixaurora", diff --git a/platforms/forge/src/main/resources/META-INF/mods.toml b/platforms/forge/src/main/resources/META-INF/mods.toml index 0655d54a..bfb8a903 100644 --- a/platforms/forge/src/main/resources/META-INF/mods.toml +++ b/platforms/forge/src/main/resources/META-INF/mods.toml @@ -23,6 +23,7 @@ Thanks to: DenaryDev dima-dencep EuropaYou + N4TH4NOT parly Pixaurora Setadokalo diff --git a/platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 9b498668..e0e5f335 100644 --- a/platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/platforms/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -23,6 +23,7 @@ Thanks to: DenaryDev dima-dencep EuropaYou + N4TH4NOT parly Pixaurora Setadokalo diff --git a/platforms/quilt/src/main/resources/quilt.mod.json b/platforms/quilt/src/main/resources/quilt.mod.json index 5d09ce1a..87613718 100644 --- a/platforms/quilt/src/main/resources/quilt.mod.json +++ b/platforms/quilt/src/main/resources/quilt.mod.json @@ -14,6 +14,7 @@ "DenaryDev": "Contributor", "dima-dencep": "Contributor", "EuropaYou": ["Contributor", "Translator"], + "N4TH4NOT": "Contributor", "parly": "Contributor", "Pixaurora": ["Contributor" ,"Translator"], "Setadokalo": "Contributor", From 6e81217d56b1660d4170793513a9bf749b35efae Mon Sep 17 00:00:00 2001 From: LostLuma Date: Sat, 1 Jun 2024 06:37:51 +0200 Subject: [PATCH 2/2] Don't update current volume on state change if transitions are off --- .../src/main/java/dynamic_fps/impl/DynamicFPSMod.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java b/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java index 8e1f3609..4057a459 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/DynamicFPSMod.java @@ -197,8 +197,10 @@ public static void handleStateChange(PowerState previous, PowerState current) { } // Update volume of current sounds for users not using smooth volume transition - for (SoundSource source : SoundSource.values()) { - ((DuckSoundEngine) minecraft.getSoundManager().soundEngine).dynamic_fps$updateVolume(source); + if (!volumeTransitionSpeed().isActive()) { + for (SoundSource source : SoundSource.values()) { + ((DuckSoundEngine) minecraft.getSoundManager().soundEngine).dynamic_fps$updateVolume(source); + } } if (before.graphicsState() != config.graphicsState()) {