/*
* Copyright 2010 PC-NG Inc..
* 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.PropertyException;
import edu.cmu.sphinx.util.props.PropertySheet;
import java.util.*;
/**
* Applies cepstral variance normalization (CVN), so that each coefficient
* will have unit variance. You need to put this element after the means
* normalizer in frontend pipeline.
* <p>
* CVN is sited to improve the stability of the decoding with the additive
* noise, so it might be useful in some situations.
*
* @see LiveCMN
*/
public class BatchVarNorm extends BaseDataProcessor {
private double[] variances; // array of current sums
private List<Data> cepstraList;
private int numberDataCepstra;
public BatchVarNorm() {
initLogger();
}
/* (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);
}
/** Initializes this BatchCMN. */
@Override
public void initialize() {
super.initialize();
variances = null;
cepstraList = new LinkedList<Data>();
}
/** Initializes the sums array and clears the cepstra list. */
private void reset() {
variances = null; // clears the sums array
cepstraList.clear();
numberDataCepstra = 0;
}
/**
* Returns the next Data object, which is a normalized cepstrum. Signal objects are returned unmodified.
*
* @return the next available Data object, returns null if no Data object is available
* @throws DataProcessingException if there is an error processing data
*/
@Override
public Data getData() throws DataProcessingException {
Data output = null;
if (!cepstraList.isEmpty()) {
output = cepstraList.remove(0);
} else {
reset();
// read the cepstra of the entire utterance, calculate
// and apply variance normalization
if (readUtterance() > 0) {
normalizeList();
output = cepstraList.remove(0); //getData();
}
}
return output;
}
/**
* Reads the cepstra of the entire Utterance into the cepstraList.
*
* @return the number cepstra (with Data) read
* @throws DataProcessingException if an error occurred reading the Data
*/
private int readUtterance() throws DataProcessingException {
Data input = null;
do {
input = getPredecessor().getData();
if (input != null) {
if (input instanceof DoubleData) {
numberDataCepstra++;
double[] cepstrumData = ((DoubleData) input).getValues();
if (variances == null) {
variances = new double[cepstrumData.length];
} else {
if (variances.length != cepstrumData.length) {
throw new Error
("Inconsistent cepstrum lengths: sums: " +
variances.length + ", cepstrum: " +
cepstrumData.length);
}
}
// add the cepstrum data to the sums
for (int j = 0; j < cepstrumData.length; j++) {
variances[j] += cepstrumData[j] * cepstrumData[j];
}
cepstraList.add(input);
} else if (input instanceof DataEndSignal || input instanceof SpeechEndSignal) {
cepstraList.add(input);
break;
} else { // DataStartSignal or other Signal
cepstraList.add(input);
}
}
} while (input != null);
return numberDataCepstra;
}
/** Normalizes the list of Data. */
private void normalizeList() {
// calculate the variance first
for (int i = 0; i < variances.length; i++) {
variances[i] = Math.sqrt(numberDataCepstra / variances[i]);
}
for (Data data : cepstraList) {
if (data instanceof DoubleData) {
double[] cepstrum = ((DoubleData)data).getValues();
for (int j = 0; j < cepstrum.length; j++) {
cepstrum[j] *= variances[j];
}
}
}
}
}