149 lines
5.6 KiB
Java
149 lines
5.6 KiB
Java
package jace.lawless;
|
|
|
|
import java.io.File;
|
|
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 org.lwjgl.system.MemoryUtil;
|
|
|
|
import javafx.util.Duration;
|
|
|
|
public class Media {
|
|
int totalSamples = 0;
|
|
float totalDuration = 0;
|
|
long sampleRate = 0;
|
|
boolean isStereo = true;
|
|
ShortBuffer sampleBuffer;
|
|
File tempFile;
|
|
|
|
public Media(String resourcePath) throws IOException {
|
|
System.out.println("Loading media: " + resourcePath);
|
|
byte[] oggFile;
|
|
try (InputStream oggStream = getClass().getResourceAsStream(resourcePath)) {
|
|
oggFile = oggStream.readAllBytes();
|
|
}
|
|
|
|
try (MemoryStack stack = MemoryStack.stackPush()) {
|
|
ByteBuffer oggBuffer = MemoryUtil.memAlloc(oggFile.length);
|
|
oggBuffer.put(oggFile);
|
|
oggBuffer.flip();
|
|
IntBuffer error = stack.callocInt(1);
|
|
Long decoder = STBVorbis.stb_vorbis_open_memory(oggBuffer, error, null);
|
|
if (decoder == null || decoder <= 0) {
|
|
throw new RuntimeException("Failed to open Ogg Vorbis file. Error: " + getError(error.get(0)) + " -- file is located at " + resourcePath);
|
|
}
|
|
STBVorbisInfo info = STBVorbisInfo.malloc(stack);
|
|
STBVorbis.stb_vorbis_get_info(decoder, info);
|
|
totalSamples = STBVorbis.stb_vorbis_stream_length_in_samples(decoder);
|
|
totalDuration = STBVorbis.stb_vorbis_stream_length_in_seconds(decoder);
|
|
sampleRate = info.sample_rate();
|
|
isStereo = info.channels() == 2;
|
|
|
|
sampleBuffer = MemoryUtil.memAllocShort(totalSamples);
|
|
STBVorbis.stb_vorbis_get_samples_short_interleaved(decoder, isStereo?2:1, sampleBuffer);
|
|
STBVorbis.stb_vorbis_close(decoder);
|
|
sampleBuffer.rewind();
|
|
} catch (RuntimeException ex) {
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
public String getError(int vorbisErrorCode) {
|
|
switch (vorbisErrorCode) {
|
|
case STBVorbis.VORBIS__no_error:
|
|
return "VORBIS_no_error";
|
|
case STBVorbis.VORBIS_need_more_data:
|
|
return "VORBIS_need_more_data";
|
|
case STBVorbis.VORBIS_invalid_api_mixing:
|
|
return "VORBIS_invalid_api_mixing";
|
|
case STBVorbis.VORBIS_outofmem:
|
|
return "VORBIS_outofmem";
|
|
case STBVorbis.VORBIS_feature_not_supported:
|
|
return "VORBIS_feature_not_supported";
|
|
case STBVorbis.VORBIS_too_many_channels:
|
|
return "VORBIS_too_many_channels";
|
|
case STBVorbis.VORBIS_file_open_failure:
|
|
return "VORBIS_file_open_failure";
|
|
case STBVorbis.VORBIS_seek_without_length:
|
|
return "VORBIS_seek_without_length";
|
|
case STBVorbis.VORBIS_unexpected_eof:
|
|
return "VORBIS_unexpected_eof";
|
|
case STBVorbis.VORBIS_seek_invalid:
|
|
return "VORBIS_seek_invalid";
|
|
case STBVorbis.VORBIS_invalid_setup:
|
|
return "VORBIS_invalid_setup";
|
|
case STBVorbis.VORBIS_invalid_stream:
|
|
return "VORBIS_invalid_stream";
|
|
case STBVorbis.VORBIS_missing_capture_pattern:
|
|
return "VORBIS_missing_capture_pattern";
|
|
case STBVorbis.VORBIS_invalid_stream_structure_version:
|
|
return "VORBIS_invalid_stream_structure_version";
|
|
case STBVorbis.VORBIS_continued_packet_flag_invalid:
|
|
return "VORBIS_continued_packet_flag_invalid";
|
|
case STBVorbis.VORBIS_incorrect_stream_serial_number:
|
|
return "VORBIS_incorrect_stream_serial_number";
|
|
case STBVorbis.VORBIS_invalid_first_page:
|
|
return "VORBIS_invalid_first_page";
|
|
case STBVorbis.VORBIS_bad_packet_type:
|
|
return "VORBIS_bad_packet_type";
|
|
case STBVorbis.VORBIS_cant_find_last_page:
|
|
return "VORBIS_cant_find_last_page";
|
|
case STBVorbis.VORBIS_seek_failed:
|
|
return "VORBIS_seek_failed";
|
|
case STBVorbis.VORBIS_ogg_skeleton_not_supported:
|
|
return "VORBIS_ogg_skeleton_not_supported";
|
|
default:
|
|
return "Unknown error code: " + vorbisErrorCode;
|
|
}
|
|
}
|
|
|
|
public void close() {
|
|
MemoryUtil.memFree(sampleBuffer);
|
|
if (tempFile != null && tempFile.exists())
|
|
tempFile.delete();
|
|
}
|
|
|
|
public void seekToTime(Duration millis) {
|
|
int sampleNumber = (int) (millis.toMillis() * sampleRate / 1000);
|
|
sampleNumber = Math.max(0, Math.min(sampleNumber, totalSamples));
|
|
sampleBuffer.position(sampleNumber * (isStereo ? 2 : 1));
|
|
}
|
|
|
|
public boolean isEnded() {
|
|
return sampleBuffer.remaining() == 0;
|
|
}
|
|
|
|
public void restart() {
|
|
sampleBuffer.rewind();
|
|
}
|
|
|
|
public short getNextLeftSample() {
|
|
// read next sample for left and right channels
|
|
if (isEnded()) {
|
|
return 0;
|
|
}
|
|
return sampleBuffer.get();
|
|
}
|
|
|
|
public short getNextRightSample() {
|
|
if (isEnded()) {
|
|
return 0;
|
|
}
|
|
return isStereo ? sampleBuffer.get() : sampleBuffer.get(sampleBuffer.position());
|
|
}
|
|
|
|
public java.time.Duration getCurrentTime() {
|
|
int sampleNumber = sampleBuffer.position() / (isStereo ? 2 : 1);
|
|
return java.time.Duration.ofMillis((long) (sampleNumber * 1000 / sampleRate));
|
|
}
|
|
|
|
public float getTotalDuration() {
|
|
return totalDuration;
|
|
}
|
|
} |