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