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

marytts.unitselection.analysis.Phone Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010 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.unitselection.analysis;

import java.util.Arrays;

import marytts.modules.phonemiser.Allophone;
import marytts.unitselection.concat.BaseUnitConcatenator.UnitData;
import marytts.unitselection.select.HalfPhoneTarget;
import marytts.unitselection.select.SelectedUnit;
import marytts.util.data.Datagram;
import marytts.util.math.MathUtils;

import org.apache.commons.lang.ArrayUtils;
import org.w3c.dom.Element;

/**
 * Convenience class containing the selected units and targets of a phone segment, and a host of getters to access their prosodic
 * attributes
 * 
 * @author steiner
 * 
 */
public class Phone {
	private HalfPhoneTarget leftTarget;

	private HalfPhoneTarget rightTarget;

	private SelectedUnit leftUnit;

	private SelectedUnit rightUnit;

	private double sampleRate;

	private double[] leftF0Targets;

	private double[] rightF0Targets;

	/**
	 * Main constructor
	 * 
	 * @param leftUnit
	 *            which can be null
	 * @param rightUnit
	 *            which can be null
	 * @param sampleRate
	 *            of the TimelineReader containing the SelectedUnits, needed to provide duration information
	 * @throws IllegalArgumentException
	 *             if both the left and right units are null
	 */
	public Phone(SelectedUnit leftUnit, SelectedUnit rightUnit, int sampleRate) throws IllegalArgumentException {
		this.leftUnit = leftUnit;
		this.rightUnit = rightUnit;
		this.sampleRate = (double) sampleRate;
		// targets are extracted from the units for easier access:
		try {
			this.leftTarget = (HalfPhoneTarget) leftUnit.getTarget();
		} catch (NullPointerException e) {
			// leave at null
		}
		try {
			this.rightTarget = (HalfPhoneTarget) rightUnit.getTarget();
		} catch (NullPointerException e) {
			if (leftTarget == null) {
				throw new IllegalArgumentException("A phone's left and right halves cannot both be null!");
			} else {
				// leave at null
			}
		}
	}

	/**
	 * Get the left selected halfphone unit of this phone
	 * 
	 * @return the left unit, or null if there is no left unit
	 */
	public SelectedUnit getLeftUnit() {
		return leftUnit;
	}

	/**
	 * Get the right selected halfphone unit of this phone
	 * 
	 * @return the right unit, or null if there is no right unit
	 */
	public SelectedUnit getRightUnit() {
		return rightUnit;
	}

	/**
	 * Get the left halfphone target of this phone
	 * 
	 * @return the left target, or null if there is no left target
	 */
	public HalfPhoneTarget getLeftTarget() {
		return leftTarget;
	}

	/**
	 * Get the right halfphone target of this phone
	 * 
	 * @return the right target, or null if there is no right target
	 */
	public HalfPhoneTarget getRightTarget() {
		return rightTarget;
	}

	/**
	 * Get the datagrams from a SelectedUnit
	 * 
	 * @param unit
	 *            the SelectedUnit
	 * @return the datagrams in an array, or null if unit is null
	 */
	private Datagram[] getUnitFrames(SelectedUnit unit) {
		UnitData unitData = getUnitData(unit);
		Datagram[] frames = null;
		try {
			frames = unitData.getFrames();
		} catch (NullPointerException e) {
			// leave at null
		}
		return frames;
	}

	/**
	 * Get this phone's left unit's Datagrams
	 * 
	 * @return the left unit's Datagrams in an array, or null if there is no left unit
	 */
	public Datagram[] getLeftUnitFrames() {
		return getUnitFrames(leftUnit);
	}

	/**
	 * Get this phone's right unit's Datagrams
	 * 
	 * @return the right unit's Datagrams in an array, or null if there is no right unit
	 */
	public Datagram[] getRightUnitFrames() {
		return getUnitFrames(rightUnit);
	}

	/**
	 * Get all Datagrams in this phone's units
	 * 
	 * @return the left and right unit's Datagrams in an array
	 */
	public Datagram[] getUnitDataFrames() {
		Datagram[] leftUnitFrames = getLeftUnitFrames();
		Datagram[] rightUnitFrames = getRightUnitFrames();
		Datagram[] frames = (Datagram[]) ArrayUtils.addAll(leftUnitFrames, rightUnitFrames);
		return frames;
	}

