2 * 11/19/04 1.0 moved to LGPL.
\r
4 * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net
\r
6 * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net
\r
8 * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,
\r
9 * Temporary removed seek functionality. mdm@techie.com
\r
11 * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
\r
13 * 04/14/97 : Added function prototypes for new syncing and seeking
\r
14 * mechanisms. Also made this file portable. Changes made by Jeff Tsay
\r
16 * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
\r
17 * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
\r
18 * @(#) Berlin University of Technology
\r
19 *-----------------------------------------------------------------------
\r
20 * This program is free software; you can redistribute it and/or modify
\r
21 * it under the terms of the GNU Library General Public License as published
\r
22 * by the Free Software Foundation; either version 2 of the License, or
\r
23 * (at your option) any later version.
\r
25 * This program is distributed in the hope that it will be useful,
\r
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
28 * GNU Library General Public License for more details.
\r
30 * You should have received a copy of the GNU Library General Public
\r
31 * License along with this program; if not, write to the Free Software
\r
32 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\r
33 *----------------------------------------------------------------------
\r
36 package javazoom.jl.decoder;
\r
38 import java.io.BufferedInputStream;
\r
39 import java.io.ByteArrayInputStream;
\r
40 import java.io.IOException;
\r
41 import java.io.InputStream;
\r
42 import java.io.PushbackInputStream;
\r
46 * The <code>Bistream</code> class is responsible for parsing
\r
47 * an MPEG audio bitstream.
\r
49 * <b>REVIEW:</b> much of the parsing currently occurs in the
\r
50 * various decoders. This should be moved into this class and associated
\r
53 public final class Bitstream implements BitstreamErrors
\r
56 * Synchronization control constant for the initial
\r
57 * synchronization to the start of a frame.
\r
59 static byte INITIAL_SYNC = 0;
\r
62 * Synchronization control constant for non-initial frame
\r
65 static byte STRICT_SYNC = 1;
\r
67 // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
\r
69 * Maximum size of the frame buffer.
\r
71 private static final int BUFFER_INT_SIZE = 433;
\r
74 * The frame buffer that holds the data for the current frame.
\r
76 private final int[] framebuffer = new int[BUFFER_INT_SIZE];
\r
79 * Number of valid bytes in the frame buffer.
\r
81 private int framesize;
\r
84 * The bytes read from the stream.
\r
86 private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];
\r
89 * Index into <code>framebuffer</code> where the next bits are
\r
92 private int wordpointer;
\r
95 * Number (0-31, from MSB to LSB) of next bit for get_bits()
\r
97 private int bitindex;
\r
100 * The current specified syncword
\r
102 private int syncword;
\r
105 * Audio header position in stream.
\r
107 private int header_pos = 0;
\r
112 private boolean single_ch_mode;
\r
113 //private int current_frame_number;
\r
114 //private int last_frame_number;
\r
116 private final int bitmask[] = {0, // dummy
\r
117 0x00000001, 0x00000003, 0x00000007, 0x0000000F,
\r
118 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
\r
119 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
\r
120 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
\r
123 private final PushbackInputStream source;
\r
125 private final Header header = new Header();
\r
127 private final byte syncbuf[] = new byte[4];
\r
129 private Crc16[] crc = new Crc16[1];
\r
131 private byte[] rawid3v2 = null;
\r
133 private boolean firstframe = true;
\r
137 * Construct a IBitstream that reads data from a
\r
138 * given InputStream.
\r
140 * @param in The InputStream to read from.
\r
142 public Bitstream(InputStream in)
\r
144 if (in==null) throw new NullPointerException("in");
\r
145 in = new BufferedInputStream(in);
\r
148 //source = new PushbackInputStream(in, 1024);
\r
149 source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);
\r
152 //current_frame_number = -1;
\r
153 //last_frame_number = -1;
\r
157 * Return position of the first audio header.
\r
158 * @return size of ID3v2 tag frames.
\r
160 public int header_pos()
\r
166 * Load ID3v2 frames.
\r
167 * @param in MP3 InputStream.
\r
170 private void loadID3v2(InputStream in)
\r
175 // Read ID3v2 header (10 bytes).
\r
177 size = readID3v2Header(in);
\r
178 header_pos = size;
\r
180 catch (IOException e)
\r
186 // Unread ID3v2 header (10 bytes).
\r
189 catch (IOException e)
\r
192 // Load ID3v2 tags.
\r
197 rawid3v2 = new byte[size];
\r
198 in.read(rawid3v2,0,rawid3v2.length);
\r
201 catch (IOException e)
\r
206 * Parse ID3v2 tag header to find out size of ID3v2 frames.
\r
207 * @param in MP3 InputStream
\r
208 * @return size of ID3v2 frames + header
\r
209 * @throws IOException
\r
212 private int readID3v2Header(InputStream in) throws IOException
\r
214 byte[] id3header = new byte[4];
\r
216 in.read(id3header,0,3);
\r
218 if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))
\r
220 in.read(id3header,0,3);
\r
221 int majorVersion = id3header[0];
\r
222 int revision = id3header[1];
\r
223 in.read(id3header,0,4);
\r
224 size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
\r
230 * Return raw ID3v2 frames + header.
\r
231 * @return ID3v2 InputStream or null if ID3v2 frames are not available.
\r
233 public InputStream getRawID3v2()
\r
235 if (rawid3v2 == null) return null;
\r
238 ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
\r
244 * Close the Bitstream.
\r
245 * @throws BitstreamException
\r
247 public void close() throws BitstreamException
\r
253 catch (IOException ex)
\r
255 throw newBitstreamException(STREAM_ERROR, ex);
\r
260 * Reads and parses the next frame from the input source.
\r
261 * @return the Header describing details of the frame read,
\r
262 * or null if the end of the stream has been reached.
\r
264 public Header readFrame() throws BitstreamException
\r
266 Header result = null;
\r
269 result = readNextFrame();
\r
270 // E.B, Parse VBR (if any) first frame.
\r
271 if (firstframe == true)
\r
273 result.parseVBR(frame_bytes);
\r
274 firstframe = false;
\r
277 catch (BitstreamException ex)
\r
279 if ((ex.getErrorCode()==INVALIDFRAME))
\r
281 // Try to skip this frame.
\r
282 //System.out.println("INVALIDFRAME");
\r
286 result = readNextFrame();
\r
288 catch (BitstreamException e)
\r
290 if ((e.getErrorCode()!=STREAM_EOF))
\r
292 // wrap original exception so stack trace is maintained.
\r
293 throw newBitstreamException(e.getErrorCode(), e);
\r
297 else if ((ex.getErrorCode()!=STREAM_EOF))
\r
299 // wrap original exception so stack trace is maintained.
\r
300 throw newBitstreamException(ex.getErrorCode(), ex);
\r
307 * Read next MP3 frame.
\r
308 * @return MP3 frame header.
\r
309 * @throws BitstreamException
\r
311 private Header readNextFrame() throws BitstreamException
\r
313 if (framesize == -1)
\r
322 * Read next MP3 frame.
\r
323 * @throws BitstreamException
\r
325 private void nextFrame() throws BitstreamException
\r
327 // entire frame is read by the header class.
\r
328 header.read_header(this, crc);
\r
332 * Unreads the bytes read from the frame.
\r
333 * @throws BitstreamException
\r
335 // REVIEW: add new error codes for this.
\r
336 public void unreadFrame() throws BitstreamException
\r
338 if (wordpointer==-1 && bitindex==-1 && (framesize>0))
\r
342 source.unread(frame_bytes, 0, framesize);
\r
344 catch (IOException ex)
\r
346 throw newBitstreamException(STREAM_ERROR);
\r
354 public void closeFrame()
\r
362 * Determines if the next 4 bytes of the stream represent a
\r
365 public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException
\r
367 int read = readBytes(syncbuf, 0, 4);
\r
368 int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
\r
372 source.unread(syncbuf, 0, read);
\r
374 catch (IOException ex)
\r
378 boolean sync = false;
\r
385 sync = isSyncMark(headerstring, syncmode, syncword);
\r
393 // REVIEW: this class should provide inner classes to
\r
394 // parse the frame contents. Eventually, readBits will
\r
396 public int readBits(int n)
\r
398 return get_bits(n);
\r
401 public int readCheckedBits(int n)
\r
403 // REVIEW: implement CRC check.
\r
404 return get_bits(n);
\r
407 protected BitstreamException newBitstreamException(int errorcode)
\r
409 return new BitstreamException(errorcode, null);
\r
411 protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)
\r
413 return new BitstreamException(errorcode, throwable);
\r
417 * Get next 32 bits from bitstream.
\r
418 * They are stored in the headerstring.
\r
419 * syncmod allows Synchro flag ID
\r
420 * The returned value is False at the end of stream.
\r
423 int syncHeader(byte syncmode) throws BitstreamException
\r
427 // read additional 2 bytes
\r
428 int bytesRead = readBytes(syncbuf, 0, 3);
\r
430 if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);
\r
432 headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);
\r
436 headerstring <<= 8;
\r
438 if (readBytes(syncbuf, 3, 1)!=1)
\r
439 throw newBitstreamException(STREAM_EOF, null);
\r
441 headerstring |= (syncbuf[3] & 0x000000FF);
\r
443 sync = isSyncMark(headerstring, syncmode, syncword);
\r
447 //current_frame_number++;
\r
448 //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;
\r
450 return headerstring;
\r
453 public boolean isSyncMark(int headerstring, int syncmode, int word)
\r
455 boolean sync = false;
\r
457 if (syncmode == INITIAL_SYNC)
\r
459 //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
\r
460 sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
\r
464 sync = ((headerstring & 0xFFF80C00) == word) &&
\r
465 (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
\r
468 // filter out invalid sample rate
\r
470 sync = (((headerstring >>> 10) & 3)!=3);
\r
471 // filter out invalid layer
\r
473 sync = (((headerstring >>> 17) & 3)!=0);
\r
474 // filter out invalid version
\r
476 sync = (((headerstring >>> 19) & 3)!=1);
\r
482 * Reads the data for the next frame. The frame is not parsed
\r
483 * until parse frame is called.
\r
485 int read_frame_data(int bytesize) throws BitstreamException
\r
488 numread = readFully(frame_bytes, 0, bytesize);
\r
489 framesize = bytesize;
\r
496 * Parses the data previously read with read_frame_data().
\r
498 void parse_frame() throws BitstreamException
\r
500 // Convert Bytes read to int
\r
502 byte[] byteread = frame_bytes;
\r
503 int bytesize = framesize;
\r
505 // Check ID3v1 TAG (True only if last frame).
\r
506 //for (int t=0;t<(byteread.length)-2;t++)
\r
508 // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
\r
510 // System.out.println("ID3v1 detected at offset "+t);
\r
511 // throw newBitstreamException(INVALIDFRAME, null);
\r
515 for (int k=0;k<bytesize;k=k+4)
\r
523 if (k+1<bytesize) b1 = byteread[k+1];
\r
524 if (k+2<bytesize) b2 = byteread[k+2];
\r
525 if (k+3<bytesize) b3 = byteread[k+3];
\r
526 framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);
\r
533 * Read bits from buffer into the lower bits of an unsigned int.
\r
534 * The LSB contains the latest read bit of the stream.
\r
535 * (1 <= number_of_bits <= 16)
\r
537 public int get_bits(int number_of_bits)
\r
539 int returnvalue = 0;
\r
540 int sum = bitindex + number_of_bits;
\r
543 // There is a problem here, wordpointer could be -1 ?!
\r
544 if (wordpointer < 0) wordpointer = 0;
\r
549 // all bits contained in *wordpointer
\r
550 returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];
\r
551 // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];
\r
552 if ((bitindex += number_of_bits) == 32)
\r
555 wordpointer++; // added by me!
\r
557 return returnvalue;
\r
560 // E.B : Check that ?
\r
561 //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
\r
562 //wordpointer++; // Added by me!
\r
563 //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
\r
564 int Right = (framebuffer[wordpointer] & 0x0000FFFF);
\r
566 int Left = (framebuffer[wordpointer] & 0xFFFF0000);
\r
567 returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);
\r
569 returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))
\r
570 returnvalue &= bitmask[number_of_bits];
\r
571 bitindex = sum - 32;
\r
572 return returnvalue;
\r
576 * Set the word we want to sync the header to.
\r
577 * In Big-Endian byte order
\r
579 void set_syncword(int syncword0)
\r
581 syncword = syncword0 & 0xFFFFFF3F;
\r
582 single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
\r
585 * Reads the exact number of bytes from the source
\r
586 * input stream into a byte array.
\r
588 * @param b The byte array to read the specified number
\r
590 * @param offs The index in the array where the first byte
\r
591 * read should be stored.
\r
592 * @param len the number of bytes to read.
\r
594 * @exception BitstreamException is thrown if the specified
\r
595 * number of bytes could not be read from the stream.
\r
597 private int readFully(byte[] b, int offs, int len)
\r
598 throws BitstreamException
\r
605 int bytesread = source.read(b, offs, len);
\r
606 if (bytesread == -1)
\r
613 //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());
\r
615 nRead = nRead + bytesread;
\r
620 catch (IOException ex)
\r
622 throw newBitstreamException(STREAM_ERROR, ex);
\r
628 * Simlar to readFully, but doesn't throw exception when
\r
631 private int readBytes(byte[] b, int offs, int len)
\r
632 throws BitstreamException
\r
634 int totalBytesRead = 0;
\r
639 int bytesread = source.read(b, offs, len);
\r
640 if (bytesread == -1)
\r
644 totalBytesRead += bytesread;
\r
649 catch (IOException ex)
\r
651 throw newBitstreamException(STREAM_ERROR, ex);
\r
653 return totalBytesRead;
\r