From 0d896d6aad31e0fa8855db468105dead607de36c Mon Sep 17 00:00:00 2001 From: Jona Walpert Date: Thu, 30 Apr 2026 11:33:39 +0200 Subject: [PATCH] Add: Add sound manager to play sounds --- .../cs108/casono/ui/sound/SoundManager.java | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/ui/sound/SoundManager.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/ui/sound/SoundManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/ui/sound/SoundManager.java new file mode 100644 index 0000000..d0100b4 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/ui/sound/SoundManager.java @@ -0,0 +1,176 @@ +package ch.unibas.dmi.dbis.cs108.casono.ui.sound; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import javafx.animation.PauseTransition; +import javafx.application.Platform; +import javafx.scene.media.AudioClip; +import javafx.util.Duration; + +/** + * Manages sound effects for the Casono game. Provides methods to play sounds for card reveals and + * button clicks. + */ +public class SoundManager { + private static final SoundManager INSTANCE = new SoundManager(); + private final Map soundCache = new HashMap<>(); + private float volume = 0.5f; // Volume level 0.0 - 1.0 + private static final long CARD_REVEAL_STAGGER_MS = 120; // Delay between card reveal sounds + + private SoundManager() {} + + /** Returns the singleton instance of SoundManager. */ + public static SoundManager getInstance() { + return INSTANCE; + } + + /** + * Pre-loads critical sounds into cache to avoid delays on first play. Should be called at + * application startup. + */ + public void preloadSounds() { + try { + getOrLoadSound("button-click"); + getOrLoadSound("card-reveal"); + } catch (Exception e) { + System.err.println("Error preloading sounds: " + e.getMessage()); + } + } + + /** Plays a button click sound. */ + public void playButtonClick() { + playSound("button-click"); + } + + /** Plays a card reveal sound (for dealt or community cards). */ + public void playCardReveal() { + playSound("card-reveal"); + } + + /** + * Plays multiple card reveal sounds in sequence with slight stagger/overlap. Used when multiple + * cards are revealed at once (e.g., Flop reveals 3 cards, Turn reveals 1 card). Sounds play + * with a slight delay between them to simulate a person revealing cards one by one. + * + * @param count The number of cards being revealed. Each card gets its own sound with stagger. + */ + public void playCardRevealMultiple(int count) { + if (count <= 0) { + return; + } + if (count == 1) { + playCardReveal(); + return; + } + + for (int i = 0; i < count; i++) { + final int cardIndex = i; + long delayMs = cardIndex * CARD_REVEAL_STAGGER_MS; + + PauseTransition pause = new PauseTransition(Duration.millis(delayMs)); + pause.setOnFinished( + event -> + Platform.runLater( + () -> { + try { + playCardReveal(); + } catch (Exception e) { + System.err.println( + "Error playing card reveal sound: " + + e.getMessage()); + } + })); + pause.play(); + } + } + + /** Plays a card shuffle sound. */ + public void playCardShuffle() { + playSound("card-shuffle"); + } + + /** Plays a pot/chip sound. */ + public void playChip() { + playSound("chip"); + } + + /** + * Plays a generic sound by key. The sound file should exist at: /sounds/{category}/{key}.mp3 or + * .wav + */ + public void playSound(String soundKey) { + try { + AudioClip audioClip = getOrLoadSound(soundKey); + if (audioClip != null) { + audioClip.setVolume(volume); + audioClip.play(); + } + } catch (Exception e) { + System.err.println("Error playing sound: " + soundKey); + e.printStackTrace(); + } + } + + /** Gets or loads a sound from cache. Categorizes sounds automatically based on key prefix. */ + private AudioClip getOrLoadSound(String soundKey) { + if (soundCache.containsKey(soundKey)) { + return soundCache.get(soundKey); + } + + String category = categorizeSound(soundKey); + String resourcePath = String.format("/sounds/%s/%s.wav", category, soundKey); + + try { + URL soundUrl = getClass().getResource(resourcePath); + if (soundUrl != null) { + AudioClip clip = new AudioClip(soundUrl.toString()); + soundCache.put(soundKey, clip); + return clip; + } else { + System.err.println("Sound resource not found: " + resourcePath); + return null; + } + } catch (Exception e) { + System.err.println("Failed to load sound: " + resourcePath); + e.printStackTrace(); + return null; + } + } + + /** Categorizes a sound based on its key prefix. */ + private String categorizeSound(String soundKey) { + if (soundKey.startsWith("button")) { + return "buttons"; + } else if (soundKey.startsWith("card")) { + return "cards"; + } else { + return "game"; + } + } + + /** Sets the volume for sound effects (0.0 - 1.0). */ + public void setVolume(float volume) { + this.volume = Math.max(0.0f, Math.min(1.0f, volume)); + } + + /** Gets the current volume level. */ + public float getVolume() { + return volume; + } + + /** Clears the sound cache. */ + public void clearCache() { + soundCache.clear(); + } + + /** Mutes all sounds by setting volume to 0. */ + public void mute() { + setVolume(0.0f); + } + + /** Unmutes sounds by restoring to default volume. */ + public void unmute() { + setVolume(0.5f); + } +}