/**
* Copyright 2004-2006 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* This file is part of MARY TTS.
*
* MARY TTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package marytts.util.data;
import marytts.signalproc.process.InlineDataProcessor;
/**
* @author Marc Schröder
*
*/
public class BufferedDoubleDataSource extends BaseDoubleDataSource {
public static final int DEFAULT_BUFFERSIZE = 8192;
protected double[] buf;
protected int readPos = 0;
protected int writePos = 0;
protected InlineDataProcessor dataProcessor = null;
public BufferedDoubleDataSource(double[] inputData) {
this(inputData, null);
}
public BufferedDoubleDataSource(double[] inputData, InlineDataProcessor dataProcessor) {
super();
buf = new double[inputData.length];
System.arraycopy(inputData, 0, buf, 0, buf.length);
writePos = buf.length;
dataLength = buf.length;
this.dataProcessor = dataProcessor;
if (dataProcessor != null)
dataProcessor.applyInline(buf, 0, writePos);
}
public BufferedDoubleDataSource(DoubleDataSource inputSource) {
this(inputSource, null);
}
public BufferedDoubleDataSource(DoubleDataSource inputSource, InlineDataProcessor dataProcessor) {
super(inputSource);
buf = new double[DEFAULT_BUFFERSIZE];
this.dataProcessor = dataProcessor;
}
/**
* Whether or not any more data can be read from this data source.
*
* @return true if another call to getData() will return data, false otherwise.
*/
public boolean hasMoreData() {
if (currentlyInBuffer() > 0 || inputSource != null && inputSource.hasMoreData())
return true;
return false;
}
/**
* Amount of data currently in the buffer. If hasMoreData() returns true, this number may increase.
*
* @return the number of doubles that can currently be read without recourse to an input source.
*/
public int currentlyInBuffer() {
assert writePos >= readPos;
return writePos - readPos;
}
/**
* The number of doubles that can currently be read from this double data source without blocking. This number can change over
* time.
*
* @return the number of doubles that can currently be read without blocking
*/
public int available() {
int available = currentlyInBuffer();
if (inputSource != null)
available += inputSource.available();
return available;
}
protected int bufferSpaceLeft() {
return buf.length - currentlyInBuffer();
}
/**
* Try to get length doubles from this DoubleDataSource, and copy them into target, starting from targetPos.
*
* @param target
* the double array to write into
* @param targetPos
* position in target where to start writing
* @param length
* the amount of data requested
* @return the amount of data actually delivered. If the returned value is less than length, only that many data items have
* been copied into target; further calls will return 0 and not copy anything.
*/
@Override
public int getData(double[] target, int targetPos, int length) {
// if (target.length < targetPos+length)
// throw new IllegalArgumentException("Not enough space left in target array");
if (currentlyInBuffer() < length) { // first need to try and read some more data
readIntoBuffer(length - currentlyInBuffer());
}
int toDeliver = length;
if (currentlyInBuffer() < length)
toDeliver = currentlyInBuffer();
System.arraycopy(buf, readPos, target, targetPos, toDeliver);
readPos += toDeliver;
return toDeliver;
}
/**
* Attempt to get more data from the input source. If less than this can be read, the possible amount will be read, but
* canReadMore() will return false afterwards.
*
* @param minLength
* the amount of data to get from the input source
* @return true if the requested amount could be read, false if none or less data could be read.
*/
protected boolean readIntoBuffer(int minLength) {
if (inputSource == null) {
return false;
}
if (!inputSource.hasMoreData()) {
return false;
}
if (bufferSpaceLeft() < minLength) {
// current buffer cannot hold the data requested;
// need to make it larger
increaseBufferSize(minLength + currentlyInBuffer());
} else if (buf.length - writePos < minLength) {
compact(); // create a contiguous space for the new data
}
// Now we have a buffer that can hold at least minLength new data points
int readSum = 0;
readSum = inputSource.getData(buf, writePos, minLength);
writePos += readSum;
if (dataProcessor != null) {
dataProcessor.applyInline(buf, writePos - readSum, readSum);
}
return readSum == minLength;
}
/**
* Increase the underlying buffer array in size, so that the new size is at least minSize
*
* @param minSize
* the minimum new size of the array.
*/
protected void increaseBufferSize(int minSize) {
int newLength = buf.length;
while (newLength < minSize)
newLength *= 2;
double[] newBuf = new double[newLength];
int avail = currentlyInBuffer();
System.arraycopy(buf, readPos, newBuf, 0, avail);
buf = newBuf;
readPos = 0;
writePos = avail;
}
/**
* Compact the buffer, so that the data in the buffer starts at the beginning of the underlying array.
*
*/
protected void compact() {
if (readPos == 0)
return;
int avail = writePos - readPos;
System.arraycopy(buf, readPos, buf, 0, avail);
readPos = 0;
writePos = avail;
}
}