diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java index b223e5f..3372086 100644 --- a/app/src/main/java/org/toop/Main.java +++ b/app/src/main/java/org/toop/Main.java @@ -6,15 +6,11 @@ import org.toop.framework.audio.AudioEventListener; import org.toop.framework.audio.AudioVolumeManager; import org.toop.framework.audio.MusicManager; import org.toop.framework.audio.SoundEffectManager; -import org.toop.framework.audio.interfaces.AudioManager; -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; import org.toop.framework.resource.ResourceManager; -import javax.sound.sampled.Clip; - public final class Main { static void main(String[] args) { initSystems(); @@ -25,8 +21,8 @@ public final class Main { ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets")); new Thread(NetworkingClientManager::new).start(); new Thread(() -> { - AudioEventListener a = - new AudioEventListener( + AudioEventListener a = + new AudioEventListener<>( new MusicManager(), new SoundEffectManager(), new AudioVolumeManager() diff --git a/app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav b/app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav new file mode 100644 index 0000000..f55d5d0 Binary files /dev/null and b/app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav differ diff --git a/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 b/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 new file mode 100644 index 0000000..7105fde Binary files /dev/null and b/app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3 differ 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 962ea9c..a29939f 100644 --- a/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java +++ b/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java @@ -5,15 +5,16 @@ import org.toop.framework.audio.interfaces.MusicManager; import org.toop.framework.audio.interfaces.SoundEffectManager; import org.toop.framework.audio.interfaces.VolumeManager; import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.resource.types.AudioResource; -public class AudioEventListener { - private final MusicManager musicManager; - private final SoundEffectManager soundEffectManager; +public class AudioEventListener { + private final MusicManager musicManager; + private final SoundEffectManager soundEffectManager; private final VolumeManager audioVolumeManager; public AudioEventListener( - MusicManager musicManager, - SoundEffectManager soundEffectManager, + MusicManager musicManager, + SoundEffectManager soundEffectManager, VolumeManager audioVolumeManager ) { this.musicManager = musicManager; diff --git a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java index b2edd79..603734d 100644 --- a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java +++ b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java @@ -1,10 +1,8 @@ package org.toop.framework.audio; -import javafx.scene.media.MediaPlayer; -import javax.sound.sampled.Clip; -import javax.sound.sampled.FloatControl; import org.toop.framework.audio.interfaces.AudioManager; import org.toop.framework.audio.interfaces.VolumeManager; +import org.toop.framework.resource.types.AudioResource; public class AudioVolumeManager implements VolumeManager { private double volume = 0.0; @@ -13,54 +11,39 @@ public class AudioVolumeManager implements VolumeManager { public AudioVolumeManager() {} - private void updateMusicVolume(T mediaPlayer) { - mediaPlayer.setVolume(this.musicVolume * this.volume); - } - - private void updateSoundEffectVolume(T clip) { - if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) { - FloatControl volumeControl = - (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); - float min = volumeControl.getMinimum(); - float max = volumeControl.getMaximum(); - float dB = - (float) - (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001)) - * 20.0); // convert linear to dB - dB = Math.max(min, Math.min(max, dB)); - volumeControl.setValue(dB); - } + private void updateVolume(T resource, double level) { + resource.updateVolume(level); } private double limitVolume(double volume) { - if (volume > 1.0) return 1.0; - else return Math.max(volume, 0.0); + return Math.min(1.0, Math.max(0.0, volume / 100)); } @Override - public void setVolume(double newVolume, AudioManager sm, AudioManager mm) { - this.volume = limitVolume(newVolume / 100); - for (var clip : sm.getActiveAudio()) { - this.updateSoundEffectVolume((Clip) clip); + public void setVolume( + double newVolume, AudioManager sm, AudioManager mm) { + this.volume = limitVolume(newVolume); + for (T clip : sm.getActiveAudio()) { + this.updateVolume(clip, fxVolume * volume); } - for (var mediaPlayer : mm.getActiveAudio()) { - this.updateMusicVolume((MediaPlayer) mediaPlayer); + for (K mediaPlayer : mm.getActiveAudio()) { + this.updateVolume(mediaPlayer, musicVolume * volume); } } @Override - public void setFxVolume(double newVolume, AudioManager sm) { - this.fxVolume = limitVolume(newVolume / 100); - for (var clip : sm.getActiveAudio()) { - this.updateSoundEffectVolume((Clip) clip); // TODO: What if not clip + public void setFxVolume(double newVolume, AudioManager sm) { + this.fxVolume = limitVolume(newVolume); + for (T clip : sm.getActiveAudio()) { + this.updateVolume(clip, fxVolume * volume); } } @Override - public void setMusicVolume(double newVolume, AudioManager mm) { - this.musicVolume = limitVolume(newVolume / 100); - for (var mediaPlayer : mm.getActiveAudio()) { - this.updateMusicVolume((MediaPlayer) mediaPlayer); // TODO; What if not MediaPlayer + public void setMusicVolume(double newVolume, AudioManager mm) { + this.musicVolume = limitVolume(newVolume); + for (T mediaPlayer : mm.getActiveAudio()) { + this.updateVolume(mediaPlayer, musicVolume * volume); } } diff --git a/framework/src/main/java/org/toop/framework/audio/MusicManager.java b/framework/src/main/java/org/toop/framework/audio/MusicManager.java index e0ad1b3..4b2b87e 100644 --- a/framework/src/main/java/org/toop/framework/audio/MusicManager.java +++ b/framework/src/main/java/org/toop/framework/audio/MusicManager.java @@ -1,7 +1,5 @@ package org.toop.framework.audio; -import javafx.scene.media.MediaPlayer; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.resource.ResourceManager; @@ -9,35 +7,34 @@ import org.toop.framework.resource.resources.MusicAsset; import java.util.*; -public class MusicManager implements org.toop.framework.audio.interfaces.MusicManager { +public class MusicManager implements org.toop.framework.audio.interfaces.MusicManager { private static final Logger logger = LogManager.getLogger(MusicManager.class); -// private final List musicAssets = new ArrayList<>(); // TODO - private final List backgroundMusic = new LinkedList<>(); + private final List backgroundMusic = new LinkedList<>(); private int playingIndex = 0; private boolean playing = false; public MusicManager() {} @Override - public Collection getActiveAudio() { + public Collection getActiveAudio() { return backgroundMusic; } private void addBackgroundMusic(MusicAsset musicAsset) { - backgroundMusic.add(new MediaPlayer(musicAsset.getMedia())); + backgroundMusic.add(musicAsset); } - private void addBackgroundMusic(MediaPlayer mediaPlayer) { - backgroundMusic.add(mediaPlayer); - } - - public void play() { // TODO maybe remove VolumeManager from input + public void play() { + if (playing) { + logger.warn("MusicManager is already playing."); + return; + } backgroundMusic.clear(); - List shuffledArray = + List shuffledArray = new ArrayList<>( ResourceManager.getAllOfType(MusicAsset.class).stream() .map(ma -> - initMediaPlayer(new MediaPlayer(ma.getResource().getMedia()))) + initMediaPlayer(ma.getResource())) .toList()); Collections.shuffle(shuffledArray); backgroundMusic.addAll(shuffledArray); @@ -50,35 +47,35 @@ public class MusicManager implements org.toop.framework.audio.interfaces.MusicMa playingIndex = 0; } - MediaPlayer ma = backgroundMusic.get(playingIndex); + MusicAsset ma = backgroundMusic.get(playingIndex); if (ma == null) { logger.error("Background music player is null. Queue: {}", - backgroundMusic.stream().map(e -> e.getMedia().getSource())); + backgroundMusic.stream().map(e -> e.getMediaPlayer().getMedia().getSource())); return; } - logger.info("Background music player is playing: {}", ma.getMedia().getSource()); //TODO shorten to name - ma.play(); + logger.info("Background music player is playing: {}", ma.getMediaPlayer().getMedia().getSource()); //TODO shorten to name + ma.getMediaPlayer().play(); this.playing = true; } - private MediaPlayer initMediaPlayer(MediaPlayer mediaPlayer) { - mediaPlayer.setOnEndOfMedia(mediaPlayer::stop); + private MusicAsset initMediaPlayer(MusicAsset ma) { + ma.getMediaPlayer().setOnEndOfMedia(() -> ma.getMediaPlayer().stop()); - mediaPlayer.setOnError( () -> { - logger.error("Error playing music: {}", mediaPlayer.getMedia().getSource()); - backgroundMusic.remove(mediaPlayer); - mediaPlayer.stop(); + ma.getMediaPlayer().setOnError( () -> { + logger.error("Error playing music: {}", ma.getMediaPlayer().getError()); // TODO + backgroundMusic.remove(ma); + ma.getMediaPlayer().stop(); }); - mediaPlayer.setOnStopped( () -> { - mediaPlayer.stop(); + ma.getMediaPlayer().setOnStopped( () -> { + ma.getMediaPlayer().stop(); playingIndex++; this.playing = false; backgroundMusicPlayer(); }); - return mediaPlayer; + return ma; } } diff --git a/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java b/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java index 4bfea79..00d1a2b 100644 --- a/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java +++ b/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java @@ -2,18 +2,17 @@ package org.toop.framework.audio; import org.toop.framework.resource.resources.SoundEffectAsset; -import javax.sound.sampled.Clip; import java.util.Collection; import java.util.HashMap; import java.util.Map; -public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager { - private final Map activeSoundEffects = new HashMap<>(); +public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager { + private final Map activeSoundEffects = new HashMap<>(); private final HashMap audioResources = new HashMap<>(); - - public Collection getActiveAudio() { - return this.activeSoundEffects.values(); + @Override + public Collection getActiveAudio() { + return this.audioResources.values(); } @Override diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java index 3c7635d..928bb83 100644 --- a/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java +++ b/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java @@ -1,5 +1,7 @@ package org.toop.framework.audio.interfaces; -public interface MusicManager extends AudioManager { +import org.toop.framework.resource.types.AudioResource; + +public interface MusicManager extends AudioManager { void play(); } diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java index ef6c1b5..3beddc3 100644 --- a/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java +++ b/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java @@ -1,6 +1,9 @@ package org.toop.framework.audio.interfaces; -public interface SoundEffectManager extends AudioManager { +import org.toop.framework.resource.resources.SoundEffectAsset; +import org.toop.framework.resource.types.AudioResource; + +public interface SoundEffectManager extends AudioManager { void play(String name, boolean loop); void stop(long clipId); } diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java index 4d8ed32..e1bc077 100644 --- a/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java +++ b/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java @@ -1,9 +1,11 @@ package org.toop.framework.audio.interfaces; +import org.toop.framework.resource.types.AudioResource; + public interface VolumeManager { - void setVolume(double newVolume, AudioManager sm, AudioManager mm); - void setFxVolume(double newVolume, AudioManager sm); - void setMusicVolume(double newVolume, AudioManager mm); + void setVolume(double newVolume, AudioManager sm, AudioManager mm); + void setFxVolume(double newVolume, AudioManager sm); + void setMusicVolume(double newVolume, AudioManager mm); double getVolume(); double getFxVolume(); double getMusicVolume(); diff --git a/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java index d60b6bc..45423ee 100644 --- a/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java +++ b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java @@ -2,33 +2,39 @@ package org.toop.framework.resource.resources; import java.io.*; import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import org.toop.framework.resource.types.AudioResource; import org.toop.framework.resource.types.FileExtension; import org.toop.framework.resource.types.LoadableResource; @FileExtension({"mp3"}) -public class MusicAsset extends BaseResource implements LoadableResource { - private Media media; +public class MusicAsset extends BaseResource implements LoadableResource, AudioResource { + private MediaPlayer mediaPlayer; public MusicAsset(final File audioFile) { super(audioFile); } - public Media getMedia() { - if (media == null) { - media = new Media(file.toURI().toString()); - } - return media; + public MediaPlayer getMediaPlayer() { + load(); + return mediaPlayer; } @Override public void load() { - if (media == null) media = new Media(file.toURI().toString()); + if (mediaPlayer == null) { + mediaPlayer = new MediaPlayer(new Media(file.toURI().toString())); + } this.isLoaded = true; } @Override public void unload() { - media = null; + if (mediaPlayer != null) { + mediaPlayer.stop(); + mediaPlayer.dispose(); + mediaPlayer = null; + } isLoaded = false; } @@ -36,4 +42,11 @@ public class MusicAsset extends BaseResource implements LoadableResource { public boolean isLoaded() { return isLoaded; } + + @Override + public void updateVolume(double volume) { + if (mediaPlayer != null) { + mediaPlayer.setVolume(volume); + } + } } diff --git a/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java index c55306a..b0428e0 100644 --- a/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java +++ b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java @@ -3,12 +3,15 @@ package org.toop.framework.resource.resources; import java.io.*; import java.nio.file.Files; import javax.sound.sampled.*; + +import org.toop.framework.resource.types.AudioResource; import org.toop.framework.resource.types.FileExtension; import org.toop.framework.resource.types.LoadableResource; @FileExtension({"wav"}) -public class SoundEffectAsset extends BaseResource implements LoadableResource { +public class SoundEffectAsset extends BaseResource implements LoadableResource, AudioResource { private byte[] rawData; + private Clip clip = null; public SoundEffectAsset(final File audioFile) { super(audioFile); @@ -27,6 +30,7 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { inputStream = downSampleAudio(inputStream, baseFormat); clip.open( inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary. + this.clip = clip; return clip; } @@ -77,4 +81,22 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { public boolean isLoaded() { return this.isLoaded; } + + @Override + public void updateVolume(double volume) { + { + if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) { + FloatControl volumeControl = + (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); + float min = volumeControl.getMinimum(); + float max = volumeControl.getMaximum(); + float dB = + (float) + (Math.log10(Math.max(volume, 0.0001)) + * 20.0); // convert linear to dB + dB = Math.max(min, Math.min(max, dB)); + volumeControl.setValue(dB); + } + } + } } diff --git a/framework/src/main/java/org/toop/framework/resource/types/AudioResource.java b/framework/src/main/java/org/toop/framework/resource/types/AudioResource.java new file mode 100644 index 0000000..9ea1c21 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/resource/types/AudioResource.java @@ -0,0 +1,6 @@ +package org.toop.framework.resource.types; + +public interface AudioResource { + void updateVolume(double volume); + // TODO play and stop +}