try to make mp3decoder compile
[IRC.git] / Robust / src / Tests / ssJava / mp3decoder / Bitstream.java
1 /*\r
2  * 11/19/04  1.0 moved to LGPL.\r
3  * \r
4  * 11/17/04      Uncomplete frames discarded. E.B, javalayer@javazoom.net \r
5  *\r
6  * 12/05/03      ID3v2 tag returned. E.B, javalayer@javazoom.net \r
7  *\r
8  * 12/12/99      Based on Ibitstream. Exceptions thrown on errors,\r
9  *                       Temporary removed seek functionality. mdm@techie.com\r
10  *\r
11  * 02/12/99 : Java Conversion by E.B , javalayer@javazoom.net\r
12  *\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
15  *\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
24  *\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
29  *\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
34  */\r
35 \r
36 \r
37 /**\r
38  * The <code>Bistream</code> class is responsible for parsing\r
39  * an MPEG audio bitstream.\r
40  *\r
41  * <b>REVIEW:</b> much of the parsing currently occurs in the\r
42  * various decoders. This should be moved into this class and associated\r
43  * inner classes.\r
44  */\r
45 public final class Bitstream implements BitstreamErrors\r
46 {\r
47         /**\r
48          * Synchronization control constant for the initial\r
49          * synchronization to the start of a frame.\r
50          */\r
51         static byte             INITIAL_SYNC = 0;\r
52 \r
53         /**\r
54          * Synchronization control constant for non-initial frame\r
55          * synchronizations.\r
56          */\r
57         static byte             STRICT_SYNC = 1;\r
58 \r
59         // max. 1730 bytes per frame: 144 * 384kbit/s / 32000 Hz + 2 Bytes CRC\r
60         /**\r
61          * Maximum size of the frame buffer.\r
62          */\r
63         private static final int        BUFFER_INT_SIZE = 433;\r
64 \r
65         /**\r
66          * The frame buffer that holds the data for the current frame.\r
67          */\r
68         private final int[]             framebuffer = new int[BUFFER_INT_SIZE];\r
69 \r
70         /**\r
71          * Number of valid bytes in the frame buffer.\r
72          */\r
73         private int                             framesize;\r
74 \r
75         /**\r
76          * The bytes read from the stream.\r
77          */\r
78         private byte[]                  frame_bytes = new byte[BUFFER_INT_SIZE*4];\r
79 \r
80         /**\r
81          * Index into <code>framebuffer</code> where the next bits are\r
82          * retrieved.\r
83          */\r
84         private int                             wordpointer;\r
85 \r
86         /**\r
87          * Number (0-31, from MSB to LSB) of next bit for get_bits()\r
88          */\r
89         private int                             bitindex;\r
90 \r
91         /**\r
92          * The current specified syncword\r
93          */\r
94         private int                             syncword;\r
95         \r
96         /**\r
97          * Audio header position in stream.\r
98          */\r
99         private int                             header_pos = 0;\r
100 \r
101         /**\r
102          *\r
103          */\r
104         private boolean                 single_ch_mode;\r
105   //private int                         current_frame_number;\r
106   //private int                         last_frame_number;\r
107 \r
108         private final int               bitmask[] = {0, // dummy\r
109          0x00000001, 0x00000003, 0x00000007, 0x0000000F,\r
110          0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,\r
111          0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,\r
112          0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,\r
113      0x0001FFFF };\r
114 \r
115         private final PushbackInputStream       source;\r
116 \r
117         private final Header                    header = new Header();\r
118 \r
119         private final byte                              syncbuf[] = new byte[4];\r
120 \r
121         private Crc16[]                                 crc = new Crc16[1];\r
122 \r
123         private byte[]                                  rawid3v2 = null;\r
124 \r
125         private boolean                                 firstframe = true;\r
126 \r
127 \r
128         /**\r
129          * Construct a IBitstream that reads data from a\r
130          * given InputStream.\r
131          *\r
132          * @param in    The InputStream to read from.\r
133          */\r
134         public Bitstream(InputStream in)\r
135         {\r
136                 if (in==null) throw new NullPointerException("in");\r
137                 in = new BufferedInputStream(in);               \r
138                 loadID3v2(in);\r
139                 firstframe = true;\r
140                 //source = new PushbackInputStream(in, 1024);\r
141                 source = new PushbackInputStream(in, BUFFER_INT_SIZE*4);\r
142                 \r
143                 closeFrame();\r
144                 //current_frame_number = -1;\r
145                 //last_frame_number = -1;\r
146         }\r
147 \r
148         /**\r
149          * Return position of the first audio header.\r
150          * @return size of ID3v2 tag frames.\r
151          */\r
152         public int header_pos()\r
153         {\r
154                 return header_pos;\r
155         }\r
156         \r
157         /**\r
158          * Load ID3v2 frames.\r
159          * @param in MP3 InputStream.\r
160          * @author JavaZOOM\r
161          */\r
162         private void loadID3v2(InputStream in)\r
163         {               \r
164                 int size = -1;\r
165                 try\r
166                 {\r
167                         // Read ID3v2 header (10 bytes).\r
168                         in.mark(10);                    \r
169                         size = readID3v2Header(in);\r
170                         header_pos = size;                      \r
171                 }\r
172                 catch (IOException e)\r
173                 {}\r
174                 finally\r
175                 {\r
176                         try\r
177                         {\r
178                                 // Unread ID3v2 header (10 bytes).\r
179                                 in.reset();\r
180                         }\r
181                         catch (IOException e)\r
182                         {}\r
183                 }\r
184                 // Load ID3v2 tags.\r
185                 try\r
186                 {\r
187                         if (size > 0)\r
188                         {\r
189                                 rawid3v2 = new byte[size];\r
190                                 in.read(rawid3v2,0,rawid3v2.length);\r
191                         }                       \r
192                 }\r
193                 catch (IOException e)\r
194                 {}\r
195         }\r
196         \r
197         /**\r
198          * Parse ID3v2 tag header to find out size of ID3v2 frames. \r
199          * @param in MP3 InputStream\r
200          * @return size of ID3v2 frames + header\r
201          * @throws IOException\r
202          * @author JavaZOOM\r
203          */\r
204         private int readID3v2Header(InputStream in) throws IOException\r
205         {               \r
206                 byte[] id3header = new byte[4];\r
207                 int size = -10;\r
208                 in.read(id3header,0,3);\r
209                 // Look for ID3v2\r
210                 if ( (id3header[0]=='I') && (id3header[1]=='D') && (id3header[2]=='3'))\r
211                 {\r
212                         in.read(id3header,0,3);\r
213                         int majorVersion = id3header[0];\r
214                         int revision = id3header[1];\r
215                         in.read(id3header,0,4);\r
216                         size = (int) (id3header[0] << 21) + (id3header[1] << 14) + (id3header[2] << 7) + (id3header[3]);\r
217                 }\r
218                 return (size+10);\r
219         }\r
220         \r
221         /**\r
222          * Return raw ID3v2 frames + header.\r
223          * @return ID3v2 InputStream or null if ID3v2 frames are not available.\r
224          */\r
225         public InputStream getRawID3v2()\r
226         {\r
227                 if (rawid3v2 == null) return null;\r
228                 else\r
229                 {\r
230                         ByteArrayInputStream bain = new ByteArrayInputStream(rawid3v2);         \r
231                         return bain;\r
232                 }\r
233         }\r
234 \r
235         /**\r
236          * Close the Bitstream.\r
237          * @throws BitstreamException\r
238          */\r
239         public void close() throws BitstreamException\r
240         {\r
241                 try\r
242                 {\r
243                         source.close();\r
244                 }\r
245                 catch (IOException ex)\r
246                 {\r
247                         throw newBitstreamException(STREAM_ERROR, ex);\r
248                 }\r
249         }\r
250 \r
251         /**\r
252          * Reads and parses the next frame from the input source.\r
253          * @return the Header describing details of the frame read,\r
254          *      or null if the end of the stream has been reached.\r
255          */\r
256         public Header readFrame() throws BitstreamException\r
257         {\r
258                 Header result = null;\r
259                 try\r
260                 {\r
261                         result = readNextFrame();\r
262                         // E.B, Parse VBR (if any) first frame.\r
263                         if (firstframe == true)\r
264                         {\r
265                                 result.parseVBR(frame_bytes);\r
266                                 firstframe = false;\r
267                         }                       \r
268                 }\r
269                 catch (BitstreamException ex)\r
270                 {\r
271                         if ((ex.getErrorCode()==INVALIDFRAME))\r
272                         {\r
273                                 // Try to skip this frame.\r
274                                 //System.out.println("INVALIDFRAME");\r
275                                 try\r
276                                 {\r
277                                         closeFrame();\r
278                                         result = readNextFrame();\r
279                                 }\r
280                                 catch (BitstreamException e)\r
281                                 {\r
282                                         if ((e.getErrorCode()!=STREAM_EOF))\r
283                                         {\r
284                                                 // wrap original exception so stack trace is maintained.\r
285                                                 throw newBitstreamException(e.getErrorCode(), e);\r
286                                         }\r
287                                 }\r
288                         }\r
289                         else if ((ex.getErrorCode()!=STREAM_EOF))\r
290                         {\r
291                                 // wrap original exception so stack trace is maintained.\r
292                                 throw newBitstreamException(ex.getErrorCode(), ex);\r
293                         }\r
294                 }\r
295                 return result;\r
296         }\r
297 \r
298         /**\r
299          * Read next MP3 frame.\r
300          * @return MP3 frame header.\r
301          * @throws BitstreamException\r
302          */\r
303         private Header readNextFrame() throws BitstreamException\r
304         {\r
305                 if (framesize == -1)\r
306                 {\r
307                         nextFrame();\r
308                 }\r
309                 return header;\r
310         }\r
311 \r
312 \r
313         /**\r
314          * Read next MP3 frame.\r
315          * @throws BitstreamException\r
316          */\r
317         private void nextFrame() throws BitstreamException\r
318         {\r
319                 // entire frame is read by the header class.\r
320                 header.read_header(this, crc);\r
321         }\r
322 \r
323         /**\r
324          * Unreads the bytes read from the frame.\r
325          * @throws BitstreamException\r
326          */\r
327         // REVIEW: add new error codes for this.\r
328         public void unreadFrame() throws BitstreamException\r
329         {\r
330                 if (wordpointer==-1 && bitindex==-1 && (framesize>0))\r
331                 {\r
332                         try\r
333                         {\r
334                                 source.unread(frame_bytes, 0, framesize);\r
335                         }\r
336                         catch (IOException ex)\r
337                         {\r
338                                 throw newBitstreamException(STREAM_ERROR);\r
339                         }\r
340                 }\r
341         }\r
342 \r
343         /**\r
344          * Close MP3 frame.\r
345          */\r
346         public void closeFrame()\r
347         {\r
348                 framesize = -1;\r
349                 wordpointer = -1;\r
350                 bitindex = -1;\r
351         }\r
352 \r
353         /**\r
354          * Determines if the next 4 bytes of the stream represent a\r
355          * frame header.\r
356          */\r
357         public boolean isSyncCurrentPosition(int syncmode) throws BitstreamException\r
358         {\r
359                 int read = readBytes(syncbuf, 0, 4);\r
360                 int headerstring = ((syncbuf[0] << 24) & 0xFF000000) | ((syncbuf[1] << 16) & 0x00FF0000) | ((syncbuf[2] << 8) & 0x0000FF00) | ((syncbuf[3] << 0) & 0x000000FF);\r
361 \r
362                 try\r
363                 {\r
364                         source.unread(syncbuf, 0, read);\r
365                 }\r
366                 catch (IOException ex)\r
367                 {\r
368                 }\r
369 \r
370                 boolean sync = false;\r
371                 switch (read)\r
372                 {\r
373                         case 0:\r
374                                 sync = true;\r
375                                 break;\r
376                         case 4:\r
377                                 sync = isSyncMark(headerstring, syncmode, syncword);\r
378                                 break;\r
379                 }\r
380 \r
381                 return sync;\r
382         }\r
383 \r
384 \r
385         // REVIEW: this class should provide inner classes to\r
386         // parse the frame contents. Eventually, readBits will\r
387         // be removed.\r
388         public int readBits(int n)\r
389         {\r
390                 return get_bits(n);\r
391         }\r
392 \r
393         public int readCheckedBits(int n)\r
394         {\r
395                 // REVIEW: implement CRC check.\r
396                 return get_bits(n);\r
397         }\r
398 \r
399         protected BitstreamException newBitstreamException(int errorcode)\r
400         {\r
401                 return new BitstreamException(errorcode, null);\r
402         }\r
403         protected BitstreamException newBitstreamException(int errorcode, Throwable throwable)\r
404         {\r
405                 return new BitstreamException(errorcode, throwable);\r
406         }\r
407 \r
408   /**\r
409    * Get next 32 bits from bitstream.\r
410    * They are stored in the headerstring.\r
411    * syncmod allows Synchro flag ID\r
412    * The returned value is False at the end of stream.\r
413    */\r
414 \r
415         int syncHeader(byte syncmode) throws BitstreamException\r
416         {\r
417                 boolean sync;\r
418                 int headerstring;\r
419                 // read additional 2 bytes\r
420                 int bytesRead = readBytes(syncbuf, 0, 3);\r
421 \r
422                 if (bytesRead!=3) throw newBitstreamException(STREAM_EOF, null);\r
423 \r
424                 headerstring = ((syncbuf[0] << 16) & 0x00FF0000) | ((syncbuf[1] << 8) & 0x0000FF00) | ((syncbuf[2] << 0) & 0x000000FF);\r
425 \r
426                 do\r
427                 {\r
428                         headerstring <<= 8;\r
429 \r
430                         if (readBytes(syncbuf, 3, 1)!=1)\r
431                                 throw newBitstreamException(STREAM_EOF, null);\r
432 \r
433                         headerstring |= (syncbuf[3] & 0x000000FF);\r
434 \r
435                         sync = isSyncMark(headerstring, syncmode, syncword);\r
436                 }\r
437                 while (!sync);\r
438 \r
439                 //current_frame_number++;\r
440                 //if (last_frame_number < current_frame_number) last_frame_number = current_frame_number;\r
441 \r
442                 return headerstring;\r
443         }\r
444 \r
445         public boolean isSyncMark(int headerstring, int syncmode, int word)\r
446         {\r
447                 boolean sync = false;\r
448 \r
449                 if (syncmode == INITIAL_SYNC)\r
450                 {\r
451                         //sync =  ((headerstring & 0xFFF00000) == 0xFFF00000);\r
452                         sync =  ((headerstring & 0xFFE00000) == 0xFFE00000);    // SZD: MPEG 2.5\r
453                 }\r
454                 else\r
455                 {\r
456                         sync =  ((headerstring & 0xFFF80C00) == word) &&\r
457                             (((headerstring & 0x000000C0) == 0x000000C0) == single_ch_mode);\r
458                 }\r
459 \r
460                 // filter out invalid sample rate\r
461                 if (sync)\r
462                         sync = (((headerstring >>> 10) & 3)!=3);\r
463                 // filter out invalid layer\r
464                 if (sync)\r
465                         sync = (((headerstring >>> 17) & 3)!=0);\r
466                 // filter out invalid version\r
467                 if (sync)\r
468                         sync = (((headerstring >>> 19) & 3)!=1);\r
469 \r
470                 return sync;\r
471         }\r
472 \r
473         /**\r
474          * Reads the data for the next frame. The frame is not parsed\r
475          * until parse frame is called.\r
476          */\r
477         int read_frame_data(int bytesize) throws BitstreamException\r
478         {\r
479                 int     numread = 0;\r
480                 numread = readFully(frame_bytes, 0, bytesize);\r
481                 framesize = bytesize;\r
482                 wordpointer = -1;\r
483             bitindex = -1;\r
484             return numread;\r
485         }\r
486 \r
487   /**\r
488    * Parses the data previously read with read_frame_data().\r
489    */\r
490   void parse_frame() throws BitstreamException\r
491   {\r
492         // Convert Bytes read to int\r
493         int     b=0;\r
494         byte[] byteread = frame_bytes;\r
495         int bytesize = framesize;\r
496 \r
497         // Check ID3v1 TAG (True only if last frame).\r
498         //for (int t=0;t<(byteread.length)-2;t++)\r
499         //{\r
500         //      if ((byteread[t]=='T') && (byteread[t+1]=='A') && (byteread[t+2]=='G'))\r
501         //      {\r
502         //              System.out.println("ID3v1 detected at offset "+t);\r
503         //              throw newBitstreamException(INVALIDFRAME, null);\r
504         //      }       \r
505         //}\r
506         \r
507         for (int k=0;k<bytesize;k=k+4)\r
508         {\r
509                 int convert = 0;\r
510                 byte b0 = 0;\r
511                 byte b1 = 0;\r
512                 byte b2 = 0;\r
513                 byte b3 = 0;\r
514                 b0 = byteread[k];\r
515                 if (k+1<bytesize) b1 = byteread[k+1];\r
516                 if (k+2<bytesize) b2 = byteread[k+2];\r
517                 if (k+3<bytesize) b3 = byteread[k+3];\r
518                 framebuffer[b++] = ((b0 << 24) &0xFF000000) | ((b1 << 16) & 0x00FF0000) | ((b2 << 8) & 0x0000FF00) | (b3 & 0x000000FF);\r
519         }\r
520         wordpointer = 0;\r
521     bitindex = 0;\r
522   }\r
523 \r
524   /**\r
525    * Read bits from buffer into the lower bits of an unsigned int.\r
526    * The LSB contains the latest read bit of the stream.\r
527    * (1 <= number_of_bits <= 16)\r
528    */\r
529   public int get_bits(int number_of_bits)\r
530   {\r
531         int                             returnvalue = 0;\r
532         int                     sum = bitindex + number_of_bits;\r
533 \r
534         // E.B\r
535         // There is a problem here, wordpointer could be -1 ?!\r
536     if (wordpointer < 0) wordpointer = 0;\r
537     // E.B : End.\r
538 \r
539         if (sum <= 32)\r
540         {\r
541            // all bits contained in *wordpointer\r
542            returnvalue = (framebuffer[wordpointer] >>> (32 - sum)) & bitmask[number_of_bits];\r
543            // returnvalue = (wordpointer[0] >> (32 - sum)) & bitmask[number_of_bits];\r
544            if ((bitindex += number_of_bits) == 32)\r
545            {\r
546                  bitindex = 0;\r
547                  wordpointer++; // added by me!\r
548            }\r
549            return returnvalue;\r
550     }\r
551 \r
552     // E.B : Check that ?\r
553     //((short[])&returnvalue)[0] = ((short[])wordpointer + 1)[0];\r
554     //wordpointer++; // Added by me!\r
555     //((short[])&returnvalue + 1)[0] = ((short[])wordpointer)[0];\r
556         int Right = (framebuffer[wordpointer] & 0x0000FFFF);\r
557         wordpointer++;\r
558         int Left = (framebuffer[wordpointer] & 0xFFFF0000);\r
559         returnvalue = ((Right << 16) & 0xFFFF0000) | ((Left >>> 16)& 0x0000FFFF);\r
560 \r
561     returnvalue >>>= 48 - sum;  // returnvalue >>= 16 - (number_of_bits - (32 - bitindex))\r
562     returnvalue &= bitmask[number_of_bits];\r
563     bitindex = sum - 32;\r
564     return returnvalue;\r
565 }\r
566 \r
567         /**\r
568          * Set the word we want to sync the header to.\r
569          * In Big-Endian byte order\r
570          */\r
571         void set_syncword(int syncword0)\r
572         {\r
573                 syncword = syncword0 & 0xFFFFFF3F;\r
574                 single_ch_mode = ((syncword0 & 0x000000C0) == 0x000000C0);\r
575         }\r
576         /**\r
577          * Reads the exact number of bytes from the source\r
578          * input stream into a byte array.\r
579          *\r
580          * @param b             The byte array to read the specified number\r
581          *                              of bytes into.\r
582          * @param offs  The index in the array where the first byte\r
583          *                              read should be stored.\r
584          * @param len   the number of bytes to read.\r
585          *\r
586          * @exception BitstreamException is thrown if the specified\r
587          *              number of bytes could not be read from the stream.\r
588          */\r
589         private int readFully(byte[] b, int offs, int len)\r
590                 throws BitstreamException\r
591         {               \r
592                 int nRead = 0;\r
593                 try\r
594                 {\r
595                         while (len > 0)\r
596                         {\r
597                                 int bytesread = source.read(b, offs, len);\r
598                                 if (bytesread == -1)\r
599                                 {\r
600                                         while (len-->0)\r
601                                         {\r
602                                                 b[offs++] = 0;\r
603                                         }\r
604                                         break;\r
605                                         //throw newBitstreamException(UNEXPECTED_EOF, new EOFException());\r
606                                 }\r
607                                 nRead = nRead + bytesread;\r
608                                 offs += bytesread;\r
609                                 len -= bytesread;\r
610                         }\r
611                 }\r
612                 catch (IOException ex)\r
613                 {\r
614                         throw newBitstreamException(STREAM_ERROR, ex);\r
615                 }\r
616                 return nRead;\r
617         }\r
618 \r
619         /**\r
620          * Simlar to readFully, but doesn't throw exception when\r
621          * EOF is reached.\r
622          */\r
623         private int readBytes(byte[] b, int offs, int len)\r
624                 throws BitstreamException\r
625         {\r
626                 int totalBytesRead = 0;\r
627                 try\r
628                 {\r
629                         while (len > 0)\r
630                         {\r
631                                 int bytesread = source.read(b, offs, len);\r
632                                 if (bytesread == -1)\r
633                                 {\r
634                                         break;\r
635                                 }\r
636                                 totalBytesRead += bytesread;\r
637                                 offs += bytesread;\r
638                                 len -= bytesread;\r
639                         }\r
640                 }\r
641                 catch (IOException ex)\r
642                 {\r
643                         throw newBitstreamException(STREAM_ERROR, ex);\r
644                 }\r
645                 return totalBytesRead;\r
646         }\r
647 }\r