Files
TetrisClone/src/main/java/eu/midnightdust/yaytris/util/SoundUtil.java
2025-07-09 13:00:48 +02:00

114 lines
4.4 KiB
Java

package eu.midnightdust.yaytris.util;
import eu.midnightdust.yaytris.Settings;
import javax.sound.sampled.*;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class SoundUtil {
private static final Map<String, MusicThread> musicThreads = new HashMap<>();
public static void playMusic(String fileLocation, boolean looped) {
if (musicThreads.containsKey(fileLocation)) stopMusic(fileLocation);
MusicThread musicThread = new MusicThread(fileLocation, looped);
musicThread.start();
musicThreads.put(fileLocation, musicThread);
}
public static void stopMusic(String fileLocation) {
if (musicThreads.containsKey(fileLocation)) musicThreads.get(fileLocation).stopMusic();
}
// Adapted from: https://www.baeldung.com/java-play-sound
public static void playSoundClip(String fileLocation) {
try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) { // FIXME: Support audio files from JAR. File streams won't work here for some reason.
AudioFormat format = stream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(new LineUpdateListener());
audioClip.open(stream);
audioClip.start();
setVolume(audioClip, Settings.soundVolume);
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
throw new RuntimeException(e);
}
}
private static URL getResource(String fileLocation) {
return SoundUtil.class.getResource(fileLocation);
}
// Adapted from: https://stackoverflow.com/a/40698149
private static void setVolume(Line line, int volume) {
if (volume < 0 || volume > 100)
throw new IllegalArgumentException("Volume not valid: " + volume);
FloatControl gainControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
gainControl.setValue(20f * (float) Math.log10(volume/100f));
}
public static class MusicThread extends Thread {
private static final int BUFFER_SIZE = 8192;
private final boolean looped;
private final String fileLocation;
private boolean playing;
MusicThread(String fileLocation, boolean looped) {
this.fileLocation = fileLocation;
this.looped = looped;
this.playing = true;
}
public void stopMusic() {
this.playing = false;
}
@Override
public void run() {
do {playMusic(fileLocation);} while (looped && playing);
}
// Adapted from: https://www.baeldung.com/java-play-sound
private void playMusic(String fileLocation) {
try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) {
AudioFormat format = stream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(format);
sourceDataLine.start();
setVolume(sourceDataLine, Settings.musicVolume);
float fadeOut = 1.0f;
byte[] bufferBytes = new byte[BUFFER_SIZE];
int readBytes;
while ((readBytes = stream.read(bufferBytes)) != -1) {
if (!playing) {
fadeOut = (float) Math.sin(fadeOut-0.01f); // Sinus fade-out
setVolume(sourceDataLine, (int) (fadeOut * Settings.musicVolume));
if (fadeOut <= 0) break;
}
sourceDataLine.write(bufferBytes, 0, readBytes);
}
sourceDataLine.drain();
sourceDataLine.close();
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
throw new RuntimeException(e);
}
}
}
public static class LineUpdateListener implements LineListener {
@Override
public void update(LineEvent event) {
if (LineEvent.Type.STOP == event.getType()) {
event.getLine().close();
}
}
}
}