mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 02:44:50 +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.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> {
|
||||
private final MusicManager<T> musicManager;
|
||||
private final SoundEffectManager<K> soundEffectManager;
|
||||
@@ -33,7 +37,10 @@ public class AudioEventListener<T extends AudioResource, K extends AudioResource
|
||||
.listen(this::handleMusicVolumeChange)
|
||||
.listen(this::handleGetVolume)
|
||||
.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) {
|
||||
@@ -45,7 +52,7 @@ public class AudioEventListener<T extends AudioResource, K extends AudioResource
|
||||
}
|
||||
|
||||
private void handleStopSound(AudioEvents.StopEffect event) {
|
||||
this.soundEffectManager.stop(event.clipId());
|
||||
this.soundEffectManager.stop(event.fileName());
|
||||
}
|
||||
|
||||
private void handleMusicStart(AudioEvents.StartBackgroundMusic event) {
|
||||
|
||||
@@ -1,27 +1,64 @@
|
||||
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 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.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager<SoundEffectAsset> {
|
||||
private final Map<Long, SoundEffectAsset> activeSoundEffects = new HashMap<>();
|
||||
private final HashMap<String, SoundEffectAsset> audioResources = new HashMap<>();
|
||||
private static final Logger logger = LogManager.getLogger(SoundEffectManager.class);
|
||||
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
|
||||
public Collection<SoundEffectAsset> getActiveAudio() {
|
||||
return this.audioResources.values();
|
||||
return this.soundEffectResources.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
public void stop(long clipId) {
|
||||
//TODO
|
||||
public void stop(String name){
|
||||
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. */
|
||||
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 {}
|
||||
|
||||
|
||||
@@ -3,7 +3,11 @@ package org.toop.framework.audio.interfaces;
|
||||
import org.toop.framework.resource.resources.SoundEffectAsset;
|
||||
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> {
|
||||
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.LoadableResource;
|
||||
|
||||
import static javax.sound.sampled.LineEvent.Type.CLOSE;
|
||||
import static javax.sound.sampled.LineEvent.Type.STOP;
|
||||
|
||||
@FileExtension({"wav"})
|
||||
public class SoundEffectAsset extends BaseResource implements LoadableResource, AudioResource {
|
||||
private byte[] rawData;
|
||||
private Clip clip = null;
|
||||
private final Clip clip = AudioSystem.getClip();
|
||||
|
||||
public SoundEffectAsset(final File audioFile) {
|
||||
public SoundEffectAsset(final File audioFile) throws LineUnavailableException {
|
||||
super(audioFile);
|
||||
}
|
||||
|
||||
// Gets a new clip to play
|
||||
public Clip getNewClip()
|
||||
throws LineUnavailableException, UnsupportedAudioFileException, IOException {
|
||||
// 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;
|
||||
public Clip getClip() {
|
||||
if (!this.isLoaded()) {this.load();} return this.clip;
|
||||
}
|
||||
|
||||
// Generates a new audio stream from byte array
|
||||
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) {
|
||||
private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) {
|
||||
AudioFormat decodedFormat =
|
||||
new AudioFormat(
|
||||
AudioFormat.Encoding.PCM_SIGNED,
|
||||
@@ -61,19 +39,35 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource,
|
||||
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
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;
|
||||
} catch (IOException e) {
|
||||
} catch (LineUnavailableException | UnsupportedAudioFileException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -112,12 +106,15 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource,
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
// TODO
|
||||
if (!isLoaded()) load();
|
||||
|
||||
this.clip.setFramePosition(0); // rewind to the start
|
||||
this.clip.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// TODO
|
||||
if (this.clip.isRunning()) this.clip.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user