	/**
	 * Get the number of Datagrams in a SelectedUnit's UnitData
	 * 
	 * @param unit
	 *            whose Datagrams to count
	 * @return the number of Datagrams in the unit, or 0 if unit is null
	 */
	private int getNumberOfUnitFrames(SelectedUnit unit) {
		int numberOfFrames = 0;
		try {
			Datagram[] frames = getUnitData(unit).getFrames();
			numberOfFrames = frames.length;
		} catch (NullPointerException e) {
			// leave at 0
		}
		return numberOfFrames;
	}

	/**
	 * Get the number of Datagrams in this phone's left unit
	 * 
	 * @return the number of Datagrams in the left unit, or 0 if there is no left unit
	 */
	public int getNumberOfLeftUnitFrames() {
		return getNumberOfUnitFrames(leftUnit);
	}

	/**
	 * Get the number of Datagrams in this phone's right unit
	 * 
	 * @return the number of Datagrams in the right unit, or 0 if there is no right unit
	 */
	public int getNumberOfRightUnitFrames() {
		return getNumberOfUnitFrames(rightUnit);
	}

	/**
	 * Get the number of Datagrams in this phone's left and right units
	 * 
	 * @return the number of Datagrams in this phone
	 */
	public int getNumberOfFrames() {
		return getNumberOfLeftUnitFrames() + getNumberOfRightUnitFrames();
	}

	/**
	 * Get the durations (in seconds) of each Datagram in this phone's units
	 * 
	 * @return the left and right unit's Datagram durations (in seconds) in an array
	 */
	public double[] getFrameDurations() {
		Datagram[] frames = getUnitDataFrames();
		double[] durations = new double[frames.length];
		for (int f = 0; f < frames.length; f++) {
			durations[f] = frames[f].getDuration() / sampleRate;
		}
		return durations;
	}

	/**
	 * Get the target duration (in seconds) of a HalfPhoneTarget
	 * 
	 * @param target
	 *            the target whose duration to get
	 * @return the target duration in seconds, or 0 if target is null
	 */
	private double getTargetDuration(HalfPhoneTarget target) {
		double duration = 0;
		try {
			duration = target.getTargetDurationInSeconds();
		} catch (NullPointerException e) {
			// leave at 0
		}
		return duration;
	}

	/**
	 * Get this phone's left target's duration (in seconds)
	 * 
	 * @return the left target's duration in seconds, or 0 if there is no left target
	 */
	public double getLeftTargetDuration() {
		return getTargetDuration(leftTarget);
	}

	/**
	 * Get this phone's right target's duration (in seconds)
	 * 
	 * @return the right target's duration in seconds, or 0 if there is no right target
	 */
	public double getRightTargetDuration() {
		return getTargetDuration(rightTarget);
	}

	/**
	 * Get the predicted duration of this phone (which is the sum of the left and right target's duration)
	 * 
	 * @return the predicted phone duration in seconds
	 */
	public double getPredictedDuration() {
		return getLeftTargetDuration() + getRightTargetDuration();
	}

	/**
	 * Convenience getter for a SelectedUnit's UnitData
	 * 
	 * @param unit
	 *            the unit whose UnitData to get
	 * @return the UnitData of the unit, or null, if unit is null
	 */
	private UnitData getUnitData(SelectedUnit unit) {
		UnitData unitData = null;
		try {
			unitData = (UnitData) unit.getConcatenationData();
		} catch (NullPointerException e) {
			// leave at null
		}
		return unitData;
	}

	/**
	 * Get this phone's left unit's UnitData
	 * 
	 * @return the left unit's UnitData, or null if there is no left unit
	 */
	public UnitData getLeftUnitData() {
		return getUnitData(leftUnit);
	}

	/**
	 * Get this phone's right unit's UnitData
	 * 
	 * @return the right unit's UnitData, or null if there is no right unit
	 */
	public UnitData getRightUnitData() {
		return getUnitData(rightUnit);
	}

	/**
	 * Get the actual duration (in seconds) of a SelectedUnit, from its UnitData
	 * 
	 * @param unit
	 *            whose duration to get
	 * @return the unit's duration in seconds, or 0 if unit is null
	 */
	private double getUnitDuration(SelectedUnit unit) {
		int durationInSamples = 0;
		try {
			durationInSamples = getUnitData(unit).getUnitDuration();
		} catch (NullPointerException e) {
			// leave at 0
		}
		double duration = durationInSamples / sampleRate;
		return duration;
	}

