1. /*
  2. * @(#)AudioInputStream.java 1.27 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.sound.sampled;
  8. import java.io.InputStream;
  9. import java.io.PushbackInputStream;
  10. import java.io.IOException;
  11. /**
  12. * An audio input stream is an input stream with a specified audio format and
  13. * length. The length is expressed in sample frames, not bytes.
  14. * Several methods are provided for reading a certain number of bytes from
  15. * the stream, or an unspecified number of bytes.
  16. * The audio input stream keeps track of the last byte that was read.
  17. * You can skip over an arbitrary number of bytes to get to a later position
  18. * for reading. An audio input stream may support marks. When you set a mark,
  19. * the current position is remembered so that you can return to it later.
  20. * <p>
  21. * The <code>AudioSystem</code> class includes many methods that manipulate
  22. * <code>AudioInputStream</code> objects.
  23. * For example, the methods let you:
  24. * <ul>
  25. * <li> obtain an
  26. * audio input stream from an external audio file, stream, or URL
  27. * <li> write an external file from an audio input stream
  28. * <li> convert an audio input stream to a different audio format
  29. * </ul>
  30. *
  31. * @author David Rivas
  32. * @author Kara Kytle
  33. * @version 1.27, 03/01/23
  34. *
  35. * @see AudioSystem
  36. * @see Clip#open(AudioInputStream) Clip.open(AudioInputStream)
  37. * @since 1.3
  38. */
  39. public class AudioInputStream extends InputStream {
  40. /**
  41. * The <code>InputStream</code> from which this <code>AudioInputStream</code>
  42. * object was constructed.
  43. */
  44. private PushbackInputStream stream;
  45. /**
  46. * PushbackInputStream overrides markSupported() to always return false.
  47. * However, it does not override the superclass mark() and reset() methods.
  48. * So, if these methods are supported in the stream on which the PushbackInputStream
  49. * is based, they actually do work.
  50. */
  51. private boolean markSupported;
  52. /**
  53. * The format of the audio data contained in the stream.
  54. */
  55. protected AudioFormat format;
  56. /**
  57. * This stream's length, in sample frames.
  58. */
  59. protected long frameLength;
  60. /**
  61. * The size of each frame, in bytes.
  62. */
  63. protected int frameSize;
  64. /**
  65. * The current position in this stream, in sample frames (zero-based).
  66. */
  67. protected long framePos;
  68. /**
  69. * The position where a mark was set.
  70. */
  71. private long markpos;
  72. /**
  73. * Constructs an audio input stream that has the requested format and length in sample frames,
  74. * using audio data from the specified input stream.
  75. * @param stream the stream on which this <code>AudioInputStream</code>
  76. * object is based
  77. * @param format the format of this stream's audio data
  78. * @param length the length in sample frames of the data in this stream
  79. */
  80. /*
  81. * Constructs an audio input stream that has the requested format and length in sample frames,
  82. * using audio data from the specified input stream.
  83. * @param stream the stream on which this <code>AudioInputStream</code>
  84. * object is based
  85. * @param format the format of this stream's audio data
  86. * @param length the length in sample frames of the data in this stream
  87. * @see AudioSystem#NOT_SPECIFIED
  88. */
  89. public AudioInputStream(InputStream stream, AudioFormat format, long length) {
  90. super();
  91. this.format = format;
  92. this.frameLength = length;
  93. this.frameSize = format.getFrameSize();
  94. if( this.frameSize == AudioSystem.NOT_SPECIFIED ) {
  95. this.frameSize = 1;
  96. }
  97. if( this.frameSize > 1 ) {
  98. this.stream = new PushbackInputStream(stream, frameSize-1);
  99. } else {
  100. this.stream = new PushbackInputStream(stream, 1);
  101. }
  102. framePos = 0;
  103. markpos = 0;
  104. markSupported = stream.markSupported();
  105. }
  106. /**
  107. * Constructs an audio input stream that reads its data from the target
  108. * data line indicated. The format of the stream is the same as that of
  109. * the target data line, and the length is AudioSystem#NOT_SPECIFIED.
  110. * @param line the target data line from which this stream obtains its data.
  111. * @see AudioSystem#NOT_SPECIFIED
  112. */
  113. public AudioInputStream(TargetDataLine line) {
  114. TargetDataLineInputStream tstream = new TargetDataLineInputStream(line);
  115. format = line.getFormat();
  116. frameLength = AudioSystem.NOT_SPECIFIED;
  117. frameSize = format.getFrameSize();
  118. if( frameSize == AudioSystem.NOT_SPECIFIED ) {
  119. frameSize = 1;
  120. }
  121. if( this.frameSize > 1 ) {
  122. this.stream = new PushbackInputStream(tstream, frameSize-1);
  123. } else {
  124. this.stream = new PushbackInputStream(tstream, 1);
  125. }
  126. framePos = 0;
  127. markpos = 0;
  128. markSupported = stream.markSupported();
  129. }
  130. /**
  131. * Obtains the audio format of the sound data in this audio input stream.
  132. * @return an audio format object describing this stream's format
  133. */
  134. public AudioFormat getFormat() {
  135. return format;
  136. }
  137. /**
  138. * Obtains the length of the stream, expressed in sample frames rather than bytes.
  139. * @return the length in sample frames
  140. */
  141. public long getFrameLength() {
  142. return frameLength;
  143. }
  144. // $$jb: 06.02.99: Deleting this method...
  145. /*
  146. * Obtains the current read position in the stream, in sample frames. This
  147. * indicates the zero-based number of the next sample frame that will be read.
  148. * @return the current read position
  149. */
  150. /*
  151. public long getFramePosition() {
  152. return framePos;
  153. }
  154. */
  155. /**
  156. * Reads the next byte of data from the audio input stream. The audio input
  157. * stream's frame size must be one byte, or an <code>IOException</code>
  158. * will be thrown.
  159. *
  160. * @return the next byte of data, or -1 if the end of the stream is reached
  161. * @throws IOException if an input or output error occurs
  162. * @see #read(byte[], int, int)
  163. * @see #read(byte[])
  164. * @see #available
  165. * <p>
  166. */
  167. public int read() throws IOException {
  168. int temp;
  169. if( frameSize != 1 ) {
  170. throw new IOException("cannot read a single byte if frame size != 1");
  171. }
  172. if( (frameLength != AudioSystem.NOT_SPECIFIED) && (framePos >= frameLength) ) {
  173. return -1;
  174. } else {
  175. temp = stream.read();
  176. // update position only if an error is not returned from
  177. // the underlying stream
  178. if( temp >= 0 ) {
  179. framePos++;
  180. }
  181. return temp;
  182. }
  183. }
  184. /**
  185. * Reads some number of bytes from the audio input stream and stores them into
  186. * the buffer array <code>b</code>. The number of bytes actually read is
  187. * returned as an integer. This method blocks until input data is
  188. * available, the end of the stream is detected, or an exception is thrown.
  189. * @param b the buffer into which the data is read
  190. * @return the total number of bytes read into the buffer, or -1 if there
  191. * is no more data because the end of the stream has been reached
  192. * @throws IOException if an input or output error occurs
  193. * @see #read(byte[], int, int)
  194. * @see #read()
  195. * @see #available
  196. */
  197. /*
  198. * <p>
  199. * ISSUE: what happens if (b.length % frame size != 0)?? do we throw
  200. * an exception, read the requested number of bytes, or read an integral
  201. * number of frames and return the number of bytes read?
  202. */
  203. public int read(byte[] b) throws IOException {
  204. return read(b,0,b.length);
  205. }
  206. /**
  207. * Reads up to a specified maximum number of bytes of data from the audio
  208. * stream, putting them into the given byte array.
  209. * @param b the buffer into which the data is read
  210. * @param off the offset, from the beginning of array <code>b</code>, at which
  211. * the data will be written
  212. * @param len the maximum number of bytes to read
  213. * @return the total number of bytes read into the buffer, or -1 if there
  214. * is no more data because the end of the stream has been reached
  215. * @throws IOException if an input or output error occurs
  216. * @see #read(byte[])
  217. * @see #read()
  218. * @see #skip
  219. * @see #available
  220. */
  221. /*
  222. * <p>
  223. * ISSUE: what happens if (len % frame size != 0)?? do we throw
  224. * an exception, read the requested number of bytes, or read an integral
  225. * number of frames and return the number of bytes read?
  226. */
  227. public int read(byte[] b, int off, int len) throws IOException {
  228. int bytesRead;
  229. int extraBytes;
  230. // make sure we don't read fractions of a frame.
  231. if( (len%frameSize) != 0 ) {
  232. len -= (len%frameSize);
  233. }
  234. if( frameLength != AudioSystem.NOT_SPECIFIED ) {
  235. if( framePos >= frameLength ) {
  236. return -1;
  237. } else {
  238. // don't try to read beyond our own set length in frames
  239. if( (lenframeSize) > (frameLength-framePos) ) {
  240. len = (int) (frameLength-framePos) * frameSize;
  241. }
  242. }
  243. }
  244. bytesRead = stream.read(b, off, len);
  245. if (bytesRead > 0) {
  246. extraBytes = bytesRead % frameSize;
  247. if (extraBytes > 0) {
  248. stream.unread(b, (off + bytesRead - extraBytes), extraBytes);
  249. bytesRead -= extraBytes;
  250. }
  251. }
  252. // make sure to update our framePos
  253. framePos += bytesReadframeSize;
  254. return bytesRead;
  255. }
  256. /**
  257. * Skips over and discards a specified number of bytes from this
  258. * audio input stream.
  259. * @param n the requested number of bytes to be skipped
  260. * @return the actual number of bytes skipped
  261. * @throws IOException if an input or output error occurs
  262. * @see #read
  263. * @see #available
  264. */
  265. public long skip(long n) throws IOException {
  266. long temp;
  267. // make sure not to skip fractional frames
  268. if( (n%frameSize) != 0 ) {
  269. n -= (n%frameSize);
  270. }
  271. if( frameLength != AudioSystem.NOT_SPECIFIED ) {
  272. // don't skip more than our set length in frames.
  273. if( (nframeSize) > (frameLength-framePos) ) {
  274. n = (frameLength-framePos) * frameSize;
  275. }
  276. }
  277. temp = stream.skip(n);
  278. // if no error, update our position.
  279. if( temp%frameSize != 0 ) {
  280. // Throw an IOException if we've skipped a fractional number of frames
  281. throw new IOException("Could not skip an integer number of frames.");
  282. }
  283. if( temp >= 0 ) {
  284. framePos += tempframeSize;
  285. }
  286. return temp;
  287. }
  288. /**
  289. * Returns the maximum number of bytes that can be read (or skipped over) from this
  290. * audio input stream without blocking. This limit applies only to the next invocation of
  291. * a <code>read</code> or <code>skip</code> method for this audio input stream; the limit
  292. * can vary each time these methods are invoked.
  293. * @return the number of bytes that can be read from this audio input stream without blocking
  294. * @throws IOException if an input or output error occurs
  295. * @see #read(byte[], int, int)
  296. * @see #read(byte[])
  297. * @see #read()
  298. * @see #skip
  299. */
  300. public int available() throws IOException {
  301. int temp;
  302. temp = stream.available();
  303. // don't return greater than our set length in frames
  304. if( (frameLength != AudioSystem.NOT_SPECIFIED) && ( (tempframeSize) > (frameLength-framePos)) ) {
  305. return (int) (frameLength-framePos) * frameSize;
  306. } else {
  307. return temp;
  308. }
  309. }
  310. /**
  311. * Closes this audio input stream and releases any system resources associated
  312. * with the stream.
  313. * @throws IOException if an input or output error occurs
  314. */
  315. public void close() throws IOException {
  316. stream.close();
  317. }
  318. /**
  319. * Marks the current position in this audio input stream.
  320. * @param readlimit the maximum number of bytes that can be read before
  321. * the mark position becomes invalid.
  322. * @see #reset
  323. * @see #markSupported
  324. */
  325. public void mark(int readlimit) {
  326. stream.mark(readlimit);
  327. if( markSupported() ) {
  328. markpos = framePos;
  329. }
  330. }
  331. /**
  332. * Repositions this audio input stream to the position it had at the time its
  333. * <code>mark</code> method was last invoked.
  334. * @throws IOException if an input or output error occurs.
  335. * @see #mark
  336. * @see #markSupported
  337. */
  338. public void reset() throws IOException {
  339. stream.reset();
  340. framePos = markpos;
  341. }
  342. /**
  343. * Tests whether this audio input stream supports the <code>mark</code> and
  344. * <code>reset</code> methods.
  345. * @return <code>true</code> if this stream supports the <code>mark</code>
  346. * and <code>reset</code> methods; <code>false</code> otherwise
  347. * @see #mark
  348. * @see #reset
  349. */
  350. public boolean markSupported() {
  351. return markSupported;
  352. }
  353. /**
  354. * Private inner class that makes a TargetDataLine look like an InputStream.
  355. */
  356. private class TargetDataLineInputStream extends InputStream {
  357. /**
  358. * The TargetDataLine on which this TargetDataLineInputStream is based.
  359. */
  360. TargetDataLine line;
  361. TargetDataLineInputStream(TargetDataLine line) {
  362. super();
  363. this.line = line;
  364. }
  365. public int available() throws IOException {
  366. return line.available();
  367. }
  368. //$$fb 2001-07-16: added this method to correctly close the underlying TargetDataLine.
  369. // fixes bug 4479984
  370. public void close() throws IOException {
  371. // the line needs to be flushed and stopped to avoid a dead lock...
  372. // Probably related to bugs 4417527, 4334868, 4383457
  373. if (line.isActive()) {
  374. line.flush();
  375. line.stop();
  376. }
  377. line.close();
  378. }
  379. public int read() throws IOException {
  380. byte[] b = new byte[1];
  381. int value = read(b, 0, 1);
  382. if (value == -1) {
  383. return -1;
  384. }
  385. value = (int)b[0];
  386. if (line.getFormat().getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
  387. value += 128;
  388. }
  389. return value;
  390. }
  391. public int read(byte[] b, int off, int len) throws IOException {
  392. try {
  393. return line.read(b, off, len);
  394. } catch (IllegalArgumentException e) {
  395. throw new IOException(e.getMessage());
  396. }
  397. }
  398. }
  399. }