/** * Copyright 2007, 2011 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 <http://www.gnu.org/licenses/>. * */ package marytts.signalproc.analysis; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.traversal.NodeIterator; import marytts.exceptions.InvalidDataException; import marytts.util.dom.DomUtils; import marytts.util.io.FileUtils; import marytts.util.string.StringUtils; /** * A collection of EST formatted labels with ascii text file input/output functionality * * @author Oytun Türk, reworked by Marc Schröder */ public class Labels extends AlignmentData { public Label[] items; public Labels(Label[] items) { this.items = deepCopy(items); } public Labels(String[] lines) { this(lines, 3); } public Labels(String[] lines, int minItemsPerLine) { initFromLines(lines, minItemsPerLine); } // Create ESTLabels from existing ones public Labels(Labels e) { if (e != null) { this.items = deepCopy(e.items); } } public Labels(InputStream in) throws IOException { initFromStream(in); } public Labels(String labelFile) throws IOException { initFromStream(new FileInputStream(labelFile)); } public Labels(Document acoustparams) { initFromAcoustparams(acoustparams); } public String[] getLabelSymbols() { if (items == null) return null; String[] symbols = new String[items.length]; for (int i = 0; i < items.length; i++) { symbols[i] = items[i].phn; } return symbols; } private void initFromStream(InputStream in) throws IOException { String allText = FileUtils.getStreamAsString(in, "ASCII"); String[] lines = allText.split("\n"); initFromLines(lines, 3); } private void initFromLines(String[] lines, int minimumItemsInOneLine) { ArrayList<Label> labels = new ArrayList<Label>(); for (int i = 0; i < lines.length; i++) { String[] labelInfos = lines[i].trim().split("\\s+"); if (labelInfos.length >= minimumItemsInOneLine && StringUtils.isNumeric(labelInfos[0]) && StringUtils.isNumeric(labelInfos[1])) { Label l = new Label(); labels.add(l); if (labelInfos.length > 0) l.time = Float.parseFloat(labelInfos[0]); if (labelInfos.length > 1) l.status = Integer.parseInt(labelInfos[1]); if (labelInfos.length > 2) l.phn = labelInfos[2].trim(); int restStartMin = 4; if (labelInfos.length > 3 && StringUtils.isNumeric(labelInfos[3])) l.ll = Float.parseFloat(labelInfos[3]); else { restStartMin = 3; l.ll = Float.NEGATIVE_INFINITY; } // Read additional fields if any in String format // also convert these to double values if they are // numeric if (labelInfos.length > restStartMin) { int numericCount = 0; l.rest = new String[labelInfos.length - restStartMin]; l.valuesRest = new double[labelInfos.length - restStartMin]; for (int j = 0; j < l.rest.length; j++) { l.rest[j] = labelInfos[j + restStartMin]; if (StringUtils.isNumeric(l.rest[j])) l.valuesRest[j] = Double.valueOf(l.rest[j]); else l.valuesRest[j] = Double.NEGATIVE_INFINITY; } } } } items = (Label[]) labels.toArray(new Label[0]); } /** * * @param acoustparams * @throws InvalidDataException * if any phone or boundary in acoustparams has no duration. */ private void initFromAcoustparams(Document acoustparams) { ArrayList<Label> labels = new ArrayList<Label>(); String PHONE = "ph"; String A_PHONE_DURATION = "d"; String A_PHONE_SYMBOL = "p"; String A_PHONE_END = "end"; String BOUNDARY = "boundary"; String A_BOUNDARY_DURATION = "duration"; NodeIterator it = DomUtils.createNodeIterator(acoustparams, PHONE, BOUNDARY); Element e = null; double startTime = 0; double endTime = 0; double duration = 0; double endResetCorrection = 0; String phoneSymbol; while ((e = (Element) it.nextNode()) != null) { startTime = /* previous */endTime; if (e.getTagName().equals(PHONE)) { phoneSymbol = e.getAttribute(A_PHONE_SYMBOL); // Expect end attribute to reset itself sometimes: double endValue = Double.parseDouble(e.getAttribute(A_PHONE_END)); if (endValue < startTime) { // reset endResetCorrection = startTime; } endTime = endResetCorrection + endValue; // Too imprecise to use only full milliseconds: // if (!e.hasAttribute(A_PHONE_DURATION)) { // throw new InvalidDataException("No duration for phone '"+phoneSymbol+"'"); // } // duration = 0.001 * Double.parseDouble(e.getAttribute(A_PHONE_DURATION)); // endTime = startTime + duration; } else { // BOUNDARY assert e.getTagName().equals(BOUNDARY); if (!e.hasAttribute(A_BOUNDARY_DURATION)) { throw new InvalidDataException("No duration for boundary"); } duration = 0.001 * Double.parseDouble(e.getAttribute(A_BOUNDARY_DURATION)); endTime = startTime + duration; phoneSymbol = "_"; } labels.add(new Label(endTime, phoneSymbol)); } items = (Label[]) labels.toArray(new Label[0]); } public void print() { for (int i = 0; i < items.length; i++) items[i].print(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); for (int i = 0; i < items.length; i++) { sb.append("(").append(items[i]).append(") "); } sb.append("]"); return sb.toString(); } /** * For the given time, return the index of the label at that time, if any. * * @param time * time in seconds * @return the index of the label at that time, or -1 if there isn't any */ public int getLabelIndexAtTime(double time) { if (items == null) { return -1; } // We return the first label whose end time is >= time: for (int i = 0; i < items.length; i++) { if (items[i].time >= time) { return i; } } return -1; } private Label[] deepCopy(Label[] in) { if (in == null) return null; Label[] out = new Label[in.length]; for (int i = 0; i < in.length; i++) { if (in[i] != null) { out[i] = new Label(in[i]); } } return out; } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof Labels)) return false; Labels o = (Labels) other; return Arrays.deepEquals(this.items, o.items); } @Override public int hashCode() { return 0; } }