Frames | No Frames |
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: }