	/**
	 * Get this phone's left unit's duration (in seconds)
	 * 
	 * @return the left unit's duration in seconds, or 0 if there is no left unit
	 */
	public double getLeftUnitDuration() {
		return getUnitDuration(leftUnit);
	}

	/**
	 * Get this phone's right unit's duration (in seconds)
	 * 
	 * @return the right unit's duration in seconds, or 0 if there is no right unit
	 */
	public double getRightUnitDuration() {
		return getUnitDuration(rightUnit);
	}

	/**
	 * Get the realized duration (in seconds) of this phone (which is the sum of the durations of the left and right units)
	 * 
	 * @return the phone's realized duration in seconds
	 */
	public double getRealizedDuration() {
		return getLeftUnitDuration() + getRightUnitDuration();
	}

	/**
	 * Get the factor needed to convert the realized duration of a unit to the target duration
	 * 
	 * @param unit
	 *            whose realized duration to convert
	 * @param target
	 *            whose duration to match
	 * @return the multiplication factor to convert from the realized duration to the target duration, or 0 if the unit duration
	 *         is 0
	 */
	private double getDurationFactor(SelectedUnit unit, HalfPhoneTarget target) {
		double unitDuration = getUnitDuration(unit);
		if (unitDuration <= 0) {
			// throw new ArithmeticException("Realized duration must be greater than 0!");
			return 0;
		}
		double targetDuration = getTargetDuration(target);
		double durationFactor = targetDuration / unitDuration;
		return durationFactor;
	}

	/**
	 * Get the factor to convert this phone's left unit's duration into this phone's left target duration
	 * 
	 * @return the left duration factor
	 */
	public double getLeftDurationFactor() {
		return getDurationFactor(leftUnit, leftTarget);
	}

	/**
	 * Get the factor to convert this phone's right unit's duration into this phone's right target duration
	 * 
	 * @return the right duration factor
	 */
	public double getRightDurationFactor() {
		return getDurationFactor(rightUnit, rightTarget);
	}

	/**
	 * Get the duration factors for this phone, one per datagram. Each factor corresponding to a datagram in the left unit is that
	 * required to convert the left unit's duration to the left target duration, and likewise for the right unit's datagrams.
	 * 
	 * @return the left and right duration factors for this phone, in an array whose size matched the Datagrams in this phone's
	 *         units
	 */
	public double[] getFramewiseDurationFactors() {
		double[] durationFactors = new double[getNumberOfFrames()];
		int numberOfLeftUnitFrames = getNumberOfLeftUnitFrames();
		double leftDurationFactor = getLeftDurationFactor();
		Arrays.fill(durationFactors, 0, numberOfLeftUnitFrames, leftDurationFactor);
		double rightDurationFactor = getRightDurationFactor();
		Arrays.fill(durationFactors, numberOfLeftUnitFrames, getNumberOfFrames(), rightDurationFactor);
		return durationFactors;
	}

	/**
	 * Set the target F0 values of this phone's left half, with one value per Datagram in the phone's left unit
	 * 
	 * @param f0TargetValues
	 *            array of target F0 values to assign to the left halfphone
	 * @throws IllegalArgumentException
	 *             if the length of f0TargetValues does not match the number of Datagrams in the phone's left unit
	 */
	public void setLeftTargetF0Values(double[] f0TargetValues) throws IllegalArgumentException {
		int numberOfLeftUnitFrames = getNumberOfLeftUnitFrames();
		if (f0TargetValues.length != numberOfLeftUnitFrames) {
			throw new IllegalArgumentException("Wrong number of F0 targets (" + f0TargetValues.length
					+ ") for number of frames (" + numberOfLeftUnitFrames + " in halfphone: '" + leftUnit.toString() + "'");
		}
		this.leftF0Targets = f0TargetValues;
	}

	/**
	 * Set the target F0 values of this phone's right half, with one value per Datagram in the phone's right unit
	 * 
	 * @param f0TargetValues
	 *            array of target F0 values to assign to the right halfphone
	 * @throws IllegalArgumentException
	 *             if the length of f0TargetValues does not match the number of Datagrams in the phone's right unit
	 */
	public void setRightTargetF0Values(double[] f0TargetValues) {
		if (f0TargetValues.length != getNumberOfRightUnitFrames()) {
			throw new IllegalArgumentException("Wrong number of F0 targets (" + f0TargetValues.length
					+ ") for number of frames (" + getNumberOfRightUnitFrames() + " in halfphone: '" + rightUnit.toString() + "'");
		}
		this.rightF0Targets = f0TargetValues;
	}

