001/* 002 Copyright (C) 2005 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.sound.sampled; 040 041import java.io.IOException; 042import java.io.InputStream; 043 044/** 045 * This is an InputStream which is specialized for reading audio files. 046 * In particular it only allows operations to act on a multiple of 047 * the audio stream's frame size. 048 * @since 1.3 049 */ 050public class AudioInputStream extends InputStream 051{ 052 /** The format of the audio stream. */ 053 protected AudioFormat format; 054 055 /** The length of the audio stream in frames. */ 056 protected long frameLength; 057 058 /** The current frame position, starting from frame zero. */ 059 protected long framePos; 060 061 /** The size of a frame in bytes. */ 062 protected int frameSize; 063 064 // I wonder why this class doesn't inherit from FilterInputStream. 065 private InputStream input; 066 067 // The saved frame position, used for mark/reset. 068 private long markedFramePos; 069 070 /** 071 * Create a new AudioInputStream given an underlying InputStream, 072 * the audio format, and the length of the data in frames. The 073 * frame size is taken from the format. 074 * @param is the underlying input stream 075 * @param fmt the format of the data 076 * @param length the length of the data in frames 077 */ 078 public AudioInputStream(InputStream is, AudioFormat fmt, long length) 079 { 080 this.format = fmt; 081 this.frameLength = length; 082 this.framePos = 0; 083 this.frameSize = fmt.getFrameSize(); 084 this.input = is; 085 } 086 087 /** 088 * Create a new AudioInputStream given a TargetDataLine. The audio 089 * format and the frame size are taken from the line. 090 * @param line the TargetDataLine 091 */ 092 public AudioInputStream(TargetDataLine line) 093 { 094 this(new TargetInputStream(line), line.getFormat(), 095 AudioSystem.NOT_SPECIFIED); 096 } 097 098 /** 099 * 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}