1 /*
   2  * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.media.sound;
  27 
  28 import java.io.BufferedInputStream;
  29 import java.io.DataInputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.net.URL;
  35 
  36 import javax.sound.sampled.AudioFileFormat;
  37 import javax.sound.sampled.AudioFormat;
  38 import javax.sound.sampled.AudioInputStream;
  39 import javax.sound.sampled.AudioSystem;
  40 import javax.sound.sampled.UnsupportedAudioFileException;
  41 
  42 
  43 /**
  44  * AU file reader.
  45  *
  46  * @author Kara Kytle
  47  * @author Jan Borgersen
  48  * @author Florian Bomers
  49  */
  50 public final class AuFileReader extends SunFileReader {
  51 
  52     // METHODS TO IMPLEMENT AudioFileReader
  53 
  54     /**
  55      * Obtains the audio file format of the input stream provided.  The stream must
  56      * point to valid audio file data.  In general, audio file providers may
  57      * need to read some data from the stream before determining whether they
  58      * support it.  These parsers must
  59      * be able to mark the stream, read enough data to determine whether they
  60      * support the stream, and, if not, reset the stream's read pointer to its original
  61      * position.  If the input stream does not support this, this method may fail
  62      * with an IOException.
  63      * @param stream the input stream from which file format information should be
  64      * extracted
  65      * @return an <code>AudioFileFormat</code> object describing the audio file format
  66      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
  67      * file data recognized by the system
  68      * @throws IOException if an I/O exception occurs
  69      * @see InputStream#markSupported
  70      * @see InputStream#mark
  71      */
  72     public AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException {
  73 
  74         AudioFormat format = null;
  75         AuFileFormat fileFormat = null;
  76         int maxReadLength = 28;
  77         boolean bigendian  = false;
  78         int magic          = -1;
  79         int headerSize     = -1;
  80         int dataSize       = -1;
  81         int encoding_local = -1;
  82         int sampleRate     = -1;
  83         int frameRate      = -1;
  84         int frameSize      = -1;
  85         int channels       = -1;
  86         final int sampleSizeInBits;
  87         int length = 0;
  88         int nread = 0;
  89         AudioFormat.Encoding encoding = null;
  90 
  91         DataInputStream dis = new DataInputStream( stream );
  92 
  93         dis.mark(maxReadLength);
  94 
  95         magic = dis.readInt();
  96 
  97         if (! (magic == AuFileFormat.AU_SUN_MAGIC) || (magic == AuFileFormat.AU_DEC_MAGIC) ||
  98             (magic == AuFileFormat.AU_SUN_INV_MAGIC) || (magic == AuFileFormat.AU_DEC_INV_MAGIC) ) {
  99 
 100             // not AU, reset the stream, place into exception, throw exception
 101             dis.reset();
 102             throw new UnsupportedAudioFileException("not an AU file");
 103         }
 104 
 105         if ((magic == AuFileFormat.AU_SUN_MAGIC) || (magic == AuFileFormat.AU_DEC_MAGIC)) {
 106             bigendian = true;        // otherwise little-endian
 107         }
 108 
 109         headerSize     = (bigendian==true ? dis.readInt() : rllong(dis) );  nread += 4;
 110         dataSize       = (bigendian==true ? dis.readInt() : rllong(dis) );  nread += 4;
 111         encoding_local = (bigendian==true ? dis.readInt() : rllong(dis) );  nread += 4;
 112         sampleRate     = (bigendian==true ? dis.readInt() : rllong(dis) );  nread += 4;
 113         channels       = (bigendian==true ? dis.readInt() : rllong(dis) );  nread += 4;
 114         if (channels <= 0) {
 115             dis.reset();
 116             throw new UnsupportedAudioFileException("Invalid number of channels");
 117         }
 118 
 119         frameRate = sampleRate;
 120 
 121         switch (encoding_local) {
 122         case AuFileFormat.AU_ULAW_8:
 123             encoding = AudioFormat.Encoding.ULAW;
 124             sampleSizeInBits = 8;
 125             break;
 126         case AuFileFormat.AU_ALAW_8:
 127             encoding = AudioFormat.Encoding.ALAW;
 128             sampleSizeInBits = 8;
 129             break;
 130         case AuFileFormat.AU_LINEAR_8:
 131             // $$jb: 04.29.99: 8bit linear is *signed*, not *unsigned*
 132             encoding = AudioFormat.Encoding.PCM_SIGNED;
 133             sampleSizeInBits = 8;
 134             break;
 135         case AuFileFormat.AU_LINEAR_16:
 136             encoding = AudioFormat.Encoding.PCM_SIGNED;
 137             sampleSizeInBits = 16;
 138             break;
 139         case AuFileFormat.AU_LINEAR_24:
 140             encoding = AudioFormat.Encoding.PCM_SIGNED;
 141 
 142             sampleSizeInBits = 24;
 143             break;
 144         case AuFileFormat.AU_LINEAR_32:
 145             encoding = AudioFormat.Encoding.PCM_SIGNED;
 146 
 147             sampleSizeInBits = 32;
 148             break;
 149             // $jb: 03.19.99: we don't support these ...
 150             /*          case AuFileFormat.AU_FLOAT:
 151                         encoding = new AudioFormat.FLOAT;
 152                         sampleSizeInBits = 32;
 153                         break;
 154                         case AuFileFormat.AU_DOUBLE:
 155                         encoding = new AudioFormat.DOUBLE;
 156                         sampleSizeInBits = 8;
 157                         break;
 158                         case AuFileFormat.AU_ADPCM_G721:
 159                         encoding = new AudioFormat.G721_ADPCM;
 160                         sampleSizeInBits = 16;
 161                         break;
 162                         case AuFileFormat.AU_ADPCM_G723_3:
 163                         encoding = new AudioFormat.G723_3;
 164                         sampleSize = 24;
 165                         SamplePerUnit = 8;
 166                         break;
 167                         case AuFileFormat.AU_ADPCM_G723_5:
 168                         encoding = new AudioFormat.G723_5;
 169                         sampleSize = 40;
 170                         SamplePerUnit = 8;
 171                         break;
 172             */
 173         default:
 174                 // unsupported filetype, throw exception
 175                 dis.reset();
 176                 throw new UnsupportedAudioFileException("not a valid AU file");
 177         }
 178 
 179         frameSize = calculatePCMFrameSize(sampleSizeInBits, channels);
 180         //$$fb 2002-11-02: fix for 4629669: AU file reader: problems with empty files
 181         if( dataSize < 0 ) {
 182             length = AudioSystem.NOT_SPECIFIED;
 183         } else {
 184             //$$fb 2003-10-20: fix for 4940459: AudioInputStream.getFrameLength() returns 0 instead of NOT_SPECIFIED
 185             length = dataSize / frameSize;
 186         }
 187 
 188         format = new AudioFormat( encoding, (float)sampleRate, sampleSizeInBits,
 189                                   channels, frameSize, (float)frameRate, bigendian);
 190 
 191         fileFormat = new AuFileFormat( AudioFileFormat.Type.AU, dataSize+headerSize,
 192                                        format, length);
 193 
 194         dis.reset(); // Throws IOException
 195         return fileFormat;
 196 
 197     }
 198 
 199 
 200     /**
 201      * Obtains the audio file format of the URL provided.  The URL must
 202      * point to valid audio file data.
 203      * @param url the URL from which file format information should be
 204      * extracted
 205      * @return an <code>AudioFileFormat</code> object describing the audio file format
 206      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
 207      * file data recognized by the system
 208      * @throws IOException if an I/O exception occurs
 209      */
 210     public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {
 211 
 212         InputStream                             urlStream = null;
 213         BufferedInputStream             bis = null;
 214         AudioFileFormat                 fileFormat = null;
 215         AudioFormat                             format = null;
 216 
 217         urlStream = url.openStream();   // throws IOException
 218 
 219         try {
 220             bis = new BufferedInputStream( urlStream, bisBufferSize );
 221 
 222             fileFormat = getAudioFileFormat( bis );             // throws UnsupportedAudioFileException
 223         } finally {
 224             urlStream.close();
 225         }
 226 
 227         return fileFormat;
 228     }
 229 
 230 
 231     /**
 232      * Obtains the audio file format of the File provided.  The File must
 233      * point to valid audio file data.
 234      * @param file the File from which file format information should be
 235      * extracted
 236      * @return an <code>AudioFileFormat</code> object describing the audio file format
 237      * @throws UnsupportedAudioFileException if the File does not point to valid audio
 238      * file data recognized by the system
 239      * @throws IOException if an I/O exception occurs
 240      */
 241     public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {
 242 
 243         FileInputStream                 fis = null;
 244         BufferedInputStream             bis = null;
 245         AudioFileFormat                 fileFormat = null;
 246         AudioFormat                             format = null;
 247 
 248         fis = new FileInputStream( file );      // throws IOException
 249         // part of fix for 4325421
 250         try {
 251             bis = new BufferedInputStream( fis, bisBufferSize );
 252             fileFormat = getAudioFileFormat( bis );             // throws UnsupportedAudioFileException
 253         } finally {
 254             fis.close();
 255         }
 256 
 257         return fileFormat;
 258     }
 259 
 260 
 261     /**
 262      * Obtains an audio stream from the input stream provided.  The stream must
 263      * point to valid audio file data.  In general, audio file providers may
 264      * need to read some data from the stream before determining whether they
 265      * support it.  These parsers must
 266      * be able to mark the stream, read enough data to determine whether they
 267      * support the stream, and, if not, reset the stream's read pointer to its original
 268      * position.  If the input stream does not support this, this method may fail
 269      * with an IOException.
 270      * @param stream the input stream from which the <code>AudioInputStream</code> should be
 271      * constructed
 272      * @return an <code>AudioInputStream</code> object based on the audio file data contained
 273      * in the input stream.
 274      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
 275      * file data recognized by the system
 276      * @throws IOException if an I/O exception occurs
 277      * @see InputStream#markSupported
 278      * @see InputStream#mark
 279      */
 280     public AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException {
 281 
 282         DataInputStream dis = null;
 283         int headerSize;
 284         AudioFileFormat fileFormat = null;
 285         AudioFormat format = null;
 286 
 287 
 288         fileFormat = getAudioFileFormat( stream ); // throws UnsupportedAudioFileException, IOException
 289 
 290         // if we passed this call, we have an AU file.
 291 
 292         format = fileFormat.getFormat();
 293 
 294         dis = new DataInputStream(stream);
 295 
 296         // now seek past the header
 297 
 298         dis.readInt(); // magic
 299         headerSize     = (format.isBigEndian()==true ? dis.readInt() : rllong(dis) );
 300         dis.skipBytes( headerSize - 8 );
 301 
 302 
 303         // we've got everything, and the stream should be at the
 304         // beginning of the data chunk, so return an AudioInputStream.
 305 
 306         return new AudioInputStream(dis, format, fileFormat.getFrameLength());
 307     }
 308 
 309 
 310     /**
 311      * Obtains an audio stream from the URL provided.  The URL must
 312      * point to valid audio file data.
 313      * @param url the URL for which the <code>AudioInputStream</code> should be
 314      * constructed
 315      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
 316      * to by the URL
 317      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
 318      * file data recognized by the system
 319      * @throws IOException if an I/O exception occurs
 320      */
 321     public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {
 322 
 323         InputStream                             urlStream = null;
 324         BufferedInputStream             bis = null;
 325         AudioFileFormat                 fileFormat = null;
 326 
 327         urlStream = url.openStream();   // throws IOException
 328         AudioInputStream result = null;
 329         try {
 330             bis = new BufferedInputStream( urlStream, bisBufferSize );
 331             result = getAudioInputStream( (InputStream)bis );
 332         } finally {
 333             if (result == null) {
 334                 urlStream.close();
 335             }
 336         }
 337         return result;
 338     }
 339 
 340 
 341     /**
 342      * Obtains an audio stream from the File provided.  The File must
 343      * point to valid audio file data.
 344      * @param file the File for which the <code>AudioInputStream</code> should be
 345      * constructed
 346      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
 347      * to by the File
 348      * @throws UnsupportedAudioFileException if the File does not point to valid audio
 349      * file data recognized by the system
 350      * @throws IOException if an I/O exception occurs
 351      */
 352     public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
 353 
 354         FileInputStream                 fis = null;
 355         BufferedInputStream             bis = null;
 356         AudioFileFormat                 fileFormat = null;
 357 
 358         fis = new FileInputStream( file );      // throws IOException
 359         AudioInputStream result = null;
 360         // part of fix for 4325421
 361         try {
 362             bis = new BufferedInputStream( fis, bisBufferSize );
 363             result = getAudioInputStream( (InputStream)bis );
 364         } finally {
 365             if (result == null) {
 366                 fis.close();
 367             }
 368         }
 369 
 370         return result;
 371     }
 372 }