485 lines
18 KiB
Java
485 lines
18 KiB
Java
/*
|
|
* 21.04.2004 Original verion. davagin@udm.ru.
|
|
*-----------------------------------------------------------------------
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
package davaguine.jeq.core;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
/**
|
|
* The EqualizerInputStream class
|
|
* Author: Dmitry Vaguine
|
|
* Date: 02.05.2004
|
|
* Time: 12:00:29
|
|
*/
|
|
public class EqualizerInputStream extends InputStream {
|
|
private InputStream stream;
|
|
private IIR iir;
|
|
|
|
private final static int BUFFER_SIZE = 65536;
|
|
private byte[] inbuf = new byte[BUFFER_SIZE];
|
|
private int[] workbuf = new int[BUFFER_SIZE];
|
|
private byte[] outbuf = new byte[BUFFER_SIZE];
|
|
private int inpos = 0;
|
|
private int inlen = 0;
|
|
private int outpos = 0;
|
|
private int outlen = 0;
|
|
|
|
private boolean signed;
|
|
private int samplesize;
|
|
private boolean bigendian;
|
|
|
|
/**
|
|
* Constructs new EqualizerInputStream object
|
|
*
|
|
* @param stream is an input stream for pcm data
|
|
* @param samplerate is a sample rate of input data
|
|
* @param channels is the number of channels
|
|
* @param signed indicates that the data is signed
|
|
* @param samplesize is the sample bit size of data
|
|
* @param bigendian indicates that the dat is in "big endian" encoding
|
|
* @param bands is the number of bands
|
|
*/
|
|
public EqualizerInputStream(InputStream stream, float samplerate, int channels, boolean signed, int samplesize, boolean bigendian, int bands) {
|
|
this.stream = stream;
|
|
this.iir = new IIR(bands, samplerate, channels);
|
|
this.signed = signed;
|
|
this.samplesize = samplesize;
|
|
this.bigendian = bigendian;
|
|
|
|
if (!isParamsSupported(samplerate, channels, samplesize, bands))
|
|
throw new IllegalArgumentException("Unsupported sample bit size");
|
|
}
|
|
|
|
/**
|
|
* Returns Controls of equalizer
|
|
*
|
|
* @return Controls of equalizer
|
|
*/
|
|
public IIRControls getControls() {
|
|
return iir.getControls();
|
|
}
|
|
|
|
/**
|
|
* This is special method for checking of supported parameters of equalizer
|
|
*
|
|
* @param bands is the number of bands
|
|
* @param samplerate is the sample rate of data
|
|
* @param channels is the number of channels
|
|
* @param samplesize is the size of sample in bits
|
|
* @return true if parameters are supported
|
|
*/
|
|
public static boolean isParamsSupported(float samplerate, int channels, int samplesize, int bands) {
|
|
switch (samplesize) {
|
|
case 8:
|
|
case 16:
|
|
case 24:
|
|
case 32:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return IIR.isParamsSupported(bands, samplerate, channels);
|
|
}
|
|
|
|
private boolean fillInBuffer() throws IOException {
|
|
if (inpos != 0 && inlen > 0)
|
|
System.arraycopy(inbuf, inpos, inbuf, 0, inlen);
|
|
inpos = 0;
|
|
int num;
|
|
boolean eof = false;
|
|
while (inlen != inbuf.length) {
|
|
num = stream.read(inbuf, inlen, inbuf.length - inlen);
|
|
if (num < 0) {
|
|
eof = true;
|
|
break;
|
|
}
|
|
inlen += num;
|
|
}
|
|
return eof;
|
|
}
|
|
|
|
private void fillOutBuffer() {
|
|
if (outpos != 0 && outlen > 0)
|
|
System.arraycopy(outbuf, outpos, outbuf, 0, outlen);
|
|
outpos = 0;
|
|
int len = outbuf.length - outlen;
|
|
len = inlen < len ? inlen : len;
|
|
len = convertToInt(len);
|
|
if (len > 0) {
|
|
iir.iir(workbuf, len);
|
|
len = convertToByte(outbuf, outlen, len);
|
|
outlen += len;
|
|
}
|
|
}
|
|
|
|
private int convertToInt(int length) {
|
|
int l = length;
|
|
int temp;
|
|
byte a1[];
|
|
switch (samplesize) {
|
|
case 8: {
|
|
if (length > 0) {
|
|
System.arraycopy(inbuf, 0, workbuf, 0, length);
|
|
inpos += length;
|
|
inlen -= length;
|
|
}
|
|
break;
|
|
}
|
|
case 16: {
|
|
l = length >> 1;
|
|
if (l > 0) {
|
|
if (bigendian)
|
|
for (int i = 0; i < l; i++) {
|
|
temp = (((a1 = inbuf)[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff);
|
|
workbuf[i] = signed && temp > 32767 ? temp - 65536 : temp;
|
|
}
|
|
else
|
|
for (int i = 0; i < l; i++) {
|
|
temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8);
|
|
workbuf[i] = signed && temp > 32767 ? temp - 65536 : temp;
|
|
}
|
|
inlen -= inpos;
|
|
}
|
|
break;
|
|
}
|
|
case 24: {
|
|
l = length / 3;
|
|
if (l > 0) {
|
|
if (bigendian)
|
|
for (int i = 0; i < l; i++) {
|
|
temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8) | ((a1[inpos++] & 0xff) << 16);
|
|
workbuf[i] = signed && temp > 8388607 ? temp - 16777216 : temp;
|
|
}
|
|
else
|
|
for (int i = 0; i < l; i++) {
|
|
temp = (((a1 = inbuf)[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff);
|
|
workbuf[i] = signed && temp > 8388607 ? temp - 16777216 : temp;
|
|
}
|
|
inlen -= inpos;
|
|
}
|
|
break;
|
|
}
|
|
case 32: {
|
|
l = length / 4;
|
|
if (l > 0) {
|
|
if (bigendian)
|
|
for (int i = 0; i < l; i++) {
|
|
temp = ((a1 = inbuf)[inpos++] & 0xff) | ((a1[inpos++] & 0xff) << 8) | ((a1[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 24);
|
|
workbuf[i] = temp; //signed && temp > 8388607 ? temp - 16777216 : temp;
|
|
}
|
|
else
|
|
for (int i = 0; i < l; i++) {
|
|
temp = (((a1 = inbuf)[inpos++] & 0xff) << 24) | ((a1[inpos++] & 0xff) << 16) | ((a1[inpos++] & 0xff) << 8) | (a1[inpos++] & 0xff);
|
|
workbuf[i] = temp; //signed && temp > 8388607 ? temp - 16777216 : temp;
|
|
}
|
|
inlen -= inpos;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return l;
|
|
}
|
|
|
|
private int wrap8Bit(int data) {
|
|
if (data > 127)
|
|
data = 127;
|
|
else if (data < -128)
|
|
data = -128;
|
|
if (data < 0)
|
|
data += 256;
|
|
return data;
|
|
}
|
|
|
|
private int wrap16Bit(int data) {
|
|
if (data > 32767)
|
|
data = 32767;
|
|
else if (data < -32768)
|
|
data = -32768;
|
|
if (data < 0)
|
|
data += 65536;
|
|
return data;
|
|
}
|
|
|
|
private int wrap24Bit(int data) {
|
|
if (data > 8388607)
|
|
data = 8388607;
|
|
else if (data < -8388608)
|
|
data = -8388608;
|
|
if (data < 0)
|
|
data += 16777216;
|
|
return data;
|
|
}
|
|
|
|
private int convertToByte(byte[] b, int off, int length) {
|
|
int p = off;
|
|
int d;
|
|
switch (samplesize) {
|
|
case 8: {
|
|
for (int i = 0; i < length; i++)
|
|
b[p++] = (byte) (wrap8Bit(workbuf[i]) & 0xff);
|
|
break;
|
|
}
|
|
case 16: {
|
|
if (bigendian) {
|
|
for (int i = 0; i < length; i++) {
|
|
d = wrap16Bit(workbuf[i]);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
b[p++] = (byte) (d & 0xff);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < length; i++) {
|
|
d = wrap16Bit(workbuf[i]);
|
|
b[p++] = (byte) (d & 0xff);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 24: {
|
|
if (bigendian) {
|
|
for (int i = 0; i < length; i++) {
|
|
d = wrap24Bit(workbuf[i]);
|
|
b[p++] = (byte) (d & 0xff);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
b[p++] = (byte) ((d & 0xff0000) >> 16);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < length; i++) {
|
|
d = wrap24Bit(workbuf[i]);
|
|
b[p++] = (byte) ((d & 0xff0000) >> 16);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
b[p++] = (byte) (d & 0xff);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 32: {
|
|
if (bigendian) {
|
|
for (int i = 0; i < length; i++) {
|
|
d = workbuf[i];
|
|
b[p++] = (byte) (d & 0xff);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
b[p++] = (byte) ((d & 0xff0000) >> 16);
|
|
b[p++] = (byte) ((d & 0xff000000) >> 24);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < length; i++) {
|
|
d = wrap24Bit(workbuf[i]);
|
|
b[p++] = (byte) ((d & 0xff000000) >> 24);
|
|
b[p++] = (byte) ((d & 0xff0000) >> 16);
|
|
b[p++] = (byte) ((d & 0xff00) >> 8);
|
|
b[p++] = (byte) (d & 0xff);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
return p - off;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of bytes that can be read (or skipped over) from
|
|
* this input stream without blocking by the next caller of a method for
|
|
* this input stream. The next caller might be the same thread or
|
|
* another thread.
|
|
*
|
|
* @return the number of bytes that can be read from this input stream
|
|
* without blocking.
|
|
* @throws IOException if an I/O error occurs.
|
|
*/
|
|
public int available() throws IOException {
|
|
return outlen;
|
|
}
|
|
|
|
/**
|
|
* Closes this input stream and releases any system resources associated
|
|
* with the stream.
|
|
*
|
|
* @throws IOException if an I/O error occurs.
|
|
*/
|
|
public void close() throws IOException {
|
|
stream.close();
|
|
}
|
|
|
|
/**
|
|
* <p> The <code>mark</code> method of <code>EqualizerInputStream</code> does
|
|
* nothing.
|
|
*
|
|
* @param readlimit the maximum limit of bytes that can be read before
|
|
* the mark position becomes invalid.
|
|
*/
|
|
public synchronized void mark(int readlimit) {
|
|
}
|
|
|
|
/**
|
|
* Tests if this input stream supports the <code>mark</code> and
|
|
* <code>reset</code> methods. Whether or not <code>mark</code> and
|
|
* <code>reset</code> are supported is an invariant property of a
|
|
* particular input stream instance. The <code>markSupported</code> method
|
|
* of <code>EqualizerInputStream</code> returns <code>false</code>.
|
|
*
|
|
* @return false
|
|
*/
|
|
public boolean markSupported() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Reads the next byte of data from the input stream. The value byte is
|
|
* returned as an <code>int</code> in the range <code>0</code> to
|
|
* <code>255</code>. If no byte is available because the end of the stream
|
|
* has been reached, the value <code>-1</code> is returned. This method
|
|
* blocks until input data is available, the end of the stream is detected,
|
|
* or an exception is thrown.
|
|
*
|
|
* @return the next byte of data, or <code>-1</code> if the end of the
|
|
* stream is reached.
|
|
* @throws IOException if an I/O error occurs.
|
|
*/
|
|
public int read() throws IOException {
|
|
if (outlen == 0) {
|
|
boolean eof = fillInBuffer();
|
|
fillOutBuffer();
|
|
if (outlen == 0 && eof)
|
|
return -1;
|
|
if (outlen == 0 && !eof)
|
|
throw new IOException("Impossible state");
|
|
}
|
|
int b = outbuf[outpos++];
|
|
outlen--;
|
|
return b;
|
|
}
|
|
|
|
/**
|
|
* Reads up to <code>len</code> bytes of data from the input stream into
|
|
* an array of bytes. An attempt is made to read as many as
|
|
* <code>len</code> bytes, but a smaller number may be read.
|
|
* The number of bytes actually read is returned as an integer.
|
|
* <p/>
|
|
* <p> This method blocks until input data is available, end of file is
|
|
* detected, or an exception is thrown.
|
|
* <p/>
|
|
* <p> If <code>b</code> is <code>null</code>, a
|
|
* <code>NullPointerException</code> is thrown.
|
|
* <p/>
|
|
* <p> If <code>off</code> is negative, or <code>len</code> is negative, or
|
|
* <code>off+len</code> is greater than the length of the array
|
|
* <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
|
|
* thrown.
|
|
* <p/>
|
|
* <p> If <code>len</code> is zero, then no bytes are read and
|
|
* <code>0</code> is returned; otherwise, there is an attempt to read at
|
|
* least one byte. If no byte is available because the stream is at end of
|
|
* file, the value <code>-1</code> is returned; otherwise, at least one
|
|
* byte is read and stored into <code>b</code>.
|
|
* <p/>
|
|
* <p> The first byte read is stored into element <code>b[off]</code>, the
|
|
* next one into <code>b[off+1]</code>, and so on. The number of bytes read
|
|
* is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
|
|
* bytes actually read; these bytes will be stored in elements
|
|
* <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
|
|
* leaving elements <code>b[off+</code><i>k</i><code>]</code> through
|
|
* <code>b[off+len-1]</code> unaffected.
|
|
* <p/>
|
|
* <p> In every case, elements <code>b[0]</code> through
|
|
* <code>b[off]</code> and elements <code>b[off+len]</code> through
|
|
* <code>b[b.length-1]</code> are unaffected.
|
|
* <p/>
|
|
* <p> If the first byte cannot be read for any reason other than end of
|
|
* file, then an <code>IOException</code> is thrown. In particular, an
|
|
* <code>IOException</code> is thrown if the input stream has been closed.
|
|
*
|
|
* @param b the buffer into which the data is read.
|
|
* @param off the start offset in array <code>b</code>
|
|
* at which the data is written.
|
|
* @param len the maximum number of bytes to read.
|
|
* @return the total number of bytes read into the buffer, or
|
|
* <code>-1</code> if there is no more data because the end of
|
|
* the stream has been reached.
|
|
* @throws IOException if an I/O error occurs.
|
|
* @throws NullPointerException if <code>b</code> is <code>null</code>.
|
|
*/
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
if (outlen < len) {
|
|
boolean eof = fillInBuffer();
|
|
fillOutBuffer();
|
|
if (outlen == 0 && eof)
|
|
return -1;
|
|
if (outlen == 0 && !eof)
|
|
throw new IOException("Impossible state");
|
|
}
|
|
len = outlen < len ? outlen : len;
|
|
if (len > 0) {
|
|
System.arraycopy(outbuf, outpos, b, off, len);
|
|
outpos += len;
|
|
outlen -= len;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* <p>The method <code>reset</code> for class <code>EqualizerInputStream</code>
|
|
* does nothing except throw an <code>IOException</code>.
|
|
*
|
|
* @throws IOException as an indication that the mark feature doesn't supported by EqualizerInputStream.
|
|
*/
|
|
public void reset() throws IOException {
|
|
throw new IOException("mark/reset not supported");
|
|
}
|
|
|
|
/**
|
|
* Skips over and discards <code>n</code> bytes of data from this input
|
|
* stream. The <code>skip</code> method may, for a variety of reasons, end
|
|
* up skipping over some smaller number of bytes, possibly <code>0</code>.
|
|
* This may result from any of a number of conditions; reaching end of file
|
|
* before <code>n</code> bytes have been skipped is only one possibility.
|
|
* The actual number of bytes skipped is returned. If <code>n</code> is
|
|
* negative, no bytes are skipped.
|
|
*
|
|
* @param n the number of bytes to be skipped.
|
|
* @return the actual number of bytes skipped.
|
|
* @throws IOException if an I/O error occurs.
|
|
*/
|
|
public long skip(long n) throws IOException {
|
|
int l;
|
|
if (n <= outlen) {
|
|
outpos += n;
|
|
outlen -= n;
|
|
return n;
|
|
}
|
|
n -= outlen;
|
|
l = outlen;
|
|
outlen = 0;
|
|
outpos = 0;
|
|
if (n <= inlen) {
|
|
inpos += n;
|
|
inlen -= n;
|
|
return l + n;
|
|
}
|
|
n -= inlen;
|
|
l += inlen;
|
|
inlen = 0;
|
|
inpos = 0;
|
|
return stream.skip(n) + l;
|
|
}
|
|
|
|
}
|