Skip to content

Per/settings #117

Merged
merged 2 commits into from
May 15, 2026
Merged
Show file tree
Hide file tree
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
11 changes: 11 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/Audio.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package edu.ntnu.idi.idatt2003.gruppe42.Audio;

/**
* Represents a Audio enum.
*/
public enum Audio {

// Sound effects
Expand All @@ -19,10 +22,18 @@ public enum Audio {
this.isSFX = isSFX;
}

/**
* Returns the path.
*
* @return the value
*/
public String getPath() {
return path;
}

/**
* Issfx method.
*/
public boolean isSFX() {
return isSFX;
}
Expand Down
125 changes: 108 additions & 17 deletions src/main/java/edu/ntnu/idi/idatt2003/gruppe42/Audio/AudioManager.java
Original file line number Diff line number Diff line change
@@ -1,65 +1,156 @@
package edu.ntnu.idi.idatt2003.gruppe42.Audio;

import javax.sound.sampled.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineEvent;

/**
* Represents a AudioManager class.
*/
public final class AudioManager {

public class AudioManager {
private static final AudioManager INSTANCE = new AudioManager();

private static final AudioManager instance = new AudioManager();
private final Map<Audio, byte[]> sfxCache = new EnumMap<>(Audio.class);
private MediaPlayer bgPlayer;

private final DoubleProperty masterVolume = new SimpleDoubleProperty(0.8);
private final DoubleProperty sfxVolume = new SimpleDoubleProperty(0.8);

private AudioManager() {
for (Audio audio : Audio.values()) {
if (audio.isSFX()) {
try (var stream = getClass().getResourceAsStream(audio.getPath())) {
sfxCache.put(audio, stream.readAllBytes());
if (stream != null) {
sfxCache.put(audio, stream.readAllBytes());
}
} catch (IOException e) {
e.printStackTrace();
System.err.println("[Audio] Failed to cache " + audio + ": " + e.getMessage());
}
}
}

// Keep background music volume in sync with master × 0.5 (bg is quieter)
masterVolume.addListener((obs, ov, nv) -> {
if (bgPlayer != null) {
bgPlayer.setVolume(nv.doubleValue() * 0.5);
}
});
}

public static AudioManager getInstance() { return instance; }
public static AudioManager get() { return INSTANCE; }


public void playSFX(Audio audio) {
/**
* Playsfx method.
*/
public void playSFX(final Audio audio) {
byte[] data = sfxCache.get(audio);
if (data == null) return;

double volume = masterVolume.get() * sfxVolume.get();

new Thread(() -> {
try {
var stream = AudioSystem.getAudioInputStream(
new java.io.ByteArrayInputStream(data));
Clip clip = AudioSystem.getClip();
var bais = new java.io.ByteArrayInputStream(data);
var stream = AudioSystem.getAudioInputStream(bais);
Clip clip = AudioSystem.getClip();
clip.open(stream);

// Apply volume via MASTER_GAIN
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl gain = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
float db = volumeToDb(volume);
gain.setValue(Math.max(gain.getMinimum(), Math.min(gain.getMaximum(), db)));
}

clip.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP) clip.close();
});
clip.start();

} catch (Exception e) {
e.printStackTrace();
System.err.println("[Audio] SFX playback failed: " + e.getMessage());
}
}).start();
}, "sfx-thread").start();
}

public void playBgMusic(Audio audio) {

/**
* Playbgmusic method.
*/
public void playBgMusic(final Audio audio) {
stopBgMusic();
var url = getClass().getResource(audio.getPath()).toExternalForm();
String url = getClass().getResource(audio.getPath()).toExternalForm();
bgPlayer = new MediaPlayer(new Media(url));
bgPlayer.setVolume(0.5);
bgPlayer.setVolume(masterVolume.get() * 0.5);
bgPlayer.setCycleCount(MediaPlayer.INDEFINITE);
bgPlayer.play();
}

/**
* Stopbgmusic method.
*/
public void stopBgMusic() {
if (bgPlayer != null) {
bgPlayer.stop();
bgPlayer.dispose();
bgPlayer = null;
}
}


/**
* Mastervolumeproperty method.
*/
public DoubleProperty masterVolumeProperty() { return masterVolume; }
/**
* Sfxvolumeproperty method.
*/
public DoubleProperty sfxVolumeProperty() { return sfxVolume; }

/**
* Returns the mastervolume.
*
* @return the value
*/
public double getMasterVolume() { return masterVolume.get(); }
/**
* Returns the sfxvolume.
*
* @return the value
*/
public double getSfxVolume() { return sfxVolume.get(); }

/**
* Sets the mastervolume.
*
* @param value the new value
*/
public void setMasterVolume(final double v) { masterVolume.set(clamp(v)); }
/**
* Sets the sfxvolume.
*
* @param value the new value
*/
public void setSfxVolume(final double v) { sfxVolume.set(clamp(v)); }


/** Converts a 0.0–1.0 linear volume to decibels for FloatControl. */
private static float volumeToDb(final double volume) {
if (volume <= 0.0) return -80.0f;
return (float) (20.0 * Math.log10(volume));
}

private static double clamp(final double v) {
return Math.max(0.0, Math.min(1.0, v));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@

import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.AppStoreApp;

/**
* Controller for the App Store.
*/
/** Controller for the App Store. */
public class AppStoreController implements AppController {
/**
* Constructs a new AppStoreController.
*
* @param appStoreApp the app store view
*/
/** Constructs the controller. */
public AppStoreController(final AppStoreApp appStoreApp) {
}

/** Processes next tick. */
@Override
public void nextTick() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,14 @@
import edu.ntnu.idi.idatt2003.gruppe42.View.Apps.BankApp;
import javafx.application.Platform;

/**
* Controller for the {@link BankApp}.
* Handles updating the player's financial status and portfolio view.
*/
/** Controller for Bank app. */
public record BankAppController(BankApp bankApp, Player player) implements AppController {
/**
* Constructs a new BankAppController.
*
* @param bankApp the bank app view
* @param player the player model
*/
/** Constructs the controller. */
public BankAppController {
nextTick();
}

/** Processes next tick. */
@Override
public void nextTick() {
Platform.runLater(() -> {
Expand Down
Loading