marytts.tools.voiceimport.CARTBuilder Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2006 DFKI GmbH.
* All Rights Reserved. Use is subject to license terms.
*
* This file is part of MARY TTS.
*
* MARY TTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
package marytts.tools.voiceimport;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import marytts.cart.CART;
import marytts.cart.FeatureVectorCART;
import marytts.cart.LeafNode;
import marytts.cart.Node;
import marytts.cart.LeafNode.LeafType;
import marytts.cart.impose.FeatureArrayIndexer;
import marytts.cart.impose.MaryNode;
import marytts.cart.io.MaryCARTReader;
import marytts.cart.io.MaryCARTWriter;
import marytts.cart.io.WagonCARTReader;
import marytts.cart.io.WagonCARTWriter;
import marytts.exceptions.MaryConfigurationException;
import marytts.features.FeatureDefinition;
import marytts.features.FeatureVector;
import marytts.unitselection.data.FeatureFileReader;
import marytts.unitselection.data.MCepDatagram;
import marytts.unitselection.data.MCepTimelineReader;
import marytts.unitselection.data.UnitFileReader;
import marytts.util.data.Datagram;
import marytts.util.io.StreamGobbler;
public class CARTBuilder extends VoiceImportComponent {
private MCepTimelineReader mcepTimeline;
private UnitFileReader unitFile;
private String wagonDirName;
private String wagonDescFile;
private String wagonFeatsFile;
private String wagonCartFile;
private String wagonDisTabsFile;
private int numProcesses;
private boolean callWagon;
private DatabaseLayout db;
private int percent = 0;
public final String ACFEATUREFILE = "CARTBuilder.acFeatureFile";
public final String FEATURESEQFILE = "CARTBuilder.featureSeqFile";
public final String TOPLEVELTREEFILE = "CARTBuilder.topLevelTreeFile";
public final String CARTFILE = "CARTBuilder.cartFile";
public final String MCEPTIMELINE = "CARTBuilder.mcepTimeline";
public final String UNITFILE = "CARTBuilder.unitFile";
public final String READFEATURESEQUENCE = "CARTBuilder.readFeatureSequence";
public final String MAXLEAFSIZE = "CARTBuilder.maxLeafSize";
public final String CALLWAGON = "CARTBuilder.callWagon";
public final String NUMPROCESSES = "CARTBuilder.numProcesses";
public String getName() {
return "CARTBuilder";
}
@Override
protected void initialiseComp() {
callWagon = Boolean.parseBoolean(db.getProp(CALLWAGON));
wagonDirName = db.getProp(DatabaseLayout.TEMPDIR);
wagonDescFile = wagonDirName + "wagon.desc";
wagonFeatsFile = wagonDirName + "wagon.feats";
wagonCartFile = wagonDirName + "wagon.cart";
wagonDisTabsFile = wagonDirName + "wagon.distabs";
// make sure that we have at least a feature sequence file
File featSeqFile = new File(getProp(FEATURESEQFILE));
if (!featSeqFile.exists()) {
File topLevelTreeFile = new File(getProp(TOPLEVELTREEFILE));
if (!topLevelTreeFile.exists()) {
try {
PrintWriter featSeqOut = new PrintWriter(new OutputStreamWriter(new FileOutputStream(featSeqFile), "UTF-8"));
featSeqOut.println("# Automatically generated feature sequence file for CARTBuilder\n"
+ "# Add features (one per line) to refine\n"
+ "# Defines the feature sequence used to build the top-level CART\n" + "phone");
featSeqOut.flush();
featSeqOut.close();
} catch (Exception e) {
System.out.println("Warning: no feature sequence file " + getProp(FEATURESEQFILE)
+ " and no top level tree file " + getProp(TOPLEVELTREEFILE) + "; CARTBuilder will not run.");
}
}
}
String numProcessesString = getProp(NUMPROCESSES);
if (numProcessesString == null) {
numProcesses = 1;
} else {
try {
numProcesses = Integer.parseInt(numProcessesString);
} catch (NumberFormatException nfe) {
numProcesses = 1;
}
}
if (numProcesses < 1)
numProcesses = 1;
}
public SortedMap getDefaultProps(DatabaseLayout theDb) {
this.db = theDb;
if (props == null) {
props = new TreeMap();
String filedir = db.getProp(DatabaseLayout.FILEDIR);
String maryext = db.getProp(DatabaseLayout.MARYEXT);
props.put(ACFEATUREFILE, filedir + "halfphoneFeatures_ac" + maryext);
props.put(FEATURESEQFILE, db.getProp(DatabaseLayout.CONFIGDIR) + "featureSequence.txt");
props.put(TOPLEVELTREEFILE, db.getProp(DatabaseLayout.CONFIGDIR) + "topLevel.tree");
props.put(CARTFILE, filedir + "cart" + maryext);
props.put(MCEPTIMELINE, filedir + "timeline_mcep" + maryext);
props.put(UNITFILE, filedir + "halfphoneUnits" + maryext);
props.put(READFEATURESEQUENCE, "true");
props.put(MAXLEAFSIZE, "10000000");
props.put(CALLWAGON, "false");
props.put(NUMPROCESSES, "1");
}
return props;
}
protected void setupHelp() {
props2Help = new TreeMap();
props2Help.put(ACFEATUREFILE, "file containing all halfphone units and their target cost features"
+ " plus the acoustic target cost features");
props2Help.put(FEATURESEQFILE, "file containing the feature sequence for the basic tree");
props2Help.put(TOPLEVELTREEFILE, "file containing the basic tree");
props2Help.put(CARTFILE, "file containing the preselection CART. Will be created by this module");
props2Help.put(MCEPTIMELINE, "file containing the mcep files");
props2Help.put(UNITFILE, "file containing all halfphone units");
props2Help.put(READFEATURESEQUENCE, "if \"true\", basic tree is read from feature sequence file;"
+ " if \"false\", basic tree is read from top level tree file.");
props2Help.put(MAXLEAFSIZE, "the maximum number of units in a leaf of the basic tree");
props2Help.put(NUMPROCESSES, "number of wagon processes to run in parallel - bewteen 1 and the number of CPUs");
props2Help.put(CALLWAGON,
"whether to call wagon to build an acoustics-based pre-selection sub-tree for each top-level leaf");
}
public boolean compute() throws Exception {
WagonCARTWriter wr = new WagonCARTWriter();
long time = System.currentTimeMillis();
// read in the features with feature file indexer
System.out.println("Reading feature file ...");
String featureFile = getProp(ACFEATUREFILE);
FeatureFileReader ffr = FeatureFileReader.getFeatureFileReader(featureFile);
FeatureVector[] featureVectorsCopy = ffr.getCopyOfFeatureVectors();
FeatureDefinition featureDefinition = ffr.getFeatureDefinition();
// remove the feature vectors of edge units
List fVList = new ArrayList();
int edgeIndex = featureDefinition.getFeatureIndex(FeatureDefinition.EDGEFEATURE);
for (int i = 0; i < featureVectorsCopy.length; i++) {
FeatureVector nextFV = featureVectorsCopy[i];
if (!nextFV.isEdgeVector(edgeIndex))
fVList.add(nextFV);
}
int fVListSize = fVList.size();
int removed = featureVectorsCopy.length - fVListSize;
System.out.println("Removed " + removed + " edge vectors; " + "remaining vectors : " + fVListSize);
FeatureVector[] featureVectors = new FeatureVector[fVListSize];
for (int i = 0; i < featureVectors.length; i++) {
featureVectors[i] = (FeatureVector) fVList.get(i);
}
CART topLevelCART;
boolean fromFeatureSequence = Boolean.valueOf(getProp(READFEATURESEQUENCE)).booleanValue();
if (fromFeatureSequence) {
/* Build the top level tree from a feature sequence */
FeatureArrayIndexer fai = new FeatureArrayIndexer(featureVectors, featureDefinition);
System.out.println(" ... done!");
// read in the feature sequence
// open the file
System.out.println("Reading feature sequence ...");
String featSeqFile = getProp(FEATURESEQFILE);
BufferedReader buf = new BufferedReader(new FileReader(new File(featSeqFile)));
// each line contains one feature
String line = buf.readLine();
// collect features in a list
List features = new ArrayList();
while (line != null) {
// Skip empty lines and lines starting with #:
if (!(line.trim().equals("") || line.startsWith("#"))) {
features.add(line.trim());
}
line = buf.readLine();
}
// convert list to int array
int[] featureSequence = new int[features.size()];
for (int i = 0; i < features.size(); i++) {
featureSequence[i] = featureDefinition.getFeatureIndex((String) features.get(i));
}
System.out.println(" ... done!");
// sort the features according to feature sequence
System.out.println("Sorting features ...");
fai.deepSort(featureSequence);
System.out.println(" ... done!");
// get the resulting tree
MaryNode topLevelTree = fai.getTree();
// convert the top-level CART to Wagon Format
System.out.println("Building CART from tree ...");
topLevelCART = new FeatureVectorCART(topLevelTree, fai);
System.out.println(" ... done!");
} else {
/* read in the top-level tree from file */
String filename = getProp(TOPLEVELTREEFILE);
System.out.println("Reading empty top-level tree from file " + filename + " ...");
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filename)), "UTF-8"));
topLevelCART = new CART();
WagonCARTReader wagonReader = new WagonCARTReader(LeafType.FeatureVectorLeafNode);
topLevelCART.setRootNode(wagonReader.load(reader, featureDefinition));
System.out.println(" ... done!");
// fill in the leafs of the tree
System.out.println("Filling leafs of top-level tree ...");
wagonReader.fillLeafs(topLevelCART.getRootNode(), featureVectors);
System.out.println(" ... done!");
}
System.out.println("Checking top-level CART for reasonable leaf sizes ...");
int minSize = 5;
int maxSize = Integer.parseInt(getProp(MAXLEAFSIZE));
int nTooSmall = 0;
int nTooBig = 0;
int nLeaves = 0;
for (LeafNode leaf : topLevelCART.getLeafNodes()) {
if (leaf.getNumberOfData() < minSize) {
// Ignore a few meaningless combinations:
String path = leaf.getDecisionPath();
if (path.indexOf("phone==0") == -1 && path.indexOf("vc==0") == -1
&& !(path.indexOf("prev_vc==+") != -1 && path.indexOf("prev_c") != -1)
&& !(path.indexOf("prev_vc==-") != -1 && path.indexOf("prev_vheight") != -1)) {
// System.out.println("leaf too small: "+leaf.getDecisionPath());
nTooSmall++;
}
} else if (leaf.getNumberOfData() > maxSize) {
System.out.println(" LEAF TOO BIG: " + leaf.getDecisionPath());
nTooBig++;
}
nLeaves++;
}
if (nTooSmall > 0 || nTooBig > 0) {
System.out.println("Bad top-level cart: " + nTooSmall + "/" + nLeaves + " leaves are too small, " + nTooBig + "/"
+ nLeaves + " are too big");
} else {
System.out.println("... OK!");
}
if (callWagon) {
boolean ok = replaceLeaves(topLevelCART, featureDefinition);
if (!ok) {
System.out.println("Could not replace leaves");
return false;
}
}
// dump big CART to binary file
String destinationFile = getProp(CARTFILE);
MaryCARTWriter ww = new MaryCARTWriter();
ww.dumpMaryCART(topLevelCART, destinationFile);
// say how long you took
long timeDiff = System.currentTimeMillis() - time;
System.out.println("Processing took " + timeDiff + " milliseconds.");
return true;
}
/**
* Read in the CARTs from festival/trees/ directory, and store them in a CARTMap
*
* @param filename
* the festvox directory of a voice
* @param featDef
* featDef
* @throws IOException
* IOException
* @throws MaryConfigurationException
* MaryConfigurationException
* @return cart
*/
public CART importCART(String filename, FeatureDefinition featDef) throws IOException, MaryConfigurationException {
// open CART-File
System.out.println("Reading CART from " + filename + " ...");
// create a wagon cart reader for this class of tree
WagonCARTReader wagonReader = new WagonCARTReader(LeafType.IntAndFloatArrayLeafNode);
// build and return CART
// old: CART cart = new ExtendedClassificationTree();
CART cart = new CART();
// old: cart.load(filename,featDef,null);
cart.setRootNode(wagonReader.load(filename, featDef, null));
System.out.println(" ... done!");
return cart;
}
/**
* For each leaf in the CART, run Wagon on the feature vectors in this CART, and replace leaf by resulting CART
*
* @param cart
* the CART
* @param featureDefinition
* the definition of the features
* @throws IOException
* IOException
* @throws MaryConfigurationException
* MaryConfigurationException
* @return true when done
*/
public boolean replaceLeaves(CART cart, FeatureDefinition featureDefinition) throws IOException, MaryConfigurationException {
try {
System.out.println("Replacing Leaves ...");
System.out.println("Cart has " + cart.getNumNodes() + " nodes");
// create wagon dir if it does not exist
File wagonDir = new File(wagonDirName);
if (!wagonDir.exists()) {
wagonDir.mkdir();
}
// get the filenames for the various files used by wagon
String featureDefFile = wagonDescFile;
String featureVectorsFile = wagonFeatsFile;
String cartFile = wagonCartFile;
String distanceTableFile = wagonDisTabsFile;
// dump the feature definitions
PrintWriter out = new PrintWriter(new FileOutputStream(new File(featureDefFile)));
Set featuresToIgnore = new HashSet();
featuresToIgnore.add("unit_logf0");
featuresToIgnore.add("unit_duration");
featureDefinition.generateAllDotDescForWagon(out, featuresToIgnore);
out.close();
System.out.println("Will run " + numProcesses + " wagon processes in parallel");
WagonCallerThread[] wagons = new WagonCallerThread[numProcesses];
int stop = 50; // do not want leaves smaller than this
List leaves = new ArrayList();
for (LeafNode leaf : cart.getLeafNodes()) {
leaves.add(leaf);
}
int nLeaves = leaves.size();
System.out.println("Computing acoustic subtrees for " + nLeaves + " unit clusters");
/* call Wagon successively */
// go through the CART
int wagonID = 0;
for (int i = 0; i < nLeaves; i++) {
long startTime = System.currentTimeMillis();
percent = 100 * i / nLeaves;
LeafNode leaf = (LeafNode) leaves.get(i);
FeatureVector[] featureVectors = ((LeafNode.FeatureVectorLeafNode) leaf).getFeatureVectors();
if (featureVectors.length <= stop)
continue;
wagonID++;
System.out.println("Leaf replacement no. " + wagonID + " started at " + new Date());
// dump the feature vectors
System.out.println(wagonID + "> Dumping " + featureVectors.length + " feature vectors...");
String featureFileName = featureVectorsFile + wagonID;
dumpFeatureVectors(featureVectors, featureDefinition, featureFileName);
long endTime = System.currentTimeMillis();
System.out.println(wagonID + ">... dumping feature vectors took " + (endTime - startTime) + " ms");
startTime = endTime;
// dump the distance tables
System.out.println(wagonID + "> Computing distance tables...");
String distanceFileName = distanceTableFile + wagonID;
buildAndDumpDistanceTables(featureVectors, distanceFileName, featureDefinition);
endTime = System.currentTimeMillis();
System.out.println(wagonID + "> ... computing distance tables took " + (endTime - startTime) + " ms");
startTime = endTime;
// Dispatch call to Wagon to one of the wagon callers:
WagonCallerThread wagon = new WagonCallerThread(String.valueOf(wagonID), leaf, featureDefinition, featureVectors,
featureDefFile, featureFileName, distanceFileName, cartFile + wagonID, 0, stop,
db.getProp(DatabaseLayout.ESTDIR));
boolean dispatched = false;
while (!dispatched) {
for (int w = 0; w < numProcesses && !dispatched; w++) {
if (wagons[w] == null) {
System.out.println("Dispatching wagon " + wagonID + " as process " + (w + 1) + " out of "
+ numProcesses);
wagons[w] = wagon;
wagon.start();
dispatched = true;
} else if (wagons[w].finished()) {
if (!wagons[w].success()) {
System.out.println("Wagon " + wagons[w].id() + " failed. Aborting");
return false;
}
System.out.println("Dispatching wagon " + wagonID + " as process " + (w + 1) + " out of "
+ numProcesses);
wagons[w] = wagon;
wagon.start();
dispatched = true;
}
}
if (!dispatched) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
}
}
}
// Now make sure we wait for all wagons to finish
for (int w = 0; w < numProcesses; w++) {
if (wagons[w] != null) {
while (!wagons[w].finished()) {
try {
wagons[w].join();
} catch (InterruptedException ie) {
}
}
if (!wagons[w].success()) {
System.out.println("Wagon " + wagons[w].id() + " failed. Aborting");
return false;
}
}
}
} catch (IOException ioe) {
IOException newIOE = new IOException("Error replacing leaves");
newIOE.initCause(ioe);
throw newIOE;
}
System.out.println(" ... done!");
return true;
}
/**
* Dump the given feature vectors to a file with the given filename
*
* @param featureVectors
* the feature vectors
* @param featDef
* the feature definition
* @param filename
* the filename
* @throws FileNotFoundException
* FileNotFoundException
*/
public void dumpFeatureVectors(FeatureVector[] featureVectors, FeatureDefinition featDef, String filename)
throws FileNotFoundException {
// open file
PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(filename)));
// get basic feature info
int numByteFeats = featDef.getNumberOfByteFeatures();
int numShortFeats = featDef.getNumberOfShortFeatures();
int numFloatFeats = featDef.getNumberOfContinuousFeatures();
// loop through the feature vectors
for (int i = 0; i < featureVectors.length; i++) {
// Print the feature string
out.print(i + " " + featDef.toFeatureString(featureVectors[i]));
// print a newline if this is not the last vector
if (i + 1 != featureVectors.length) {
out.print("\n");
}
}
// dump and close
out.flush();
out.close();
}
/**
* Build the distance tables for the units from which we have the feature vectors and dump them to a file with the given
* filename
*
* @param featureVectors
* the feature vectors of the units
* @param filename
* the filename
* @param featDef
* featDef
* @throws IOException
* IOException
* @throws MaryConfigurationException
* MaryConfigurationException
*/
public void buildAndDumpDistanceTables(FeatureVector[] featureVectors, String filename, FeatureDefinition featDef)
throws IOException, MaryConfigurationException {
/* Load the MelCep timeline and the unit file */
if (mcepTimeline == null) {
try {
mcepTimeline = new MCepTimelineReader(getProp(MCEPTIMELINE));
} catch (IOException e) {
throw new RuntimeException("Failed to read the Mel-Cepstrum timeline [" + getProp(MCEPTIMELINE)
+ "] due to the following IOException: ", e);
}
}
if (unitFile == null) {
try {
unitFile = new UnitFileReader(getProp(UNITFILE));
} catch (IOException e) {
throw new RuntimeException("Failed to read the unit file [" + getProp(UNITFILE)
+ "] due to the following IOException: ", e);
}
}
/* Dereference the number of units once and for all */
int numUnits = featureVectors.length;
/*
* Read the Mel Cepstra for each unit, and cumulate their sufficient statistics in the same loop
*/
double[][][] melCep = new double[numUnits][][];
double val = 0;
double[] sum = new double[mcepTimeline.getOrder()];
double[] sumSq = new double[mcepTimeline.getOrder()];
double[] sigma2 = new double[mcepTimeline.getOrder()];
double N = 0.0;
for (int i = 0; i < numUnits; i++) {
// System.out.println( "FEATURE_VEC_IDX=" + i + " UNITIDX=" + featureVectors[i].getUnitIndex() );
/* Read the datagrams for the current unit */
Datagram[] buff = null;
MCepDatagram[] dat = null;
// System.out.println( featDef.toFeatureString( featureVectors[i] ) );
try {
buff = mcepTimeline.getDatagrams(unitFile.getUnit(featureVectors[i].getUnitIndex()), unitFile.getSampleRate());
// System.out.println( "NUMFRAMES=" + buff.length );
dat = new MCepDatagram[buff.length];
for (int d = 0; d < buff.length; d++) {
dat[d] = (MCepDatagram) (buff[d]);
}
} catch (Exception e) {
throw new RuntimeException("Failed to read the datagrams for unit number [" + featureVectors[i].getUnitIndex()
+ "] from the Mel-cepstrum timeline due to the following Exception: ", e);
}
N += (double) (dat.length); // Update the frame counter
melCep[i] = new double[dat.length][];
for (int j = 0; j < dat.length; j++) {
melCep[i][j] = dat[j].getCoeffsAsDouble();
/* Cumulate the sufficient statistics */
for (int k = 0; k < mcepTimeline.getOrder(); k++) {
val = melCep[i][j][k];
sum[k] += val;
sumSq[k] += (val * val);
}
}
}
/* Finalize the variance calculation */
for (int k = 0; k < mcepTimeline.getOrder(); k++) {
val = sum[k];
sigma2[k] = (sumSq[k] - (val * val) / N) / N;
}
// System.out.println("Read MFCCs, now computing distances");
/* Compute the unit distance matrix */
double[][] dist = new double[numUnits][numUnits];
for (int i = 0; i < numUnits; i++) {
dist[i][i] = 0.0; // <= Set the diagonal to 0.0
for (int j = 1; j < numUnits; j++) {
/*
* Get the DTW distance between the two sequences: System.out.println( "Entering DTW : " + featDef.getFeatureName(
* 0 ) + " " + featureVectors[i].getFeatureAsString( 0, featDef ) + ".length=" + melCep[i].length + " ; " +
* featureVectors[j].getFeatureAsString( 0, featDef ) + ".length=" + melCep[j].length + " ." );
* System.out.flush();
*/
if (melCep[i].length == 0 || melCep[j].length == 0) {
if (melCep[i].length == melCep[j].length) { // both 0 length
dist[i][j] = dist[j][i] = 0;
} else {
dist[i][j] = dist[j][i] = 100000; // a large number
}
} else {
// dist[i][j] = dist[j][i] = dtwDist( melCep[i], melCep[j], sigma2 );
// System.out.println("Using Mahalanobis distance\n"+
// "Distance is "+dist[i][j]);
double f0Weight = 100; // ad hoc value
double durWeight = 1000; // ad hoc value
double spectralDist = stretchDist(melCep[i], melCep[j], sigma2);
double f0Dist = f0Weight * f0Dist(featureVectors[i], featureVectors[j], featDef);
double durDist = durWeight * durDist(featureVectors[i], featureVectors[j], featDef);
// System.out.println("Spectral distance: "+spectralDist+" -- F0 distance: "+f0Dist+" -- Duration distance: "+durDist);
dist[i][j] = dist[j][i] = spectralDist + f0Dist + durDist;
}
}
}
/* Write the matrix to disk */
// System.out.println( "Writing distance matrix to file [" + filename + "]");
PrintWriter out = new PrintWriter(new BufferedOutputStream(new FileOutputStream(filename)));
for (int i = 0; i < numUnits; i++) {
for (int j = 0; j < numUnits; j++) {
out.print((float) (dist[i][j]) + " ");
}
out.print("\n");
}
out.flush();
out.close();
}
private double f0Dist(FeatureVector fv1, FeatureVector fv2, FeatureDefinition fd) {
int iLogF0 = fd.getFeatureIndex("unit_logf0");
float logf0_1 = fv1.getContinuousFeature(iLogF0);
float logF0_2 = fv2.getContinuousFeature(iLogF0);
return Math.abs(logf0_1 - logF0_2);
}
private double durDist(FeatureVector fv1, FeatureVector fv2, FeatureDefinition fd) {
int iLogF0 = fd.getFeatureIndex("unit_duration");
float logf0_1 = fv1.getContinuousFeature(iLogF0);
float logF0_2 = fv2.getContinuousFeature(iLogF0);
return Math.abs(logf0_1 - logF0_2);
}
/**
* Computes an average Mahalanobis distance along the simple time-stretched correspondence between two frame sequences.
*
* @param seq1
* a frame sequence
* @param seq2
* another frame sequence
* @param sigma2
* the variance of the vectors
* @return the average Mahalanobis distance between the two frame sequences
*/
private double stretchDist(double[][] seq1, double[][] seq2, double[] sigma2) {
double[][] shorter;
double[][] longer;
if (seq1.length < seq2.length) {
shorter = seq1;
longer = seq2;
} else {
shorter = seq2;
longer = seq1;
}
float lengthFactor = shorter.length / (float) longer.length;
double totalDist = 0;
for (int i = 0; i < longer.length; i++) {
int iShorter = (int) (lengthFactor * i);
double dist = mahalanobis(longer[i], shorter[iShorter], sigma2);
if (Double.isInfinite(dist) || Double.isNaN(dist))
dist = 100000; // a large number
totalDist += dist;
}
return totalDist / longer.length;
}
/**
* Computes an average Mahalanobis distance along the optimal DTW path between two vector sequences.
*
* The DTW constraint used here is: D(i,j) = min { D(i-2,j-1) + 2*d(i-1,j) + d(i,j) ; D(i-1,j-1) + 2*d(i,j) ; D(i-1,j-2) +
* 2*d(i,j-1) + d(i,j) }
*
* At the end of the DTW, the cumulated distance is normalized by the number of local distances cumulated along the optimal
* path. Hence, the resulting unit distance is homogeneous to an average having the order of magnitude of a single Mahalanobis
* distance, and that for each pair of units.
*
* @param seq1
* The first sequence of (Mel-cepstrum) vectors.
* @param seq2
* The second sequence of (Mel-cepstrum) vectors.
* @param sigma2
* The variance of the vectors.
* @return The average Mahalanobis distance along the optimal DTW path.
*/
private double dtwDist(double[][] seq1, double[][] seq2, double[] sigma2) {
if ((seq1.length <= 0) || (seq2.length <= 0)) {
throw new RuntimeException("Can't compute a DTW distance from a sequence with length 0 or negative. "
+ "(seq1.length=" + seq1.length + "; seq2.length=" + seq2.length + ")");
}
int l1 = seq1.length;
int l2 = seq2.length;
double[][] d = new double[l1][l2];
double[][] D = new double[l1][l2];
int[][] Nd = new int[l1][l2]; // <= Number of cumulated distances, for the final averaging
double[] minV = new double[3];
int[] minNd = new int[3];
int minIdx = 0;
/* Fill the local distance matrix */
for (int i = 0; i < l1; i++) {
for (int j = 0; j < l2; j++) {
d[i][j] = mahalanobis(seq1[i], seq2[j], sigma2);
}
}
/* Compute the optimal DTW distance: */
/* - 1st row/column: */
/* (This part works for 1 frame or more in either sequence.) */
D[0][0] = 2 * d[0][0];
for (int i = 1; i < l1; i++) {
D[i][0] = d[i][0];
Nd[i][0] = 1;
}
for (int i = 1; i < l2; i++) {
D[0][i] = d[0][i];
Nd[0][i] = 1;
}
/* - 2nd row/column: */
/* (This part works for 2 frames or more in either sequence.) */
/* corner: i==1, j==1 */
if ((l1 > 1) && (l2 > 1)) {
minV[0] = 2 * d[0][1] + d[1][1];
minNd[0] = 3;
minV[1] = D[0][0] + 2 * d[1][1];
minNd[1] = Nd[0][0] + 2;
minV[2] = 2 * d[1][0] + d[1][1];
minNd[2] = 3;
minIdx = minV[0] < minV[1] ? 0 : 1;
minIdx = minV[2] < minV[minIdx] ? 2 : minIdx;
D[1][1] = minV[minIdx];
Nd[1][1] = minNd[minIdx];
/* 2nd row: j==1 ; 2nd col: i==1 */
for (int i = 2; i < l1; i++) {
// Row:
minV[0] = D[i - 2][0] + 2 * d[i - 1][1] + d[i][1];
minNd[0] = Nd[i - 2][0] + 3;
minV[1] = D[i - 1][0] + 2 * d[i][1];
minNd[1] = Nd[i - 1][0] + 2;
minV[2] = 2 * d[i][0] + d[i][1];
minNd[2] = 3;
minIdx = minV[0] < minV[1] ? 0 : 1;
minIdx = minV[2] < minV[minIdx] ? 2 : minIdx;
D[i][1] = minV[minIdx];
Nd[i][1] = minNd[minIdx];
}
for (int i = 2; i < l2; i++) {
// Column:
minV[0] = 2 * d[0][i] + d[1][i];
minNd[0] = 3;
minV[1] = D[0][i - 1] + 2 * d[1][i];
minNd[1] = Nd[0][i - 1] + 2;
minV[2] = D[0][i - 2] + 2 * d[1][i - 1] + d[1][i];
minNd[2] = Nd[0][i - 2] + 3;
minIdx = minV[0] < minV[1] ? 0 : 1;
minIdx = minV[2] < minV[minIdx] ? 2 : minIdx;
D[1][i] = minV[minIdx];
Nd[1][i] = minNd[minIdx];
}
}
/* - Rest of the matrix: */
/* (This part works for 3 frames or more in either sequence.) */
if ((l1 > 2) && (l2 > 2)) {
for (int i = 2; i < l1; i++) {
for (int j = 2; j < l2; j++) {
minV[0] = D[i - 2][j - 1] + 2 * d[i - 1][j] + d[i][j];
minNd[0] = Nd[i - 2][j - 1] + 3;
minV[1] = D[i - 1][j - 1] + 2 * d[i][j];
minNd[1] = Nd[i - 1][j - 1] + 2;
minV[2] = D[i - 1][j - 2] + 2 * d[i][j - 1] + d[i][j];
minNd[0] = Nd[i - 1][j - 2] + 3;
minIdx = minV[0] < minV[1] ? 0 : 1;
minIdx = minV[2] < minV[minIdx] ? 2 : minIdx;
D[i][j] = minV[minIdx];
Nd[i][j] = minNd[minIdx];
}
}
}
/* Return */
return (D[l1 - 1][l2 - 1] / (double) (Nd[l1 - 1][l2 - 1]));
}
/**
* Mahalanobis distance between two feature vectors.
*
* @param v1
* A feature vector.
* @param v2
* Another feature vector.
* @param sigma2
* The variance of the distribution of the considered feature vectors.
* @return The mahalanobis distance between v1 and v2.
*/
private double mahalanobis(double[] v1, double[] v2, double[] sigma2) {
double sum = 0.0;
double diff = 0.0;
for (int i = 0; i < v1.length; i++) {
diff = v1[i] - v2[i];
sum += ((diff * diff) / sigma2[i]);
}
return (sum);
}
/**
* Provide the progress of computation, in percent, or -1 if that feature is not implemented.
*
* @return -1 if not implemented, or an integer between 0 and 100.
*/
public int getProgress() {
return percent;
}
public static class WagonCallerThread extends Thread {
// the Edinburgh Speech tools directory
protected String ESTDIR;
protected String arguments;
protected File cartFile;
protected File valueFile;
protected File distanceTableFile;
protected String id;
protected LeafNode leafToReplace;
protected FeatureDefinition featureDefinition;
protected FeatureVector[] featureVectors;
protected boolean finished = false;
protected boolean success = false;
public WagonCallerThread(String id, LeafNode leafToReplace, FeatureDefinition featureDefinition,
FeatureVector[] featureVectors, String descFilename, String valueFilename, String distanceTableFilename,
String cartFilename, int balance, int stop, String ESTDIR) {
this.id = id;
this.leafToReplace = leafToReplace;
this.featureDefinition = featureDefinition;
this.featureVectors = featureVectors;
this.ESTDIR = ESTDIR;
this.valueFile = new File(valueFilename);
this.distanceTableFile = new File(distanceTableFilename);
this.cartFile = new File(cartFilename);
this.arguments = "-desc " + descFilename + " -data " + valueFilename + " -balance " + balance + " -distmatrix "
+ distanceTableFilename + " -stop " + stop + " -output " + cartFilename;
}
public void run() {
try {
long startTime = System.currentTimeMillis();
System.out.println(id + "> Calling wagon as follows:");
System.out.println(ESTDIR + "/bin/wagon " + arguments);
Process p = Runtime.getRuntime().exec(ESTDIR + "/bin/wagon " + arguments);
// collect the output
// read from error stream
StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), id + " err");
// read from output stream
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), id + " out");
// start reading from the streams
errorGobbler.start();
outputGobbler.start();
p.waitFor();
if (p.exitValue() != 0) {
finished = true;
success = false;
} else {
success = true;
System.out.println(id + "> Wagon call took " + (System.currentTimeMillis() - startTime) + " ms");
// read in the resulting CART
System.out.println(id + "> Reading CART");
BufferedReader buf = new BufferedReader(new FileReader(cartFile));
// old CART newCART = new ExtendedClassificationTree(buf, featureDefinition);
CART newCART = new CART();
WagonCARTReader wagonReader = new WagonCARTReader(LeafType.IntAndFloatArrayLeafNode);
newCART.setRootNode(wagonReader.load(buf, featureDefinition));
buf.close();
// Fix the new cart's leaves:
// They are currently the index numbers in featureVectors;
// but what we need is the unit index numbers!
for (LeafNode leaf : newCART.getLeafNodes()) {
int[] data = (int[]) leaf.getAllData();
for (int i = 0; i < data.length; i++) {
data[i] = featureVectors[data[i]].getUnitIndex();
}
}
// replace the leaf by the CART
System.out.println(id + "> Replacing leaf");
System.out.println(id + "> (before: " + leafToReplace.getRootNode().getNumberOfNodes() + " nodes, adding "
+ newCART.getNumNodes() + ")");
Node newNode = CART.replaceLeafByCart(newCART, leafToReplace);
System.out.println(id + "> done -- cart now has " + newNode.getRootNode().getNumberOfNodes() + " nodes.");
finished = true;
}
if (!Boolean.getBoolean("wagon.keepfiles")) {
valueFile.delete();
distanceTableFile.delete();
}
} catch (Exception e) {
e.printStackTrace();
finished = true;
success = false;
throw new RuntimeException("Exception running wagon");
}
}
public boolean finished() {
return finished;
}
public boolean success() {
return success;
}
public String id() {
return id;
}
}
public static void main(String[] args) throws Exception {
CARTBuilder cartBuilder = new CARTBuilder();
DatabaseLayout db = new DatabaseLayout(cartBuilder);
// compute
/*
* boolean ok = cartBuilder.compute(); if (ok) System.out.println("Finished successfully!"); else
* System.out.println("Failed.");
*/
// loading a cart in MaryCART format
String path = "/project/mary/marcela/Unit-Selection-voices/DFKI_German_Poker/mary_files/";
String halfPhonesFile = path + "halfphoneFeatures.mry";
FeatureFileReader ffr = FeatureFileReader.getFeatureFileReader(halfPhonesFile);
FeatureDefinition feaDef = ffr.getFeatureDefinition();
String cartFile = path + "cart.mry.new";
MaryCARTReader rm = new MaryCARTReader();
CART cart = rm.load(cartFile);
// loading a cart in WagonCART format
// String cartFile = path + "cart.mry";
// WagonCARTReader wr = new WagonCARTReader("ExtendedClassificationTree");
// cart.setRootNode(wr.load(cartFile,feaDef, null));
System.out.println("Finished loading the tree!");
// check the leaves and their decision paths
String pwFile = path + "decnodes.txt";
PrintWriter pw = new PrintWriter(new FileWriter(new File(pwFile)));
System.out.println("Number of nodes loaded: " + cart.getNumNodes());
int i = 0;
for (LeafNode leaf : cart.getLeafNodes()) {
if (leaf.getNumberOfData() > 0) {
i++;
pw.println(i + ": " + leaf.getDecisionPath());
} else
pw.println(" " + leaf.getDecisionPath());
}
pw.close();
System.out.println("Generated decision paths file:" + pwFile);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy