Implemented untested OGG playback for music

This commit is contained in:
Brendan Robert 2023-10-24 23:30:50 -05:00
parent 06c16733c2
commit 59ceac0b59
5 changed files with 88 additions and 14 deletions

View File

@ -201,6 +201,10 @@
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-openal</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl-stb</artifactId>
</dependency>
<dependency>
<groupId>org.lwjgl</groupId>
<artifactId>lwjgl</artifactId>
@ -216,7 +220,7 @@
<artifactId>jlayer</artifactId>
<version>1.0.2-gdx</version>
</dependency>
</dependencies>
</dependencies>
<profiles>
<profile>
<id>lwjgl-natives-linux-amd64</id>

View File

@ -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) {

View File

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

View File

@ -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)) {

View File

@ -38,6 +38,7 @@ module lawlesslegends {
requires javafx.media;
requires jdk.jsobject;
requires org.lwjgl.openal;
requires org.lwjgl.stb;
// requires org.reflections;