/*
* Copyright 1999-2002 Carnegie Mellon University.
* Portions Copyright 2002 Sun Microsystems, Inc.
* Portions Copyright 2002 Mitsubishi Electric Research Laboratories.
* All Rights Reserved. Use is subject to license terms.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
*
* adapted by Christophe Cerisara june 2007
*
*/
package edu.cmu.sphinx.frontend.util;
import edu.cmu.sphinx.frontend.*;
import edu.cmu.sphinx.util.Utilities;
import edu.cmu.sphinx.util.props.*;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Produces Mel-cepstrum data from an InputStream. To set the input stream with cepstral data, use the {@link
* #setInputStream(InputStream) setInputStream} method, and then call {@link #getData} to obtain the Data
* objects that have cepstrum data in it.
*/
public class StreamHTKCepstrum extends BaseDataProcessor {
/** The property specifying whether the input is in binary. */
@S4Boolean(defaultValue = true)
public final static String PROP_BINARY = "binary";
/** The property specifying whether the input is big endian. */
@S4Boolean(defaultValue = true)
public final static String PROP_BIGENDIAN = "bigEndian";
/** The property name for frame size in milliseconds. */
@S4Double(defaultValue = 25.625)
public static final String PROP_FRAME_SIZE_MS = "frameSizeInMs";
/** The property name for frame shift in milliseconds, which has a default value of 10F. */
@S4Double(defaultValue = 10.0)
public static final String PROP_FRAME_SHIFT_MS = "frameShiftInMs";
/** The property specifying the length of the cepstrum data. */
@S4Integer(defaultValue = 13)
public static final String PROP_CEPSTRUM_LENGTH = "cepstrumLength";
/** The property that defines the sample rate */
@S4Integer(defaultValue = 16000)
public static final String PROP_SAMPLE_RATE = "sampleRate";
private DataInputStream binaryStream; // for binary files
private int numPoints;
private int curPoint;
private int cepstrumLength;
private int frameShift;
private int frameSize;
private int sampleRate;
private long firstSampleNumber;
private boolean bigEndian;
public StreamHTKCepstrum( float frameShiftMs, float frameSizeMs, boolean bigEndian, int sampleRate ) {
initLogger();
this.bigEndian = bigEndian;
this.sampleRate = sampleRate;
this.frameShift = DataUtil.getSamplesPerWindow(sampleRate, frameShiftMs);
this.frameSize = DataUtil.getSamplesPerShift(sampleRate, frameSizeMs);
}
public StreamHTKCepstrum( ) {
}
/*
* (non-Javadoc)
*
* @see edu.cmu.sphinx.util.props.Configurable#newProperties(edu.cmu.sphinx.util.props.PropertySheet)
*/
@Override
public void newProperties(PropertySheet ps) throws PropertyException {
super.newProperties(ps);
float frameShiftMs = ps.getFloat(PROP_FRAME_SHIFT_MS
);
float frameSizeMs = ps.getFloat(PROP_FRAME_SIZE_MS
);
bigEndian = ps.getBoolean(PROP_BIGENDIAN);
sampleRate = ps.getInt(PROP_SAMPLE_RATE);
frameShift = DataUtil.getSamplesPerWindow(sampleRate, frameShiftMs);
frameSize = DataUtil.getSamplesPerShift(sampleRate, frameSizeMs);
logger = ps.getLogger();
}
/** Constructs a StreamCepstrumSource that reads MelCepstrum data from the given path. */
@Override
public void initialize() {
super.initialize();
curPoint = -1;
firstSampleNumber = 0;
// we don't want any more that endianness be set at the upper level, as BatchReco
// utilizes an ad-hoc procedure to decide on endianness.. We want it to be fixed in the config file
//bigEndian = true;
}
/**
* Sets the InputStream to read cepstral data from.
*
* @param stream the InputStream to read cepstral data from
* @throws IOException if an I/O error occurs
*/
public void setInputStream(InputStream stream)
throws IOException {
{
// TODO: update sampleRate
binaryStream = new DataInputStream(new BufferedInputStream(stream));
if (bigEndian) {
numPoints = binaryStream.readInt();
int sampPeriod = binaryStream.readInt();
short sampSize = binaryStream.readShort();
short parmKind = binaryStream.readShort();
cepstrumLength = sampSize / 4;
numPoints *= cepstrumLength;
logger.info("Sample period is " + sampPeriod);
logger.info("Sample size " + sampSize);
logger.info("Parameter kind " + parmKind);
logger.info("BigEndian");
} else {
numPoints = Utilities.readLittleEndianInt(binaryStream);
int sampPeriod = Utilities.readLittleEndianInt(binaryStream);
short sampSize = readLittleEndianShort(binaryStream);
short parmKind = readLittleEndianShort(binaryStream);
cepstrumLength = sampSize/4;
numPoints *= cepstrumLength;
logger.info("Sample period is " + sampPeriod);
logger.info("Sample size " + sampSize);
logger.info("Parameter kind " + parmKind);
logger.info("LittleEndian");
}
System.out.println("Frames: " + numPoints / cepstrumLength);
}
curPoint = -1;
firstSampleNumber = 0;
}
public static short readLittleEndianShort(DataInputStream dataStream)
throws IOException {
short bits = 0x0000;
for (int shift = 0; shift < 16; shift += 8) {
int byteRead = (0x00ff & dataStream.readByte());
bits |= (byteRead << shift);
}
return bits;
}
/**
* Returns the next Data object, which is the mel cepstrum of the input frame. However, it can also be other Data
* objects like DataStartSignal.
*
* @return the next available Data object, returns null if no Data object is available
* @throws DataProcessingException if a data processing error occurs
*/
@Override
public Data getData() throws DataProcessingException {
Data data;
if (curPoint == -1) {
data = new DataStartSignal(sampleRate);
curPoint++;
} else if (curPoint == numPoints) {
if (numPoints > 0) {
firstSampleNumber =
(firstSampleNumber - frameShift + frameSize - 1);
}
// send a DataEndSignal
int numberFrames = curPoint / cepstrumLength;
int totalSamples = (numberFrames - 1) * frameShift + frameSize;
long duration = (long)
(((double) totalSamples / (double) sampleRate) * 1000.0);
data = new DataEndSignal(duration);
try {
binaryStream.close();
curPoint++;
} catch (IOException ioe) {
throw new DataProcessingException("IOException closing cepstrum stream", ioe);
}
} else if (curPoint > numPoints) {
data = null;
} else {
double[] vectorData = new double[cepstrumLength];
for (int i = 0; i < cepstrumLength; i++) {
try {
if (bigEndian) {
vectorData[i] = binaryStream.readFloat();
} else {
vectorData[i] = Utilities.readLittleEndianFloat(binaryStream);
}
curPoint++;
} catch (IOException ioe) {
throw new DataProcessingException("IOException reading from cepstrum stream", ioe);
}
}
// System.out.println("Read: " + curPoint);
data = new DoubleData
(vectorData, sampleRate, firstSampleNumber);
firstSampleNumber += frameShift;
// System.out.println(data);
}
return data;
}
}