Home | History | Annotate | Download | only in audio
      1 /**
      2  * Copyright (c) 2007, Slick 2D
      3  *
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
      7  * conditions are met:
      8  *
      9  * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
     10  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
     11  * in the documentation and/or other materials provided with the distribution. Neither the name of the Slick 2D nor the names of
     12  * its contributors may be used to endorse or promote products derived from this software without specific prior written
     13  * permission.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
     16  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
     17  * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     19  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     20  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     21  */
     22 
     23 package com.badlogic.gdx.backends.lwjgl.audio;
     24 
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.nio.ByteBuffer;
     28 import java.nio.ByteOrder;
     29 
     30 import org.lwjgl.BufferUtils;
     31 
     32 import com.badlogic.gdx.Gdx;
     33 import com.badlogic.gdx.utils.GdxRuntimeException;
     34 import com.badlogic.gdx.utils.StreamUtils;
     35 import com.jcraft.jogg.Packet;
     36 import com.jcraft.jogg.Page;
     37 import com.jcraft.jogg.StreamState;
     38 import com.jcraft.jogg.SyncState;
     39 import com.jcraft.jorbis.Block;
     40 import com.jcraft.jorbis.Comment;
     41 import com.jcraft.jorbis.DspState;
     42 import com.jcraft.jorbis.Info;
     43 
     44 /** An input stream to read Ogg Vorbis.
     45  * @author kevin */
     46 public class OggInputStream extends InputStream {
     47 	private final static int BUFFER_SIZE = 512;
     48 
     49 	/** The conversion buffer size */
     50 	private int convsize = BUFFER_SIZE * 4;
     51 	/** The buffer used to read OGG file */
     52 	private byte[] convbuffer;
     53 	/** The stream we're reading the OGG file from */
     54 	private InputStream input;
     55 	/** The audio information from the OGG header */
     56 	private Info oggInfo = new Info(); // struct that stores all the static vorbis bitstream settings
     57 	/** True if we're at the end of the available data */
     58 	private boolean endOfStream;
     59 
     60 	/** The Vorbis SyncState used to decode the OGG */
     61 	private SyncState syncState = new SyncState(); // sync and verify incoming physical bitstream
     62 	/** The Vorbis Stream State used to decode the OGG */
     63 	private StreamState streamState = new StreamState(); // take physical pages, weld into a logical stream of packets
     64 	/** The current OGG page */
     65 	private Page page = new Page(); // one Ogg bitstream page. Vorbis packets are inside
     66 	/** The current packet page */
     67 	private Packet packet = new Packet(); // one raw packet of data for decode
     68 
     69 	/** The comment read from the OGG file */
     70 	private Comment comment = new Comment(); // struct that stores all the bitstream user comments
     71 	/** The Vorbis DSP stat eused to decode the OGG */
     72 	private DspState dspState = new DspState(); // central working state for the packet->PCM decoder
     73 	/** The OGG block we're currently working with to convert PCM */
     74 	private Block vorbisBlock = new Block(dspState); // local working space for packet->PCM decode
     75 
     76 	/** Temporary scratch buffer */
     77 	byte[] buffer;
     78 	/** The number of bytes read */
     79 	int bytes = 0;
     80 	/** The true if we should be reading big endian */
     81 	boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
     82 	/** True if we're reached the end of the current bit stream */
     83 	boolean endOfBitStream = true;
     84 	/** True if we're initialise the OGG info block */
     85 	boolean inited = false;
     86 
     87 	/** The index into the byte array we currently read from */
     88 	private int readIndex;
     89 	/** The byte array store used to hold the data read from the ogg */
     90 	private ByteBuffer pcmBuffer;
     91 	/** The total number of bytes */
     92 	private int total;
     93 
     94 	/** Create a new stream to decode OGG data
     95 	 *
     96 	 * @param input The input stream from which to read the OGG file */
     97 	public OggInputStream (InputStream input) {
     98 		this(input, null);
     99 	}
    100 
    101 	/** Create a new stream to decode OGG data, reusing buffers from another stream.
    102 	 *
    103 	 * It's not a good idea to use the old stream instance afterwards.
    104 	 *
    105 	 * @param input The input stream from which to read the OGG file
    106 	 * @param previousStream The stream instance to reuse buffers from, may be null */
    107 	OggInputStream (InputStream input, OggInputStream previousStream) {
    108 		if (previousStream == null) {
    109 			convbuffer = new byte[convsize];
    110 			pcmBuffer = BufferUtils.createByteBuffer(4096 * 500);
    111 		} else {
    112 			convbuffer = previousStream.convbuffer;
    113 			pcmBuffer = previousStream.pcmBuffer;
    114 		}
    115 
    116 		this.input = input;
    117 		try {
    118 			total = input.available();
    119 		} catch (IOException ex) {
    120 			throw new GdxRuntimeException(ex);
    121 		}
    122 
    123 		init();
    124 	}
    125 
    126 	/** Get the number of bytes on the stream
    127 	 *
    128 	 * @return The number of the bytes on the stream */
    129 	public int getLength () {
    130 		return total;
    131 	}
    132 
    133 	public int getChannels () {
    134 		return oggInfo.channels;
    135 	}
    136 
    137 	public int getSampleRate () {
    138 		return oggInfo.rate;
    139 	}
    140 
    141 	/** Initialise the streams and thread involved in the streaming of OGG data */
    142 	private void init () {
    143 		initVorbis();
    144 		readPCM();
    145 	}
    146 
    147 	/** @see java.io.InputStream#available() */
    148 	public int available () {
    149 		return endOfStream ? 0 : 1;
    150 	}
    151 
    152 	/** Initialise the vorbis decoding */
    153 	private void initVorbis () {
    154 		syncState.init();
    155 	}
    156 
    157 	/** Get a page and packet from that page
    158 	 *
    159 	 * @return True if there was a page available */
    160 	private boolean getPageAndPacket () {
    161 		// grab some data at the head of the stream. We want the first page
    162 		// (which is guaranteed to be small and only contain the Vorbis
    163 		// stream initial header) We need the first page to get the stream
    164 		// serialno.
    165 
    166 		// submit a 4k block to libvorbis' Ogg layer
    167 		int index = syncState.buffer(BUFFER_SIZE);
    168 		if (index == -1) return false;
    169 
    170 		buffer = syncState.data;
    171 		if (buffer == null) {
    172 			endOfStream = true;
    173 			return false;
    174 		}
    175 
    176 		try {
    177 			bytes = input.read(buffer, index, BUFFER_SIZE);
    178 		} catch (Exception e) {
    179 			throw new GdxRuntimeException("Failure reading Vorbis.", e);
    180 		}
    181 		syncState.wrote(bytes);
    182 
    183 		// Get the first page.
    184 		if (syncState.pageout(page) != 1) {
    185 			// have we simply run out of data? If so, we're done.
    186 			if (bytes < BUFFER_SIZE) return false;
    187 
    188 			// error case. Must not be Vorbis data
    189 			throw new GdxRuntimeException("Input does not appear to be an Ogg bitstream.");
    190 		}
    191 
    192 		// Get the serial number and set up the rest of decode.
    193 		// serialno first; use it to set up a logical stream
    194 		streamState.init(page.serialno());
    195 
    196 		// extract the initial header from the first page and verify that the
    197 		// Ogg bitstream is in fact Vorbis data
    198 
    199 		// I handle the initial header first instead of just having the code
    200 		// read all three Vorbis headers at once because reading the initial
    201 		// header is an easy way to identify a Vorbis bitstream and it's
    202 		// useful to see that functionality seperated out.
    203 
    204 		oggInfo.init();
    205 		comment.init();
    206 		if (streamState.pagein(page) < 0) {
    207 			// error; stream version mismatch perhaps
    208 			throw new GdxRuntimeException("Error reading first page of Ogg bitstream.");
    209 		}
    210 
    211 		if (streamState.packetout(packet) != 1) {
    212 			// no page? must not be vorbis
    213 			throw new GdxRuntimeException("Error reading initial header packet.");
    214 		}
    215 
    216 		if (oggInfo.synthesis_headerin(comment, packet) < 0) {
    217 			// error case; not a vorbis header
    218 			throw new GdxRuntimeException("Ogg bitstream does not contain Vorbis audio data.");
    219 		}
    220 
    221 		// At this point, we're sure we're Vorbis. We've set up the logical
    222 		// (Ogg) bitstream decoder. Get the comment and codebook headers and
    223 		// set up the Vorbis decoder
    224 
    225 		// The next two packets in order are the comment and codebook headers.
    226 		// They're likely large and may span multiple pages. Thus we reead
    227 		// and submit data until we get our two pacakets, watching that no
    228 		// pages are missing. If a page is missing, error out; losing a
    229 		// header page is the only place where missing data is fatal. */
    230 
    231 		int i = 0;
    232 		while (i < 2) {
    233 			while (i < 2) {
    234 				int result = syncState.pageout(page);
    235 				if (result == 0) break; // Need more data
    236 				// Don't complain about missing or corrupt data yet. We'll
    237 				// catch it at the packet output phase
    238 
    239 				if (result == 1) {
    240 					streamState.pagein(page); // we can ignore any errors here
    241 					// as they'll also become apparent
    242 					// at packetout
    243 					while (i < 2) {
    244 						result = streamState.packetout(packet);
    245 						if (result == 0) break;
    246 						if (result == -1) {
    247 							// Uh oh; data at some point was corrupted or missing!
    248 							// We can't tolerate that in a header. Die.
    249 							throw new GdxRuntimeException("Corrupt secondary header.");
    250 						}
    251 
    252 						oggInfo.synthesis_headerin(comment, packet);
    253 						i++;
    254 					}
    255 				}
    256 			}
    257 			// no harm in not checking before adding more
    258 			index = syncState.buffer(BUFFER_SIZE);
    259 			if (index == -1) return false;
    260 			buffer = syncState.data;
    261 			try {
    262 				bytes = input.read(buffer, index, BUFFER_SIZE);
    263 			} catch (Exception e) {
    264 				throw new GdxRuntimeException("Failed to read Vorbis.", e);
    265 			}
    266 			if (bytes == 0 && i < 2) {
    267 				throw new GdxRuntimeException("End of file before finding all Vorbis headers.");
    268 			}
    269 			syncState.wrote(bytes);
    270 		}
    271 
    272 		convsize = BUFFER_SIZE / oggInfo.channels;
    273 
    274 		// OK, got and parsed all three headers. Initialize the Vorbis
    275 		// packet->PCM decoder.
    276 		dspState.synthesis_init(oggInfo); // central decode state
    277 		vorbisBlock.init(dspState); // local state for most of the decode
    278 		// so multiple block decodes can
    279 		// proceed in parallel. We could init
    280 		// multiple vorbis_block structures
    281 		// for vd here
    282 
    283 		return true;
    284 	}
    285 
    286 	/** Decode the OGG file as shown in the jogg/jorbis examples */
    287 	private void readPCM () {
    288 		boolean wrote = false;
    289 
    290 		while (true) { // we repeat if the bitstream is chained
    291 			if (endOfBitStream) {
    292 				if (!getPageAndPacket()) {
    293 					break;
    294 				}
    295 				endOfBitStream = false;
    296 			}
    297 
    298 			if (!inited) {
    299 				inited = true;
    300 				return;
    301 			}
    302 
    303 			float[][][] _pcm = new float[1][][];
    304 			int[] _index = new int[oggInfo.channels];
    305 			// The rest is just a straight decode loop until end of stream
    306 			while (!endOfBitStream) {
    307 				while (!endOfBitStream) {
    308 					int result = syncState.pageout(page);
    309 
    310 					if (result == 0) {
    311 						break; // need more data
    312 					}
    313 
    314 					if (result == -1) { // missing or corrupt data at this page position
    315 						// throw new GdxRuntimeException("Corrupt or missing data in bitstream.");
    316 						Gdx.app.log("gdx-audio", "Error reading OGG: Corrupt or missing data in bitstream.");
    317 					} else {
    318 						streamState.pagein(page); // can safely ignore errors at
    319 						// this point
    320 						while (true) {
    321 							result = streamState.packetout(packet);
    322 
    323 							if (result == 0) break; // need more data
    324 							if (result == -1) { // missing or corrupt data at this page position
    325 								// no reason to complain; already complained above
    326 							} else {
    327 								// we have a packet. Decode it
    328 								int samples;
    329 								if (vorbisBlock.synthesis(packet) == 0) { // test for success!
    330 									dspState.synthesis_blockin(vorbisBlock);
    331 								}
    332 
    333 								// **pcm is a multichannel float vector. In stereo, for
    334 								// example, pcm[0] is left, and pcm[1] is right. samples is
    335 								// the size of each channel. Convert the float values
    336 								// (-1.<=range<=1.) to whatever PCM format and write it out
    337 
    338 								while ((samples = dspState.synthesis_pcmout(_pcm, _index)) > 0) {
    339 									float[][] pcm = _pcm[0];
    340 									// boolean clipflag = false;
    341 									int bout = (samples < convsize ? samples : convsize);
    342 
    343 									// convert floats to 16 bit signed ints (host order) and
    344 									// interleave
    345 									for (int i = 0; i < oggInfo.channels; i++) {
    346 										int ptr = i * 2;
    347 										// int ptr=i;
    348 										int mono = _index[i];
    349 										for (int j = 0; j < bout; j++) {
    350 											int val = (int)(pcm[i][mono + j] * 32767.);
    351 											// might as well guard against clipping
    352 											if (val > 32767) {
    353 												val = 32767;
    354 											}
    355 											if (val < -32768) {
    356 												val = -32768;
    357 											}
    358 											if (val < 0) val = val | 0x8000;
    359 
    360 											if (bigEndian) {
    361 												convbuffer[ptr] = (byte)(val >>> 8);
    362 												convbuffer[ptr + 1] = (byte)(val);
    363 											} else {
    364 												convbuffer[ptr] = (byte)(val);
    365 												convbuffer[ptr + 1] = (byte)(val >>> 8);
    366 											}
    367 											ptr += 2 * (oggInfo.channels);
    368 										}
    369 									}
    370 
    371 									int bytesToWrite = 2 * oggInfo.channels * bout;
    372 									if (bytesToWrite > pcmBuffer.remaining()) {
    373 										throw new GdxRuntimeException("Ogg block too big to be buffered: " + bytesToWrite + " :: " + pcmBuffer.remaining());
    374 									} else {
    375 										pcmBuffer.put(convbuffer, 0, bytesToWrite);
    376 									}
    377 
    378 									wrote = true;
    379 									dspState.synthesis_read(bout); // tell libvorbis how
    380 									// many samples we
    381 									// actually consumed
    382 								}
    383 							}
    384 						}
    385 						if (page.eos() != 0) {
    386 							endOfBitStream = true;
    387 						}
    388 
    389 						if ((!endOfBitStream) && (wrote)) {
    390 							return;
    391 						}
    392 					}
    393 				}
    394 
    395 				if (!endOfBitStream) {
    396 					bytes = 0;
    397 					int index = syncState.buffer(BUFFER_SIZE);
    398 					if (index >= 0) {
    399 						buffer = syncState.data;
    400 						try {
    401 							bytes = input.read(buffer, index, BUFFER_SIZE);
    402 						} catch (Exception e) {
    403 							throw new GdxRuntimeException("Error during Vorbis decoding.", e);
    404 						}
    405 					} else {
    406 						bytes = 0;
    407 					}
    408 					syncState.wrote(bytes);
    409 					if (bytes == 0) {
    410 						endOfBitStream = true;
    411 					}
    412 				}
    413 			}
    414 
    415 			// clean up this logical bitstream; before exit we see if we're
    416 			// followed by another [chained]
    417 			streamState.clear();
    418 
    419 			// ogg_page and ogg_packet structs always point to storage in
    420 			// libvorbis. They're never freed or manipulated directly
    421 
    422 			vorbisBlock.clear();
    423 			dspState.clear();
    424 			oggInfo.clear(); // must be called last
    425 		}
    426 
    427 		// OK, clean up the framer
    428 		syncState.clear();
    429 		endOfStream = true;
    430 	}
    431 
    432 	public int read () {
    433 		if (readIndex >= pcmBuffer.position()) {
    434 			pcmBuffer.clear();
    435 			readPCM();
    436 			readIndex = 0;
    437 		}
    438 		if (readIndex >= pcmBuffer.position()) {
    439 			return -1;
    440 		}
    441 
    442 		int value = pcmBuffer.get(readIndex);
    443 		if (value < 0) {
    444 			value = 256 + value;
    445 		}
    446 		readIndex++;
    447 
    448 		return value;
    449 	}
    450 
    451 	public boolean atEnd () {
    452 		return endOfStream && (readIndex >= pcmBuffer.position());
    453 	}
    454 
    455 	public int read (byte[] b, int off, int len) {
    456 		for (int i = 0; i < len; i++) {
    457 			int value = read();
    458 			if (value >= 0) {
    459 				b[i] = (byte)value;
    460 			} else {
    461 				if (i == 0) {
    462 					return -1;
    463 				} else {
    464 					return i;
    465 				}
    466 			}
    467 		}
    468 
    469 		return len;
    470 	}
    471 
    472 	public int read (byte[] b) {
    473 		return read(b, 0, b.length);
    474 	}
    475 
    476 	public void close () {
    477 		StreamUtils.closeQuietly(input);
    478 	}
    479 }
    480