
com.threerings.openal.OggStreamDecoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nenya Show documentation
Show all versions of nenya Show documentation
Facilities for making networked multiplayer games.
The newest version!
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.openal;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import org.lwjgl.openal.AL10;
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
{
@Override
public void init (InputStream in)
throws IOException
{
_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];
}
@Override
public int getFormat ()
{
return (_info.channels == 1) ? AL10.AL_FORMAT_MONO16 : AL10.AL_FORMAT_STEREO16;
}
@Override
public int getFrequency ()
{
return _info.rate;
}
@Override
public int read (ByteBuffer buf)
throws IOException
{
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;
}
/**
* 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;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy