/* * Copyright 1999-2004 Carnegie Mellon University. * Portions Copyright 2004 Sun Microsystems, Inc. * Portions Copyright 2004 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.linguist.flat; import edu.cmu.sphinx.linguist.SearchGraph; import edu.cmu.sphinx.linguist.SearchState; import edu.cmu.sphinx.linguist.WordSearchState; import edu.cmu.sphinx.linguist.acoustic.*; import edu.cmu.sphinx.linguist.dictionary.Pronunciation; import edu.cmu.sphinx.linguist.dictionary.Word; import edu.cmu.sphinx.util.LogMath; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Constructs a loop of all the context-independent phones. This loop is used in the static flat linguist for detecting * out-of-grammar utterances. A 'phoneInsertionProbability' will be added to the score each time a new phone is entered. * To obtain the all-phone search graph loop, simply called the method {@link #getSearchGraph() getSearchGraph}. * <p> * For futher details of this approach cf. 'Modeling Out-of-vocabulary Words for Robust Speech Recognition', Brazzi, * 2000, Proc. ICSLP */ public class CIPhoneLoop { public final AcousticModel model; private final float logPhoneInsertionProbability; public final float logOne = LogMath.LOG_ONE; /** * Creates the CIPhoneLoop with the given acoustic model and phone insertion probability * * @param model the acoustic model * @param logPhoneInsertionProbability the insertion probability */ public CIPhoneLoop(AcousticModel model, float logPhoneInsertionProbability) { this.model = model; this.logPhoneInsertionProbability = logPhoneInsertionProbability; } /** * Creates a new loop of all the context-independent phones. * * @return the phone loop search graph */ public SearchGraph getSearchGraph() { return new PhoneLoopSearchGraph(); } protected class PhoneLoopSearchGraph implements SearchGraph { protected final Map<String, SearchState> existingStates; protected final SentenceHMMState firstState; /** Constructs a phone loop search graph. */ public PhoneLoopSearchGraph() { existingStates = new HashMap<String, SearchState>(); firstState = new UnknownWordState(); SentenceHMMState branchState = new BranchOutState(firstState); attachState(firstState, branchState, logOne, logOne); SentenceHMMState lastState = new LoopBackState(firstState); lastState.setFinalState(true); attachState(lastState, branchState, logOne, logOne); for (Iterator<Unit> i = model.getContextIndependentUnitIterator(); i.hasNext();) { UnitState unitState = new UnitState(i.next(), HMMPosition.UNDEFINED); // attach unit state to the branch out state attachState(branchState, unitState, logOne, logPhoneInsertionProbability); HMM hmm = model.lookupNearestHMM (unitState.getUnit(), unitState.getPosition(), false); HMMState initialState = hmm.getInitialState(); HMMStateState hmmTree = new HMMStateState(unitState, initialState); addStateToCache(hmmTree); // attach first HMM state to the unit state attachState(unitState, hmmTree, logOne, logOne); // expand the HMM tree HMMStateState finalState = expandHMMTree(unitState, hmmTree); // attach final state of HMM tree to the loopback state attachState(finalState, lastState, logOne, logOne); } } /** * Retrieves initial search state * * @return the set of initial search state */ public SearchState getInitialState() { return firstState; } /** * Returns the number of different state types maintained in the search graph * * @return the number of different state types */ public int getNumStateOrder() { return 5; } public boolean getWordTokenFirst() { return false; } /** * Checks to see if a state that matches the given state already exists * * @param state the state to check * @return true if a state with an identical signature already exists. */ private SentenceHMMState getExistingState(SentenceHMMState state) { return (SentenceHMMState) existingStates.get(state.getSignature()); } /** * Adds the given state to the cache of states * * @param state the state to add */ protected void addStateToCache(SentenceHMMState state) { existingStates.put(state.getSignature(), state); } /** * Expands the given hmm state tree * * @param parent the parent of the tree * @param tree the tree to expand * @return the final state in the tree */ protected HMMStateState expandHMMTree(UnitState parent, HMMStateState tree) { HMMStateState retState = tree; for (HMMStateArc arc : tree.getHMMState().getSuccessors()) { HMMStateState newState; if (arc.getHMMState().isEmitting()) { newState = new HMMStateState (parent, arc.getHMMState()); } else { newState = new NonEmittingHMMState (parent, arc.getHMMState()); } SentenceHMMState existingState = getExistingState(newState); float logProb = arc.getLogProbability(); if (existingState != null) { attachState(tree, existingState, logOne, logProb); } else { attachState(tree, newState, logOne, logProb); addStateToCache(newState); retState = expandHMMTree(parent, newState); } } return retState; } protected void attachState(SentenceHMMState prevState, SentenceHMMState nextState, float logLanguageProbability, float logInsertionProbability) { SentenceHMMStateArc arc = new SentenceHMMStateArc (nextState, logLanguageProbability, logInsertionProbability); prevState.connect(arc); } } } @SuppressWarnings("serial") class UnknownWordState extends SentenceHMMState implements WordSearchState { public Pronunciation getPronunciation() { return Word.UNKNOWN.getPronunciations()[0]; } @Override public int getOrder() { return 0; } @Override public String getName() { return "UnknownWordState"; } /** * Returns true if this UnknownWordState indicates the start of a word. Returns false if this UnknownWordState * indicates the end of a word. * * @return true if this UnknownWordState indicates the start of a word, false if this UnknownWordState indicates the * end of a word */ @Override public boolean isWordStart() { return true; } } @SuppressWarnings("serial") class LoopBackState extends SentenceHMMState { LoopBackState(SentenceHMMState parent) { super("CIPhonesLoopBackState", parent, 0); } @Override public int getOrder() { return 1; } } @SuppressWarnings("serial") class BranchOutState extends SentenceHMMState { BranchOutState(SentenceHMMState parent) { super("BranchOutState", parent, 0); } @Override public int getOrder() { return 1; } }