diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index 636565c..ce1b23d 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -2,7 +2,6 @@ package org.toop;
import org.toop.app.App;
import org.toop.framework.audio.*;
-import org.toop.framework.audio.interfaces.VolumeManager;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
import org.toop.framework.resource.ResourceLoader;
@@ -25,10 +24,10 @@ public final class Main {
musicManager,
soundEffectManager,
new AudioVolumeManager()
- .registerManager(VolumeTypes.MASTERVOLUME, musicManager)
- .registerManager(VolumeTypes.MASTERVOLUME, soundEffectManager)
- .registerManager(VolumeTypes.FX, soundEffectManager)
- .registerManager(VolumeTypes.MUSIC, musicManager)
+ .registerManager(VolumeControl.MASTERVOLUME, musicManager)
+ .registerManager(VolumeControl.MASTERVOLUME, soundEffectManager)
+ .registerManager(VolumeControl.FX, soundEffectManager)
+ .registerManager(VolumeControl.MUSIC, musicManager)
).initListeners();
}).start();
}
diff --git a/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java b/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
index 86cda08..2f56052 100644
--- a/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
+++ b/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
@@ -57,25 +57,22 @@ public class AudioEventListener
Key responsibilities:
+ *Example usage:
+ *{@code
+ * AudioVolumeManager volumeManager = new AudioVolumeManager();
+ *
+ * // Register music manager to MUSIC volume type
+ * volumeManager.registerManager(VolumeControl.MUSIC, musicManager);
+ *
+ * // Set master volume to 80%
+ * volumeManager.setVolume(0.8, VolumeControl.MASTERVOLUME);
+ *
+ * // Set FX volume to 50% of master
+ * volumeManager.setVolume(0.5, VolumeControl.FX);
+ *
+ * // Retrieve current MUSIC volume
+ * double musicVol = volumeManager.getVolume(VolumeControl.MUSIC);
+ * }
+ */
public class AudioVolumeManager implements VolumeManager {
- public void setVolume(double newVolume, VolumeTypes type) {
- type.setVolume(newVolume, VolumeTypes.MASTERVOLUME.getVolume());
+ /**
+ * Sets the volume for a specific volume type.
+ * + * This method automatically takes into account the master volume + * for non-master types. + * + * @param newVolume the desired volume level (0.0 to 1.0) + * @param type the {@link VolumeControl} category to update + */ + @Override + public void setVolume(double newVolume, VolumeControl type) { + type.setVolume(newVolume, VolumeControl.MASTERVOLUME.getVolume()); } - public double getVolume(VolumeTypes type) { + /** + * Returns the current volume for the specified {@link VolumeControl} category. + * + * @param type the volume category + * @return the current volume (0.0 to 1.0) + */ + @Override + public double getVolume(VolumeControl type) { return type.getVolume(); } - public AudioVolumeManager registerManager(VolumeTypes type, AudioManager extends AudioResource> manager) { + /** + * Registers an {@link AudioManager} with the specified {@link VolumeControl} category. + *
+ * All active audio resources managed by the given {@link AudioManager} will + * automatically receive volume updates when the volume type changes. + * + * @param type the volume type to register the manager under + * @param manager the audio manager to register + * @return the current {@link AudioVolumeManager} instance (for method chaining) + */ + public AudioVolumeManager registerManager(VolumeControl type, AudioManager extends AudioResource> manager) { if (manager != null) { type.addManager(manager); } - return AudioVolumeManager.this; + return this; } - - @Override - public void updateAllVolumes() { - double masterVolume = VolumeTypes.MASTERVOLUME.getVolume(); - - for (VolumeTypes type : VolumeTypes.values()) { - if (type != VolumeTypes.MASTERVOLUME) { // skip master itself - type.setVolume(type.getVolume(), masterVolume); - } - } - } - -} \ No newline at end of file +} diff --git a/framework/src/main/java/org/toop/framework/audio/VolumeControl.java b/framework/src/main/java/org/toop/framework/audio/VolumeControl.java new file mode 100644 index 0000000..697ef76 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/audio/VolumeControl.java @@ -0,0 +1,159 @@ +package org.toop.framework.audio; + +import org.toop.framework.audio.interfaces.AudioManager; +import org.toop.framework.resource.types.AudioResource; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Enum representing different categories of audio volume in the application. + *
+ * Each volume type maintains its own volume level and a list of {@link AudioManager}s + * that manage audio resources of that type. The enum provides methods to set, get, + * and propagate volume changes, including master volume adjustments that automatically + * update dependent volume types (FX and MUSIC). + *
+ * + *Volume types:
+ *Key features:
+ *Example usage:
+ *{@code
+ * // Add a music manager to the MUSIC volume type
+ * VolumeControl.MUSIC.addManager(musicManager);
+ *
+ * // Set master volume to 80%
+ * VolumeControl.MASTERVOLUME.setVolume(0.8, 0);
+ *
+ * // Set FX volume to 50% of master
+ * VolumeControl.FX.setVolume(0.5, VolumeControl.MASTERVOLUME.getVolume());
+ *
+ * // Retrieve current music volume
+ * double musicVol = VolumeControl.MUSIC.getVolume();
+ * }
+ */
+public enum VolumeControl {
+ MASTERVOLUME(),
+ FX(),
+ MUSIC();
+
+ private final List+ * If this type is {@link #MASTERVOLUME}, all dependent volume types + * (FX, MUSIC, etc.) are automatically updated to reflect the new master volume. + * Otherwise, the volume is scaled by the provided master volume. + * + * @param newVolume the new volume level (0.0 to 1.0) + * @param currentMasterVolume the current master volume for scaling non-master types + */ + public void setVolume(double newVolume, double currentMasterVolume) { + this.volume = clamp(newVolume); + + if (this == MASTERVOLUME) { + for (VolumeControl type : VolumeControl.values()) { + if (type != MASTERVOLUME) { + type.masterVolume = this.volume; + type.broadcastVolume(type.computeEffectiveVolume()); + } + } + } else { + this.masterVolume = clamp(currentMasterVolume); + broadcastVolume(computeEffectiveVolume()); + } + } + + /** + * Computes the effective volume for this type, taking into account + * the master volume if this is not {@link #MASTERVOLUME}. + * + * @return the effective volume (0.0 to 1.0) + */ + private double computeEffectiveVolume() { + return (this == MASTERVOLUME) ? volume : volume * masterVolume; + } + + /** + * Updates all registered audio managers with the given effective volume. + * + * @param effectiveVolume the volume to apply to all active audio resources + */ + private void broadcastVolume(double effectiveVolume) { + managers.stream() + .filter(Objects::nonNull) + .forEach(manager -> manager.getActiveAudio() + .forEach(aud -> aud.updateVolume(effectiveVolume))); + } + + /** + * Clamps a volume value to the valid range [0.0, 1.0]. + * + * @param vol the volume to clamp + * @return the clamped volume + */ + private double clamp(double vol) { + return Math.max(0, Math.min(vol, 1.0)); + } + + /** + * Gets the current volume for this type. + * + * @return the current volume (0.0 to 1.0) + */ + public double getVolume() { + return volume; + } + + /** + * Registers an {@link AudioManager} to this volume type. + *
+ * Duplicate managers are ignored. Managers will receive volume updates
+ * when this type's volume changes.
+ *
+ * @param manager the audio manager to register
+ */
+ public void addManager(AudioManager extends AudioResource> manager) {
+ if (manager != null && !managers.contains(manager)) {
+ managers.add(manager);
+ }
+ }
+
+ /**
+ * Removes a previously registered {@link AudioManager} from this type.
+ *
+ * @param manager the audio manager to remove
+ */
+ public void removeManager(AudioManager extends AudioResource> manager) {
+ if (manager != null) {
+ managers.remove(manager);
+ }
+ }
+
+ /**
+ * Returns an unmodifiable view of all registered audio managers for this type.
+ *
+ * @return a list of registered audio managers
+ */
+ public List
+ * Implementations of this interface are responsible for controlling the volume levels
+ * of different categories of audio (e.g., master volume, music, sound effects) and
+ * updating the associated audio managers or resources accordingly.
+ * Typical responsibilities include: Example usage:
+ *
+ *
+ * {@code
+ * VolumeManager volumeManager = ...;
+ * // Set master volume to 80%
+ * volumeManager.setVolume(0.8, VolumeControl.MASTERVOLUME);
+ *
+ * // Set music volume to 50% of master
+ * volumeManager.setVolume(0.5, VolumeControl.MUSIC);
+ *
+ * // Retrieve current FX volume
+ * double fxVolume = volumeManager.getVolume(VolumeControl.FX);
+ * }
+ */
public interface VolumeManager {
- void setVolume(double newVolume, VolumeTypes type);
- double getVolume(VolumeTypes type);
- void updateAllVolumes();
+
+ /**
+ *
+ * Sets the volume to for the specified {@link VolumeControl}.
+ *
+ * @param newVolume The volume to be set to.
+ * @param type The type of volume to change.
+ */
+ void setVolume(double newVolume, VolumeControl type);
+
+ /**
+ * Gets the current volume for the specified {@link VolumeControl}.
+ *
+ * @param type the type of volume to get.
+ * @return The volume as a {@link Double}
+ */
+ double getVolume(VolumeControl type);
}