-/*\r
- * 11/19/04 1.0 moved to LGPL.\r
- * \r
- * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net \r
- *\r
- * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net \r
- *\r
- * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,\r
- * Temporary removed seek functionality. mdm@techie.com\r
- *\r
- * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net\r
- *\r
- * 04/14/97 : Added function prototypes for new syncing and seeking\r
- * mechanisms. Also made this file portable. Changes made by Jeff Tsay\r
- *\r
- * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34\r
- * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)\r
- * @(#) Berlin University of Technology\r
- *-----------------------------------------------------------------------\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU Library General Public License as published\r
- * by the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU Library General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Library General Public\r
- * License along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- *----------------------------------------------------------------------\r
- */\r
-\r
-\r
-/**\r
- * The <code>Bistream</code> class is responsible for parsing\r
- * an MPEG audio bitstream.\r
- *\r
- * <b>REVIEW:</b> much of the parsing currently occurs in the\r
- * various decoders. This should be moved into this class and associated\r
- * inner classes.\r
- */\r
-public final class Bitstream implements BitstreamErrors\r
-{\r
- /**\r
- * Synchronization control constant for the initial\r
- * synchronization to the start of a frame.\r
- */\r
- static final byte INITIAL_SYNC = 0;\r
-\r
- /**\r
- * Synchronization control constant for non-initial frame\r
- * synchronizations.\r
- */\r
- static final byte STRICT_SYNC = 1;\r
-\r
- // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC\r
- /**\r
- * Maximum size of the frame buffer.\r
- */\r
- private static final int BUFFER_INT_SIZE = 433;\r
-\r
- /**\r
- * The frame buffer that holds the data for the current frame.\r
- */\r
- private final int[] framebuffer = new int[BUFFER_INT_SIZE];\r
-\r
- /**\r
- * Number of valid bytes in the frame buffer.\r
- */\r
- private int framesize;\r
-\r
- /**\r
- * The bytes read from the stream.\r
- */\r
- private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];\r
-\r
- /**\r
- * Index into <code>framebuffer</code> where the next bits are\r
- * retrieved.\r
- */\r
- private int wordpointer;\r
-\r
- /**\r
- * Number (0-31, from MSB to LSB) of next bit for get_bits()\r
- */\r
- private int bitindex;\r
-\r
- /**\r
- * The current specified syncword\r
- */\r
- private int syncword;\r
- \r
- /**\r
- * Audio header position in stream.\r
- */\r
- private int header_pos = 0;\r
-\r
- /**\r
- *\r
- */\r
- private boolean single_ch_mode;\r
- //private int current_frame_number;\r
- //private int last_frame_number;\r
-\r
- private final int bitmask[] = {0, // dummy\r
- 0x00000001, 0x00000003, 0x00000007, 0x0000000F,\r
- 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,\r
- 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,\r
- 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,\r
- 0x0001FFFF };\r
-\r
- private final PushbackInputStream source;\r
-\r
- private final Header header = new Header();\r
-\r
- private final byte syncbuf[] = new byte[4];\r
-\r
- private Crc16[] crc = new Crc16[1];\r
-\r
- private byte[] rawid3v2 = null;\r
-\r
- private boolean firstframe = true;\r
-\r
-\r
- /**\r
- * Construct a IBitstream that reads data from a\r
- * given InputStream.\r
- *\r
- * @param in The InputStream to read from.\r
- */\r
- public Bitstream(InputStream in)\r
- {\r
- if (in==null) throw new NullPointerException("in");\r
- in = new BufferedInputStream(in); \r
- loadID3v2(in);\r
- firstframe = true;\r
- //source = new PushbackInputStream(in, 1024);\r
- source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);\r
- \r
- closeFrame();\r
- //current_frame_number = -1;\r
- //last_frame_number = -1;\r
- }\r
-\r
- /**\r
- * Return position of the first audio header.\r
- * @return size of ID3v2 tag frames.\r
- */\r
- public int header_pos()\r
- {\r
- return header_pos;\r
- }\r
- \r
- /**\r
- * Load ID3v2 frames.\r
- * @param in MP3 InputStream.\r
- * @author JavaZOOM\r
- */\r
- private void loadID3v2(InputStream in)\r
- { \r
- int size = -1;\r
- try\r
- {\r
- // Read ID3v2 header (10 bytes).\r
- in.mark(10); \r
- size = readID3v2Header(in);\r
- header_pos = size; \r
- }\r
- catch (IOException e)\r
- {}\r
- finally\r
- {\r
- try\r
- {\r
- // Unread ID3v2 header (10 bytes).\r
- in.reset();\r
- }\r
- catch (IOException e)\r
- {}\r
- }\r
- // Load ID3v2 tags.\r
- try\r
- {\r
- if (size > 0)\r
- {\r
- rawid3v2 = new byte[size];\r
- in.read(rawid3v2,0,rawid3v2.length);\r
- } \r
- }\r
- catch (IOException e)\r
- {}\r
- }\r
- \r
- /**\r
- * Parse ID3v2 tag header to find out size of ID3v2 frames. \r
- * @param in MP3 InputStream\r
- * @return size of ID3v2 frames + header\r
- * @throws IOException\r
- * @author JavaZOOM\r
- */\r
- private int readID3v2Header(InputStream in) throws IOException\r
- { \r
- byte[] id3header = new byte[4];\r
- int size = -10;\r
- in.read(id3header,0,3);\r
- // Look for ID3v2\r
- if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))\r
- {\r
- in.read(id3header,0,3);\r
- int majorVersion = id3header[0];\r
- int revision = id3header[1];\r
- in.read(id3header,0,4);\r
- size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);\r
- }\r
- return (size+10);\r
- }\r
- \r
- /**\r
- * Return raw ID3v2 frames + header.\r
- * @return ID3v2 InputStream or null if ID3v2 frames are not available.\r
- */\r
- public InputStream getRawID3v2()\r
- {\r
- if (rawid3v2 == null) return null;\r
- else\r
- {\r
- ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2); \r
- return bain;\r
- }\r
- }\r
-\r
- /**\r
- * Close the Bitstream.\r
- * @throws BitstreamException\r
- */\r
- public void close() throws BitstreamException\r
- {\r
- try\r
- {\r
- source.close();\r
- }\r
- catch (IOException ex)\r
- {\r
- throw newBitstreamException(STREAM_ERROR, ex);\r
- }\r
- }\r
-\r
- /**\r
- * Reads and parses the next frame from the input source.\r
- * @return the Header describing details of the frame read,\r
- * or null if the end of the stream has been reached.\r
- */\r
- public Header readFrame() throws BitstreamException\r
- {\r
- Header result = null;\r
- try\r
- {\r
- result = readNextFrame();\r
- // E.B, Parse VBR (if any) first frame.\r
- if (firstframe == true)\r
- {\r
- result.parseVBR(frame_bytes);\r
- firstframe = false;\r
- } \r
- }\r
- catch (BitstreamException ex)\r
- {\r
- if ((ex.getErrorCode()==INVALIDFRAME))\r
- {\r
- // Try to skip this frame.\r
- //System.out.println("INVALIDFRAME");\r
- try\r
- {\r
- closeFrame();\r
- result = readNextFrame();\r
- }\r
- catch (BitstreamException e)\r
- {\r
- if ((e.getErrorCode()!=STREAM_EOF))\r
- {\r
- // wrap original exception so stack trace is maintained.\r
- throw newBitstreamException(e.getErrorCode(), e);\r
- }\r
- }\r
- }\r
- else if ((ex.getErrorCode()!=STREAM_EOF))\r
- {\r
- // wrap original exception so stack trace is maintained.\r
- throw newBitstreamException(ex.getErrorCode(), ex);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Read next MP3 frame.\r
- * @return MP3 frame header.\r
- * @throws BitstreamException\r
- */\r
- private Header readNextFrame() throws BitstreamException\r
- {\r
- if (framesize == -1)\r
- {\r
- nextFrame();\r
- }\r
- return header;\r
- }\r
-\r
-\r
- /**\r
- * Read next MP3 frame.\r
- * @throws BitstreamException\r
- */\r
- private void nextFrame() throws BitstreamException\r
- {\r
- // entire frame is read by the header class.\r
- header.read_header(this, crc);\r
- }\r
-\r
- /**\r
- * Unreads the bytes read from the frame.\r
- * @throws BitstreamException\r
- */\r
- // REVIEW: add new error codes for this.\r
- public void unreadFrame() throws BitstreamException\r
- {\r
- if (wordpointer==-1 && bitindex==-1 && (framesize>0))\r
- {\r
- try\r
- {\r
- source.unread(frame_bytes, 0, framesize);\r
- }\r
- catch (IOException ex)\r
- {\r
- throw newBitstreamException(STREAM_ERROR);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Close MP3 frame.\r
- */\r
- public void closeFrame()\r
- {\r
- framesize = -1;\r
- wordpointer = -1;\r
- bitindex = -1;\r
- }\r
-\r
- /**\r
- * Determines if the next 4 bytes of the stream represent a\r
- * frame header.\r
- */\r
- public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException\r
- {\r
- int read = readBytes(syncbuf, 0, 4);\r
- int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);\r
-\r
- try\r
- {\r
- source.unread(syncbuf, 0, read);\r
- }\r
- catch (IOException ex)\r
- {\r
- }\r
-\r
- boolean sync = false;\r
- switch (read)\r
- {\r
- case 0:\r
- sync = true;\r
- break;\r
- case 4:\r
- sync = isSyncMark(headerstring, syncmode, syncword);\r
- break;\r
- }\r
-\r
- return sync;\r
- }\r
-\r
-\r
- // REVIEW: this class should provide inner classes to\r
- // parse the frame contents. Eventually, readBits will\r
- // be removed.\r
- public int readBits(int n)\r
- {\r
- return get_bits(n);\r
- }\r
-\r
- public int readCheckedBits(int n)\r
- {\r
- // REVIEW: implement CRC check.\r
- return get_bits(n);\r
- }\r
-\r
- protected BitstreamException newBitstreamException(int errorcode)\r
- {\r
- return new BitstreamException(errorcode, null);\r
- }\r
- protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)\r
- {\r
- return new BitstreamException(errorcode, throwable);\r
- }\r
-\r
- /**\r
- * Get next 32 bits from bitstream.\r
- * They are stored in the headerstring.\r
- * syncmod allows Synchro flag ID\r
- * The returned value is False at the end of stream.\r
- */\r
-\r
- int syncHeader(byte syncmode) throws BitstreamException\r
- {\r
- boolean sync;\r
- int headerstring;\r
- // read additional 2 bytes\r
- int bytesRead = readBytes(syncbuf, 0, 3);\r
-\r
- if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);\r
-\r
- headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);\r
-\r
- do\r
- {\r
- headerstring <<= 8;\r
-\r
- if (readBytes(syncbuf, 3, 1)!=1)\r
- throw newBitstreamException(STREAM_EOF, null);\r
-\r
- headerstring |= (syncbuf[3] & 0x000000FF);\r
-\r
- sync = isSyncMark(headerstring, syncmode, syncword);\r
- }\r
- while (!sync);\r
-\r
- //current_frame_number++;\r
- //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;\r
-\r
- return headerstring;\r
- }\r
-\r
- public boolean isSyncMark(int headerstring, int syncmode, int word)\r
- {\r
- boolean sync = false;\r
-\r
- if (syncmode == INITIAL_SYNC)\r
- {\r
- //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);\r
- sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5\r
- }\r
- else\r
- {\r
- sync = ((headerstring & 0xFFF80C00) == word) &&\r
- (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);\r
- }\r
-\r
- // filter out invalid sample rate\r
- if (sync)\r
- sync = (((headerstring >>> 10) & 3)!=3);\r
- // filter out invalid layer\r
- if (sync)\r
- sync = (((headerstring >>> 17) & 3)!=0);\r
- // filter out invalid version\r
- if (sync)\r
- sync = (((headerstring >>> 19) & 3)!=1);\r
-\r
- return sync;\r
- }\r
-\r
- /**\r
- * Reads the data for the next frame. The frame is not parsed\r
- * until parse frame is called.\r
- */\r
- int read_frame_data(int bytesize) throws BitstreamException\r
- {\r
- int numread = 0;\r
- numread = readFully(frame_bytes, 0, bytesize);\r
- framesize = bytesize;\r
- wordpointer = -1;\r
- bitindex = -1;\r
- return numread;\r
- }\r
-\r
- /**\r
- * Parses the data previously read with read_frame_data().\r
- */\r
- void parse_frame() throws BitstreamException\r
- {\r
- // Convert Bytes read to int\r
- int b=0;\r
- byte[] byteread = frame_bytes;\r
- int bytesize = framesize;\r
-\r
- // Check ID3v1 TAG (True only if last frame).\r
- //for (int t=0;t<(byteread.length)-2;t++)\r
- //{\r
- // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))\r
- // {\r
- // System.out.println("ID3v1 detected at offset "+t);\r
- // throw newBitstreamException(INVALIDFRAME, null);\r
- // } \r
- //}\r
- \r
- for (int k=0;k<bytesize;k=k+4)\r
- {\r
- int convert = 0;\r
- byte b0 = 0;\r
- byte b1 = 0;\r
- byte b2 = 0;\r
- byte b3 = 0;\r
- b0 = byteread[k];\r
- if (k+1<bytesize) b1 = byteread[k+1];\r
- if (k+2<bytesize) b2 = byteread[k+2];\r
- if (k+3<bytesize) b3 = byteread[k+3];\r
- framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);\r
- }\r
- wordpointer = 0;\r
- bitindex = 0;\r
- }\r
-\r
- /**\r
- * Read bits from buffer into the lower bits of an unsigned int.\r
- * The LSB contains the latest read bit of the stream.\r
- * (1 <= number_of_bits <= 16)\r
- */\r
- public int get_bits(int number_of_bits)\r
- {\r
- int returnvalue = 0;\r
- int sum = bitindex + number_of_bits;\r
-\r
- // E.B\r
- // There is a problem here, wordpointer could be -1 ?!\r
- if (wordpointer < 0) wordpointer = 0;\r
- // E.B : End.\r
-\r
- if (sum <= 32)\r
- {\r
- // all bits contained in *wordpointer\r
- returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];\r
- // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];\r
- if ((bitindex += number_of_bits) == 32)\r
- {\r
- bitindex = 0;\r
- wordpointer++; // added by me!\r
- }\r
- return returnvalue;\r
- }\r
-\r
- // E.B : Check that ?\r
- //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];\r
- //wordpointer++; // Added by me!\r
- //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];\r
- int Right = (framebuffer[wordpointer] & 0x0000FFFF);\r
- wordpointer++;\r
- int Left = (framebuffer[wordpointer] & 0xFFFF0000);\r
- returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);\r
-\r
- returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))\r
- returnvalue &= bitmask[number_of_bits];\r
- bitindex = sum - 32;\r
- return returnvalue;\r
-}\r
-\r
- /**\r
- * Set the word we want to sync the header to.\r
- * In Big-Endian byte order\r
- */\r
- void set_syncword(int syncword0)\r
- {\r
- syncword = syncword0 & 0xFFFFFF3F;\r
- single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);\r
- }\r
- /**\r
- * Reads the exact number of bytes from the source\r
- * input stream into a byte array.\r
- *\r
- * @param b The byte array to read the specified number\r
- * of bytes into.\r
- * @param offs The index in the array where the first byte\r
- * read should be stored.\r
- * @param len the number of bytes to read.\r
- *\r
- * @exception BitstreamException is thrown if the specified\r
- * number of bytes could not be read from the stream.\r
- */\r
- private int readFully(byte[] b, int offs, int len)\r
- throws BitstreamException\r
- { \r
- int nRead = 0;\r
- try\r
- {\r
- while (len > 0)\r
- {\r
- int bytesread = source.read(b, offs, len);\r
- if (bytesread == -1)\r
- {\r
- while (len-->0)\r
- {\r
- b[offs++] = 0;\r
- }\r
- break;\r
- //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());\r
- }\r
- nRead = nRead + bytesread;\r
- offs += bytesread;\r
- len -= bytesread;\r
- }\r
- }\r
- catch (IOException ex)\r
- {\r
- throw newBitstreamException(STREAM_ERROR, ex);\r
- }\r
- return nRead;\r
- }\r
-\r
- /**\r
- * Simlar to readFully, but doesn't throw exception when\r
- * EOF is reached.\r
- */\r
- private int readBytes(byte[] b, int offs, int len)\r
- throws BitstreamException\r
- {\r
- int totalBytesRead = 0;\r
- try\r
- {\r
- while (len > 0)\r
- {\r
- int bytesread = source.read(b, offs, len);\r
- if (bytesread == -1)\r
- {\r
- break;\r
- }\r
- totalBytesRead += bytesread;\r
- offs += bytesread;\r
- len -= bytesread;\r
- }\r
- }\r
- catch (IOException ex)\r
- {\r
- throw newBitstreamException(STREAM_ERROR, ex);\r
- }\r
- return totalBytesRead;\r
- }\r
-}\r
+/*
+ * 11/19/04 1.0 moved to LGPL.
+ *
+ * 11/17/04 Uncomplete frames discarded. E.B, javalayer@javazoom.net
+ *
+ * 12/05/03 ID3v2 tag returned. E.B, javalayer@javazoom.net
+ *
+ * 12/12/99 Based on Ibitstream. Exceptions thrown on errors,
+ * Temporary removed seek functionality. mdm@techie.com
+ *
+ * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net
+ *
+ * 04/14/97 : Added function prototypes for new syncing and seeking
+ * mechanisms. Also made this file portable. Changes made by Jeff Tsay
+ *
+ * @(#) ibitstream.h 1.5, last edit: 6/15/94 16:55:34
+ * @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
+ * @(#) Berlin University of Technology
+ *-----------------------------------------------------------------------
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------
+ */
+
+
+/**
+ * The <code>Bistream</code> class is responsible for parsing
+ * an MPEG audio bitstream.
+ *
+ * <b>REVIEW:</b> much of the parsing currently occurs in the
+ * various decoders. This should be moved into this class and associated
+ * inner classes.
+ */
+@LATTICE("FB<F,FF<F,WP<BI,FF*,WP*,BI*")
+@METHODDEFAULT("OUT<THIS,THIS<VAR,VAR<IN,VAR*,THISLOC=THIS,GLOBALLOC=IN")
+public final class Bitstream implements BitstreamErrors
+{
+ /**
+ * Synchronization control constant for the initial
+ * synchronization to the start of a frame.
+ */
+ @LOC("F") static byte INITIAL_SYNC = 0;
+
+ /**
+ * Synchronization control constant for non-initial frame
+ * synchronizations.
+ */
+
+ @LOC("F") static byte STRICT_SYNC = 1;
+
+ // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC
+ /**
+ * Maximum size of the frame buffer.
+ */
+ @LOC("F") private static final int BUFFER_INT_SIZE = 433;
+
+ /**
+ * The frame buffer that holds the data for the current frame.
+ */
+ @LOC("FB") private final int[] framebuffer = new int[BUFFER_INT_SIZE];
+
+ /**
+ * Number of valid bytes in the frame buffer.
+ */
+ @LOC("F") private int framesize;
+
+ /**
+ * The bytes read from the stream.
+ */
+ @LOC("FB") private byte[] frame_bytes = new byte[BUFFER_INT_SIZE*4];
+
+ /**
+ * Index into <code>framebuffer</code> where the next bits are
+ * retrieved.
+ */
+ @LOC("WP") private int wordpointer;
+
+ /**
+ * Number (0-31, from MSB to LSB) of next bit for get_bits()
+ */
+ @LOC("BI") private int bitindex;
+
+ /**
+ * The current specified syncword
+ */
+ @LOC("F") private int syncword;
+
+ /**
+ * Audio header position in stream.
+ */
+ @LOC("F")private int header_pos = 0;
+
+ /**
+ *
+ */
+ @LOC("F") private boolean single_ch_mode;
+ //private int current_frame_number;
+ //private int last_frame_number;
+
+ @LOC("F") private final int bitmask[] = {0, // dummy
+ 0x00000001, 0x00000003, 0x00000007, 0x0000000F,
+ 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
+ 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
+ 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,
+ 0x0001FFFF };
+
+ @LOC("F") private final PushbackInputStream source;
+
+ @LOC("F") private final Header header = new Header();
+
+ @LOC("F") private final byte syncbuf[] = new byte[4];
+
+ @LOC("F") private Crc16[] crc = new Crc16[1];
+
+ @LOC("F") private byte[] rawid3v2 = null;
+
+ @LOC("FF") private boolean firstframe = true;
+
+
+ /**
+ * Construct a IBitstream that reads data from a
+ * given InputStream.
+ *
+ * @param in The InputStream to read from.
+ */
+ public Bitstream(InputStream in)
+ {
+ if (in==null) throw new NullPointerException("in");
+ in = new BufferedInputStream(in);
+ loadID3v2(in);
+ firstframe = true;
+ //source = new PushbackInputStream(in, 1024);
+ source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);
+
+ closeFrame();
+ //current_frame_number = -1;
+ //last_frame_number = -1;
+ }
+
+ /**
+ * Return position of the first audio header.
+ * @return size of ID3v2 tag frames.
+ */
+ public int header_pos()
+ {
+ return header_pos;
+ }
+
+ /**
+ * Load ID3v2 frames.
+ * @param in MP3 InputStream.
+ * @author JavaZOOM
+ */
+ private void loadID3v2(InputStream in)
+ {
+ int size = -1;
+ try
+ {
+ // Read ID3v2 header (10 bytes).
+ in.mark(10);
+ size = readID3v2Header(in);
+ header_pos = size;
+ }
+ catch (IOException e)
+ {}
+ finally
+ {
+ try
+ {
+ // Unread ID3v2 header (10 bytes).
+ in.reset();
+ }
+ catch (IOException e)
+ {}
+ }
+ // Load ID3v2 tags.
+ try
+ {
+ if (size > 0)
+ {
+ rawid3v2 = new byte[size];
+ in.read(rawid3v2,0,rawid3v2.length);
+ }
+ }
+ catch (IOException e)
+ {}
+ }
+
+ /**
+ * Parse ID3v2 tag header to find out size of ID3v2 frames.
+ * @param in MP3 InputStream
+ * @return size of ID3v2 frames + header
+ * @throws IOException
+ * @author JavaZOOM
+ */
+ private int readID3v2Header(InputStream in) throws IOException
+ {
+ byte[] id3header = new byte[4];
+ int size = -10;
+ in.read(id3header,0,3);
+ // Look for ID3v2
+ if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))
+ {
+ in.read(id3header,0,3);
+ int majorVersion = id3header[0];
+ int revision = id3header[1];
+ in.read(id3header,0,4);
+ size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);
+ }
+ return (size+10);
+ }
+
+ /**
+ * Return raw ID3v2 frames + header.
+ * @return ID3v2 InputStream or null if ID3v2 frames are not available.
+ */
+ public InputStream getRawID3v2()
+ {
+ if (rawid3v2 == null) return null;
+ else
+ {
+ ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);
+ return bain;
+ }
+ }
+
+ /**
+ * Close the Bitstream.
+ * @throws BitstreamException
+ */
+ public void close() throws BitstreamException
+ {
+ try
+ {
+ source.close();
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR, ex);
+ }
+ }
+
+ /**
+ * Reads and parses the next frame from the input source.
+ * @return the Header describing details of the frame read,
+ * or null if the end of the stream has been reached.
+ */
+ public Header readFrame() throws BitstreamException
+ {
+ Header result = null;
+ try
+ {
+ result = readNextFrame();
+ // E.B, Parse VBR (if any) first frame.
+ if (firstframe == true)
+ {
+ result.parseVBR(frame_bytes);
+ firstframe = false;
+ }
+ }
+ catch (BitstreamException ex)
+ {
+ if ((ex.getErrorCode()==INVALIDFRAME))
+ {
+ // Try to skip this frame.
+ //System.out.println("INVALIDFRAME");
+ try
+ {
+ closeFrame();
+ result = readNextFrame();
+ }
+ catch (BitstreamException e)
+ {
+ if ((e.getErrorCode()!=STREAM_EOF))
+ {
+ // wrap original exception so stack trace is maintained.
+ throw newBitstreamException(e.getErrorCode(), e);
+ }
+ }
+ }
+ else if ((ex.getErrorCode()!=STREAM_EOF))
+ {
+ // wrap original exception so stack trace is maintained.
+ throw newBitstreamException(ex.getErrorCode(), ex);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Read next MP3 frame.
+ * @return MP3 frame header.
+ * @throws BitstreamException
+ */
+ private Header readNextFrame() throws BitstreamException
+ {
+ if (framesize == -1)
+ {
+ nextFrame();
+ }
+ return header;
+ }
+
+
+ /**
+ * Read next MP3 frame.
+ * @throws BitstreamException
+ */
+ private void nextFrame() throws BitstreamException
+ {
+ // entire frame is read by the header class.
+ header.read_header(this, crc);
+ }
+
+ /**
+ * Unreads the bytes read from the frame.
+ * @throws BitstreamException
+ */
+ // REVIEW: add new error codes for this.
+ public void unreadFrame() throws BitstreamException
+ {
+ if (wordpointer==-1 && bitindex==-1 && (framesize>0))
+ {
+ try
+ {
+ source.unread(frame_bytes, 0, framesize);
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR);
+ }
+ }
+ }
+
+ /**
+ * Close MP3 frame.
+ */
+ public void closeFrame()
+ {
+ framesize = -1;
+ wordpointer = -1;
+ bitindex = -1;
+ }
+
+ /**
+ * Determines if the next 4 bytes of the stream represent a
+ * frame header.
+ */
+ public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException
+ {
+ int read = readBytes(syncbuf, 0, 4);
+ int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);
+
+ try
+ {
+ source.unread(syncbuf, 0, read);
+ }
+ catch (IOException ex)
+ {
+ }
+
+ boolean sync = false;
+ switch (read)
+ {
+ case 0:
+ sync = true;
+ break;
+ case 4:
+ sync = isSyncMark(headerstring, syncmode, syncword);
+ break;
+ }
+
+ return sync;
+ }
+
+
+ // REVIEW: this class should provide inner classes to
+ // parse the frame contents. Eventually, readBits will
+ // be removed.
+ public int readBits(int n)
+ {
+ return get_bits(n);
+ }
+
+ public int readCheckedBits(int n)
+ {
+ // REVIEW: implement CRC check.
+ return get_bits(n);
+ }
+
+ protected BitstreamException newBitstreamException(int errorcode)
+ {
+ return new BitstreamException(errorcode, null);
+ }
+ protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)
+ {
+ return new BitstreamException(errorcode, throwable);
+ }
+
+ /**
+ * Get next 32 bits from bitstream.
+ * They are stored in the headerstring.
+ * syncmod allows Synchro flag ID
+ * The returned value is False at the end of stream.
+ */
+
+ int syncHeader(byte syncmode) throws BitstreamException
+ {
+ boolean sync;
+ int headerstring;
+ // read additional 2 bytes
+ int bytesRead = readBytes(syncbuf, 0, 3);
+
+ if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);
+
+ headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);
+
+ do
+ {
+ headerstring <<= 8;
+
+ if (readBytes(syncbuf, 3, 1)!=1)
+ throw newBitstreamException(STREAM_EOF, null);
+
+ headerstring |= (syncbuf[3] & 0x000000FF);
+
+ sync = isSyncMark(headerstring, syncmode, syncword);
+ }
+ while (!sync);
+
+ //current_frame_number++;
+ //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;
+
+ return headerstring;
+ }
+
+ public boolean isSyncMark(int headerstring, int syncmode, int word)
+ {
+ boolean sync = false;
+
+ if (syncmode == INITIAL_SYNC)
+ {
+ //sync = ((headerstring & 0xFFF00000) == 0xFFF00000);
+ sync = ((headerstring & 0xFFE00000) == 0xFFE00000); // SZD: MPEG 2.5
+ }
+ else
+ {
+ sync = ((headerstring & 0xFFF80C00) == word) &&
+ (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);
+ }
+
+ // filter out invalid sample rate
+ if (sync)
+ sync = (((headerstring >>> 10) & 3)!=3);
+ // filter out invalid layer
+ if (sync)
+ sync = (((headerstring >>> 17) & 3)!=0);
+ // filter out invalid version
+ if (sync)
+ sync = (((headerstring >>> 19) & 3)!=1);
+
+ return sync;
+ }
+
+ /**
+ * Reads the data for the next frame. The frame is not parsed
+ * until parse frame is called.
+ */
+ int read_frame_data(int bytesize) throws BitstreamException
+ {
+ int numread = 0;
+ numread = readFully(frame_bytes, 0, bytesize);
+ framesize = bytesize;
+ wordpointer = -1;
+ bitindex = -1;
+ return numread;
+ }
+
+ /**
+ * Parses the data previously read with read_frame_data().
+ */
+ @LATTICE("GLOBAL<B,B<BNUM,BNUM<K,K<BYTE,BYTE<THIS,B*,K*,THISLOC=THIS,GLOBALLOC=GLOBAL")
+ void parse_frame() throws BitstreamException
+ {
+ // Convert Bytes read to int
+ @LOC("B") int b=0;
+ @LOC("BYTE") byte[] byteread = frame_bytes;
+ @LOC("BYTE") int bytesize = framesize;
+
+ // Check ID3v1 TAG (True only if last frame).
+ //for (int t=0;t<(byteread.length)-2;t++)
+ //{
+ // if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))
+ // {
+ // System.out.println("ID3v1 detected at offset "+t);
+ // throw newBitstreamException(INVALIDFRAME, null);
+ // }
+ //}
+
+ for (@LOC("K") int k=0;k<bytesize;k=k+4)
+ {
+ @LOC("BYTE") int convert = 0;
+ @LOC("BNUM") byte b0 = 0;
+ @LOC("BNUM") byte b1 = 0;
+ @LOC("BNUM") byte b2 = 0;
+ @LOC("BNUM") byte b3 = 0;
+ b0 = byteread[k];
+ if (k+1<bytesize) b1 = byteread[k+1];
+ if (k+2<bytesize) b2 = byteread[k+2];
+ if (k+3<bytesize) b3 = byteread[k+3];
+ framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);
+ }
+ wordpointer = 0;
+ bitindex = 0;
+ }
+
+ /**
+ * Read bits from buffer into the lower bits of an unsigned int.
+ * The LSB contains the latest read bit of the stream.
+ * (1 <= number_of_bits <= 16)
+ */
+ @LATTICE("OUT<RL,RL<THIS,THIS<IN,OUT*,THISLOC=THIS")
+ @RETURNLOC("OUT")
+ public int get_bits(@LOC("IN")int number_of_bits)
+ {
+ @LOC("OUT") int returnvalue = 0;
+ @LOC("THIS,Bitstream.BI") int sum = bitindex + number_of_bits;
+
+ // E.B
+ // There is a problem here, wordpointer could be -1 ?!
+ if (wordpointer < 0) wordpointer = 0;
+ // E.B : End.
+
+ if (sum <= 32)
+ {
+ // all bits contained in *wordpointer
+ returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];
+ // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];
+ if ((bitindex += number_of_bits) == 32)
+ {
+ bitindex = 0;
+ wordpointer++; // added by me!
+ }
+ return returnvalue;
+ }
+
+ // E.B : Check that ?
+ //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];
+ //wordpointer++; // Added by me!
+ //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];
+ @LOC("RL") int Right = (framebuffer[wordpointer] & 0x0000FFFF);
+ wordpointer++;
+ @LOC("RL") int Left = (framebuffer[wordpointer] & 0xFFFF0000);
+ returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);
+
+ returnvalue >>>= 48 - sum; // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))
+ returnvalue &= bitmask[number_of_bits];
+ bitindex = sum - 32;
+ return returnvalue;
+}
+
+ /**
+ * Set the word we want to sync the header to.
+ * In Big-Endian byte order
+ */
+ void set_syncword(@LOC("IN") int syncword0)
+ {
+ syncword = syncword0 & 0xFFFFFF3F;
+ single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);
+ }
+ /**
+ * Reads the exact number of bytes from the source
+ * input stream into a byte array.
+ *
+ * @param b The byte array to read the specified number
+ * of bytes into.
+ * @param offs The index in the array where the first byte
+ * read should be stored.
+ * @param len the number of bytes to read.
+ *
+ * @exception BitstreamException is thrown if the specified
+ * number of bytes could not be read from the stream.
+ */
+ @LATTICE("OUT<VAR,VAR<THIS,THIS<IN,IN*,VAR*,THISLOC=THIS")
+ @RETURNLOC("OUT")
+ private int readFully(@LOC("OUT") byte[] b, @LOC("IN") int offs, @LOC("IN") int len)
+ throws BitstreamException
+ {
+ @LOC("VAR") int nRead = 0;
+ try
+ {
+ while (len > 0)
+ {
+ @LOC("IN") int bytesread = source.read(b, offs, len);
+ if (bytesread == -1)
+ {
+ while (len-->0)
+ {
+ b[offs++] = 0;
+ }
+ break;
+ //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());
+ }
+ nRead = nRead + bytesread;
+ offs += bytesread;
+ len -= bytesread;
+ }
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR, ex);
+ }
+ return nRead;
+ }
+
+ /**
+ * Simlar to readFully, but doesn't throw exception when
+ * EOF is reached.
+ */
+ @LATTICE("OUT<VAR,VAR<THIS,THIS<IN,IN*,VAR*,THISLOC=THIS")
+ @RETURNLOC("OUT")
+ private int readBytes(@LOC("OUT") byte[] b, @LOC("IN") int offs, @LOC("IN") int len)
+ throws BitstreamException
+ {
+ @LOC("VAR") int totalBytesRead = 0;
+ try
+ {
+ while (len > 0)
+ {
+ @LOC("IN") int bytesread = source.read(b, offs, len);
+ if (bytesread == -1)
+ {
+ break;
+ }
+ totalBytesRead += bytesread;
+ offs += bytesread;
+ len -= bytesread;
+ }
+ }
+ catch (IOException ex)
+ {
+ throw newBitstreamException(STREAM_ERROR, ex);
+ }
+ return totalBytesRead;
+ }
+}