aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/client')
-rw-r--r--src/main/java/gregtech/client/ElectricJukeboxSound.java95
-rw-r--r--src/main/java/gregtech/client/ISeekingSound.java14
-rw-r--r--src/main/java/gregtech/client/SeekingOggCodec.java98
3 files changed, 207 insertions, 0 deletions
diff --git a/src/main/java/gregtech/client/ElectricJukeboxSound.java b/src/main/java/gregtech/client/ElectricJukeboxSound.java
new file mode 100644
index 0000000000..0ea81ab537
--- /dev/null
+++ b/src/main/java/gregtech/client/ElectricJukeboxSound.java
@@ -0,0 +1,95 @@
+package gregtech.client;
+
+import net.minecraft.client.audio.ISound;
+import net.minecraft.client.audio.ITickableSound;
+import net.minecraft.util.ResourceLocation;
+
+public class ElectricJukeboxSound implements ISound, ISeekingSound, ITickableSound {
+
+ public final ResourceLocation soundResource;
+ public float volume = 1.0F;
+ public float pitch = 1.0F;
+ public float xPosition;
+ public float yPosition;
+ public float zPosition;
+ public boolean repeating = false;
+ public int repeatDelay = 0;
+ public ISound.AttenuationType attenuationType = AttenuationType.LINEAR;
+ public boolean donePlaying = false;
+
+ public final long seekMs;
+
+ public ElectricJukeboxSound(ResourceLocation resource, long seekMs) {
+ this.soundResource = resource;
+ this.seekMs = seekMs;
+ }
+
+ public ElectricJukeboxSound(ResourceLocation soundResource, float volume, long seekMs, float xPosition,
+ float yPosition, float zPosition) {
+ this(soundResource, seekMs);
+ this.volume = volume;
+ this.xPosition = xPosition;
+ this.yPosition = yPosition;
+ this.zPosition = zPosition;
+ }
+
+ @Override
+ public long getSeekMillisecondOffset() {
+ return seekMs;
+ }
+
+ @Override
+ public ResourceLocation getPositionedSoundLocation() {
+ return soundResource;
+ }
+
+ @Override
+ public boolean canRepeat() {
+ return repeating;
+ }
+
+ @Override
+ public int getRepeatDelay() {
+ return repeatDelay;
+ }
+
+ @Override
+ public float getVolume() {
+ return volume;
+ }
+
+ @Override
+ public float getPitch() {
+ return pitch;
+ }
+
+ @Override
+ public float getXPosF() {
+ return xPosition;
+ }
+
+ @Override
+ public float getYPosF() {
+ return yPosition;
+ }
+
+ @Override
+ public float getZPosF() {
+ return zPosition;
+ }
+
+ @Override
+ public AttenuationType getAttenuationType() {
+ return attenuationType;
+ }
+
+ @Override
+ public boolean isDonePlaying() {
+ return donePlaying;
+ }
+
+ @Override
+ public void update() {
+ // no-op
+ }
+}
diff --git a/src/main/java/gregtech/client/ISeekingSound.java b/src/main/java/gregtech/client/ISeekingSound.java
new file mode 100644
index 0000000000..091b56f802
--- /dev/null
+++ b/src/main/java/gregtech/client/ISeekingSound.java
@@ -0,0 +1,14 @@
+package gregtech.client;
+
+import net.minecraft.client.audio.ISound;
+
+/**
+ * Metadata on a sound object used to seek it when starting playback.
+ */
+public interface ISeekingSound extends ISound {
+
+ /**
+ * @return The number of milliseconds to seek by.
+ */
+ long getSeekMillisecondOffset();
+}
diff --git a/src/main/java/gregtech/client/SeekingOggCodec.java b/src/main/java/gregtech/client/SeekingOggCodec.java
new file mode 100644
index 0000000000..7949578f4f
--- /dev/null
+++ b/src/main/java/gregtech/client/SeekingOggCodec.java
@@ -0,0 +1,98 @@
+package gregtech.client;
+
+import java.net.URL;
+
+import javax.sound.sampled.AudioFormat;
+
+import net.minecraft.util.ResourceLocation;
+
+import org.apache.commons.lang3.StringUtils;
+
+import paulscode.sound.SoundBuffer;
+import paulscode.sound.codecs.CodecJOrbis;
+
+/**
+ * A somewhat hacky codec that allows starting music playback from the middle of a Ogg Vorbis file.
+ * Registers for URLs of the form: {@literal jar:blah/blah.jar!blah/music.ogg?seek_ms=5000&ext=.gt5oggseek}
+ */
+public class SeekingOggCodec extends CodecJOrbis {
+
+ public static final String EXTENSION = "gt5oggseek";
+
+ private volatile boolean fullyInitialized = false;
+ private volatile SoundBuffer nextBuffer = null;
+
+ /**
+ * Encodes the given millisecond seek amount into a URL/resource name suffix that can be appended to the sound path
+ * to start playing from that point onwards.
+ */
+ public static String getEncodedSeekSuffxix(long milliseconds) {
+ return String.format("?seek_ms=%d&ext=." + EXTENSION, milliseconds);
+ }
+
+ /**
+ * @return The given path with the seeking metadata stripped from the URL
+ */
+ public static String stripSeekMetadata(String path) {
+ while (path.endsWith("." + EXTENSION)) {
+ int qMark = path.lastIndexOf('?');
+ if (qMark == -1) {
+ break;
+ }
+ path = path.substring(0, qMark);
+ }
+ return path;
+ }
+
+ /**
+ * Turns the input sound ResourceLocation into one that is seeked forward the given number of milliseconds
+ */
+ public static ResourceLocation seekResource(ResourceLocation loc, long milliseconds) {
+ String original = loc.getResourcePath();
+ original = stripSeekMetadata(original);
+ return new ResourceLocation(loc.getResourceDomain(), original + getEncodedSeekSuffxix(milliseconds));
+ }
+
+ @Override
+ public boolean initialize(URL url) {
+ final String textUrl = url.toString();
+ final String[] queryParts = url.getQuery()
+ .split("&");
+ long seekMs = 0;
+ for (String part : queryParts) {
+ if (!part.startsWith("seek_ms=")) {
+ continue;
+ }
+ part = StringUtils.removeStart(part, "seek_ms=");
+ seekMs = Long.parseLong(part);
+ }
+
+ if (!super.initialize(url)) {
+ return false;
+ }
+
+ final AudioFormat format = this.getAudioFormat();
+ final long samplesPerS = (long) format.getSampleRate();
+ final int bytesPerSample = (format.getChannels() * format.getSampleSizeInBits() / 8);
+
+ long remainingBytes = seekMs * samplesPerS * bytesPerSample / 1000L;
+
+ while (remainingBytes > 0) {
+ final SoundBuffer buf = read();
+ if (buf == null || buf.audioData == null) {
+ return false;
+ }
+ remainingBytes -= buf.audioData.length;
+ }
+
+ synchronized (this) {
+ fullyInitialized = true;
+ }
+ return true;
+ }
+
+ @Override
+ public synchronized boolean initialized() {
+ return fullyInitialized;
+ }
+}