mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Split SoundEffectManager from AudioManager. (#171)
Clips no longer create a new clip instance each time they are played. A singular clip is made for each resource and is opened/closed when loaded/unloaded. When a clip is played that is already playing it'll stop playback and start again. Clip volume handling isn't done very well.
This commit is contained in:
@@ -7,6 +7,10 @@ import org.toop.framework.audio.interfaces.VolumeManager;
|
|||||||
import org.toop.framework.eventbus.EventFlow;
|
import org.toop.framework.eventbus.EventFlow;
|
||||||
import org.toop.framework.resource.types.AudioResource;
|
import org.toop.framework.resource.types.AudioResource;
|
||||||
|
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class AudioEventListener<T extends AudioResource, K extends AudioResource> {
|
public class AudioEventListener<T extends AudioResource, K extends AudioResource> {
|
||||||
private final MusicManager<T> musicManager;
|
private final MusicManager<T> musicManager;
|
||||||
private final SoundEffectManager<K> soundEffectManager;
|
private final SoundEffectManager<K> soundEffectManager;
|
||||||
@@ -33,7 +37,10 @@ public class AudioEventListener<T extends AudioResource, K extends AudioResource
|
|||||||
.listen(this::handleMusicVolumeChange)
|
.listen(this::handleMusicVolumeChange)
|
||||||
.listen(this::handleGetVolume)
|
.listen(this::handleGetVolume)
|
||||||
.listen(this::handleGetFxVolume)
|
.listen(this::handleGetFxVolume)
|
||||||
.listen(this::handleGetMusicVolume);
|
.listen(this::handleGetMusicVolume)
|
||||||
|
.listen(AudioEvents.ClickButton.class, _ -> {
|
||||||
|
soundEffectManager.play("medium-button-click.wav", false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleStopMusicManager(AudioEvents.StopAudioManager event) {
|
private void handleStopMusicManager(AudioEvents.StopAudioManager event) {
|
||||||
@@ -45,7 +52,7 @@ public class AudioEventListener<T extends AudioResource, K extends AudioResource
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleStopSound(AudioEvents.StopEffect event) {
|
private void handleStopSound(AudioEvents.StopEffect event) {
|
||||||
this.soundEffectManager.stop(event.clipId());
|
this.soundEffectManager.stop(event.fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleMusicStart(AudioEvents.StartBackgroundMusic event) {
|
private void handleMusicStart(AudioEvents.StartBackgroundMusic event) {
|
||||||
|
|||||||
@@ -1,27 +1,64 @@
|
|||||||
package org.toop.framework.audio;
|
package org.toop.framework.audio;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.framework.resource.ResourceManager;
|
||||||
|
import org.toop.framework.resource.ResourceMeta;
|
||||||
|
import org.toop.framework.resource.resources.BaseResource;
|
||||||
import org.toop.framework.resource.resources.SoundEffectAsset;
|
import org.toop.framework.resource.resources.SoundEffectAsset;
|
||||||
|
|
||||||
|
import javax.sound.sampled.Clip;
|
||||||
|
import javax.sound.sampled.LineEvent;
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager<SoundEffectAsset> {
|
public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager<SoundEffectAsset> {
|
||||||
private final Map<Long, SoundEffectAsset> activeSoundEffects = new HashMap<>();
|
private static final Logger logger = LogManager.getLogger(SoundEffectManager.class);
|
||||||
private final HashMap<String, SoundEffectAsset> audioResources = new HashMap<>();
|
private final HashMap<String, SoundEffectAsset> soundEffectResources;
|
||||||
|
|
||||||
|
public SoundEffectManager(){
|
||||||
|
// If there are duplicates, takes discards the first
|
||||||
|
soundEffectResources = ResourceManager.getAllOfType(SoundEffectAsset.class).stream()
|
||||||
|
.collect(Collectors.toMap(ResourceMeta::getName, ResourceMeta::getResource, (a, b) -> b, HashMap::new));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<SoundEffectAsset> getActiveAudio() {
|
public Collection<SoundEffectAsset> getActiveAudio() {
|
||||||
return this.audioResources.values();
|
return this.soundEffectResources.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void play(String name, boolean loop) {
|
public void play(String name, boolean loop) {
|
||||||
//TODO
|
SoundEffectAsset asset = soundEffectResources.get(name);
|
||||||
|
|
||||||
|
if (asset == null) {
|
||||||
|
logger.warn("Unable to load audio asset: {}", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset.play();
|
||||||
|
// TODO: Volume of Sound Effect isn't set when loading. When loading an effect it will be full volume.
|
||||||
|
logger.debug("Playing sound: {}", asset.getFile().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop(long clipId) {
|
public void stop(String name){
|
||||||
//TODO
|
SoundEffectAsset asset = soundEffectResources.get(name);
|
||||||
|
|
||||||
|
if (asset == null) {
|
||||||
|
logger.warn("Unable to load audio asset: {}", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset.stop();
|
||||||
|
|
||||||
|
logger.debug("Stopped sound: {}", asset.getFile().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class AudioEvents extends EventsBase {
|
|||||||
/** Starts playing a sound. */
|
/** Starts playing a sound. */
|
||||||
public record PlayEffect(String fileName, boolean loop) implements EventWithoutSnowflake {}
|
public record PlayEffect(String fileName, boolean loop) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record StopEffect(long clipId) implements EventWithoutSnowflake {}
|
public record StopEffect(String fileName) implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
|
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ package org.toop.framework.audio.interfaces;
|
|||||||
import org.toop.framework.resource.resources.SoundEffectAsset;
|
import org.toop.framework.resource.resources.SoundEffectAsset;
|
||||||
import org.toop.framework.resource.types.AudioResource;
|
import org.toop.framework.resource.types.AudioResource;
|
||||||
|
|
||||||
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public interface SoundEffectManager<T extends AudioResource> extends AudioManager<T> {
|
public interface SoundEffectManager<T extends AudioResource> extends AudioManager<T> {
|
||||||
void play(String name, boolean loop);
|
void play(String name, boolean loop);
|
||||||
void stop(long clipId);
|
void stop(String name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,45 +8,23 @@ import org.toop.framework.resource.types.AudioResource;
|
|||||||
import org.toop.framework.resource.types.FileExtension;
|
import org.toop.framework.resource.types.FileExtension;
|
||||||
import org.toop.framework.resource.types.LoadableResource;
|
import org.toop.framework.resource.types.LoadableResource;
|
||||||
|
|
||||||
|
import static javax.sound.sampled.LineEvent.Type.CLOSE;
|
||||||
|
import static javax.sound.sampled.LineEvent.Type.STOP;
|
||||||
|
|
||||||
@FileExtension({"wav"})
|
@FileExtension({"wav"})
|
||||||
public class SoundEffectAsset extends BaseResource implements LoadableResource, AudioResource {
|
public class SoundEffectAsset extends BaseResource implements LoadableResource, AudioResource {
|
||||||
private byte[] rawData;
|
private final Clip clip = AudioSystem.getClip();
|
||||||
private Clip clip = null;
|
|
||||||
|
|
||||||
public SoundEffectAsset(final File audioFile) {
|
public SoundEffectAsset(final File audioFile) throws LineUnavailableException {
|
||||||
super(audioFile);
|
super(audioFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets a new clip to play
|
// Gets a new clip to play
|
||||||
public Clip getNewClip()
|
public Clip getClip() {
|
||||||
throws LineUnavailableException, UnsupportedAudioFileException, IOException {
|
if (!this.isLoaded()) {this.load();} return this.clip;
|
||||||
// Get a new clip from audio system
|
|
||||||
Clip clip = AudioSystem.getClip();
|
|
||||||
|
|
||||||
// Insert a new audio stream into the clip
|
|
||||||
AudioInputStream inputStream = this.getAudioStream();
|
|
||||||
AudioFormat baseFormat = inputStream.getFormat();
|
|
||||||
if (baseFormat.getSampleSizeInBits() > 16)
|
|
||||||
inputStream = downSampleAudio(inputStream, baseFormat);
|
|
||||||
clip.open(
|
|
||||||
inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
|
|
||||||
this.clip = clip;
|
|
||||||
return clip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a new audio stream from byte array
|
private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) {
|
||||||
private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException {
|
|
||||||
// Check if raw data is loaded into memory
|
|
||||||
if (!this.isLoaded()) {
|
|
||||||
this.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn rawData into an input stream and turn that into an audio input stream;
|
|
||||||
return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData));
|
|
||||||
}
|
|
||||||
|
|
||||||
private AudioInputStream downSampleAudio(
|
|
||||||
AudioInputStream audioInputStream, AudioFormat baseFormat) {
|
|
||||||
AudioFormat decodedFormat =
|
AudioFormat decodedFormat =
|
||||||
new AudioFormat(
|
new AudioFormat(
|
||||||
AudioFormat.Encoding.PCM_SIGNED,
|
AudioFormat.Encoding.PCM_SIGNED,
|
||||||
@@ -61,19 +39,35 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource,
|
|||||||
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
|
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
try {
|
try {
|
||||||
this.rawData = Files.readAllBytes(file.toPath());
|
if (this.isLoaded){
|
||||||
|
return; // Return if it is already loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a new audio stream into the clip
|
||||||
|
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(this.getFile())));
|
||||||
|
AudioFormat baseFormat = inputStream.getFormat();
|
||||||
|
if (baseFormat.getSampleSizeInBits() > 16)
|
||||||
|
inputStream = downSampleAudio(inputStream, baseFormat);
|
||||||
|
this.clip.open(inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
|
||||||
this.isLoaded = true;
|
this.isLoaded = true;
|
||||||
} catch (IOException e) {
|
} catch (LineUnavailableException | UnsupportedAudioFileException | IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unload() {
|
public void unload() {
|
||||||
this.rawData = null;
|
if (!this.isLoaded) return; // Return if already unloaded
|
||||||
|
|
||||||
|
if (clip.isRunning()) clip.stop(); // Stops playback of the clip
|
||||||
|
|
||||||
|
clip.close(); // Releases native resources (empties buffer)
|
||||||
|
|
||||||
this.isLoaded = false;
|
this.isLoaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +106,15 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void play() {
|
public void play() {
|
||||||
// TODO
|
if (!isLoaded()) load();
|
||||||
|
|
||||||
|
this.clip.setFramePosition(0); // rewind to the start
|
||||||
|
this.clip.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
// TODO
|
if (this.clip.isRunning()) this.clip.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user