package edu.cmu.sphinx.frontend.databranch;
import edu.cmu.sphinx.frontend.BaseDataProcessor;
import edu.cmu.sphinx.frontend.Data;
import edu.cmu.sphinx.frontend.DataProcessingException;
import edu.cmu.sphinx.util.props.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* A FIFO-buffer for <code>Data</code>-elements.
* <p>
* <code>Data</code>s are inserted to the buffer using the <code>processDataFrame</code>-method.
*/
public class DataBufferProcessor extends BaseDataProcessor implements DataListener {
/** The FIFO- data buffer. */
private final List<Data> featureBuffer = new LinkedList<Data>();
/**
* If this property is set <code>true</code> the buffer will wait for new data until it returns from a
* <code>getData</code>-call. Enable this flag if the buffer should serve as starting point for a new
* feature-pull-chain..
*/
@S4Boolean(defaultValue = false)
public static final String PROP_WAIT_IF_EMPTY = "waitIfEmpty";
private boolean waitIfEmpty;
/**
* The time in milliseconds which will be waited between two attempts to read a data element from the buffer when
* being in <code>waitIfEmpty</code>-mode
*/
@S4Integer(defaultValue = 10)
public static final String PROP_WAIT_TIME_MS = "waitTimeMs";
private long waitTime;
/** The maximal size of the buffer in frames. The oldest frames will be removed if the buffer grows out of bounds. */
@S4Integer(defaultValue = 50000)
public static final String PROP_BUFFER_SIZE = "maxBufferSize";
private int maxBufferSize;
@S4ComponentList(type = Configurable.class, beTolerant = true)
public static final String DATA_LISTENERS = "dataListeners";
private List<DataListener> dataListeners = new ArrayList<DataListener>();
/**
* @param maxBufferSize The maximal size of the buffer in frames. The oldest frames will be removed if the buffer grows out of bounds.
* @param waitIfEmpty If this property is set <code>true</code> the buffer will wait for new data until it returns from a
* <code>getData</code>-call. Enable this flag if the buffer should serve as starting point for a new
* feature-pull-chain.
* @param waitTime The time in milliseconds which will be waited between two attempts to read a data element from the buffer when
* being in <code>waitIfEmpty</code>-mode
* @param listeners listeners to get notified
*/
public DataBufferProcessor(int maxBufferSize, boolean waitIfEmpty, int waitTime, List<? extends Configurable> listeners) {
initLogger();
this.maxBufferSize = maxBufferSize;
this.waitIfEmpty = waitIfEmpty;
if (waitIfEmpty) // if false we don't need the value
this.waitTime = waitTime;
for (Configurable configurable : listeners) {
assert configurable instanceof DataListener;
addDataListener((DataListener) configurable);
}
}
public DataBufferProcessor() {
}
@Override
public void newProperties(PropertySheet ps) throws PropertyException {
super.newProperties(ps);
maxBufferSize = ps.getInt(PROP_BUFFER_SIZE);
waitIfEmpty = ps.getBoolean(PROP_WAIT_IF_EMPTY);
if (waitIfEmpty) // if false we don't need the value
waitTime = ps.getInt(PROP_WAIT_TIME_MS);
dataListeners = ps.getComponentList(DATA_LISTENERS, DataListener.class);
}
public void processDataFrame(Data data) {
featureBuffer.add(data);
// inform data-listeners if necessary
for (DataListener dataListener : dataListeners) {
dataListener.processDataFrame(data);
}
//reduce the buffer-size if necessary
while (featureBuffer.size() > maxBufferSize) {
featureBuffer.remove(0);
}
}
/**
* Returns the processed Data output.
*
* @return an Data object that has been processed by this DataProcessor
* @throws edu.cmu.sphinx.frontend.DataProcessingException
* if a data processor error occurs
*/
@Override
public Data getData() throws DataProcessingException {
Data data = null;
while (waitIfEmpty && featureBuffer.isEmpty()) {
try {
Thread.sleep(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!featureBuffer.isEmpty()) {
data = featureBuffer.remove(0);
} else {
assert !waitIfEmpty;
}
return data;
}
public int getBufferSize() {
return featureBuffer.size();
}
public void clearBuffer() {
featureBuffer.clear();
}
public List<Data> getBuffer() {
return Collections.unmodifiableList(featureBuffer);
}
/** Adds a new listener.
* @param l listener to add
*/
public void addDataListener(DataListener l) {
if (l == null)
return;
dataListeners.add(l);
}
/** Removes a listener.
* @param l listener to remove
*/
public void removeDataListener(DataListener l) {
if (l == null)
return;
dataListeners.remove(l);
}
}