/** * Copyright 2009 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 java.util.concurrent.ArrayBlockingQueue; import marytts.signalproc.process.InlineDataProcessor; /** * @author marc * */ public abstract class ProducingDoubleDataSource extends BufferedDoubleDataSource implements Runnable { private static final Double END_OF_STREAM = Double.NEGATIVE_INFINITY; protected ArrayBlockingQueue<Double> queue = new ArrayBlockingQueue<Double>(1024); private Thread dataProducingThread = null; private boolean hasSentEndOfStream = false; private boolean hasReceivedEndOfStream = false; protected ProducingDoubleDataSource() { this(DoubleDataSource.NOT_SPECIFIED); } protected ProducingDoubleDataSource(long numDataThatWillBeProduced) { this(numDataThatWillBeProduced, null); } protected ProducingDoubleDataSource(InlineDataProcessor dataProcessor) { this(DoubleDataSource.NOT_SPECIFIED, dataProcessor); } protected ProducingDoubleDataSource(long numDataThatWillBeProduced, InlineDataProcessor dataProcessor) { super((DoubleDataSource) null, dataProcessor); this.dataLength = numDataThatWillBeProduced; } public void start() { dataProducingThread = new Thread(this); dataProducingThread.setDaemon(true); dataProducingThread.start(); } /** * Subclasses must implement this method such that it produces data and sends it through {@link #putOneDataPoint(double)}. * When all data is sent, the subclass must call {@link #putEndOfStream()} exactly once. */ public abstract void run(); /** * The producing thread tries to put ont data item into the queue. * * @param value * value * @throws RuntimeException * runtime exception */ public void putOneDataPoint(double value) { try { queue.put(value); } catch (InterruptedException e) { throw new RuntimeException("Unexpected interruption", e); } } protected void putEndOfStream() { putOneDataPoint(END_OF_STREAM); hasSentEndOfStream = true; } @Override public boolean hasMoreData() { checkStarted(); return !isAllProductionDataRead() || available() > 0; } @Override public int available() { checkStarted(); return currentlyInBuffer() + currentlyInQueue(); } private int currentlyInQueue() { if (isAllProductionDataRead()) { return 0; } int inQueue = queue.size(); if (hasSentEndOfStream && !hasReceivedEndOfStream) { inQueue -= 1; } return inQueue; } @Override protected boolean readIntoBuffer(int minLength) { checkStarted(); if (isAllProductionDataRead()) { 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; while (readSum < minLength) { double data = getOneDataPoint(); if (data == END_OF_STREAM) { hasReceivedEndOfStream = true; break; } buf[writePos] = data; writePos++; readSum++; } if (dataProcessor != null) { dataProcessor.applyInline(buf, writePos - readSum, readSum); } return readSum == minLength; } /** * The reading thread tries to get one data item from the queue. * * @return queue.take */ private double getOneDataPoint() { try { return queue.take(); } catch (InterruptedException e) { throw new RuntimeException("Unexpected interruption", e); } } /** * @throws IllegalStateException */ private void checkStarted() throws IllegalStateException { if (!isStarted()) { throw new IllegalStateException("Producer thread has not been started -- call start()"); } } private boolean isStarted() { return dataProducingThread != null; } private boolean isAllProductionDataRead() { return hasReceivedEndOfStream; } }