All Downloads are FREE. Search and download functionalities are using the official Maven repository.

marytts.modules.CARTDurationModeller 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.modules;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Locale;

import marytts.cart.CART;
import marytts.cart.DirectedGraph;
import marytts.cart.StringPredictionTree;
import marytts.cart.io.DirectedGraphReader;
import marytts.datatypes.MaryData;
import marytts.datatypes.MaryDataType;
import marytts.datatypes.MaryXML;
import marytts.features.FeatureDefinition;
import marytts.features.FeatureProcessorManager;
import marytts.features.FeatureRegistry;
import marytts.features.TargetFeatureComputer;
import marytts.modules.synthesis.Voice;
import marytts.server.MaryProperties;
import marytts.unitselection.select.Target;
import marytts.unitselection.select.UnitSelector;
import marytts.util.MaryRuntimeUtils;
import marytts.util.MaryUtils;
import marytts.util.dom.MaryDomUtils;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.traversal.TreeWalker;

/**
 * Predict phone durations using a CART.
 * 
 * @author Marc Schröder
 * @deprecated
 */

public class CARTDurationModeller extends InternalModule {
	protected DirectedGraph cart = new CART();
	// TODO: use a simple regression tree, with FloatLeafNode, for pausetree:
	protected StringPredictionTree pausetree;
	protected TargetFeatureComputer featureComputer;
	protected TargetFeatureComputer pauseFeatureComputer;
	private String propertyPrefix;
	private FeatureProcessorManager featureProcessorManager;

	/**
	 * Constructor which can be directly called from init info in the config file. This constructor will use the registered
	 * feature processor manager for the given locale.
	 * 
	 * @param locale
	 *            a locale string, e.g. "en"
	 * @param propertyPrefix
	 *            the prefix to be used when looking up entries in the config files, e.g. "english.duration"
	 * @throws Exception
	 *             Exception
	 */
	public CARTDurationModeller(String locale, String propertyPrefix) throws Exception {
		this(MaryUtils.string2locale(locale), propertyPrefix, FeatureRegistry.getFeatureProcessorManager(MaryUtils
				.string2locale(locale)));
	}

	/**
	 * Constructor which can be directly called from init info in the config file. Different languages can call this code with
	 * different settings.
	 * 
	 * @param locale
	 *            a locale string, e.g. "en"
	 * @param propertyPrefix
	 *            the prefix to be used when looking up entries in the config files, e.g. "english.duration"
	 * @param featprocClassInfo
	 *            a package name for an instance of FeatureProcessorManager, e.g. "marytts.language.en.FeatureProcessorManager"
	 * @throws Exception
	 *             Exception
	 */
	public CARTDurationModeller(String locale, String propertyPrefix, String featprocClassInfo) throws Exception {
		this(MaryUtils.string2locale(locale), propertyPrefix, (FeatureProcessorManager) MaryRuntimeUtils
				.instantiateObject(featprocClassInfo));
	}

	/**
	 * Constructor to be called with instantiated objects.
	 * 
	 * @param locale
	 *            locale
	 * @param propertyPrefix
	 *            the prefix to be used when looking up entries in the config files, e.g. "english.duration"
	 * @param featureProcessorManager
	 *            the manager to use when looking up feature processors.
	 */
	protected CARTDurationModeller(Locale locale, String propertyPrefix, FeatureProcessorManager featureProcessorManager) {
		super("CARTDurationModeller", MaryDataType.ALLOPHONES, MaryDataType.DURATIONS, locale);
		if (propertyPrefix.endsWith("."))
			this.propertyPrefix = propertyPrefix;
		else
			this.propertyPrefix = propertyPrefix + ".";
		this.featureProcessorManager = featureProcessorManager;
	}

	public void startup() throws Exception {
		super.startup();
		String cartFilename = MaryProperties.getFilename(propertyPrefix + "cart");
		if (cartFilename != null) { // there is a default model for the language
			File cartFile = new File(cartFilename);
			cart = new DirectedGraphReader().load(cartFile.getAbsolutePath());
			featureComputer = FeatureRegistry.getTargetFeatureComputer(featureProcessorManager, cart.getFeatureDefinition()
					.getFeatureNames());
		} else {
			cart = null;
		}

		String pauseFilename = MaryProperties.getFilename(propertyPrefix + "pausetree");
		if (pauseFilename != null) {
			File pauseFile = new File(pauseFilename);

			File pauseFdFile = new File(MaryProperties.needFilename(propertyPrefix + "pausefeatures"));
			FeatureDefinition pauseFeatureDefinition = new FeatureDefinition(new BufferedReader(new FileReader(pauseFdFile)),
					false);
			pauseFeatureComputer = FeatureRegistry.getTargetFeatureComputer(featureProcessorManager,
					pauseFeatureDefinition.getFeatureNames());
			pausetree = new StringPredictionTree(new BufferedReader(new FileReader(pauseFile)), pauseFeatureDefinition);
		} else {
			this.pausetree = null;
		}
	}

