View Javadoc
1   /*
2    * Copyright (c) 1999, 2005, 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 javax.sound.sampled;
27  
28  import java.io.InputStream;
29  import java.io.PushbackInputStream;
30  import java.io.IOException;
31  
32  
33  /**
34   * An audio input stream is an input stream with a specified audio format and
35   * length.  The length is expressed in sample frames, not bytes.
36   * Several methods are provided for reading a certain number of bytes from
37   * the stream, or an unspecified number of bytes.
38   * The audio input stream keeps track  of the last byte that was read.
39   * You can skip over an arbitrary number of bytes to get to a later position
40   * for reading. An audio input stream may support marks.  When you set a mark,
41   * the current position is remembered so that you can return to it later.
42   * <p>
43   * The <code>AudioSystem</code> class includes many methods that manipulate
44   * <code>AudioInputStream</code> objects.
45   * For example, the methods let you:
46   * <ul>
47   * <li> obtain an
48   * audio input stream from an external audio file, stream, or URL
49   * <li> write an external file from an audio input stream
50   * <li> convert an audio input stream to a different audio format
51   * </ul>
52   *
53   * @author David Rivas
54   * @author Kara Kytle
55   * @author Florian Bomers
56   *
57   * @see AudioSystem
58   * @see Clip#open(AudioInputStream) Clip.open(AudioInputStream)
59   * @since 1.3
60   */
61  public class AudioInputStream extends InputStream {
62  
63      /**
64       * The <code>InputStream</code> from which this <code>AudioInputStream</code>
65       * object was constructed.
66       */
67      private InputStream stream;
68  
69      /**
70       * The format of the audio data contained in the stream.
71       */
72      protected AudioFormat format;
73  
74      /**
75       * This stream's length, in sample frames.
76       */
77      protected long frameLength;
78  
79      /**
80       * The size of each frame, in bytes.
81       */
82      protected int frameSize;
83  
84      /**
85       * The current position in this stream, in sample frames (zero-based).
86       */
87      protected long framePos;
88  
89      /**
90       * The position where a mark was set.
91       */
92      private long markpos;
93  
94      /**
95       * When the underlying stream could only return
96       * a non-integral number of frames, store
97       * the remainder in a temporary buffer
98       */
99      private byte[] pushBackBuffer = null;
100 
101     /**
102      * number of valid bytes in the pushBackBuffer
103      */
104     private int pushBackLen = 0;
105 
106     /**
107      * MarkBuffer at mark position
108      */
109     private byte[] markPushBackBuffer = null;
110 
111     /**
112      * number of valid bytes in the markPushBackBuffer
113      */
114     private int markPushBackLen = 0;
115 
116 
117     /**
118      * Constructs an audio input stream that has the requested format and length in sample frames,
119      * using audio data from the specified input stream.
120      * @param stream the stream on which this <code>AudioInputStream</code>
121      * object is based
122      * @param format the format of this stream's audio data
123      * @param length the length in sample frames of the data in this stream
124      */
125     public AudioInputStream(InputStream stream, AudioFormat format, long length) {
126 
127         super();
128 
129         this.format = format;
130         this.frameLength = length;
131         this.frameSize = format.getFrameSize();
132 
133         // any frameSize that is not well-defined will
134         // cause that this stream will be read in bytes
135         if( this.frameSize == AudioSystem.NOT_SPECIFIED || frameSize <= 0) {
136             this.frameSize = 1;
137         }
138 
139         this.stream = stream;
140         framePos = 0;
141         markpos = 0;
142     }
143 
144 
145     /**
146      * Constructs an audio input stream that reads its data from the target
147      * data line indicated.  The format of the stream is the same as that of
148      * the target data line, and the length is AudioSystem#NOT_SPECIFIED.
149      * @param line the target data line from which this stream obtains its data.
150      * @see AudioSystem#NOT_SPECIFIED
151      */
152     public AudioInputStream(TargetDataLine line) {
153 
154         TargetDataLineInputStream tstream = new TargetDataLineInputStream(line);
155         format = line.getFormat();
156         frameLength = AudioSystem.NOT_SPECIFIED;
157         frameSize = format.getFrameSize();
158 
159         if( frameSize == AudioSystem.NOT_SPECIFIED || frameSize <= 0) {
160             frameSize = 1;
161         }
162         this.stream = tstream;
163         framePos = 0;
164         markpos = 0;
165     }
166 
167 
168     /**
169      * Obtains the audio format of the sound data in this audio input stream.
170      * @return an audio format object describing this stream's format
171      */
172     public AudioFormat getFormat() {
173         return format;
174     }
175 
176 
177     /**
178      * Obtains the length of the stream, expressed in sample frames rather than bytes.
179      * @return the length in sample frames
180      */
181     public long getFrameLength() {
182         return frameLength;
183     }
184 
185 
186     /**
187      * Reads the next byte of data from the audio input stream.  The audio input
188      * stream's frame size must be one byte, or an <code>IOException</code>
189      * will be thrown.
190      *
191      * @return the next byte of data, or -1 if the end of the stream is reached
192      * @throws IOException if an input or output error occurs
193      * @see #read(byte[], int, int)
194      * @see #read(byte[])
195      * @see #available
196      * <p>
197      */
198     public int read() throws IOException {
199         if( frameSize != 1 ) {
200             throw new IOException("cannot read a single byte if frame size > 1");
201         }
202 
203         byte[] data = new byte[1];
204         int temp = read(data);
205         if (temp <= 0) {
206             // we have a weird situation if read(byte[]) returns 0!
207             return -1;
208         }
209         return data[0] & 0xFF;
210     }
211 
212 
213     /**
214      * Reads some number of bytes from the audio input stream and stores them into
215      * the buffer array <code>b</code>. The number of bytes actually read is
216      * returned as an integer. This method blocks until input data is
217      * available, the end of the stream is detected, or an exception is thrown.
218      * <p>This method will always read an integral number of frames.
219      * If the length of the array is not an integral number
220      * of frames, a maximum of <code>b.length - (b.length % frameSize)
221      * </code> bytes will be read.
222      *
223      * @param b the buffer into which the data is read
224      * @return the total number of bytes read into the buffer, or -1 if there
225      * is no more data because the end of the stream has been reached
226      * @throws IOException if an input or output error occurs
227      * @see #read(byte[], int, int)
228      * @see #read()
229      * @see #available
230      */
231     public int read(byte[] b) throws IOException {
232         return read(b,0,b.length);
233     }
234 
235 
236     /**
237      * Reads up to a specified maximum number of bytes of data from the audio
238      * stream, putting them into the given byte array.
239      * <p>This method will always read an integral number of frames.
240      * If <code>len</code> does not specify an integral number
241      * of frames, a maximum of <code>len - (len % frameSize)
242      * </code> bytes will be read.
243      *
244      * @param b the buffer into which the data is read
245      * @param off the offset, from the beginning of array <code>b</code>, at which
246      * the data will be written
247      * @param len the maximum number of bytes to read
248      * @return the total number of bytes read into the buffer, or -1 if there
249      * is no more data because the end of the stream has been reached
250      * @throws IOException if an input or output error occurs
251      * @see #read(byte[])
252      * @see #read()
253      * @see #skip
254      * @see #available
255      */
256     public int read(byte[] b, int off, int len) throws IOException {
257 
258         // make sure we don't read fractions of a frame.
259         if( (len%frameSize) != 0 ) {
260             len -= (len%frameSize);
261             if (len == 0) {
262                 return 0;
263             }
264         }
265 
266         if( frameLength != AudioSystem.NOT_SPECIFIED ) {
267             if( framePos >= frameLength ) {
268                 return -1;
269             } else {
270 
271                 // don't try to read beyond our own set length in frames
272                 if( (len/frameSize) > (frameLength-framePos) ) {
273                     len = (int) (frameLength-framePos) * frameSize;
274                 }
275             }
276         }
277 
278         int bytesRead = 0;
279         int thisOff = off;
280 
281         // if we've bytes left from last call to read(),
282         // use them first
283         if (pushBackLen > 0 && len >= pushBackLen) {
284             System.arraycopy(pushBackBuffer, 0,
285                              b, off, pushBackLen);
286             thisOff += pushBackLen;
287             len -= pushBackLen;
288             bytesRead += pushBackLen;
289             pushBackLen = 0;
290         }
291 
292         int thisBytesRead = stream.read(b, thisOff, len);
293         if (thisBytesRead == -1) {
294             return -1;
295         }
296         if (thisBytesRead > 0) {
297             bytesRead += thisBytesRead;
298         }
299         if (bytesRead > 0) {
300             pushBackLen = bytesRead % frameSize;
301             if (pushBackLen > 0) {
302                 // copy everything we got from the beginning of the frame
303                 // to our pushback buffer
304                 if (pushBackBuffer == null) {
305                     pushBackBuffer = new byte[frameSize];
306                 }
307                 System.arraycopy(b, off + bytesRead - pushBackLen,
308                                  pushBackBuffer, 0, pushBackLen);
309                 bytesRead -= pushBackLen;
310             }
311             // make sure to update our framePos
312             framePos += bytesRead/frameSize;
313         }
314         return bytesRead;
315     }
316 
317 
318     /**
319      * Skips over and discards a specified number of bytes from this
320      * audio input stream.
321      * @param n the requested number of bytes to be skipped
322      * @return the actual number of bytes skipped
323      * @throws IOException if an input or output error occurs
324      * @see #read
325      * @see #available
326      */
327     public long skip(long n) throws IOException {
328 
329         // make sure not to skip fractional frames
330         if( (n%frameSize) != 0 ) {
331             n -= (n%frameSize);
332         }
333 
334         if( frameLength != AudioSystem.NOT_SPECIFIED ) {
335             // don't skip more than our set length in frames.
336             if( (n/frameSize) > (frameLength-framePos) ) {
337                 n = (frameLength-framePos) * frameSize;
338             }
339         }
340         long temp = stream.skip(n);
341 
342         // if no error, update our position.
343         if( temp%frameSize != 0 ) {
344 
345             // Throw an IOException if we've skipped a fractional number of frames
346             throw new IOException("Could not skip an integer number of frames.");
347         }
348         if( temp >= 0 ) {
349             framePos += temp/frameSize;
350         }
351         return temp;
352 
353     }
354 
355 
356     /**
357      * Returns the maximum number of bytes that can be read (or skipped over) from this
358      * audio input stream without blocking.  This limit applies only to the next invocation of
359      * a <code>read</code> or <code>skip</code> method for this audio input stream; the limit
360      * can vary each time these methods are invoked.
361      * Depending on the underlying stream,an IOException may be thrown if this
362      * stream is closed.
363      * @return the number of bytes that can be read from this audio input stream without blocking
364      * @throws IOException if an input or output error occurs
365      * @see #read(byte[], int, int)
366      * @see #read(byte[])
367      * @see #read()
368      * @see #skip
369      */
370     public int available() throws IOException {
371 
372         int temp = stream.available();
373 
374         // don't return greater than our set length in frames
375         if( (frameLength != AudioSystem.NOT_SPECIFIED) && ( (temp/frameSize) > (frameLength-framePos)) ) {
376             return (int) (frameLength-framePos) * frameSize;
377         } else {
378             return temp;
379         }
380     }
381 
382 
383     /**
384      * Closes this audio input stream and releases any system resources associated
385      * with the stream.
386      * @throws IOException if an input or output error occurs
387      */
388     public void close() throws IOException {
389         stream.close();
390     }
391 
392 
393     /**
394      * Marks the current position in this audio input stream.
395      * @param readlimit the maximum number of bytes that can be read before
396      * the mark position becomes invalid.
397      * @see #reset
398      * @see #markSupported
399      */
400 
401     public void mark(int readlimit) {
402 
403         stream.mark(readlimit);
404         if (markSupported()) {
405             markpos = framePos;
406             // remember the pushback buffer
407             markPushBackLen = pushBackLen;
408             if (markPushBackLen > 0) {
409                 if (markPushBackBuffer == null) {
410                     markPushBackBuffer = new byte[frameSize];
411                 }
412                 System.arraycopy(pushBackBuffer, 0, markPushBackBuffer, 0, markPushBackLen);
413             }
414         }
415     }
416 
417 
418     /**
419      * Repositions this audio input stream to the position it had at the time its
420      * <code>mark</code> method was last invoked.
421      * @throws IOException if an input or output error occurs.
422      * @see #mark
423      * @see #markSupported
424      */
425     public void reset() throws IOException {
426 
427         stream.reset();
428         framePos = markpos;
429         // re-create the pushback buffer
430         pushBackLen = markPushBackLen;
431         if (pushBackLen > 0) {
432             if (pushBackBuffer == null) {
433                 pushBackBuffer = new byte[frameSize - 1];
434             }
435             System.arraycopy(markPushBackBuffer, 0, pushBackBuffer, 0, pushBackLen);
436         }
437     }
438 
439 
440     /**
441      * Tests whether this audio input stream supports the <code>mark</code> and
442      * <code>reset</code> methods.
443      * @return <code>true</code> if this stream supports the <code>mark</code>
444      * and <code>reset</code> methods; <code>false</code> otherwise
445      * @see #mark
446      * @see #reset
447      */
448     public boolean markSupported() {
449 
450         return stream.markSupported();
451     }
452 
453 
454     /**
455      * Private inner class that makes a TargetDataLine look like an InputStream.
456      */
457     private class TargetDataLineInputStream extends InputStream {
458 
459         /**
460          * The TargetDataLine on which this TargetDataLineInputStream is based.
461          */
462         TargetDataLine line;
463 
464 
465         TargetDataLineInputStream(TargetDataLine line) {
466             super();
467             this.line = line;
468         }
469 
470 
471         public int available() throws IOException {
472             return line.available();
473         }
474 
475         //$$fb 2001-07-16: added this method to correctly close the underlying TargetDataLine.
476         // fixes bug 4479984
477         public void close() throws IOException {
478             // the line needs to be flushed and stopped to avoid a dead lock...
479             // Probably related to bugs 4417527, 4334868, 4383457
480             if (line.isActive()) {
481                 line.flush();
482                 line.stop();
483             }
484             line.close();
485         }
486 
487         public int read() throws IOException {
488 
489             byte[] b = new byte[1];
490 
491             int value = read(b, 0, 1);
492 
493             if (value == -1) {
494                 return -1;
495             }
496 
497             value = (int)b[0];
498 
499             if (line.getFormat().getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
500                 value += 128;
501             }
502 
503             return value;
504         }
505 
506 
507         public int read(byte[] b, int off, int len) throws IOException {
508             try {
509                 return line.read(b, off, len);
510             } catch (IllegalArgumentException e) {
511                 throw new IOException(e.getMessage());
512             }
513         }
514     }
515 }