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