Source for gnu.javax.sound.sampled.WAV.WAVReader

   1: /* WAVReader.java -- Read WAV files.
   2:    Copyright (C) 2006, 2012 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.javax.sound.sampled.WAV;
  39: 
  40: import java.io.File;
  41: import java.io.IOException;
  42: import java.io.InputStream;
  43: import java.io.DataInputStream;
  44: import java.io.FileInputStream;
  45: import java.net.URL;
  46: 
  47: import javax.sound.sampled.AudioFormat;
  48: import javax.sound.sampled.AudioFileFormat;
  49: import javax.sound.sampled.AudioInputStream;
  50: import javax.sound.sampled.UnsupportedAudioFileException;
  51: import javax.sound.sampled.spi.AudioFileReader;
  52: 
  53: /**
  54:  * A WAV file reader.
  55:  *
  56:  * This code reads WAV files.
  57:  *
  58:  * There are many decent documents on the web describing the WAV file
  59:  * format.  I didn't bother looking for the official document.  If it
  60:  * exists, I'm not even sure if it is freely available.  We should
  61:  * update this comment if we find out anything helpful here.  I used
  62:  * http://www.sonicspot.com/guide/wavefiles.html
  63:  *
  64:  * @author Anthony Green (green@redhat.com)
  65:  *
  66:  */
  67: public class WAVReader extends AudioFileReader
  68: {
  69:   private static long readUnsignedIntLE (DataInputStream is)
  70:     throws IOException
  71:   {
  72:     byte[] buf = new byte[4];
  73:     is.readFully(buf);
  74:     return (buf[0] & 0xFF
  75:             | ((buf[1] & 0xFF) << 8)
  76:             | ((buf[2] & 0xFF) << 16)
  77:             | ((buf[3] & 0xFF) << 24));
  78:   }
  79: 
  80:   private static short readUnsignedShortLE (DataInputStream is)
  81:     throws IOException
  82:   {
  83:     byte[] buf = new byte[2];
  84:     is.readFully(buf);
  85:     return (short) (buf[0] & 0xFF
  86:                     | ((buf[1] & 0xFF) << 8));
  87:   }
  88: 
  89:   /* Get an AudioFileFormat from the given File.
  90:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.io.File)
  91:    */
  92:   public AudioFileFormat getAudioFileFormat(File file)
  93:       throws UnsupportedAudioFileException, IOException
  94:   {
  95:     InputStream is = new FileInputStream(file);
  96:     try
  97:       {
  98:         return getAudioFileFormat(is);
  99:       }
 100:     finally
 101:       {
 102:         is.close();
 103:       }
 104:   }
 105: 
 106:   /* Get an AudioFileFormat from the given InputStream.
 107:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.io.InputStream)
 108:    */
 109:   public AudioFileFormat getAudioFileFormat(InputStream in)
 110:       throws UnsupportedAudioFileException, IOException
 111:   {
 112:     DataInputStream din;
 113: 
 114:     if (in instanceof DataInputStream)
 115:       din = (DataInputStream) in;
 116:     else
 117:       din = new DataInputStream(in);
 118: 
 119:     if (din.readInt() != 0x52494646) // "RIFF"
 120:       throw new UnsupportedAudioFileException("Invalid WAV chunk header.");
 121: 
 122:     // Read the length of this RIFF thing.
 123:     readUnsignedIntLE(din);
 124: 
 125:     if (din.readInt() != 0x57415645) // "WAVE"
 126:       throw new UnsupportedAudioFileException("Invalid WAV chunk header.");
 127: 
 128:     boolean foundFmt = false;
 129:     boolean foundData = false;
 130: 
 131:     short compressionCode = 0, numberChannels = 0, bitsPerSample = 0;
 132:     long sampleRate = 0, bytesPerSecond = 0;
 133:     long chunkLength = 0;
 134: 
 135:     while (! foundData)
 136:       {
 137:         int chunkId = din.readInt();
 138:         chunkLength = readUnsignedIntLE(din);
 139:         switch (chunkId)
 140:           {
 141:           case 0x666D7420: // "fmt "
 142:             foundFmt = true;
 143:             compressionCode = readUnsignedShortLE(din);
 144:             numberChannels = readUnsignedShortLE(din);
 145:             sampleRate = readUnsignedIntLE(din);
 146:             bytesPerSecond = readUnsignedIntLE(din);
 147:             readUnsignedShortLE(din); // blockAlign
 148:             bitsPerSample = readUnsignedShortLE(din);
 149:             din.skip(chunkLength - 16);
 150:             break;
 151:           case 0x66616374: // "fact"
 152:             // FIXME: hold compression format dependent data.
 153:             din.skip(chunkLength);
 154:             break;
 155:           case 0x64617461: // "data"
 156:             if (! foundFmt)
 157:               throw new UnsupportedAudioFileException("This implementation requires WAV fmt chunks precede data chunks.");
 158:             foundData = true;
 159:             break;
 160:           default:
 161:             // Unrecognized chunk.  Skip it.
 162:             din.skip(chunkLength);
 163:           }
 164:       }
 165: 
 166:     AudioFormat.Encoding encoding;
 167: 
 168:     switch (compressionCode)
 169:       {
 170:       case 1: // PCM/uncompressed
 171:         if (bitsPerSample <= 8)
 172:           encoding = AudioFormat.Encoding.PCM_UNSIGNED;
 173:         else
 174:           encoding = AudioFormat.Encoding.PCM_SIGNED;
 175:         break;
 176: 
 177:       default:
 178:         throw new UnsupportedAudioFileException("Unrecognized WAV compression code: 0x"
 179:                                                 + Integer.toHexString(compressionCode));
 180:       }
 181: 
 182:     return new AudioFileFormat (AudioFileFormat.Type.WAVE,
 183:                                 new AudioFormat(encoding,
 184:                                                 (float) sampleRate,
 185:                                                 bitsPerSample,
 186:                                                 numberChannels,
 187:                                                 ((bitsPerSample + 7) / 8) * numberChannels,
 188:                                                 (float) bytesPerSecond, false),
 189:                                 (int) chunkLength);
 190:   }
 191: 
 192:   /* Get an AudioFileFormat from the given URL.
 193:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.net.URL)
 194:    */
 195:   public AudioFileFormat getAudioFileFormat(URL url)
 196:       throws UnsupportedAudioFileException, IOException
 197:   {
 198:     InputStream is = url.openStream();
 199:     try
 200:       {
 201:         return getAudioFileFormat(is);
 202:       }
 203:     finally
 204:       {
 205:         is.close();
 206:       }
 207:   }
 208: 
 209:   /* Get an AudioInputStream from the given File.
 210:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.io.File)
 211:    */
 212:   public AudioInputStream getAudioInputStream(File file)
 213:       throws UnsupportedAudioFileException, IOException
 214:   {
 215:     return getAudioInputStream(new FileInputStream(file));
 216:   }
 217: 
 218:   /* Get an AudioInputStream from the given InputStream.
 219:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.io.InputStream)
 220:    */
 221:   public AudioInputStream getAudioInputStream(InputStream stream)
 222:       throws UnsupportedAudioFileException, IOException
 223:   {
 224:     AudioFileFormat aff = getAudioFileFormat(stream);
 225:     return new AudioInputStream(stream, aff.getFormat(), (long) aff.getFrameLength());
 226:   }
 227: 
 228:   /* Get an AudioInputStream from the given URL.
 229:    * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.net.URL)
 230:    */
 231:   public AudioInputStream getAudioInputStream(URL url)
 232:       throws UnsupportedAudioFileException, IOException
 233:   {
 234:     return getAudioInputStream(url.openStream());
 235:   }
 236: }