	/**
	 * Get the target F0 values for this phone's left half, with one value per Datagram in the phone's left unit
	 * 
	 * @return the target F0 values for the left halfphone in an array
	 * @throws NullPointerException
	 *             if the target F0 values for the left halfphone are null
	 */
	public double[] getLeftTargetF0Values() throws NullPointerException {
		if (leftF0Targets == null) {
			throw new NullPointerException("The left target F0 values have not been assigned!");
		}
		return leftF0Targets;
	}

	/**
	 * Get the target F0 values for this phone's right half, with one value per Datagram in the phone's right unit
	 * 
	 * @return the target F0 values for the right halfphone in an array
	 * @throws NullPointerException
	 *             if the target F0 values for the right halfphone are null
	 */
	public double[] getRightTargetF0Values() throws NullPointerException {
		if (rightF0Targets == null) {
			throw new NullPointerException("The right target F0 values have not been assigned!");
		}
		return rightF0Targets;
	}

	/**
	 * Get the target F0 values for this phone, with one value per Datagram in the phone's left and right units
	 * 
	 * @return the target F0 values with one value per Datagram in an array
	 */
	public double[] getTargetF0Values() {
		double[] f0Targets = ArrayUtils.addAll(leftF0Targets, rightF0Targets);
		return f0Targets;
	}

	/**
	 * Get the mean target F0 for this phone
	 * 
	 * @return the mean predicted F0 value
	 */
	public double getPredictedF0() {
		double meanF0 = MathUtils.mean(getTargetF0Values());
		return meanF0;
	}

	/**
	 * Get the durations (in seconds) of each Datagram in a SelectedUnit's UnitData
	 * 
	 * @param unit
	 *            whose Datagrams' durations to get
	 * @return the durations (in seconds) of each Datagram in the unit in an array, or null if unit is null
	 */
	private double[] getUnitFrameDurations(SelectedUnit unit) {
		Datagram[] frames = null;
		try {
			frames = getUnitData(unit).getFrames();
		} catch (NullPointerException e) {
			return null;
		}
		assert frames != null;

		double[] frameDurations = new double[frames.length];
		for (int f = 0; f < frames.length; f++) {
			long frameDuration = frames[f].getDuration();
			frameDurations[f] = frameDuration / sampleRate; // converting to seconds
		}
		return frameDurations;
	}

	/**
	 * Get the durations (in seconds) of each Datagram in this phone's left unit
	 * 
	 * @return the durations of each Datagram in the left unit in an array, or null if there is no left unit
	 */
	public double[] getLeftUnitFrameDurations() {
		return getUnitFrameDurations(leftUnit);
	}

	/**
	 * Get the durations (in seconds) of each Datagram in this phone's right unit
	 * 
	 * @return the durations of each Datagram in the right unit in an array, or null if there is no right unit
	 */
	public double[] getRightUnitFrameDurations() {
		return getUnitFrameDurations(rightUnit);
	}

	/**
	 * Get the durations (in seconds) of each Datagram in this phone's left and right units
	 * 
	 * @return the durations of all Datagrams in this phone in an array
	 */
	public double[] getRealizedFrameDurations() {
		return ArrayUtils.addAll(getLeftUnitFrameDurations(), getRightUnitFrameDurations());
	}