	public MaryData process(MaryData d) throws Exception {
		Document doc = d.getDocument();
		NodeIterator sentenceIt = MaryDomUtils.createNodeIterator(doc, MaryXML.SENTENCE);
		Element sentence = null;
		while ((sentence = (Element) sentenceIt.nextNode()) != null) {
			// Make sure we have the correct voice:
			Element voice = (Element) MaryDomUtils.getAncestor(sentence, MaryXML.VOICE);
			Voice maryVoice = Voice.getVoice(voice);
			if (maryVoice == null) {
				maryVoice = d.getDefaultVoice();
			}
			if (maryVoice == null) {
				// Determine Locale in order to use default voice
				Locale locale = MaryUtils.string2locale(doc.getDocumentElement().getAttribute("xml:lang"));
				maryVoice = Voice.getDefaultVoice(locale);
			}

			DirectedGraph currentCart = cart;
			TargetFeatureComputer currentFeatureComputer = featureComputer;
			if (maryVoice != null) {
				DirectedGraph voiceCart = maryVoice.getDurationGraph();
				if (voiceCart != null) {
					currentCart = voiceCart;
					logger.debug("Using voice duration graph");
					FeatureDefinition voiceFeatDef = voiceCart.getFeatureDefinition();
					currentFeatureComputer = FeatureRegistry.getTargetFeatureComputer(featureProcessorManager,
							voiceFeatDef.getFeatureNames());
				}
			}

			if (currentCart == null) {
				throw new NullPointerException("No cart for predicting duration");
			}

			// cumulative duration from beginning of sentence, in seconds:
			float end = 0;

			TreeWalker tw = MaryDomUtils.createTreeWalker(sentence, MaryXML.PHONE, MaryXML.BOUNDARY);
			Element segmentOrBoundary;
			Element previous = null;
			while ((segmentOrBoundary = (Element) tw.nextNode()) != null) {
				String phone = UnitSelector.getPhoneSymbol(segmentOrBoundary);
				Target t = new Target(phone, segmentOrBoundary);
				t.setFeatureVector(currentFeatureComputer.computeFeatureVector(t));
				float durInSeconds;
				if (segmentOrBoundary.getTagName().equals(MaryXML.BOUNDARY)) { // a pause
					durInSeconds = enterPauseDuration(segmentOrBoundary, previous, pausetree, pauseFeatureComputer);
				} else {
					float[] dur = (float[]) currentCart.interpret(t);
					assert dur != null : "Null duration";
					assert dur.length == 2 : "Unexpected duration length: " + dur.length;
					durInSeconds = dur[1];
					float stddevInSeconds = dur[0];
				}
				end += durInSeconds;
				int durInMillis = (int) (1000 * durInSeconds);
				if (segmentOrBoundary.getTagName().equals(MaryXML.BOUNDARY)) {
					segmentOrBoundary.setAttribute("duration", String.valueOf(durInMillis));
				} else { // phone
					segmentOrBoundary.setAttribute("d", String.valueOf(durInMillis));
					segmentOrBoundary.setAttribute("end", String.valueOf(end));
				}
				previous = segmentOrBoundary;
			}
		}
		MaryData output = new MaryData(outputType(), d.getLocale());
		output.setDocument(doc);
		return output;
	}

	/**
	 * 
	 * This predicts and enters the pause duration for a pause segment.
	 * 
	 * @param s
	 * @param maryVoice
	 * @return pause duration, in seconds
	 */
	private float enterPauseDuration(Element boundary, Element previous, StringPredictionTree currentPauseTree,
			TargetFeatureComputer currentPauseFeatureComputer) {
		if (!boundary.getTagName().equals(MaryXML.BOUNDARY))
			throw new IllegalArgumentException("cannot call enterPauseDuration for non-pause element");

		// If there is already a duration, keep it:
		if (boundary.hasAttribute("duration")) {
			try {
				return Float.parseFloat(boundary.getAttribute("duration")) * 0.001f;
			} catch (NumberFormatException nfe) {
			}
		}

		float duration = 0.4f; // default value

		if (previous == null || !previous.getTagName().equals(MaryXML.PHONE))
			return duration;

		if (currentPauseTree == null)
			return duration;

		assert currentPauseFeatureComputer != null;
		String phone = previous.getAttribute("p");
		Target t = new Target(phone, previous);
		t.setFeatureVector(currentPauseFeatureComputer.computeFeatureVector(t));

		String durationString = currentPauseTree.getMostProbableString(t);
		// strip off "ms"
		durationString = durationString.substring(0, durationString.length() - 2);
		try {
			duration = Float.parseFloat(durationString);
		} catch (NumberFormatException nfe) {
		}

		if (duration > 2) {
			logger.debug("Cutting long duration to 2 s -- was " + duration);
			duration = 2;
		}
		return duration;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy