diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java
index 931bd2c..0602577 100644
--- a/app/src/main/java/org/toop/app/App.java
+++ b/app/src/main/java/org/toop/app/App.java
@@ -10,10 +10,11 @@ import javafx.scene.Scene;
import javafx.stage.Stage;
import org.toop.framework.asset.AssetManager;
import org.toop.framework.asset.resources.LocalizationAsset;
+import org.toop.framework.audio.events.AudioEvents;
+import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
import java.util.Locale;
-import java.util.ResourceBundle;
public class App extends Application {
private static Stage stage;
@@ -49,6 +50,9 @@ public class App extends Application {
App.stage = stage;
App.scene = scene;
App.root = root;
+
+ new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).postEvent();
+ new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(0.3)).postEvent();
}
public static void activate(Menu menu) {
diff --git a/app/src/main/resources/assets/audio/dramatic.wav b/app/src/main/resources/assets/audio/fx/dramatic.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/dramatic.wav
rename to app/src/main/resources/assets/audio/fx/dramatic.wav
diff --git a/app/src/main/resources/assets/audio/hitsound0.wav b/app/src/main/resources/assets/audio/fx/hitsound0.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/hitsound0.wav
rename to app/src/main/resources/assets/audio/fx/hitsound0.wav
diff --git a/app/src/main/resources/assets/audio/hitsound1.wav b/app/src/main/resources/assets/audio/fx/hitsound1.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/hitsound1.wav
rename to app/src/main/resources/assets/audio/fx/hitsound1.wav
diff --git a/app/src/main/resources/assets/audio/mainmenu.wav b/app/src/main/resources/assets/audio/fx/mainmenu.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/mainmenu.wav
rename to app/src/main/resources/assets/audio/fx/mainmenu.wav
diff --git a/app/src/main/resources/assets/audio/sadtrombone.wav b/app/src/main/resources/assets/audio/fx/sadtrombone.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/sadtrombone.wav
rename to app/src/main/resources/assets/audio/fx/sadtrombone.wav
diff --git a/app/src/main/resources/assets/audio/scawymusic.wav b/app/src/main/resources/assets/audio/fx/scawymusic.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/scawymusic.wav
rename to app/src/main/resources/assets/audio/fx/scawymusic.wav
diff --git a/app/src/main/resources/assets/audio/suspensful.wav b/app/src/main/resources/assets/audio/fx/suspensful.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/suspensful.wav
rename to app/src/main/resources/assets/audio/fx/suspensful.wav
diff --git a/app/src/main/resources/assets/audio/testsound.wav b/app/src/main/resources/assets/audio/fx/testsound.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/testsound.wav
rename to app/src/main/resources/assets/audio/fx/testsound.wav
diff --git a/app/src/main/resources/assets/audio/winsound.wav b/app/src/main/resources/assets/audio/fx/winsound.wav
similarity index 100%
rename from app/src/main/resources/assets/audio/winsound.wav
rename to app/src/main/resources/assets/audio/fx/winsound.wav
diff --git a/app/src/main/resources/assets/audio/music/damned.mp3 b/app/src/main/resources/assets/audio/music/damned.mp3
new file mode 100644
index 0000000..4b0bfbc
Binary files /dev/null and b/app/src/main/resources/assets/audio/music/damned.mp3 differ
diff --git a/app/src/main/resources/assets/audio/music/extraction-point.mp3 b/app/src/main/resources/assets/audio/music/extraction-point.mp3
new file mode 100644
index 0000000..1fc6430
Binary files /dev/null and b/app/src/main/resources/assets/audio/music/extraction-point.mp3 differ
diff --git a/app/src/main/resources/assets/audio/music/godfrey.mp3 b/app/src/main/resources/assets/audio/music/godfrey.mp3
new file mode 100644
index 0000000..7cd1a30
Binary files /dev/null and b/app/src/main/resources/assets/audio/music/godfrey.mp3 differ
diff --git a/app/src/main/resources/assets/audio/music/mw2-main-menu.mp3 b/app/src/main/resources/assets/audio/music/mw2-main-menu.mp3
new file mode 100644
index 0000000..5228853
Binary files /dev/null and b/app/src/main/resources/assets/audio/music/mw2-main-menu.mp3 differ
diff --git a/framework/pom.xml b/framework/pom.xml
index 5e84787..b32b25c 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -102,6 +102,14 @@
25
+
+
+ org.openjfx
+ javafx-media
+ 25
+
+
+
org.reflections
@@ -122,24 +130,6 @@
25
25
UTF-8
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/framework/src/main/java/org/toop/framework/asset/AssetLoader.java b/framework/src/main/java/org/toop/framework/asset/AssetLoader.java
index cfc04d6..93ac189 100644
--- a/framework/src/main/java/org/toop/framework/asset/AssetLoader.java
+++ b/framework/src/main/java/org/toop/framework/asset/AssetLoader.java
@@ -60,7 +60,23 @@ public class AssetLoader {
List foundFiles = new ArrayList<>();
fileSearcher(rootFolder, foundFiles);
this.totalCount = foundFiles.size();
+
+ // measure memory before loading
+ long before = getUsedMemory();
+
loader(foundFiles);
+
+ // ~measure memory after loading
+ long after = getUsedMemory();
+ long used = after - before;
+
+ logger.info("Total files loaded: {}", this.totalCount);
+ logger.info("Memory used by assets: ~{} MB", used / (1024 * 1024));
+ }
+
+ private static long getUsedMemory() {
+ Runtime runtime = Runtime.getRuntime();
+ return runtime.totalMemory() - runtime.freeMemory();
}
/**
@@ -128,6 +144,7 @@ public class AssetLoader {
"File " + file.getName() + " is not of type " + type.getSimpleName()
);
}
+
return type.cast(resource);
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
new file mode 100644
index 0000000..87c27f6
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
@@ -0,0 +1,38 @@
+package org.toop.framework.asset.resources;
+
+import javafx.scene.media.Media;
+
+import java.io.*;
+
+@FileExtension({"mp3"})
+public class MusicAsset extends BaseResource implements LoadableResource {
+ private Media media;
+
+ public MusicAsset(final File audioFile) {
+ super(audioFile);
+ }
+
+ public Media getMedia() {
+ if (media == null) {
+ media = new Media(file.toURI().toString());
+ }
+ return media;
+ }
+
+ @Override
+ public void load() {
+ if (media == null) media = new Media(file.toURI().toString());
+ this.isLoaded = true;
+ }
+
+ @Override
+ public void unload() {
+ media = null;
+ isLoaded = false;
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return isLoaded;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/AudioAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
similarity index 86%
rename from framework/src/main/java/org/toop/framework/asset/resources/AudioAsset.java
rename to framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
index 4b00212..c9c81a8 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/AudioAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
@@ -1,12 +1,15 @@
package org.toop.framework.asset.resources;
+import javafx.scene.media.Media;
+
import javax.sound.sampled.*;
import java.io.*;
+import java.net.URI;
@FileExtension({"wav"})
-public class AudioAsset extends BaseResource implements LoadableResource {
+public class SoundEffectAsset extends BaseResource implements LoadableResource {
- public AudioAsset(final File audioFile) {
+ public SoundEffectAsset(final File audioFile) {
super(audioFile);
}
diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
index 2727358..6a508cc 100644
--- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
@@ -1,9 +1,12 @@
package org.toop.framework.audio;
+import javafx.application.Platform;
+import javafx.scene.media.MediaPlayer;
import org.toop.framework.SnowflakeGenerator;
import org.toop.framework.asset.Asset;
import org.toop.framework.asset.AssetManager;
-import org.toop.framework.asset.resources.AudioAsset;
+import org.toop.framework.asset.resources.MusicAsset;
+import org.toop.framework.asset.resources.SoundEffectAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
@@ -12,13 +15,17 @@ import java.util.*;
import javax.sound.sampled.*;
public class SoundManager {
- private final Map activeClips = new HashMap<>();
- private final HashMap audioResources = new HashMap<>();
+ private final List activeMusic = new ArrayList<>();
+ private final Queue backgroundMusicQueue = new LinkedList<>();
+ private final Map activeSoundEffects = new HashMap<>();
+ private final HashMap audioResources = new HashMap<>();
private final SnowflakeGenerator idGenerator = new SnowflakeGenerator(); // TODO: Don't create a new generator
+ private double volume = 1.0;
+
public SoundManager() {
// Get all Audio Resources and add them to a list.
- for (Asset asset : AssetManager.getAllOfType(AudioAsset.class)) {
+ for (Asset asset : AssetManager.getAllOfType(SoundEffectAsset.class)) {
try {
this.addAudioResource(asset);
} catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
@@ -27,12 +34,21 @@ public class SoundManager {
}
new EventFlow()
.listen(this::handlePlaySound)
- .listen(this::handleStopSound);
+ .listen(this::handleStopSound)
+ .listen(this::handleMusicStart)
+ .listen(this::handleVolumeChange)
+ .listen(AudioEvents.playOnClickButton.class, _ -> {
+ try {
+ playSound("hitsound0.wav", false);
+ } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
}
private void handlePlaySound(AudioEvents.PlayAudio event) {
try {
- this.playSound(event.fileNameNoExtensionAndNoDirectory(), event.loop());
+ this.playSound(event.fileName(), event.loop());
} catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
throw new RuntimeException(e);
}
@@ -42,14 +58,70 @@ public class SoundManager {
this.stopSound(event.clipId());
}
- private void addAudioResource(Asset audioAsset)
+ private void addAudioResource(Asset audioAsset)
throws IOException, UnsupportedAudioFileException, LineUnavailableException {
this.audioResources.put(audioAsset.getName(), audioAsset.getResource());
}
+ private void handleVolumeChange(AudioEvents.ChangeVolume event) {
+ if (event.newVolume() > 1.0) this.volume = 1.0;
+ else this.volume = Math.max(event.newVolume(), 0.0);
+ for (MediaPlayer mediaPlayer : this.activeMusic) {
+ mediaPlayer.setVolume(this.volume);
+ }
+ }
+
+ private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
+ backgroundMusicQueue.clear();
+ Platform.runLater(() -> {
+ backgroundMusicQueue.addAll(
+ AssetManager.getAllOfType(MusicAsset.class).stream()
+ .map(Asset::getResource)
+ .toList()
+ );
+ backgroundMusicPlayer();
+ });
+
+ }
+
+ private void addBackgroundMusic(MusicAsset musicAsset) {
+ backgroundMusicQueue.add(musicAsset);
+ }
+
+ private void backgroundMusicPlayer() {
+ MusicAsset ma = backgroundMusicQueue.poll();
+ if (ma == null) return;
+
+ MediaPlayer mediaPlayer = new MediaPlayer(ma.getMedia());
+
+ mediaPlayer.setOnEndOfMedia(() -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ mediaPlayer.dispose();
+ ma.unload();
+ backgroundMusicPlayer(); // play next
+ });
+
+ mediaPlayer.setOnStopped(() -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ ma.unload();
+ });
+
+ mediaPlayer.setOnError(() -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ ma.unload();
+ });
+
+ mediaPlayer.setVolume(this.volume);
+ mediaPlayer.play();
+ activeMusic.add(mediaPlayer);
+ }
+
private long playSound(String audioFileName, boolean loop) throws UnsupportedAudioFileException, LineUnavailableException, IOException {
- AudioAsset asset = audioResources.get(audioFileName);
+ SoundEffectAsset asset = audioResources.get(audioFileName);
// Return -1 which indicates resource wasn't available
if (asset == null){
@@ -71,12 +143,12 @@ public class SoundManager {
long clipId = idGenerator.nextId();
// store it so we can stop it later
- activeClips.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
+ activeSoundEffects.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
// remove when finished (only for non-looping sounds)
clip.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
- activeClips.remove(clipId);
+ activeSoundEffects.remove(clipId);
clip.close();
}
});
@@ -86,7 +158,7 @@ public class SoundManager {
}
public void stopSound(long clipId) {
- Clip clip = activeClips.get(clipId);
+ Clip clip = activeSoundEffects.get(clipId);
if (clip == null) {
return;
@@ -94,14 +166,14 @@ public class SoundManager {
clip.stop();
clip.close();
- activeClips.remove(clipId);
+ activeSoundEffects.remove(clipId);
}
public void stopAllSounds() {
- for (Clip clip : activeClips.values()) {
+ for (Clip clip : activeSoundEffects.values()) {
clip.stop();
clip.close();
}
- activeClips.clear();
+ activeSoundEffects.clear();
}
}
diff --git a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
index 74bc41a..11ca3df 100644
--- a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
+++ b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
@@ -1,12 +1,17 @@
package org.toop.framework.audio.events;
+import org.toop.framework.asset.resources.MusicAsset;
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
import org.toop.framework.eventbus.events.EventsBase;
public class AudioEvents extends EventsBase {
/** Starts playing a sound. */
- public record PlayAudio(String fileNameNoExtensionAndNoDirectory, boolean loop)
+ public record PlayAudio(String fileName, boolean loop)
implements EventWithoutSnowflake {}
public record StopAudio(long clipId) implements EventWithoutSnowflake {}
+
+ public record StartBackgroundMusic() implements EventWithoutSnowflake {}
+ public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {}
+ public record playOnClickButton() implements EventWithoutSnowflake {}
}