package edu.cmu.sphinx.decoder.scorer;
import edu.cmu.sphinx.decoder.search.Token;
import edu.cmu.sphinx.frontend.*;
import edu.cmu.sphinx.frontend.endpoint.SpeechEndSignal;
import edu.cmu.sphinx.frontend.util.DataUtil;
import edu.cmu.sphinx.util.props.ConfigurableAdapter;
import edu.cmu.sphinx.util.props.PropertyException;
import edu.cmu.sphinx.util.props.PropertySheet;
import edu.cmu.sphinx.util.props.S4Component;
import java.util.LinkedList;
import java.util.List;
/**
* Implements some basic scorer functionality, including a simple default
* acoustic scoring implementation which scores within the current thread, that
* can be changed by overriding the {@link #doScoring} method.
*
* <p>
* Note that all scores are maintained in LogMath log base.
*
* @author Holger Brandl
*/
public class SimpleAcousticScorer extends ConfigurableAdapter implements AcousticScorer {
/** Property the defines the frontend to retrieve features from for scoring */
@S4Component(type = BaseDataProcessor.class)
public final static String FEATURE_FRONTEND = "frontend";
protected BaseDataProcessor frontEnd;
/**
* An optional post-processor for computed scores that will normalize
* scores. If not set, no normalization will applied and the token scores
* will be returned unchanged.
*/
@S4Component(type = ScoreNormalizer.class, mandatory = false)
public final static String SCORE_NORMALIZER = "scoreNormalizer";
protected ScoreNormalizer scoreNormalizer;
private LinkedList<Data> storedData;
private boolean seenEnd = false;
@Override
public void newProperties(PropertySheet ps) throws PropertyException {
super.newProperties(ps);
this.frontEnd = (BaseDataProcessor) ps.getComponent(FEATURE_FRONTEND);
this.scoreNormalizer = (ScoreNormalizer) ps.getComponent(SCORE_NORMALIZER);
storedData = new LinkedList<Data>();
}
/**
* @param frontEnd
* the frontend to retrieve features from for scoring
* @param scoreNormalizer
* optional post-processor for computed scores that will
* normalize scores. If not set, no normalization will applied
* and the token scores will be returned unchanged.
*/
public SimpleAcousticScorer(BaseDataProcessor frontEnd, ScoreNormalizer scoreNormalizer) {
initLogger();
this.frontEnd = frontEnd;
this.scoreNormalizer = scoreNormalizer;
storedData = new LinkedList<Data>();
}
public SimpleAcousticScorer() {
}
/**
* Scores the given set of states.
*
* @param scoreableList
* A list containing scoreable objects to be scored
* @return The best scoring scoreable, or <code>null</code> if there are no
* more features to score
*/
public Data calculateScores(List<? extends Scoreable> scoreableList) {
Data data;
if (storedData.isEmpty()) {
while ((data = getNextData()) instanceof Signal) {
if (data instanceof SpeechEndSignal) {
seenEnd = true;
break;
}
if (data instanceof DataEndSignal) {
if (seenEnd)
return null;
else
break;
}
}
if (data == null)
return null;
} else {
data = storedData.poll();
}
return calculateScoresForData(scoreableList, data);
}
public Data calculateScoresAndStoreData(List<? extends Scoreable> scoreableList) {
Data data;
while ((data = getNextData()) instanceof Signal) {
if (data instanceof SpeechEndSignal) {
seenEnd = true;
break;
}
if (data instanceof DataEndSignal) {
if (seenEnd)
return null;
else
break;
}
}
if (data == null)
return null;
storedData.add(data);
return calculateScoresForData(scoreableList, data);
}
protected Data calculateScoresForData(List<? extends Scoreable> scoreableList, Data data) {
if (data instanceof SpeechEndSignal || data instanceof DataEndSignal) {
return data;
}
if (scoreableList.isEmpty())
return null;
// convert the data to FloatData if not yet done
if (data instanceof DoubleData)
data = DataUtil.DoubleData2FloatData((DoubleData) data);
Scoreable bestToken = doScoring(scoreableList, data);
// apply optional score normalization
if (scoreNormalizer != null && bestToken instanceof Token)
bestToken = scoreNormalizer.normalize(scoreableList, bestToken);
return bestToken;
}
protected Data getNextData() {
Data data = frontEnd.getData();
return data;
}
public void startRecognition() {
storedData.clear();
}
public void stopRecognition() {
// nothing needs to be done here
}
/**
* Scores a a list of <code>Scoreable</code>s given a <code>Data</code>
* -object.
*
* @param scoreableList
* The list of Scoreables to be scored
* @param data
* The <code>Data</code>-object to be used for scoring.
* @param <T> type for scorables
* @return the best scoring <code>Scoreable</code> or <code>null</code> if
* the list of scoreables was empty.
*/
protected <T extends Scoreable> T doScoring(List<T> scoreableList, Data data) {
T best = null;
float bestScore = -Float.MAX_VALUE;
for (T item : scoreableList) {
item.calculateScore(data);
if (item.getScore() > bestScore) {
bestScore = item.getScore();
best = item;
}
}
return best;
}
// Even if we don't do any meaningful allocation here, we implement the
// methods because most extending scorers do need them either.
public void allocate() {
}
public void deallocate() {
}
}