From 59ceac0b5948060143310ad3b83411f470f70843 Mon Sep 17 00:00:00 2001 From: Brendan Robert Date: Tue, 24 Oct 2023 23:30:50 -0500 Subject: [PATCH] Implemented untested OGG playback for music --- Platform/Apple/tools/jace/pom.xml | 6 +- .../main/java/jace/lawless/LawlessHacks.java | 10 ++- .../src/main/java/jace/lawless/Media.java | 65 +++++++++++++++++-- .../main/java/jace/lawless/MediaPlayer.java | 20 ++++-- .../tools/jace/src/main/java/module-info.java | 1 + 5 files changed, 88 insertions(+), 14 deletions(-) diff --git a/Platform/Apple/tools/jace/pom.xml b/Platform/Apple/tools/jace/pom.xml index d79876a9..99603a85 100644 --- a/Platform/Apple/tools/jace/pom.xml +++ b/Platform/Apple/tools/jace/pom.xml @@ -201,6 +201,10 @@ org.lwjgl lwjgl-openal + + org.lwjgl + lwjgl-stb + org.lwjgl lwjgl @@ -216,7 +220,7 @@ jlayer 1.0.2-gdx - + lwjgl-natives-linux-amd64 diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java index 40f7cc8b..0fb2ba34 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/LawlessHacks.java @@ -1,6 +1,7 @@ package jace.lawless; import java.io.BufferedReader; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; @@ -11,6 +12,8 @@ import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -136,7 +139,12 @@ public class LawlessHacks extends Cheats { System.out.println("Playing " + resourcePath); } // Log path - return new Media(resourcePath); + try { + return new Media(resourcePath); + } catch (IOException e) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Unable to load audio track " + resourcePath, e); + return null; + } } private void playMusic(int track, boolean switchScores) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/Media.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/Media.java index 8aa7e1e9..e3b21e0d 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/Media.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/Media.java @@ -1,32 +1,83 @@ package jace.lawless; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.stb.STBVorbis; +import org.lwjgl.stb.STBVorbisInfo; +import org.lwjgl.system.MemoryStack; + import javafx.util.Duration; public class Media { + long streamHandle = -1; + long totalSamples = 0; + float totalDuration = 0; + long sampleRate = 0; + boolean isStereo = true; + ShortBuffer sampleBuffer = ShortBuffer.allocate(2); - public Media(String resourcePath) { + public Media(String resourcePath) throws IOException { + // Load resource into memory + try (InputStream inputStream = getClass().getResourceAsStream(resourcePath)) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(outputStream.toByteArray()); + + IntBuffer error = IntBuffer.allocate(1); + streamHandle = STBVorbis.stb_vorbis_open_memory(byteBuffer, error, null); + + MemoryStack stack = MemoryStack.stackPush(); + STBVorbisInfo info = STBVorbisInfo.malloc(stack); + STBVorbis.stb_vorbis_get_info(streamHandle, info); + totalSamples = STBVorbis.stb_vorbis_stream_length_in_samples(streamHandle); + totalDuration = STBVorbis.stb_vorbis_stream_length_in_seconds(streamHandle); + sampleRate = info.sample_rate(); + isStereo = info.channels() == 2; + info.free(); + } + } + + public void close() { + STBVorbis.stb_vorbis_close(streamHandle); } public void seekToTime(Duration millis) { + int sampleNumber = (int) (millis.toMillis() * sampleRate / 1000); + STBVorbis.stb_vorbis_seek(streamHandle, sampleNumber); } public boolean isEnded() { - return false; + return STBVorbis.stb_vorbis_get_sample_offset(streamHandle) >= totalSamples; } public void restart() { + STBVorbis.stb_vorbis_seek_start(streamHandle); } public short getNextLeftSample() { - return 0; + // read next sample for left and right channels + int numSamples = STBVorbis.stb_vorbis_get_frame_short_interleaved(streamHandle, isStereo ? 2 : 1, sampleBuffer); + if (numSamples == 0) { + return 0; + } + return sampleBuffer.get(0); } public short getNextRightSample() { - return 0; + return isStereo ? sampleBuffer.get(1) : sampleBuffer.get(0); } public java.time.Duration getCurrentTime() { - return null; - } - + int sampleNumber = STBVorbis.stb_vorbis_get_sample_offset(streamHandle); + return java.time.Duration.ofMillis((long) (sampleNumber * 1000 / sampleRate)); + } } \ No newline at end of file diff --git a/Platform/Apple/tools/jace/src/main/java/jace/lawless/MediaPlayer.java b/Platform/Apple/tools/jace/src/main/java/jace/lawless/MediaPlayer.java index 06de038b..14b268c7 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/lawless/MediaPlayer.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/lawless/MediaPlayer.java @@ -19,7 +19,7 @@ public class MediaPlayer { Executor executor = Executors.newSingleThreadExecutor(); public static enum Status { - PLAYING, PAUSED, STOPPED + NOT_STARTED, PLAYING, PAUSED, STOPPED } public static final int INDEFINITE = -1; @@ -40,6 +40,7 @@ public class MediaPlayer { return vol; } + // NOTE: Once a song is stopped, it cannot be restarted. public void stop() { status = Status.STOPPED; try { @@ -47,6 +48,7 @@ public class MediaPlayer { } catch (InterruptedException | ExecutionException e) { // Ignore exception on shutdown } + song.close(); } public void setCycleCount(int i) { @@ -61,11 +63,19 @@ public class MediaPlayer { song.seekToTime(millis); } + public void pause() { + status = Status.PAUSED; + } + public void play() { - repeats = 0; - status = Status.PLAYING; - if (playbackBuffer == null || !playbackBuffer.isAlive()) { - playbackBuffer = SoundMixer.createBuffer(true); + if (status == Status.STOPPED) { + return; + } else if (status == Status.NOT_STARTED) { + repeats = 0; + status = Status.PLAYING; + if (playbackBuffer == null || !playbackBuffer.isAlive()) { + playbackBuffer = SoundMixer.createBuffer(true); + } } executor.execute(() -> { while (status == Status.PLAYING && (maxRepetitions == INDEFINITE || repeats < maxRepetitions)) { diff --git a/Platform/Apple/tools/jace/src/main/java/module-info.java b/Platform/Apple/tools/jace/src/main/java/module-info.java index c102bbf8..25b7d2cb 100644 --- a/Platform/Apple/tools/jace/src/main/java/module-info.java +++ b/Platform/Apple/tools/jace/src/main/java/module-info.java @@ -38,6 +38,7 @@ module lawlesslegends { requires javafx.media; requires jdk.jsobject; requires org.lwjgl.openal; + requires org.lwjgl.stb; // requires org.reflections;