marytts.tools.dbselection.FeatureMaker Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2007 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.dbselection;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import marytts.datatypes.MaryData;
import marytts.datatypes.MaryDataType;
import marytts.datatypes.MaryXML;
import marytts.features.FeatureDefinition;
import marytts.features.FeatureRegistry;
import marytts.features.FeatureVector;
import marytts.features.TargetFeatureComputer;
import marytts.modules.TargetFeatureLister;
import marytts.server.Mary;
import marytts.server.Request;
import marytts.unitselection.select.Target;
import marytts.util.MaryUtils;
import marytts.util.dom.MaryDomUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.TreeWalker;
/**
* Takes text and converts to features Needs a running Mary server
*
* @author Anna Hunecke
*
*/
public class FeatureMaker {
// locale
private static String locale; // using locale we should be able to get the default voice.
// stores result of credibility check for current sentence
protected static boolean usefulSentence;
protected static boolean unknownWords;
protected static boolean strangeSymbols;
// feature definition, features for selection and their indexes
protected static FeatureDefinition featDef;
protected static Vector selectionFeature;
protected static int[] selectionFeatureIndex;
// if true, credibility is strict, else crebibility is lax
protected static boolean strictReliability;
protected static int numSentences = 0;
protected static int numUnreliableSentences = 0;
protected static DBHandler wikiToDB;
// mySql database
private static String mysqlHost = null;
private static String mysqlDB = null;
private static String mysqlUser = null;
private static String mysqlPasswd = null;
public static void main(String[] args) throws Exception {
boolean test = false;
String dateStringIni = "";
String dateStringEnd = "";
DateFormat fullDate = new SimpleDateFormat("dd_MM_yyyy_HH:mm:ss");
Date dateIni = new Date();
dateStringIni = fullDate.format(dateIni);
/* check the arguments */
if (!readArgs(args)) {
printUsage();
return;
}
System.out.println("\nFeatureMaker started...");
/* Here the DB connection is open */
wikiToDB = new DBHandler(locale);
wikiToDB.createDBConnection(mysqlHost, mysqlDB, mysqlUser, mysqlPasswd);
// check if table exists, if exists already ask user if delete or re-use
char c;
boolean result = false, processCleanTextRecords = true;
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String table = wikiToDB.getDBselectionTableName();
if (wikiToDB.tableExist(table)) {
System.out.print(" TABLE = \"" + table + "\" already exists, should it be deleted (y/n)?");
try {
String s = br.readLine();
if (s.contentEquals("y")) {
wikiToDB.createDataBaseSelectionTable();
} else {
System.out.print(" ADDING sentences TO EXISTING dbselection TABLE \"" + table + "\" (y/n)?");
s = br.readLine();
if (s.contentEquals("y"))
processCleanTextRecords = true;
else {
processCleanTextRecords = false;
System.out
.print(" please check the \"locale\" prefix of the dbselection TABLE you want to create or add to.");
}
}
} catch (Exception e) {
System.out.println(e);
}
} else {
System.out.print(" TABLE = \"" + table + "\" does not exist, it will be created.");
wikiToDB.createDataBaseSelectionTable();
}
System.out.print("Starting builtin MARY TTS...");
Mary.startup();
System.out.println(" MARY TTS started.");
if (processCleanTextRecords) {
// Get the set of id for unprocessed records in clean_text
// this will be useful when the process is stoped and then resumed
System.out.println("\nGetting list of unprocessed clean_text records from " + wikiToDB.getCleanTextTableName());
int textId[];
textId = wikiToDB.getUnprocessedTextIds();
System.out.println("Number of unprocessed clean_text records to process --> [" + textId.length + "]");
String text;
Vector sentenceList; // this will be the list of sentences in each clean_text
String targetFeatures = "";
int i, j;
// get a list separated by spaces of the target features to extract
for (i = 0; i < selectionFeature.size(); i++)
targetFeatures += selectionFeature.elementAt(i) + " ";
/* loop over the text records in clean_text table of wiki */
// once procesed the clean_text records are marked as processed=true, so here retrieve
// the next clean_text record untill all are processed.
System.out.println("Looping over unprocessed clean_text records from wikipedia...");
System.out.println("TARGETFEATURES to extract: " + targetFeatures);
System.out.println("Starting time:" + dateStringIni + "\n");
TargetFeatureComputer featureComputer = FeatureRegistry.getTargetFeatureComputer(MaryUtils.string2locale(locale),
targetFeatures);
FeatureDefinition fdef = featureComputer.getFeatureDefinition();
PrintWriter pw = new PrintWriter(new FileWriter(new File(locale + "_featureDefinition.txt")));
fdef.writeTo(pw, false);
pw.close();
System.out.println("\nCreated featureDefinition file:" + locale + "_featureDefinition.txt");
for (i = 0; i < textId.length; i++) {
// get next unprocessed text
text = wikiToDB.getCleanText(textId[i]);
System.out.println("Processing(" + i + ") text id=" + textId[i] + " text length=" + text.length());
sentenceList = splitIntoSentences(text, textId[i], test);
if (sentenceList != null) {
int index = 0;
// loop over the sentences
int numSentencesInText = 0;
/*
* String newSentence; byte feas[]; // for directly saving a vector of bytes as BLOB in mysql DB for(j=0;
* j();
selectionFeature.add("phone");
selectionFeature.add("next_phone");
selectionFeature.add("selection_prosody");
// now parse the args
if (args.length >= 10) {
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-locale") && args.length >= i + 1)
locale = args[++i];
else if (args[i].equals("-reliability") && args.length >= i + 1) {
String credibilitySetting = args[++i];
if (credibilitySetting.equals("strict"))
strictReliability = true;
else {
if (credibilitySetting.equals("lax"))
strictReliability = false;
else
System.out.println("Unknown argument for reliability " + credibilitySetting);
}
}
else if (args[i].contentEquals("-featuresForSelection") && args.length >= (i + 1)) {
selectionFeature.clear();
String selection = args[++i];
String feas[] = selection.split(",");
for (int k = 0; k < feas.length; k++)
selectionFeature.add(feas[k]);
}
// mysql database parameters
else if (args[i].contentEquals("-mysqlHost") && args.length >= (i + 1))
mysqlHost = args[++i];
else if (args[i].contentEquals("-mysqlUser") && args.length >= (i + 1))
mysqlUser = args[++i];
else if (args[i].contentEquals("-mysqlPasswd") && args.length >= (i + 1))
mysqlPasswd = args[++i];
else if (args[i].contentEquals("-mysqlDB") && args.length >= (i + 1))
mysqlDB = args[++i];
else { // unknown argument
System.out.println("\nOption not known: " + args[i]);
return false;
}
}
} else
// arguments less than 12
return false;
if (mysqlHost == null || mysqlUser == null || mysqlPasswd == null || mysqlDB == null) {
System.out.println("\nMissing mysql parameters.\n");
printParameters();
return false;
}
if (locale == null) {
System.out.println("\nPlease specify locale = wikipedia language.\n");
printParameters();
return false;
}
printParameters();
return true;
}
/**
* Process one sentences from text to target features
*
* @param nextSentence
* the sentence
* @param textId
* the text id
* @param feas
* target features names separated by space (ex. "phone next_phone selection_prosody")
* @return the result of the processing as MaryData object
*/
protected static MaryData processSentence(String nextSentence, int textId, String feas) {
// do a bit of normalization
StringBuilder docBuf = null;
nextSentence = nextSentence.replaceAll("\\\\", "").trim();
nextSentence = nextSentence.replaceAll("\\s/\\s", "").trim();
nextSentence = nextSentence.replaceAll("^/\\s", "").trim();
MaryData d = null;
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
// process and dump
Mary.process(nextSentence, "TEXT", "TARGETFEATURES", locale, null, null, null, null, feas, os);
d = new MaryData(MaryDataType.TARGETFEATURES, null);
d.readFrom(new ByteArrayInputStream(os.toByteArray()));
// System.out.println("TARGETFEATURES:\n" + d.getPlainText());
} catch (Exception e) {
e.printStackTrace();
if (d != null) {
if (d.getPlainText() != null) {
System.out.println("Error processing sentence " + textId + ": \"" + nextSentence + "\":\n" + d.getPlainText()
+ "; skipping sentence");
} else {
if (d.getDocument() != null) {
docBuf = new StringBuilder();
getXMLAsString(d.getDocument(), docBuf);
System.out.println("Error processing sentence " + ": \"" + nextSentence + "\":\n" + docBuf.toString()
+ "; skipping sentence");
} else {
System.out.println("Error processing sentence " + textId + ": \"" + nextSentence
+ "\"; skipping sentence");
}
}
} else {
System.out.println("Error processing sentence from textId=" + textId + ": \"" + nextSentence
+ "\"; skipping sentence");
}
return null;
} catch (AssertionError ae) {
ae.printStackTrace();
System.out.println("Error processing sentence from textId=" + textId + ": \"" + nextSentence
+ "\"; skipping sentence");
return null;
}
docBuf = null;
return d;
}
/**
* Process one sentences from text to target features
*
* @param nextSentence
* the sentence
* @param textId
* the text id
* @param featureComputer
* target features names separated by space (ex. "phone next_phone selection_prosody")
* @return a byte array representing the feature vectors for the entire sentence
*/
protected static byte[] processSentenceToFeatures(String nextSentence, int textId, TargetFeatureComputer featureComputer) {
// do a bit of normalization
StringBuilder docBuf = null;
nextSentence = nextSentence.replaceAll("\\\\", "").trim();
nextSentence = nextSentence.replaceAll("\\s/\\s", "").trim();
nextSentence = nextSentence.replaceAll("^/\\s", "").trim();
if (Mary.currentState() != Mary.STATE_RUNNING)
throw new IllegalStateException("MARY system is not running");
MaryDataType inputType = MaryDataType.get("TEXT");
MaryDataType outputType = MaryDataType.get("ALLOPHONES");
Locale localeObj = MaryUtils.string2locale(locale);
try {
Request request = new Request(inputType, outputType, localeObj, null, null, null, textId, null);
request.setInputData(nextSentence);
request.process();
MaryData result = request.getOutputData();
Document doc = result.getDocument();
// Now we skip the prediction of acoustic parameters, and apply only the required feature processors
// directly to the ALLOPHONES data
// (this assumes that "feas" only contains features that do not require acoustic parameters, which seems reasonable
// here)
// First, get the list of segments and boundaries in the current document
TreeWalker tw = MaryDomUtils.createTreeWalker(doc, doc, MaryXML.PHONE, MaryXML.BOUNDARY);
List segmentsAndBoundaries = new ArrayList();
Element e;
while ((e = (Element) tw.nextNode()) != null) {
segmentsAndBoundaries.add(e);
}
String silenceSymbol = featureComputer.getPauseSymbol();
int numFeatures = featureComputer.getByteValuedFeatureProcessors().length;
List targets = TargetFeatureLister.createTargetsWithPauses(segmentsAndBoundaries, silenceSymbol);
byte[] featureData = new byte[targets.size() * numFeatures];
int off = 0;
for (Target target : targets) {
FeatureVector features = featureComputer.computeFeatureVector(target);
System.arraycopy(features.getByteValuedDiscreteFeatures(), 0, featureData, off, numFeatures);
off += numFeatures;
}
return featureData;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Process the given text with the MaryClient from Text to Chunked
*
* @param textString
* the text to process
* @param id
* id
* @return the resulting XML-Document
* @throws Exception
* Exception
*/
protected static Document phonemiseText(String textString, int id) throws Exception {
try {
/*
* ByteArrayOutputStream os = new ByteArrayOutputStream(); //process and dump Mary.process(textString,
* "TEXT","PHONEMES", locale, null, null, null, null, null, os);
*
* //read into mary data object MaryData maryData = new MaryData(MaryDataType.PHONEMES, null);
*
* maryData.readFrom(new ByteArrayInputStream(os.toByteArray()));
*
* return maryData.getDocument();
*/
if (Mary.currentState() != Mary.STATE_RUNNING)
throw new IllegalStateException("MARY system is not running");
MaryDataType inputType = MaryDataType.get("TEXT");
MaryDataType outputType = MaryDataType.get("PHONEMES");
Locale localeObj = MaryUtils.string2locale(locale);
Request request = new Request(inputType, outputType, localeObj, null, null, null, id, null);
request.setInputData(textString);
request.process();
MaryData result = request.getOutputData();
return result.getDocument();
} catch (Exception e) {
e.printStackTrace();
System.out.println("PhonemiseText: problem processing text id=" + id);
return null;
}
}
/**
* Split the text into separate sentences
*
* @param text
* the file
* @param id
* id
* @param test
* test
* @return true, if successful
* @throws Exception
* Exception
*/
protected static Vector splitIntoSentences(String text, int id, boolean test) throws Exception {
Vector sentenceList = null;
StringBuilder sentence;
// index2sentences = new TreeMap();
Document doc = phonemiseText(text, id);
// if (doc == null) return false;
if (doc != null) {
sentenceList = new Vector();
NodeList sentences = doc.getElementsByTagName("s");
int sentenceIndex = 1;
int unrelSentences = 0;
for (int j = 0; j < sentences.getLength(); j++) {
Node nextSentence = sentences.item(j);
// ignore all non-element children
if (!(nextSentence instanceof Element))
continue;
sentence = null;
// get the tokens
NodeList tokens = nextSentence.getChildNodes();
usefulSentence = true;
unknownWords = false;
strangeSymbols = false;
for (int k = 0; k < tokens.getLength(); k++) {
Node nextToken = tokens.item(k);
// ignore all non-element children
if ((nextToken instanceof Element))
sentence = collectTokens(nextToken, sentence);
}
// System.out.println(sentence);
if (sentence != null) {
if (usefulSentence) {
// store sentence in sentence map
// index2sentences.put(new Integer(sentenceIndex),sentence.toString());
// check if the sentence is not .
if (!sentence.toString().contentEquals(".")) {
sentenceList.add(sentence.toString());
// System.out.println("reliable sentence=" + sentence.toString() + "\n");
}
} else {
// just print useless sentence to log file
// System.out.println(filename+"; "+sentenceIndex+": "+sentence
// +" : is unreliable");
unrelSentences++;
/*
* if(unknownWords) System.out.println("unknownWords: " + sentence.toString()); if(strangeSymbols)
* System.out.println("strangeSymbols: " + sentence.toString());
*/
// Here the reason why is unreliable can be added to the DB.
// for the moment there is just one field reliable=false in this case.
if (!test)
wikiToDB.insertSentence(sentence.toString(), null, usefulSentence, unknownWords, strangeSymbols, id);
else {
wikiToDB.setSentenceRecord(id, "reliable", false);
if (unknownWords)
wikiToDB.setSentenceRecord(id, "unknownWords", true);
if (strangeSymbols)
wikiToDB.setSentenceRecord(id, "strangeSymbols", true);
// System.out.println("unreliable sentence: " + sentence.toString());
}
}
sentenceIndex++;
} else {
// ignore
// System.out.println("NULL SENTENCE!!!");
}
}
numUnreliableSentences += unrelSentences;
System.out.println("Inserted " + unrelSentences + " sentences from text id=" + id + " (Total unreliable = "
+ numUnreliableSentences + ")");
}
sentence = null;
return sentenceList;
}
/**
* Collect the tokens of a sentence
*
* @param nextToken
* the Node to start from checkCredibility returns 0 if the sentence is useful 1 if the sentence contains
* unknownWords (so the sentence is not useful) 2 if the sentence contains strangeSymbols (so the sentence is not
* useful)
* @param sentence
* sentence
* @return sentence
*/
protected static StringBuilder collectTokens(Node nextToken, StringBuilder sentence) {
int credibility = 0;
String tokenText, word;
String name = nextToken.getLocalName();
if (name.equals("t")) {
if ((credibility = checkReliability((Element) nextToken)) > 0) {
// memorize that we found unreliable sentence
usefulSentence = false;
if (credibility == 1)
unknownWords = true;
else if (credibility == 2)
strangeSymbols = true;
}
if (sentence == null) {
sentence = new StringBuilder();
// first word of the sentence
word = MaryDomUtils.tokenText((Element) nextToken);
sentence.append(word);
} else {
String pos = ((Element) nextToken).getAttribute("pos");
// if (pos.startsWith("$")){
if (".,'`:#$".indexOf(pos.substring(0, 1)) != -1) {
// punctuation
tokenText = MaryDomUtils.tokenText((Element) nextToken);
// just append without whitespace
sentence.append(tokenText);
// System.out.println(sentence);
} else {
// normal word, append a whitespace before it
word = MaryDomUtils.tokenText((Element) nextToken);
// System.out.println("word=" + word);
sentence.append(" " + word);
// System.out.println(sentence);
}
}
} else {
if (name.equals("mtu")) {
// get the tokens
NodeList mtuTokens = nextToken.getChildNodes();
for (int l = 0; l < mtuTokens.getLength(); l++) {
Node nextMTUToken = mtuTokens.item(l);
// ignore all non-element children
if (!(nextMTUToken instanceof Element))
continue;
collectTokens(nextMTUToken, sentence);
}
}
}
return sentence;
}
/**
* Phonemise the given document with the help of JPhonemiser
*
* g2p_method "contains-unknown-words" or "contains-strange-symbols",
*
* @param t
* t
* @return 0 if the sentence is useful 1 if the sentence contains unknownWords 2 if the sentence contains strangeSymbols
*/
protected static int checkReliability(Element t) {
// boolean newUsefulSentence = true;
int newUsefulSentence = 0;
if (t.hasAttribute("ph")) {
// we have a transcription
if (t.hasAttribute("g2p_method")) {
// check method of transcription
String method = t.getAttribute("g2p_method");
if (!method.equals("lexicon") && !method.equals("userdict")) {
if (strictReliability) {
// method other than lexicon or userdict -> unreliable
newUsefulSentence = 1;
// System.out.println(" unknownwords: method other than lexicon or userdict -> unreliable");
} else {
// lax credibility criterion
if (!method.equals("phonemiseDenglish") && !method.equals("compound") && !method.equals("rules")) { // NEW:
// method
// is
// rules
// method other than lexicon, userdict, phonemiseDenglish
// or compound -> unreliable
newUsefulSentence = 1;
// System.out.println(" unknownwords: method other than lexicon, userdict, phonemiseDenglish or compound -> unreliable");
} // else method is phonemiseDenglish or compound -> credible
}
}// else method is lexicon or userdict -> credible
} // else no method -> preprocessed -> credible
} else {
// we dont have a transcription
// if (t.hasAttribute("pos") && !t.getAttribute("pos").startsWith("$")){
String pos = t.getAttribute("pos");
if (".,'`:#$".indexOf(pos.substring(0, 1)) == -1) {
// no transcription given -> unreliable
newUsefulSentence = 2;
// System.out.println(" strangeSymbols: no transcription given -> unreliable");
} // else punctuation -> credible
}
return newUsefulSentence;
}
/**
* Convert the given xml-node and its subnodes to Strings and collect them in the given StringBuilder
*
* @param motherNode
* the xml-node
* @param ppText
* the StringBuilder
*/
protected static void getXMLAsString(Node motherNode, StringBuilder ppText) {
NodeList children = motherNode.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node nextChild = children.item(i);
String name = nextChild.getLocalName();
if (name == null) {
continue;
}
ppText.append("<" + name);
if (nextChild instanceof Element) {
if (nextChild.hasAttributes()) {
NamedNodeMap atts = nextChild.getAttributes();
for (int j = 0; j < atts.getLength(); j++) {
String nextAtt = atts.item(j).getNodeName();
ppText.append(" " + nextAtt + "=\"" + ((Element) nextChild).getAttribute(nextAtt) + "\"");
}
}
}
if (name.equals("boundary")) {
ppText.append("/>\n");
continue;
}
ppText.append(">\n");
if (name.equals("t")) {
ppText.append(MaryDomUtils.tokenText((Element) nextChild) + "\n\n");
} else {
if (nextChild.hasChildNodes()) {
getXMLAsString(nextChild, ppText);
}
ppText.append("" + name + ">\n");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy