diff --git a/etc/depends-incl.xml b/etc/depends-incl.xml
index 3386d057..780b7b93 100644
--- a/etc/depends-incl.xml
+++ b/etc/depends-incl.xml
@@ -17,9 +17,9 @@
classname="com.jmex.bui.BWindow" classpathref="classpath"/>
-
-
+
+
@@ -39,7 +39,7 @@
-
+
diff --git a/etc/libs-incl.xml b/etc/libs-incl.xml
index 06dfae4b..62da3bd3 100644
--- a/etc/libs-incl.xml
+++ b/etc/libs-incl.xml
@@ -27,6 +27,8 @@
+
+
diff --git a/src/java/com/threerings/openal/OggStreamDecoder.java b/src/java/com/threerings/openal/OggStreamDecoder.java
index 25ca8ed7..a99bf9ec 100644
--- a/src/java/com/threerings/openal/OggStreamDecoder.java
+++ b/src/java/com/threerings/openal/OggStreamDecoder.java
@@ -25,43 +25,218 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
import org.lwjgl.openal.AL10;
-import com.jmex.sound.openAL.objects.util.OggInputStream;
+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;
/**
* Decodes Ogg Vorbis streams.
*/
public class OggStreamDecoder extends StreamDecoder
{
+
// documentation inherited
public void init (InputStream in)
throws IOException
{
- _istream = new OggInputStream(in);
+ _in = in;
+ _sync.init();
+ _info.init();
+
+ // read the first packet "manually" to make sure everything's kosher
+ readChunk();
+ if (_sync.pageout(_page) != 1) {
+ throw new IOException("Input is not an Ogg bitstream.");
+ }
+ _stream.init(_page.serialno());
+ _stream.reset();
+
+ if (_stream.pagein(_page) < 0) {
+ throw new IOException("Error reading first page of Ogg bitstream data.");
+ }
+ if (_stream.packetout(_packet) != 1) {
+ throw new IOException("Error reading initial header packet.");
+ }
+
+ Comment comment = new Comment();
+ comment.init();
+ if (_info.synthesis_headerin(comment, _packet) < 0) {
+ throw new IOException("Ogg bitstream does not contain Vorbis audio data.");
+ }
+
+ // two more packets in header
+ for (int ii = 0; ii < 2; ii++) {
+ if (!readPacket()) {
+ throw new IOException("End of file before reading all Vorbis headers.");
+ }
+ _info.synthesis_headerin(comment, _packet);
+ }
+ _dsp.synthesis_init(_info);
+ _block.init(_dsp);
+ _offsets = new int[_info.channels];
}
// documentation inherited
public int getFormat ()
{
- return (_istream.getFormat() == OggInputStream.FORMAT_MONO16) ?
- AL10.AL_FORMAT_MONO16 : AL10.AL_FORMAT_STEREO16;
+ return (_info.channels == 1) ? AL10.AL_FORMAT_MONO16 : AL10.AL_FORMAT_STEREO16;
}
// documentation inherited
public int getFrequency ()
{
- return _istream.getRate();
+ return _info.rate;
}
// documentation inherited
public int read (ByteBuffer buf)
throws IOException
{
- return _istream.read(buf, buf.position(), buf.remaining());
+ ShortBuffer sbuf = buf.asShortBuffer();
+ int channels = _info.channels;
+ int total = 0;
+ while (sbuf.hasRemaining()) {
+ int samples = Math.min(readSamples(), sbuf.remaining() / channels);
+ if (samples == 0) {
+ break;
+ }
+ int length = samples * channels;
+ if (_data.length < length) {
+ _data = new short[length];
+ }
+ short[] data = _data;
+ for (int ii = 0; ii < channels; ii++) {
+ float[] pcm = _pcm[0][ii];
+ int sidx = _offsets[ii], didx = ii;
+ for (int jj = 0; jj < samples; jj++) {
+ float value = Math.min(Math.max(pcm[sidx], -1f), +1f);
+ data[didx] = (short)(value * 32767f);
+ sidx++;
+ didx += channels;
+ }
+ }
+ sbuf.put(_data, 0, length);
+ _dsp.synthesis_read(samples);
+ total += (length * 2);
+ }
+ buf.position(buf.position() + total);
+ return total;
}
- /** The underlying Ogg input stream. */
- protected OggInputStream _istream;
+ /**
+ * Reads a buffer's worth of samples.
+ *
+ * @return the number of samples read, or zero if we've reached the end of the stream.
+ */
+ protected int readSamples ()
+ throws IOException
+ {
+ int samples;
+ while ((samples = _dsp.synthesis_pcmout(_pcm, _offsets)) <= 0) {
+ if (samples == 0 && !readPacket()) {
+ return 0;
+ }
+ if (_block.synthesis(_packet) == 0) {
+ _dsp.synthesis_blockin(_block);
+ }
+ }
+ return samples;
+ }
+
+ /**
+ * Reads a packet.
+ *
+ * @return true if a packet was read, false if we've reached the end of the stream.
+ */
+ protected boolean readPacket ()
+ throws IOException
+ {
+ int result;
+ while ((result = _stream.packetout(_packet)) != 1) {
+ if (result == 0 && !readPage()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Reads in a page.
+ *
+ * @return true if a page was read, false if we've reached the end of the stream.
+ */
+ protected boolean readPage ()
+ throws IOException
+ {
+ int result;
+ while ((result = _sync.pageout(_page)) != 1) {
+ if (result == 0 && !readChunk()) {
+ return false;
+ }
+ }
+ _stream.pagein(_page);
+ return true;
+ }
+
+ /**
+ * Reads in a chunk of data from the underlying input stream.
+ *
+ * @return true if a chunk was read, false if we've reached the end of the stream.
+ */
+ protected boolean readChunk ()
+ throws IOException
+ {
+ int offset = _sync.buffer(BUFFER_SIZE);
+ int bytes = _in.read(_sync.data, offset, BUFFER_SIZE);
+ if (bytes > 0) {
+ _sync.wrote(bytes);
+ return true;
+ }
+ return false;
+ }
+
+ /** The underlying input stream. */
+ protected InputStream _in;
+
+ /** The sync state. */
+ protected SyncState _sync = new SyncState();
+
+ /** The stream state. */
+ protected StreamState _stream = new StreamState();
+
+ /** The output page. */
+ protected Page _page = new Page();
+
+ /** The output packet. */
+ protected Packet _packet = new Packet();
+
+ /** The stream info. */
+ protected Info _info = new Info();
+
+ /** The DSP state. */
+ protected DspState _dsp = new DspState();
+
+ /** The output block. */
+ protected Block _block = new Block(_dsp);
+
+ /** Holds the decoded PCM data buffers. */
+ protected float[][][] _pcm = new float[1][][];
+
+ /** The DSP offsets for each channel. */
+ protected int[] _offsets;
+
+ /** Intermediate storage for converted data. */
+ protected short[] _data = new short[0];
+
+ /** The decode buffer size. */
+ protected static final int BUFFER_SIZE = 4096 * 2;
}