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