	/**
	 * Get the F0 values from a SelectedUnit's Datagrams.
	 * 

* Since these are not stored explicitly, we are forced to rely on the inverse of the Datagram durations, which means that * during unvoiced regions, we recover a value of 100 Hz... * * @param unit * from whose Datagrams to recover the F0 values * @return the F0 value of each Datagram in the unit in an array, or null if the unit is null or any of the Datagrams have * zero duration */ private double[] getUnitF0Values(SelectedUnit unit) { double[] f0Values = null; try { double[] durations = getUnitFrameDurations(unit); if (!ArrayUtils.contains(durations, 0)) { f0Values = MathUtils.invert(durations); } else { // leave at null } } catch (IllegalArgumentException e) { // leave at null } return f0Values; } /** * Recover the F0 values from this phone's left unit's Datagrams * * @return the left unit's F0 values in an array, with one value per Datagram, or null if there is no left unit or any of the * left unit's Datagrams have zero duration */ public double[] getLeftUnitFrameF0s() { return getUnitF0Values(leftUnit); } /** * Recover the F0 values from this phone's right unit's Datagrams * * @return the right unit's F0 values in an array, with one value per Datagram, or null if there is no right unit or any of * the right unit's Datagrams have zero duration */ public double[] getRightUnitFrameF0s() { return getUnitF0Values(rightUnit); } /** * Recover the F0 values from each Datagram in this phone's left and right units * * @return the F0 values for each Datagram in this phone, or null if either the left or the right unit contain a Datagram with * zero duration */ public double[] getUnitFrameF0s() { double[] leftUnitFrameF0s = getLeftUnitFrameF0s(); double[] rightUnitFrameF0s = getRightUnitFrameF0s(); double[] unitFrameF0s = ArrayUtils.addAll(leftUnitFrameF0s, rightUnitFrameF0s); return unitFrameF0s; } /** * Get the realized F0 by recovering the F0 from all Datagrams in this phone and computing the mean * * @return the mean F0 of all Datagrams in this phone's left and right units */ public double getRealizedF0() { double meanF0 = MathUtils.mean(getUnitFrameF0s()); return meanF0; } /** * Get the factors required to convert the F0 values recovered from the Datagrams in a SelectedUnit to the target F0 values. * * @param unit * from which to recover the realized F0 values * @param target * for which to get the target F0 values * @return each Datagram's F0 factor in an array, or null if realized and target F0 values differ in length * @throws ArithmeticException * if any of the F0 values recovered from the unit's Datagrams is zero */ private double[] getUnitF0Factors(SelectedUnit unit, HalfPhoneTarget target) throws ArithmeticException { double[] unitF0Values = getUnitF0Values(unit); if (ArrayUtils.contains(unitF0Values, 0)) { throw new ArithmeticException("Unit frames must not have F0 of 0!"); } double[] targetF0Values; if (target == null || target.isLeftHalf()) { targetF0Values = getLeftTargetF0Values(); } else { targetF0Values = getRightTargetF0Values(); } double[] f0Factors = null; try { f0Factors = MathUtils.divide(targetF0Values, unitF0Values); } catch (IllegalArgumentException e) { // leave at null } return f0Factors; } /** * Get the factors to convert each of the F0 values in this phone's left half to the corresponding target value * * @return the F0 factors for this phone's left half in an array with one value per Datagram, or null if the number of * Datagrams does not match the number of left target F0 values */ public double[] getLeftF0Factors() { return getUnitF0Factors(leftUnit, leftTarget); } /** * Get the factors to convert each of the F0 values in this phone's right half to the corresponding target value * * @return the F0 factors for this phone's right half in an array with one value per Datagram, or null if the number of * Datagrams does not match the number of right target F0 values */ public double[] getRightF0Factors() { return getUnitF0Factors(rightUnit, rightTarget); } /** * Get the F0 factor for each Datagram in this phone's left and right units * * @return the F0 factors, in an array with one value per Datagram */ public double[] getF0Factors() { return ArrayUtils.addAll(getLeftF0Factors(), getRightF0Factors()); } /** * Get the Allophone represented by this * * @return the Allophone */ private Allophone getAllophone() { if (leftTarget != null) { return leftTarget.getAllophone(); } else if (rightTarget != null) { return rightTarget.getAllophone(); } return null; } /** * Determine whether this is a transient phone (i.e. a plosive) * * @return true if this is a plosive, false otherwise */ public boolean isTransient() { Allophone allophone = getAllophone(); if (allophone.isPlosive() || allophone.isAffricate()) { return true; } else { return false; } } /** * Determine whether this is a voiced phone * * @return true if this is voiced, false otherwise */ public boolean isVoiced() { Allophone allophone = getAllophone(); if (allophone.isVoiced()) { return true; } else { return false; } } /** * get the MaryXML Element corresponding to this Phone's Target *

* If both leftTarget and rightTarget are not null, their respective MaryXML Elements should be * equal. * * @return the MaryXML Element, or null if both halfphone targets are null */ public Element getMaryXMLElement() { if (leftTarget != null) { return leftTarget.getMaryxmlElement(); } else if (rightTarget != null) { return rightTarget.getMaryxmlElement(); } return null; } /** * for debugging, provide the names of the left and right targets as the string representation of this class */ public String toString() { String string = ""; if (leftTarget != null) { string += " " + leftTarget.getName(); } if (rightTarget != null) { string += " " + rightTarget.getName(); } return string; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy