/* * 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. * */ package edu.cmu.sphinx.frontend.feature; import edu.cmu.sphinx.frontend.*; import edu.cmu.sphinx.frontend.endpoint.*; import edu.cmu.sphinx.util.props.*; import java.util.*; /** * Abstract base class for windowed feature extractors like DeltasFeatureExtractor, ConcatFeatureExtractor * or S3FeatureExtractor. The main purpose of this it to collect window size cepstra frames in a buffer * and let the extractor compute the feature frame with them. */ public abstract class AbstractFeatureExtractor extends BaseDataProcessor { /** The property for the window of the DeltasFeatureExtractor. */ @S4Integer(defaultValue = 3) public static final String PROP_FEATURE_WINDOW = "windowSize"; private int bufferPosition; private Signal pendingSignal; private LinkedList<Data> outputQueue; protected int cepstraBufferEdge; protected int window; protected int currentPosition; protected int cepstraBufferSize; protected DoubleData[] cepstraBuffer; public AbstractFeatureExtractor(int window) { initLogger(); this.window = window; } public AbstractFeatureExtractor() { } /* * (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); window = ps.getInt(PROP_FEATURE_WINDOW); } /* * (non-Javadoc) * * @see edu.cmu.sphinx.frontend.DataProcessor#initialize(edu.cmu.sphinx.frontend.CommonConfig) */ @Override public void initialize() { super.initialize(); cepstraBufferSize = 256; cepstraBuffer = new DoubleData[cepstraBufferSize]; cepstraBufferEdge = cepstraBufferSize - (window * 2 + 2); outputQueue = new LinkedList<Data>(); reset(); } /** Resets the DeltasFeatureExtractor to be ready to read the next segment of data. */ private void reset() { bufferPosition = 0; currentPosition = 0; } /** * Returns the next Data object produced by this DeltasFeatureExtractor. * * @return the next available Data object, returns null if no Data is available * @throws DataProcessingException if there is a data processing error */ @Override public Data getData() throws DataProcessingException { if (outputQueue.isEmpty()) { Data input = getNextData(); if (input != null) { if (input instanceof DoubleData) { addCepstrum((DoubleData) input); computeFeatures(1); } else if (input instanceof DataStartSignal) { pendingSignal = null; outputQueue.add(input); Data start = getNextData(); int n = processFirstCepstrum(start); computeFeatures(n); if (pendingSignal != null) { outputQueue.add(pendingSignal); } } else if (input instanceof SpeechEndSignal) { // when the DataEndSignal is right at the boundary int n = replicateLastCepstrum(); computeFeatures(n); outputQueue.add(input); } else if (input instanceof DataEndSignal) { outputQueue.add(input); } } } return outputQueue.isEmpty() ? null : outputQueue.removeFirst(); } private Data getNextData() throws DataProcessingException { Data d = getPredecessor().getData(); while (d != null && !(d instanceof DoubleData || d instanceof DataEndSignal || d instanceof DataStartSignal || d instanceof SpeechEndSignal)) { outputQueue.add(d); d = getPredecessor().getData(); } return d; } /** * Replicate the given cepstrum Data object into the first window+1 number of frames in the cepstraBuffer. This is * the first cepstrum in the segment. * * @param cepstrum the Data to replicate * @return the number of Features that can be computed * @throws edu.cmu.sphinx.frontend.DataProcessingException */ private int processFirstCepstrum(Data cepstrum) throws DataProcessingException { if (cepstrum instanceof DataEndSignal) { outputQueue.add(cepstrum); return 0; } else if (cepstrum instanceof DataStartSignal) { throw new Error("Too many UTTERANCE_START"); } else { // At the start of an utterance, we replicate the first frame // into window+1 frames, and then read the next "window" number // of frames. This will allow us to compute the delta- // double-delta of the first frame. Arrays.fill(cepstraBuffer, 0, window + 1, cepstrum); bufferPosition = window + 1; bufferPosition %= cepstraBufferSize; currentPosition = window; currentPosition %= cepstraBufferSize; int numberFeatures = 1; pendingSignal = null; for (int i = 0; i < window; i++) { Data next = getNextData(); if (next != null) { if (next instanceof DoubleData) { // just a cepstra addCepstrum((DoubleData) next); } else if (next instanceof DataEndSignal || next instanceof SpeechEndSignal) { // end of segment cepstrum pendingSignal = (Signal) next; replicateLastCepstrum(); numberFeatures += i; break; } else if (next instanceof DataStartSignal) { throw new Error("Too many UTTERANCE_START"); } } } return numberFeatures; } } /** * Adds the given DoubleData object to the cepstraBuffer. * * @param cepstrum the DoubleData object to add */ private void addCepstrum(DoubleData cepstrum) { cepstraBuffer[bufferPosition++] = cepstrum; bufferPosition %= cepstraBufferSize; } /** * Replicate the last frame into the last window number of frames in the cepstraBuffer. * * @return the number of replicated Cepstrum */ private int replicateLastCepstrum() { DoubleData last; if (bufferPosition > 0) { last = cepstraBuffer[bufferPosition - 1]; } else if (bufferPosition == 0) { last = cepstraBuffer[cepstraBuffer.length - 1]; } else { throw new Error("BufferPosition < 0"); } for (int i = 0; i < window; i++) { addCepstrum(last); } return window; } /** * Converts the Cepstrum data in the cepstraBuffer into a FeatureFrame. * * @param totalFeatures the number of Features that will be produced */ private void computeFeatures(int totalFeatures) { if (totalFeatures == 1) { computeFeature(); } else { // create the Features for (int i = 0; i < totalFeatures; i++) { computeFeature(); } } } /** Computes the next Feature. */ private void computeFeature() { Data feature = computeNextFeature(); outputQueue.add(feature); } /** * Computes the next feature. Advances the pointers as well. * * @return the feature Data computed */ protected abstract Data computeNextFeature(); }