Source for javax.sound.sampled.AudioInputStream

   1: /* 
   2:    Copyright (C) 2005 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: 
  39: package javax.sound.sampled;
  40: 
  41: import java.io.IOException;
  42: import java.io.InputStream;
  43: 
  44: /**
  45:  * This is an InputStream which is specialized for reading audio files.
  46:  * In particular it only allows operations to act on a multiple of
  47:  * the audio stream's frame size.
  48:  * @since 1.3
  49:  */
  50: public class AudioInputStream extends InputStream
  51: {
  52:   /** The format of the audio stream.  */
  53:   protected AudioFormat format;
  54: 
  55:   /** The length of the audio stream in frames.  */
  56:   protected long frameLength;
  57: 
  58:   /** The current frame position, starting from frame zero.  */ 
  59:   protected long framePos;
  60: 
  61:   /** The size of a frame in bytes.  */
  62:   protected int frameSize;
  63: 
  64:   // I wonder why this class doesn't inherit from FilterInputStream.
  65:   private InputStream input;
  66: 
  67:   // The saved frame position, used for mark/reset.
  68:   private long markedFramePos;
  69: 
  70:   /**
  71:    * Create a new AudioInputStream given an underlying InputStream,
  72:    * the audio format, and the length of the data in frames.  The
  73:    * frame size is taken from the format.
  74:    * @param is the underlying input stream
  75:    * @param fmt the format of the data
  76:    * @param length the length of the data in frames
  77:    */
  78:   public AudioInputStream(InputStream is, AudioFormat fmt, long length)
  79:   {
  80:     this.format = fmt;
  81:     this.frameLength = length;
  82:     this.framePos = 0;
  83:     this.frameSize = fmt.getFrameSize();
  84:     this.input = is;
  85:   }
  86: 
  87:   /**
  88:    * Create a new AudioInputStream given a TargetDataLine.  The audio
  89:    * format and the frame size are taken from the line.
  90:    * @param line the TargetDataLine
  91:    */
  92:   public AudioInputStream(TargetDataLine line)
  93:   {
  94:     this(new TargetInputStream(line), line.getFormat(),
  95:      AudioSystem.NOT_SPECIFIED);
  96:   }
  97: 
  98:   /**
  99:    * Return the number of bytes available to be read from the
 100:    * underlying stream.  This wrapper method ensures that the result
 101:    * is always a multiple of the frame size.
 102:    */
 103:   public int available() throws IOException
 104:   {
 105:     int result = input.available();
 106:     // Ensure result is a multiple of the frame size.
 107:     if (frameSize != AudioSystem.NOT_SPECIFIED)
 108:       result -= result % frameSize;
 109:     return result;
 110:   }
 111: 
 112:   /**
 113:    * Close the stream.
 114:    */
 115:   public void close() throws IOException
 116:   {
 117:     input.close();
 118:   }
 119: 
 120:   /**
 121:    * Get the format associated with this stream.
 122:    * @return the AudioFormat
 123:    */
 124:   public AudioFormat getFormat()
 125:   {
 126:     return format;
 127:   }
 128: 
 129:   /**
 130:    * Get the length of this stream in frames.  Note that this
 131:    * may be AudioSystem#NOT_SPECIFIED.
 132:    * @return the length of the stream in frames
 133:    */
 134:   public long getFrameLength()
 135:   {
 136:     return frameLength;
 137:   }
 138: 
 139:   public void mark(int limit)
 140:   {
 141:     input.mark(limit);
 142:     markedFramePos = framePos;
 143:   }
 144: 
 145:   /**
 146:    * Return true if the underlying stream supports mark and reset,
 147:    * false otherwise.
 148:    */
 149:   public boolean markSupported()
 150:   {
 151:     return input.markSupported();
 152:   }
 153: 
 154:   /**
 155:    * Read a single byte from the underlying stream.  If the frame
 156:    * size is set, and is not one byte, an IOException will be thrown.
 157:    */
 158:   public int read() throws IOException
 159:   {
 160:     if (frameSize != 1)
 161:       throw new IOException("frame size must be 1 for read()");
 162:     int result;
 163:     if (framePos == frameLength)
 164:       result = -1;
 165:     else
 166:       result = input.read();
 167:     if (result != -1)
 168:       ++framePos;
 169:     return result;
 170:   }
 171: 
 172:   public int read(byte[] buf) throws IOException
 173:   {
 174:     return read(buf, 0, buf.length);
 175:   }
 176: 
 177:   public int read(byte[] buf, int offset, int length) throws IOException
 178:   {
 179:     int result;
 180:     if (framePos == frameLength)
 181:       result = -1;
 182:     else
 183:       {
 184:     int myFrameSize = (frameSize == AudioSystem.NOT_SPECIFIED
 185:                ? 1 : frameSize);
 186:     // Ensure length is a multiple of frame size.
 187:     length -= length % myFrameSize;
 188: 
 189:     result = 0;
 190:     while (result == 0 || result % myFrameSize != 0)
 191:       {
 192:         int val = input.read(buf, offset, length);
 193:         if (val < 0)
 194:           {
 195:         // This is a weird situation as we might have read a
 196:         // frame already.  It isn't clear at all what to do if
 197:         // we only found a partial frame.  For now we just
 198:         // return whatever we did find.
 199:         if (result == 0)
 200:           return -1;
 201:         result -= result % myFrameSize;
 202:         break;
 203:           }
 204:         result += val;
 205:       }
 206:     // assert result % myFrameSize == 0;
 207:     framePos += result / myFrameSize;
 208:       }
 209:     return result;
 210:   }
 211: 
 212:   public void reset() throws IOException
 213:   {
 214:     input.reset();
 215:     framePos = markedFramePos;    
 216:   }
 217: 
 218:   public long skip(long n) throws IOException
 219:   {
 220:     if (frameSize != AudioSystem.NOT_SPECIFIED)
 221:       n -= n % frameSize;
 222:     long actual = input.skip(n);
 223:     if (frameSize != AudioSystem.NOT_SPECIFIED)
 224:       framePos += actual / frameSize;
 225:     return actual;
 226:   }
 227: 
 228:   private static class TargetInputStream extends InputStream
 229:   {
 230:     private TargetDataLine line;
 231:     private byte[] buf;
 232: 
 233:     /**
 234:      * Create a new TargetInputStream.
 235:      * @param line the line to wrap
 236:      */
 237:     public TargetInputStream(TargetDataLine line)
 238:     {
 239:       this.line = line;
 240:       // FIXME: do we have to call line.open()?
 241:     }
 242: 
 243:     public synchronized int read() throws IOException
 244:     {
 245:       if (buf == null)
 246:     buf = new byte[1];
 247:       int count = read(buf, 0, 1);
 248:       if (count < 0)
 249:     return -1;
 250:       return buf[0];
 251:     }
 252: 
 253:     public int read(byte[] buf, int offset, int length) throws IOException
 254:     {
 255:       return line.read(buf, offset, length);
 256:     }
 257:   }
 258: }