package edu.gatech.cs2340.trydent;
import java.util.HashSet;
import java.util.Set;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
/**
* A basic class for using audio.
*/
public class Audio {
public static final String NO_MUSIC = "";
public static final double MIN_VOLUME = 0.0;
public static final double MAX_VOLUME = 1.0;
private static double masterVolume = MAX_VOLUME;
private static double musicMolume = MAX_VOLUME;
private static Set<MediaContainer> soundEffects = new HashSet<>();
private static MediaPlayer music;
private static boolean playing = true;
/**
* Sets the background music for the game.
* By default, plays immediately.
* @param musicFilename the path to the music file
*/
public static void setMusic(String musicFilename) {
if(music != null) {
music.stop();
music.dispose();
}
switch(musicFilename) {
case NO_MUSIC:
break;
default:
music = new MediaPlayer(new Media(musicFilename));
music.setCycleCount(MediaPlayer.INDEFINITE);
music.setVolume(masterVolume * musicMolume);
if(playing) music.play();
break;
}
}
/**
* Allows the creation of sound effects
* @param soundEffectFilename the path to the sound effect
* @return a builder object for creating sound effects
*/
public static SoundEffectBuilder createSoundEffect(String soundEffectFilename) {
return new SoundEffectBuilder(soundEffectFilename);
}
/**
* Sets the master volume, which affects both music and sound effects
* Volume is calculated by MasterVolumn * IndividualVolumn
* @param newMasterVolume the new master volume (should be between 0.0 and 1.0)
*/
public static void setMasterVolume(double newMasterVolume) {
masterVolume = newMasterVolume;
if(music != null) {
music.setVolume(masterVolume * musicMolume);
}
for(MediaContainer soundEffect : soundEffects) {
soundEffect.player.setVolume(masterVolume * soundEffect.playerVolume);
}
}
/**
* Sets the volume for music.
* This value is always multiplied by the master volume,
* so the resulting volumne is MasterVolume * new_music
* @param newMusicVolume the new music volume
*/
public static void setMusicVolume(double newMusicVolume) {
musicMolume = newMusicVolume;
if(music != null) {
music.setVolume(masterVolume * musicMolume);
}
}
/**
* Pauses all of the audio.
* This method is useful for when the game is paused or is minimized.
*/
public static void pauseAudio() {
playing = false;
if(music != null) {
music.pause();
}
for(MediaContainer soundEffect : soundEffects) {
soundEffect.player.pause();
}
}
/**
* Resumes audio or starts playing.
*/
public static void resumeAudio() {
playing = true;
if(music != null) {
music.play();
}
for(MediaContainer soundEffect : soundEffects) {
soundEffect.player.play();
}
}
/**
* Determines if the audio is currently playing.
* @return true if currently playing
*/
public static boolean isPlaying() {
return playing;
}
/**
* A builder class for creating sound effects.
*/
public static class SoundEffectBuilder {
private MediaPlayer soundEffect;
private double volume;
private Runnable callback;
private SoundEffectBuilder(String soundEffectFilename) {
soundEffect = new MediaPlayer(new Media(soundEffectFilename));
soundEffect.setCycleCount(1);
soundEffect.setVolume(masterVolume);
callback = () -> {};
}
/**
* This method sets the balance for a sound effect.
* This can be used to add a sense of direction to sound.
* @param balance a value between -1.0 (left) to 1.0 (right)
* @return current sound effect being built
*/
public SoundEffectBuilder setBalance(double balance) {
soundEffect.setBalance(balance);
return this;
}
/**
* Sets the volume of the sound effect.
* There is an implicit multiplication by the master volume
* @param volume the volume value between 0.0 and 1.0
* @return current sound effect being built
*/
public SoundEffectBuilder setVolume(double volume) {
this.volume = volume;
soundEffect.setVolume(masterVolume * volume);
return this;
}
/**
* Sets a callback for when the sound effect finishes.
* @param callback some action to occur when the sound effect finishes playing
* @return current sound effect being built
*/
public SoundEffectBuilder setCallback(Runnable callback) {
this.callback = callback;
return this;
}
/**
* Plays the built sound effect.
*/
public void play() {
soundEffects.add(new MediaContainer(soundEffect, volume));
if(playing) soundEffect.play();
soundEffect.setOnEndOfMedia(() -> {
TrydentEngine.runOnce(() -> {
soundEffects.remove(soundEffect);
soundEffect.dispose();
callback.run();
});
});
}
}
private static class MediaContainer {
MediaPlayer player;
double playerVolume;
MediaContainer(MediaPlayer player, double playerVolume) {
this.player = player;
this.playerVolume = playerVolume;
}
}
}