/* * AudioInputStream.java * * This file is part of Tritonus: http://www.tritonus.org/ */ /* * Copyright (c) 1999, 2000 by Matthias Pfisterer * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* |<--- this code is formatted to fit into 80 columns --->| */ package javax.sound.sampled; import java.io.InputStream; import java.io.IOException; import org.tritonus.share.TDebug; import org.tritonus.share.sampled.AudioUtils; public class AudioInputStream extends InputStream { /** Says whether we check lengths. * If true, lengths in bytes are asserted to be multiples * of the frame size. */ private static final boolean CHECK_LENGTHS = true; /** The stream that is backing the AudioInputStream. * Accessing this property, especially writing, is discouraged. */ protected InputStream stream; /** The format of this stream. * Accessing this property, especially writing, is discouraged. * You should use the method getFormat(). */ protected AudioFormat format; /** The length of the audio data. * * This value is in frames. May have the value * AudioSystem.NOT_SPECIFIED to express that the length in frames * is unknown. Accessing this property, especially writing, is * strongly discouraged. You should use the method * getFrameLength(). */ protected long frameLength; /** The size of an frame of audio data. * * This value is in bytes. May have the value * AudioSystem.NOT_SPECIFIED to express that the size of frames * is unknown. Accessing this property, especially writing, is * strongly discouraged. Its semantics is not clearly defined. */ protected int frameSize; /** The size of an frame of audio data. * * This value is used internally to check whether the number of * bytes requested in read() or skip() is a multiple of the frame * size. This value is in bytes. In contrary to frameSize, * which may be AudioSystem.NOT_SPECIFIED, this variable always * has a value >= 1. If the length in frames or the frame size of * the audio data is unknown, this value is set to 1. */ private int m_nCheckFrameSize; /** The read position in the stream. * * This value is in frames. NOTE: this property only has a * useful value if the encoding of the stream is PCM. Accessing * this property, especially writing, is strongly * discouraged. Its semantics is not clearly defined. */ protected long framePos; /** The read position in the stream. * * This value is in bytes. */ private long m_lPosition; /** The length of the audio data. * * This value is in bytes. May have the value * AudioSystem.NOT_SPECIFIED to express that the length is * unknown. */ private long m_lLengthInBytes; /** Position where the mark was set in the stream. * * This value is in bytes. */ private long m_lMarkedPosition; public AudioInputStream(InputStream inputStream, AudioFormat audioFormat, long lLengthInFrames) { if (TDebug.TraceAudioInputStream) { TDebug.out("AudioInputStream.<init>: inputStream: " + inputStream); } stream = inputStream; format = audioFormat; frameLength = lLengthInFrames; m_nCheckFrameSize = frameSize = audioFormat.getFrameSize(); if (m_nCheckFrameSize < 1) { m_nCheckFrameSize = 1; } m_lLengthInBytes = AudioUtils.getLengthInBytes( audioFormat, lLengthInFrames); m_lPosition = 0; updateFramePosition(); } public AudioInputStream(TargetDataLine targetDataLine) { this(new TargetDataLineInputStream(targetDataLine), targetDataLine.getFormat(), AudioSystem.NOT_SPECIFIED); } public AudioFormat getFormat() { return format; } public long getFrameLength() { return frameLength; } public int read() throws IOException { if (CHECK_LENGTHS) { if (getCheckFrameSize() != 1) { throw new IOException("frame size must be 1 to read a single byte"); } } if (isEndReached()) { return -1; } int nByte = stream.read(); if (nByte != -1) { m_lPosition++; updateFramePosition(); } return nByte; } public int read(byte[] abData) throws IOException { return read(abData, 0, abData.length); } public int read(byte[] abData, int nOffset, int nLength) throws IOException { //$$fb better to first check if end is reached. //otherwise, on un-aligned nLength, it will lead to a misleading exception // (as it occurs with DataInputStream.readFully) if (isEndReached()) { return -1; } if (CHECK_LENGTHS) { if (nLength % getCheckFrameSize() != 0) { throw new IOException("length must be a multiple of the frame size (length="+nLength+", frameSize="+getCheckFrameSize()+")"); } } if (m_lLengthInBytes != AudioSystem.NOT_SPECIFIED) { nLength = (int) Math.min(nLength, m_lLengthInBytes - m_lPosition); } //$$fb try to read frame-aligned (when stream returns unaligned data) //$$fb e.g. SequenceInputStream int nBytesRead=0; int thisRead; do { thisRead = stream.read(abData, nOffset, nLength); if (thisRead > 0) { nBytesRead += thisRead; nLength -= thisRead; nOffset += thisRead; } } while (thisRead > 0 && nLength > 0); if (nBytesRead <= 0 && thisRead == -1) { nBytesRead =- 1; } else { m_lPosition += nBytesRead; updateFramePosition(); } return nBytesRead; } public long skip(long nSkip) throws IOException { if (CHECK_LENGTHS) { if (nSkip % getCheckFrameSize() != 0) { throw new IOException("skip must be a multiple of the frame size"); } } if (m_lLengthInBytes != AudioSystem.NOT_SPECIFIED) { nSkip = (int) Math.min(nSkip, m_lLengthInBytes - m_lPosition); } long nSkipped = stream.skip(nSkip); m_lPosition += nSkipped; updateFramePosition(); return nSkipped; } public int available() throws IOException { if (m_lLengthInBytes == AudioSystem.NOT_SPECIFIED) { return stream.available(); } else { return (int) Math.min(stream.available(), m_lLengthInBytes - m_lPosition); } } public void close() throws IOException { stream.close(); } public void mark(int readlimit) { stream.mark(readlimit); m_lMarkedPosition = m_lPosition; } public void reset() throws IOException { stream.reset(); m_lPosition = m_lMarkedPosition; updateFramePosition(); } public boolean markSupported() { return stream.markSupported(); } /** Returns the frame size for internal checks. * * This number is is bytes. */ private int getCheckFrameSize() { return m_nCheckFrameSize; } private boolean isEndReached() { return m_lPosition >= m_lLengthInBytes && m_lLengthInBytes != AudioSystem.NOT_SPECIFIED; } /** Calculates the position in frames from the position in bytes. * * This only produces meaningful values if the encoding is PCM. * Maintaining framePos is only done for backwards compatibility * with really badly designed programs that directely access the * protected member framePos. */ private void updateFramePosition() { this.framePos = m_lPosition / getCheckFrameSize(); } /** Helper class to enable AudioInputStream to read from a TargetDataLine. */ private static class TargetDataLineInputStream extends InputStream { /** The TargetDataLine from which to read. */ private TargetDataLine m_targetDataLine; /** A buffer used for reading single bytes. * * It is allocated and used in read(). */ private byte[] m_abSingleByteBuffer = null; public TargetDataLineInputStream(TargetDataLine targetDataLine) { m_targetDataLine = targetDataLine; } public int read() { if (m_abSingleByteBuffer == null) { m_abSingleByteBuffer = new byte[1]; } int nReturn = read(m_abSingleByteBuffer, 0, 1); if (nReturn < 0) { return -1; } else { return m_abSingleByteBuffer[0]; } } public int read(byte[] abData, int nOffset, int nLength) { return m_targetDataLine.read(abData, nOffset, nLength); } } } /*** AudioInputStream.java ***/