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