com.github.bloodshura.ignitium.assets.ext.sound.internal.OggInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ignitium-assets-ext Show documentation
Show all versions of ignitium-assets-ext Show documentation
Extensions for the Ignitium Assets library, including new image and sound codecs.
package com.github.bloodshura.ignitium.assets.ext.sound.internal;
import com.github.bloodshura.ignitium.memory.Bufferer;
import com.jcraft.jogg.Packet;
import com.jcraft.jogg.Page;
import com.jcraft.jogg.StreamState;
import com.jcraft.jogg.SyncState;
import com.jcraft.jorbis.Block;
import com.jcraft.jorbis.Comment;
import com.jcraft.jorbis.DspState;
import com.jcraft.jorbis.Info;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class OggInputStream extends InputStream {
boolean endOfBitStream;
boolean inited = false;
private byte[] buffer;
private int bytes;
private final Comment comment;
private final byte[] convbuffer;
private int convsize;
private final DspState dspState;
private boolean endOfStream;
private final InputStream input;
private final Info oggInfo;
private final Packet packet;
private final Page page;
private final ByteBuffer pcmBuffer;
private int readIndex;
private final StreamState streamState;
private final SyncState syncState;
private final Block vorbisBlock;
public OggInputStream(InputStream input) throws IOException {
this.bytes = 0;
this.comment = new Comment();
this.convbuffer = new byte[Bufferer.COMMON_SIZE * 4];
this.convsize = convbuffer.length;
this.dspState = new DspState();
this.endOfBitStream = true;
this.input = input;
this.oggInfo = new Info();
this.packet = new Packet();
this.page = new Page();
this.pcmBuffer = Bufferer.newByteBuffer(Bufferer.COMMON_SIZE * 64); // 256 KB
this.streamState = new StreamState();
this.syncState = new SyncState();
this.vorbisBlock = new Block(dspState);
initVorbis();
readPCM();
}
public boolean atEnd() {
return endOfStream && readIndex >= pcmBuffer.position();
}
@Override
public int available() {
return endOfStream ? 0 : 1;
}
@Override
public void close() {
}
public int getChannels() {
return oggInfo.channels;
}
public int getSampleRate() {
return oggInfo.rate;
}
@Override
public int read() {
if (readIndex >= pcmBuffer.position()) {
pcmBuffer.clear();
try {
readPCM();
} catch (IOException ignored) {
}
this.readIndex = 0;
}
if (readIndex >= pcmBuffer.position()) {
return -1;
}
int value = pcmBuffer.get(readIndex);
if (value < 0) {
value = 256 + value;
}
readIndex++;
return value;
}
@Override
public int read(byte[] b) {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) {
for (int i = 0; i < len; i++) {
int value = read();
if (value >= 0) {
b[i] = (byte) value;
} else {
if (i == 0) {
return -1;
}
return i;
}
}
return len;
}
private boolean getPageAndPacket() throws IOException {
int index = syncState.buffer(Bufferer.COMMON_SIZE);
if (index == -1) {
return false;
}
this.buffer = syncState.data;
if (buffer == null) {
this.endOfStream = true;
return false;
}
try {
this.bytes = input.read(buffer, index, Bufferer.COMMON_SIZE);
} catch (IOException exception) {
throw new IOException("Failure reading Vorbis.");
}
syncState.wrote(bytes);
if (syncState.pageout(page) != 1) {
if (bytes < Bufferer.COMMON_SIZE) {
return false;
}
throw new IOException("Input does not appear to be an Ogg bitstream.");
}
streamState.init(page.serialno());
oggInfo.init();
comment.init();
if (streamState.pagein(page) < 0) {
throw new IOException("Error reading first page of Ogg bitstream.");
}
if (streamState.packetout(packet) != 1) {
throw new IOException("Error reading initial header packet.");
}
if (oggInfo.synthesis_headerin(comment, packet) < 0) {
throw new IOException("Ogg bitstream does not contain Vorbis audio data.");
}
int i = 0;
while (i < 2) {
while (i < 2) {
int result = syncState.pageout(page);
if (result == 0) {
break;
}
if (result == 1) {
streamState.pagein(page);
while (i < 2) {
result = streamState.packetout(packet);
if (result == 0) {
break;
}
if (result == -1) {
throw new IOException("Corrupt secondary header.");
}
oggInfo.synthesis_headerin(comment, packet);
i++;
}
}
}
index = syncState.buffer(Bufferer.COMMON_SIZE);
if (index == -1) {
return false;
}
this.buffer = syncState.data;
try {
this.bytes = input.read(buffer, index, Bufferer.COMMON_SIZE);
} catch (IOException exception) {
throw new IOException("Failed to read Vorbis.");
}
if (bytes == 0 && i < 2) {
throw new IOException("End of file before finding all Vorbis headers.");
}
syncState.wrote(bytes);
}
convsize = Bufferer.COMMON_SIZE / oggInfo.channels;
dspState.synthesis_init(oggInfo);
vorbisBlock.init(dspState);
return true;
}
private void initVorbis() {
syncState.init();
}
private void readPCM() throws IOException {
boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
boolean wrote = false;
while (true) {
if (endOfBitStream) {
if (!getPageAndPacket()) {
break;
}
this.endOfBitStream = false;
}
if (!inited) {
inited = true;
return;
}
float[][][] _pcm = new float[1][][];
int[] _index = new int[oggInfo.channels];
while (!endOfBitStream) {
while (!endOfBitStream) {
int result = syncState.pageout(page);
if (result == 0) {
break;
}
if (result > 0) {
streamState.pagein(page);
while (true) {
result = streamState.packetout(packet);
if (result == 0) {
break;
}
if (result != -1) {
int samples;
if (vorbisBlock.synthesis(packet) == 0) {
dspState.synthesis_blockin(vorbisBlock);
}
while ((samples = dspState.synthesis_pcmout(_pcm, _index)) > 0) {
float[][] pcm = _pcm[0];
int bout = samples < convsize ? samples : convsize;
for (int i = 0; i < oggInfo.channels; i++) {
int ptr = i * 2;
int mono = _index[i];
for (int j = 0; j < bout; j++) {
int val = (int) (pcm[i][mono + j] * 32767.);
if (val > 32767) {
val = 32767;
} else if (val < -32768) {
val = -32768;
} else if (val < 0) {
val = val | 0x8000;
}
if (bigEndian) {
convbuffer[ptr] = (byte) (val >>> 8);
convbuffer[ptr + 1] = (byte) val;
} else {
convbuffer[ptr] = (byte) val;
convbuffer[ptr + 1] = (byte) (val >>> 8);
}
ptr += 2 * oggInfo.channels;
}
}
int bytesToWrite = 2 * oggInfo.channels * bout;
if (bytesToWrite >= pcmBuffer.remaining()) {
throw new IOException("Ogg block too big to be buffered: " + bytesToWrite);
}
pcmBuffer.put(convbuffer, 0, bytesToWrite);
wrote = true;
dspState.synthesis_read(bout);
}
}
}
if (page.eos() != 0) {
this.endOfBitStream = true;
}
if (!endOfBitStream && wrote) {
return;
}
}
}
if (!endOfBitStream) {
int index = syncState.buffer(Bufferer.COMMON_SIZE);
this.bytes = 0;
if (index >= 0) {
this.buffer = syncState.data;
try {
bytes = input.read(buffer, index, Bufferer.COMMON_SIZE);
} catch (IOException exception) {
throw new IOException("Error during Vorbis decoding.");
}
} else {
this.bytes = 0;
}
syncState.wrote(bytes);
if (bytes == 0) {
this.endOfBitStream = true;
}
}
}
streamState.clear();
vorbisBlock.clear();
dspState.clear();
oggInfo.clear();
}
syncState.clear();
this.endOfStream = true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy