Added MusicAsset, renamed SoundAsset to be SoundEffectAsset. Created new directories for fx and music types of sound. (#105)

This commit is contained in:
Bas Antonius de Jong
2025-10-02 00:08:39 +02:00
committed by GitHub
parent 2003fcbf5b
commit fb0e16acc2
19 changed files with 142 additions and 36 deletions

View File

@@ -102,6 +102,14 @@
<version>25</version>
</dependency>
<!-- JavaFX Media -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
@@ -122,24 +130,6 @@
<target>25</target>
<release>25</release>
<encoding>UTF-8</encoding>
<!-- <compilerArgs>-->
<!-- <arg>-XDcompilePolicy=simple</arg>-->
<!-- <arg>&#45;&#45;should-stop=ifError=FLOW</arg>-->
<!-- <arg>-Xplugin:ErrorProne</arg>-->
<!-- </compilerArgs>-->
<!-- <annotationProcessorPaths>-->
<!-- <path>-->
<!-- <groupId>com.google.errorprone</groupId>-->
<!-- <artifactId>error_prone_core</artifactId>-->
<!-- <version>2.42.0</version>-->
<!-- </path>-->
<!-- &lt;!&ndash; Other annotation processors go here.-->
<!-- If 'annotationProcessorPaths' is set, processors will no longer be-->
<!-- discovered on the regular -classpath; see also 'Using Error Prone-->
<!-- together with other annotation processors' below. &ndash;&gt;-->
<!-- </annotationProcessorPaths>-->
<!-- <fork>true</fork>-->
</configuration>
</plugin>
<plugin>

View File

@@ -242,4 +242,4 @@ public class AssetLoader {
int i = name.lastIndexOf('.');
return (i > 0) ? name.substring(i + 1) : "";
}
}
}

View File

@@ -147,4 +147,4 @@ public class AssetManager {
public static void addAsset(Asset<? extends BaseResource> asset) {
assets.put(asset.getName(), asset);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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<Long, Clip> activeClips = new HashMap<>();
private final HashMap<String, AudioAsset> audioResources = new HashMap<>();
private final List<MediaPlayer> activeMusic = new ArrayList<>();
private final Queue<MusicAsset> backgroundMusicQueue = new LinkedList<>();
private final Map<Long, Clip> activeSoundEffects = new HashMap<>();
private final HashMap<String, SoundEffectAsset> 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<AudioAsset> asset : AssetManager.getAllOfType(AudioAsset.class)) {
for (Asset<SoundEffectAsset> asset : AssetManager.getAllOfType(SoundEffectAsset.class)) {
try {
this.addAudioResource(asset);
} catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
@@ -27,7 +34,9 @@ public class SoundManager {
}
new EventFlow()
.listen(this::handlePlaySound)
.listen(this::handleStopSound);
.listen(this::handleStopSound)
.listen(this::handleMusicStart)
.listen(this::handleVolumeChange);
}
private void handlePlaySound(AudioEvents.PlayAudio event) {
@@ -42,14 +51,70 @@ public class SoundManager {
this.stopSound(event.clipId());
}
private void addAudioResource(Asset<AudioAsset> audioAsset)
private void addAudioResource(Asset<SoundEffectAsset> 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 +136,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 +151,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 +159,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();
}
}

View File

@@ -1,5 +1,6 @@
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;
@@ -9,4 +10,8 @@ public class AudioEvents extends EventsBase {
implements EventWithoutSnowflake {}
public record StopAudio(long clipId) implements EventWithoutSnowflake {}
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {}
}