public MaryData process(MaryData d) throws Exception { Document doc = d.getDocument(); NodeIterator it = MaryDomUtils.createNodeIterator(doc, doc, MaryXML.TOKEN); Element t = null; while ((t = (Element) it.nextNode()) != null) { String text; // Do not touch tokens for which a transcription is already // given (exception: transcription contains a '*' character: if (t.hasAttribute("ph") && !t.getAttribute("ph").contains("*")) { continue; } if (t.hasAttribute("sounds_like")) text = t.getAttribute("sounds_like"); else text = MaryDomUtils.tokenText(t); String pos = null; // use part-of-speech if available if (t.hasAttribute("pos")) { pos = t.getAttribute("pos"); } if (text != null && !text.equals("") && (pos == null || !pos.startsWith("$") /*punctuation*/)) { // If text consists of several parts (e.g., because that was // inserted into the sounds_like attribute), each part // is transcribed separately. StringBuilder ph = new StringBuilder(); String g2pMethod = null; StringTokenizer st = new StringTokenizer(text, " -"); while (st.hasMoreTokens()) { String graph = st.nextToken(); StringBuilder helper = new StringBuilder(); String phon = phonemise(graph, pos, helper); if (ph.length() == 0) { // first part // The g2pMethod of the combined beast is // the g2pMethod of the first constituant. g2pMethod = helper.toString(); ph.append(phon); } else { // following parts ph.append(" - "); // Reduce primary to secondary stress: ph.append(phon.replace('\'', ',')); } } if (ph != null && ph.length() > 0) { setPh(t, ph.toString()); t.setAttribute("g2p_method", g2pMethod); } } } MaryData result = new MaryData(outputType(), d.getLocale()); result.setDocument(doc); return result; }
/** * For an element in a MaryXML document, do what you can to determine the appropriate * AllophoneSet. First search for the suitable voice, then if that fails, go by locale. * * @param e * @return an allophone set if there is any way of determining it, or null. * @throws MaryConfigurationException if a suitable allophone set exists in principle, but there * were problems loading it. */ public static AllophoneSet determineAllophoneSet(Element e) throws MaryConfigurationException { AllophoneSet allophoneSet = null; Element voice = (Element) MaryDomUtils.getAncestor(e, MaryXML.VOICE); Voice maryVoice = Voice.getVoice(voice); if (maryVoice == null) { // Determine Locale in order to use default voice Locale locale = MaryUtils.string2locale( e.getOwnerDocument().getDocumentElement().getAttribute("xml:lang")); maryVoice = Voice.getDefaultVoice(locale); } if (maryVoice != null) { allophoneSet = maryVoice.getAllophoneSet(); } else { Locale locale = MaryUtils.string2locale( e.getOwnerDocument().getDocumentElement().getAttribute("xml:lang")); allophoneSet = determineAllophoneSet(locale); } return allophoneSet; }
public MaryData process(MaryData d) throws Exception { Document doc = MaryXML.newDocument(); Element root = doc.getDocumentElement(); Locale locale = d.getLocale(); if (locale != null) { root.setAttribute("xml:lang", MaryUtils.locale2xmllang(locale)); } Element paragraph = MaryXML.appendChildElement(root, MaryXML.PARAGRAPH); List<Utterance> utterances = d.getUtterances(); Iterator<Utterance> it = utterances.iterator(); while (it.hasNext()) { Utterance utterance = it.next(); Element insertHere = paragraph; if (logger.getEffectiveLevel().equals(Level.DEBUG)) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); utterance.dump(pw, 2, name(), true); // padding, justRelations logger.debug("Converting the following Utterance to XML:\n" + sw.toString()); } // Make sure we have the correct voice: Voice maryVoice = null; if (utterance.getVoice() != null) { maryVoice = FreeTTSVoices.getMaryVoice(utterance.getVoice()); } if (maryVoice != null) { if (insertHere.getTagName().equals(MaryXML.VOICE)) { // Are utterance voice and voiceElement voice the same? if (maryVoice.hasName(insertHere.getAttribute("name"))) { // then insertHere is set OK, leave it like it is } else { // get one higher up, create new voice element after this // one, and make insertHere point to the new voice element Element parent = (Element) insertHere.getParentNode(); Element newVoice = MaryXML.createElement(doc, MaryXML.VOICE); parent.appendChild(newVoice); newVoice.setAttribute("name", maryVoice.getName()); insertHere = newVoice; } } else { // Check if the last child of insertHere is a voice with the right name Element lastChild = MaryDomUtils.getLastChildElement(insertHere); if (lastChild != null && lastChild.getTagName().equals(MaryXML.VOICE) && maryVoice.hasName(lastChild.getAttribute("name"))) { insertHere = lastChild; } else { // create a new voice element, insert it as a child of this // node, and let insertHere point to it Element newVoice = MaryXML.createElement(doc, MaryXML.VOICE); insertHere.appendChild(newVoice); newVoice.setAttribute("name", maryVoice.getName()); insertHere = newVoice; } } // Now insertHere is the correct <voice> element. // Any prosodic settings to insert? Element prosody = insertProsodySettings(insertHere, utterance); if (prosody != null) insertHere = prosody; } // Create a sentence element <s> for this utterance: Element sentence = MaryXML.createElement(doc, MaryXML.SENTENCE); insertHere.appendChild(sentence); fillSentence(sentence, utterance); } if (logger.getEffectiveLevel().equals(Level.DEBUG)) { logger.debug("Constructed the following XML structure:"); MaryNormalisedWriter mnw = new MaryNormalisedWriter(); ByteArrayOutputStream debugOut = new ByteArrayOutputStream(); mnw.output(doc, debugOut); logger.debug(debugOut.toString()); } MaryData output = new MaryData(outputType(), d.getLocale()); output.setDocument(doc); return output; }
/** * Convert an item in the Token relation into XML, inserting it at the specified location in the * XML tree. * * @param deep whether to create a deep structure of <syllable> and <ph> elements or not. */ protected void insertToken(Item tokenItem, Element parent, boolean deep) { if (tokenItem == null || parent == null) { throw new NullPointerException("Null arguments to insertToken()"); } Document doc = parent.getOwnerDocument(); Voice maryVoice = null; if (tokenItem.getUtterance().getVoice() != null) { maryVoice = FreeTTSVoices.getMaryVoice(tokenItem.getUtterance().getVoice()); } AllophoneSet allophoneSet = (AllophoneSet) tokenItem.getUtterance().getObject("allophoneset"); if (allophoneSet == null) { throw new NullPointerException( "Utterance does not have an AllophoneSet -- should have been set in XML2UttBase.process()"); } Element insertHere = parent; boolean needMtu = false; boolean insertPhonesFromToken = tokenItem.getFeatures().isPresent("phones"); Item testWordItem = null; if (tokenItem.getFeatures().isPresent("precedingMarks")) { String marks = tokenItem.getFeatures().getString("precedingMarks"); StringTokenizer markTok = new StringTokenizer(marks, ","); while (markTok.hasMoreTokens()) { String markStr = markTok.nextToken(); Element markEl = MaryXML.createElement(doc, MaryXML.MARK); markEl.setAttribute("name", markStr); insertHere.appendChild(markEl); } } // Any boundary preceding the word? if (tokenItem.getFeatures().isPresent("precedingBoundaryTone") || tokenItem.getFeatures().isPresent("precedingBoundaryBreakindex") || tokenItem.getFeatures().isPresent("precedingBoundaryDuration")) { Element boundary = MaryXML.createElement(doc, MaryXML.BOUNDARY); insertHere.appendChild(boundary); if (tokenItem.getFeatures().isPresent("precedingBoundaryTone")) boundary.setAttribute("tone", tokenItem.getFeatures().getString("precedingBoundaryTone")); if (tokenItem.getFeatures().isPresent("precedingBoundaryBreakindex")) boundary.setAttribute( "breakindex", tokenItem.getFeatures().getString("precedingBoundaryBreakindex")); if (tokenItem.getFeatures().isPresent("precedingBoundaryDuration")) boundary.setAttribute( "duration", tokenItem.getFeatures().getString("precedingBoundaryDuration")); } if (tokenItem.getNthDaughter(1) != null || (testWordItem = tokenItem.getDaughter()) != null && !testWordItem.toString().equals(tokenItem.toString().toLowerCase())) { // Token has more than one daughter, or the only daughter is an // expanded form -- need to create an <mtu> element needMtu = true; Element mtu = MaryXML.createElement(doc, MaryXML.MTU); parent.appendChild(mtu); mtu.setAttribute("orig", tokenItem.toString()); insertHere = mtu; } // Any words? FeatureSet tokenFeatureSet = tokenItem.getFeatures(); Item tokenDaughter = tokenItem.getDaughter(); if (tokenDaughter == null) { // no word relation present // Create a <t> element based on token information only Element t = MaryXML.createElement(doc, MaryXML.TOKEN); insertHere.appendChild(t); MaryDomUtils.setTokenText(t, tokenItem.toString()); if (insertPhonesFromToken) { String[] phones = (String[]) tokenItem.getFeatures().getObject("phones"); t.setAttribute("ph", phoneArray2phoneString(allophoneSet, phones)); insertPhonesFromToken = false; } if (tokenFeatureSet.isPresent("accent")) { t.setAttribute("accent", tokenFeatureSet.getString("accent")); } } while (tokenDaughter != null) { // Part of speech, if present, is associated with the word // relation. Item wordItem = tokenDaughter.getItemAs("Word"); Element t = null; StringBuilder sampa = new StringBuilder(); if (wordItem != null) { t = MaryXML.createElement(doc, MaryXML.TOKEN); insertHere.appendChild(t); String tokenText = null; // If there is only one, non-expanded word, use text from // tokenItem in order to retain capitalisation: if (needMtu) tokenText = wordItem.toString(); else tokenText = tokenItem.toString(); MaryDomUtils.setTokenText(t, tokenText); if (insertPhonesFromToken) { String[] phones = (String[]) tokenItem.getFeatures().getObject("phones"); t.setAttribute("ph", phoneArray2phoneString(allophoneSet, phones)); insertPhonesFromToken = false; } else if (wordItem.getFeatures().isPresent("phones")) { // the word item has phones, take them only if there are no Token phones String[] phones = (String[]) wordItem.getFeatures().getObject("phones"); t.setAttribute("ph", phoneArray2phoneString(allophoneSet, phones)); } if (tokenFeatureSet.isPresent("accent")) { t.setAttribute("accent", tokenFeatureSet.getString("accent")); } FeatureSet wordFeatureSet = wordItem.getFeatures(); if (wordFeatureSet.isPresent("pos")) t.setAttribute("pos", wordFeatureSet.getString("pos")); } // Any syllables? Item sylStruct = tokenDaughter.getItemAs("SylStructure"); if (sylStruct != null && sylStruct.hasDaughters()) { Item syllableItem = sylStruct.getDaughter(); while (syllableItem != null) { if (sampa.length() > 0) sampa.append(" - "); sampa.append(insertSyllable(syllableItem, t, deep)); syllableItem = syllableItem.getNext(); } } if (sampa.length() > 0) t.setAttribute("ph", sampa.toString()); tokenDaughter = tokenDaughter.getNext(); } // Any marks after the word but before the punctuation? if (tokenItem.getFeatures().isPresent("prePuncMarks")) { String marks = tokenItem.getFeatures().getString("prePuncMarks"); StringTokenizer markTok = new StringTokenizer(marks, ","); while (markTok.hasMoreTokens()) { String markStr = markTok.nextToken(); Element markEl = MaryXML.createElement(doc, MaryXML.MARK); markEl.setAttribute("name", markStr); insertHere.appendChild(markEl); } } // Any punctuation after the word? if (tokenItem.getFeatures().isPresent("punc")) { String puncString = tokenItem.getFeatures().getString("punc"); if (!puncString.equals("")) { Element punctuation = MaryXML.createElement(doc, MaryXML.TOKEN); MaryDomUtils.setTokenText(punctuation, puncString); String pos = null; if (puncString.equals(",")) pos = "$,"; else pos = "$PUNCT"; punctuation.setAttribute("pos", pos); parent.appendChild(punctuation); } } // Any marks after the word? if (tokenItem.getFeatures().isPresent("followingMarks")) { String marks = tokenItem.getFeatures().getString("followingMarks"); StringTokenizer markTok = new StringTokenizer(marks, ","); while (markTok.hasMoreTokens()) { String markStr = markTok.nextToken(); Element markEl = MaryXML.createElement(doc, MaryXML.MARK); markEl.setAttribute("name", markStr); insertHere.appendChild(markEl); } } // Any boundary after the word? if (tokenItemHasFollowingBoundary(tokenItem)) { Element boundary = MaryXML.createElement(doc, MaryXML.BOUNDARY); insertHere.appendChild(boundary); if (tokenItem.getFeatures().isPresent("followingBoundaryTone")) boundary.setAttribute("tone", tokenItem.getFeatures().getString("followingBoundaryTone")); int breakindex = 0; if (tokenItem.getFeatures().isPresent("followingBoundaryBreakindex")) { String breakindexString = tokenItem.getFeatures().getString("followingBoundaryBreakindex"); boundary.setAttribute("breakindex", breakindexString); try { breakindex = Integer.parseInt(breakindexString); } catch (NumberFormatException nfe) { } } if (tokenItem.getFeatures().isPresent("followingBoundaryDuration")) boundary.setAttribute( "duration", tokenItem.getFeatures().getString("followingBoundaryDuration")); else { // estimate reasonable duration values based on the break index if (breakindex >= 4) { boundary.setAttribute("duration", "400"); } else if (breakindex == 3) { boundary.setAttribute("duration", "200"); } // and no duration for boundaries with bi < 3 } } }