public void setDefaultVoice(Voice voice) { if (voice == null) { return; } // check that voice locale fits before accepting the voice: Locale voiceLocale = null; voiceLocale = voice.getLocale(); Locale docLocale = getLocale(); if (docLocale == null && getType().isXMLType() && getDocument() != null && getDocument().getDocumentElement().hasAttribute("xml:lang")) { docLocale = MaryUtils.string2locale(getDocument().getDocumentElement().getAttribute("xml:lang")); } if (docLocale != null && voiceLocale != null && !(MaryUtils.subsumes(docLocale, voiceLocale) || MaryUtils.subsumes(voiceLocale, docLocale))) { logger.warn( "Voice `" + voice.getName() + "' does not match document locale `" + docLocale + "' -- ignoring!"); } this.defaultVoice = voice; }
public Mary4To5VoiceConverter(List<VoiceComponentDescription> voiceDescriptions, File voiceZip) { voiceDescription = null; mary4Zip = voiceZip; for (VoiceComponentDescription d : voiceDescriptions) { if (d.getPackageFilename().equals(mary4Zip.getName())) { voiceDescription = d; break; } } if (voiceDescription == null) { throw new IllegalArgumentException( "No matching voice description for file " + mary4Zip.getName()); } if (!MaryUtils.isLog4jConfigured()) { BasicConfigurator.configure(); } logger = Logger.getLogger(this.getClass()); logger.info( voiceDescription.getName() + " " + voiceDescription.getVersion() + " (" + voiceDescription.getLocale() + " " + voiceDescription.getGender() + ")"); }
/** * Phonemise the word text. This starts with a simple lexicon lookup, followed by some heuristics, * and finally applies letter-to-sound rules if nothing else was successful. * * @param text the textual (graphemic) form of a word. * @param pos the part-of-speech of the word * @param g2pMethod This is an awkward way to return a second String parameter via a * StringBuilder. If a phonemisation of the text is found, this parameter will be filled with * the method of phonemisation ("lexicon", ... "rules"). * @return a phonemisation of the text if one can be generated, or null if no phonemisation method * was successful. */ public String phonemise(String text, String pos, StringBuilder g2pMethod) { // First, try a simple userdict and lexicon lookup: String result = userdictLookup(text, pos); if (result != null) { g2pMethod.append("userdict"); return result; } result = lexiconLookup(text, pos); if (result != null) { g2pMethod.append("lexicon"); return result; } // HB 150915 adding secondary lexicon result = secondary_lexiconLookup(text, pos); if (result != null) { g2pMethod.append("lexicon"); return result; } // Lookup attempts failed. Try normalising exotic letters // (diacritics on vowels, etc.), look up again: String normalised = MaryUtils.normaliseUnicodeLetters(text, getLocale()); if (!normalised.equals(text)) { result = userdictLookup(normalised, pos); if (result != null) { g2pMethod.append("userdict"); return result; } result = lexiconLookup(normalised, pos); if (result != null) { g2pMethod.append("lexicon"); return result; } // HB 150915 adding secondary lexicon result = secondary_lexiconLookup(text, pos); if (result != null) { g2pMethod.append("lexicon"); return result; } } // Cannot find it in the lexicon -- apply letter-to-sound rules // to the normalised form String phones = lts.predictPronunciation(text); try { result = lts.syllabify(phones); } catch (IllegalArgumentException e) { logger.error(String.format("Problem with token <%s> [%s]: %s", text, phones, e.getMessage())); } if (result != null) { g2pMethod.append("rules"); return result; } return null; }
/** * 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; }
/** * Instantiate an object by calling one of its constructors. * * @param objectInitInfo a string description of the object to instantiate. The objectInitInfo is * expected to have one of the following forms: * <ol> * <li>my.cool.Stuff * <li>my.cool.Stuff(any,string,args,without,spaces) * <li>my.cool.Stuff(arguments,$my.special.property,other,args) * </ol> * where 'my.special.property' is a property in one of the MARY config files. * @return the newly instantiated object. * @throws ClassNotFoundException * @throws IllegalArgumentException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException * @throws SecurityException * @throws NoSuchMethodException */ public static Object instantiateObject(String objectInitInfo) throws MaryConfigurationException { Object obj = null; String[] args = null; String className = null; try { if (objectInitInfo.contains("(")) { // arguments int firstOpenBracket = objectInitInfo.indexOf('('); className = objectInitInfo.substring(0, firstOpenBracket); int lastCloseBracket = objectInitInfo.lastIndexOf(')'); args = objectInitInfo.substring(firstOpenBracket + 1, lastCloseBracket).split(","); for (int i = 0; i < args.length; i++) { if (args[i].startsWith("$")) { // replace value with content of property named after the $ args[i] = MaryProperties.getProperty(args[i].substring(1)); } args[i] = args[i].trim(); } } else { // no arguments className = objectInitInfo; } Class<? extends Object> theClass = Class.forName(className).asSubclass(Object.class); // Now invoke Constructor with args.length String arguments if (args != null) { Class<String>[] constructorArgTypes = new Class[args.length]; Object[] constructorArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { constructorArgTypes[i] = String.class; constructorArgs[i] = args[i]; } Constructor<? extends Object> constructor = (Constructor<? extends Object>) theClass.getConstructor(constructorArgTypes); obj = constructor.newInstance(constructorArgs); } else { obj = theClass.newInstance(); } } catch (Exception e) { // try to make e's message more informative if possible throw new MaryConfigurationException( "Cannot instantiate object from '" + objectInitInfo + "': " + MaryUtils.getFirstMeaningfulMessage(e), e); } return obj; }
/** * Apply the SoP to a Target to get its predicted value * * @param target target from where to predict * @return result predicted value */ @Override protected float evaluate(Target target) { float result = 0; if (targetAttributeName.contentEquals("f0")) { result = (float) sopModels.get("f0").interpret(target); } else { if (target.getAllophone().isVowel()) result = (float) sopModels.get("vowel").interpret(target); else if (target.getAllophone().isConsonant()) result = (float) sopModels.get("consonant").interpret(target); else if (target.getAllophone().isPause()) result = (float) sopModels.get("pause").interpret(target); else { // ignore but complain MaryUtils.getLogger("SoPModel") .warn("Warning: No SoP model for target " + target.toString()); } } return result; }
/** * Set up a new wagon process. Wagon.setWagonExecutable() must be called beforehand. * * @param featureDefinition * @param featureVectors * @param aDistanceMeasure * @param dir * @param balance * @param stop * @throws IOException if there was no call to Wagon.setWagonExecutable() with an executable file * before calling this constructor. */ public Wagon( String id, FeatureDefinition featureDefinition, FeatureVector[] featureVectors, DistanceMeasure aDistanceMeasure, File dir, int balance, int stop) throws IOException { if (wagonExecutable == null || !wagonExecutable.isFile()) { throw new IOException("No wagon executable set using Wagon.setExecutable()!"); } this.logger = MaryUtils.getLogger("Wagon"); this.id = id; this.featureDefinition = featureDefinition; this.fv = featureVectors; this.distMeasure = aDistanceMeasure; this.distFile = new File(dir, id + ".dist"); this.descFile = new File(dir, id + ".desc"); this.featFile = new File(dir, id + ".feat"); this.cartFile = new File(dir, id + ".cart"); this.systemCall = wagonExecutable.getAbsolutePath() + " -desc " + descFile.getAbsolutePath() + " -data " + featFile.getAbsolutePath() + " -balance " + balance + " -distmatrix " + distFile.getAbsolutePath() + " -stop " + stop + " -output " + cartFile.getAbsolutePath(); }
/** * Verify if the java virtual machine is in a very low memory condition. The memory is considered * very low if less than half a specified value is still available for processing. "Available" * memory is calculated using <code>availableMemory()</code>.The threshold value can be specified * as the Mary property mary.lowmemory (in bytes). It defaults to 20000000 bytes. * * @return a boolean indicating whether or not the system is in very low memory condition. */ public static boolean veryLowMemoryCondition() { return MaryUtils.availableMemory() < lowMemoryThreshold() / 2; }
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; }
/** * Model for predicting duration and F0 from HMMs * * @author marcela */ public class HMMModel extends Model { /** Configuration information of the model */ private HMMData htsData = null; /** HMM trees and pdfs for this model. */ private CartTreeSet cart; /** Feature definition used when training HMMs. */ FeatureDefinition hmmFeatureDefinition; /** to calculate duration in seconds. */ private float fperiodsec; protected static Logger logger = MaryUtils.getLogger("HMMModel"); /** * If the model is instantiated because the same HHMModel is used for predicting both F0 and * duration, set this variable true; this is done in Voice.loadAcousticModels(), when creating the * models. */ private boolean predictDurAndF0 = false; /** * This list keeps a copy of the utterance model, this is done when the same HMMModel is used for * predicting durations and F0, the idea is to keep in the utterance model list the state * durations predicted together with duration, these state durations are used when predicting F0, * so the same state duration is applied. */ private Map<List<Element>, HTSUttModel> uttModels = new WeakHashMap<List<Element>, HTSUttModel>(); /** * Model constructor * * @param featureManager the feature processor manager used to compute the symbolic features used * for prediction * @param dataFileName in HMM models this data file corresponds to the configuration file of the * HMM voice * @param targetAttributeName attribute in MARYXML to predict * @param targetAttributeFormat print style, not used in HMM models * @param featureName not used in HMMModel * @param predictFrom not used in HMMModel * @param applyTo not used in HMMModel * @throws MaryConfigurationException if there are missing files or problems loading trees and pdf * files. */ public HMMModel( FeatureProcessorManager featureManager, String voiceName, InputStream dataStream, String targetAttributeName, String targetAttributeFormat, String featureName, String predictFrom, String applyTo) throws MaryConfigurationException { super( featureManager, voiceName, dataStream, targetAttributeName, targetAttributeFormat, featureName, predictFrom, applyTo); if (!(targetAttributeName.contentEquals("d") || targetAttributeName.contentEquals("f0"))) { throw new MaryConfigurationException( "targetAttributeName = " + targetAttributeName + " Not known"); } load(); } /** * This variable is set to true whenever the same HMMModel is used to predict both duration and * F0. by default the variable is false, so that means that two different HMMModels are used for * predicting duration and F0, in this case there is no state durations information to predict F0. * * @param bval */ public void setPredictDurAndF0(boolean bval) { predictDurAndF0 = bval; } /** * Load trees and pdfs, from HMM configuration file. * * @throws MaryConfigurationException if there are missing files or problems loading trees and pdf * files. */ @Override protected void loadData() throws IOException, MaryConfigurationException { if (htsData == null) htsData = new HMMData(); // we use the configuration of the HMM voice whose hmm models will be used htsData.initHMMDataForHMMModel(voiceName); cart = htsData.getCartTreeSet(); fperiodsec = ((float) htsData.getFperiod() / (float) htsData.getRate()); predictionFeatureNames = htsData.getFeatureDefinition().getFeatureNames(); } /** * Predict duration for the list of elements. If the same HMMModel is used to predict duration and * F0 then a utterance model is created and kept in a WeakHashMap, so the next call to this * module, for predicting F0, can use that utterance model. * * @param elements elements from MaryXML for which to predict the values * @throws MaryConfigurationException if error searching in HMM trees. */ @Override public void applyTo(List<Element> elements) throws MaryConfigurationException { logger.debug("predicting duration"); HTSUttModel um = predictAndSetDuration(elements, elements); if (predictDurAndF0) { // this same model will be used for predicting F0 -- remember um uttModels.put(elements, um); } } /** * Predict F0 for the list of elements and apply to another list of elements. If the same HMMModel * is used to predict duration and F0 then there must be a utterance model created in a previous * call to this module, that will be used to predict F0. If there is no previously created * utterance model then one is created. * * @param predictFromElements elements from MaryXML for which to predict the values * @param applyToElements elements from MaryXML for which to apply the predicted values * @throws MaryConfigurationException if error searching in HMM trees. */ @Override public void applyFromTo(List<Element> predictFromElements, List<Element> applyToElements) throws MaryConfigurationException { logger.debug("predicting F0"); // Two possibilities: Either we have an uttModel due to a previous call to applyTo() // (in which case the lookup key should be applyToElements), // or we don't -- in which case we must create an uttModel from the XML. HTSUttModel um; if (predictDurAndF0) { logger.debug("using already created utterance model, it contains predicted state durations."); um = uttModels.get( applyToElements); // it must be already created so get it from the uttModels Map } else { logger.debug("creating utterance model with equal values for state durations."); um = createUttModel( predictFromElements); // create a um, state durations are set equal for all states } assert um != null; predictAndSetF0(applyToElements, um); } /** * Predict durations and state durations from predictFromElements and apply durations to * applyToElements. A utterance model is created that contains the predicted state durations. * * @param predictFromElements elements to predict from * @param applyToElements elements to apply predicted durations * @return HTSUttModel a utterance model * @throws MaryConfigurationException if error searching in HMM trees. */ private HTSUttModel predictAndSetDuration( List<Element> predictFromElements, List<Element> applyToElements) throws MaryConfigurationException { List<Element> predictorElements = predictFromElements; List<Target> predictorTargets = getTargets(predictorElements); FeatureVector fv = null; HTSUttModel um = new HTSUttModel(); FeatureDefinition feaDef = htsData.getFeatureDefinition(); double diffdurOld = 0.0; double diffdurNew = 0.0; String durAttributeName = "d"; try { // (1) Predict the values for (int i = 0; i < predictorTargets.size(); i++) { fv = predictorTargets.get(i).getFeatureVector(); um.addUttModel(new HTSModel(cart.getNumStates())); HTSModel m = um.getUttModel(i); /* this function also sets the phone name, the phone between - and + */ m.setPhoneName(fv.getFeatureAsString(feaDef.getFeatureIndex("phone"), feaDef)); /* Check if context-dependent gv (gv without sil) */ if (htsData.getUseContextDependentGV()) { if (m.getPhoneName().contentEquals("_")) m.setGvSwitch(false); } /* increment number of models in utterance model */ um.setNumModel(um.getNumModel() + 1); /* update number of states */ um.setNumState(um.getNumState() + cart.getNumStates()); // Estimate state duration from state duration model (Gaussian) diffdurNew = cart.searchDurInCartTree(m, fv, htsData, diffdurOld); diffdurOld = diffdurNew; double duration = m.getTotalDur() * fperiodsec; // in seconds um.setTotalFrame(um.getTotalFrame() + m.getTotalDur()); // System.out.format("HMMModel: phone=%s duration=%.3f sec. m.getTotalDur()=%d\n", // m.getPhoneName(), duration, m.getTotalDur()); /* Find pdf for LF0, this function sets the pdf for each state. * and determines, according to the HMM models, whether the states are voiced or unvoiced, (it can be possible that some states are voiced * and some unvoiced).*/ cart.searchLf0InCartTree(m, fv, feaDef, htsData.getUV()); for (int mstate = 0; mstate < cart.getNumStates(); mstate++) { for (int frame = 0; frame < m.getDur(mstate); frame++) if (m.getVoiced(mstate)) um.setLf0Frame(um.getLf0Frame() + 1); } // set the value in elements Element element = applyToElements.get(i); // "evaluate" pseudo XPath syntax: // TODO this needs to be extended to take into account targetAttributeNames like "foo/@bar", // which would add the // bar attribute to the foo child of this element, creating the child if not already // present... if (durAttributeName.startsWith("@")) { durAttributeName = durAttributeName.replaceFirst("@", ""); } String formattedTargetValue = String.format(targetAttributeFormat, duration); // System.out.println("HMMModel: formattedTargetValue = " + formattedTargetValue); // if the attribute already exists for this element, append targetValue: if (element.hasAttribute(durAttributeName)) { formattedTargetValue = element.getAttribute(durAttributeName) + " " + formattedTargetValue; } // set the new attribute value: element.setAttribute(durAttributeName, formattedTargetValue); } return um; } catch (Exception e) { throw new MaryConfigurationException("Error searching in tree when predicting duration. ", e); } } /** * Predict F0 from the utterance model and apply to elements * * @param applyToElements elements to apply predicted F0s * @param um utterance model that contains the set of elements (phonemes) and state durations for * generating F0. * @throws MaryConfigurationException if error generating F0 out of HMMs trees and pdfs. */ private void predictAndSetF0(List<Element> applyToElements, HTSUttModel um) throws MaryConfigurationException { HTSModel m; try { String f0AttributeName = "f0"; HTSParameterGeneration pdf2par = new HTSParameterGeneration(); /* Once we have all the phone models Process UttModel */ /* Generate sequence of speech parameter vectors, generate parameters out of sequence of pdf's */ boolean debug = false; /* so it does not save the generated parameters. */ /* this function generates features just for the trees and pdf that are not null in the HMM cart*/ pdf2par.htsMaximumLikelihoodParameterGeneration(um, htsData); // (2) include the predicted values in applicableElements (as it is done in Model) boolean voiced[] = pdf2par.getVoicedArray(); int numVoiced = 0; // make sure that the number of applicable elements is the same as the predicted number of // elements assert applyToElements.size() == um.getNumModel(); float f0; String formattedTargetValue; int t = 0; for (int i = 0; i < applyToElements.size(); i++) { // this will be the same as the utterance model set m = um.getUttModel(i); int k = 1; int numVoicedInModel = m.getNumVoiced(); formattedTargetValue = ""; // System.out.format("phone = %s dur_in_frames=%d num_voiced_frames=%d : ", // m.getPhoneName(), m.getTotalDur(), numVoicedInModel); for (int mstate = 0; mstate < cart.getNumStates(); mstate++) { for (int frame = 0; frame < m.getDur(mstate); frame++) { if (voiced[ t++]) { // numVoiced and t are not the same because voiced values can be true or // false, numVoiced count just the voiced f0 = (float) Math.exp(pdf2par.getlf0Pst().getPar(numVoiced++, 0)); formattedTargetValue += "(" + Integer.toString((int) ((k * 100.0) / numVoicedInModel)) + "," + Integer.toString((int) f0) + ")"; k++; } } } Element element = applyToElements.get(i); // "evaluate" pseudo XPath syntax: // TODO this needs to be extended to take into account targetAttributeNames like "foo/@bar", // which would add the // bar attribute to the foo child of this element, creating the child if not already // present... if (f0AttributeName.startsWith("@")) { f0AttributeName = f0AttributeName.replaceFirst("@", ""); } // format targetValue according to targetAttributeFormat // String formattedTargetValue = String.format(targetAttributeFormat, targetValue); // set the new attribute value: // if the whole segment is unvoiced then f0 should not be fixed? if (formattedTargetValue.length() > 0) element.setAttribute(f0AttributeName, formattedTargetValue); // System.out.println(formattedTargetValue); } // once finished re-set to null um // um = null; } catch (Exception e) { throw new MaryConfigurationException("Error generating F0 out of HMMs trees and pdfs. ", e); } } /** * Create a utterance model list from feature vectors predicted from elements. * * @param predictFromElements elements from MaryXML from where to get feature vectors. * @return Utterance model um containing state durations and pdfs already searched on the trees to * generate F0. * @throws MaryConfigurationException if error searching in HMM trees. */ private HTSUttModel createUttModel(List<Element> predictFromElements) throws MaryConfigurationException { int i, k, s, t, mstate, frame, durInFrames, durStateInFrames, numVoicedInModel; HTSModel m; List<Element> predictorElements = predictFromElements; List<Target> predictorTargets = getTargets(predictorElements); FeatureVector fv; HTSUttModel um = new HTSUttModel(); FeatureDefinition feaDef = htsData.getFeatureDefinition(); float duration; double diffdurOld = 0.0; double diffdurNew = 0.0; float f0s[] = null; try { // (1) Predict the values for (i = 0; i < predictorTargets.size(); i++) { fv = predictorTargets.get(i).getFeatureVector(); Element e = predictFromElements.get(i); um.addUttModel(new HTSModel(cart.getNumStates())); m = um.getUttModel(i); /* this function also sets the phone name, the phone between - and + */ m.setPhoneName(fv.getFeatureAsString(feaDef.getFeatureIndex("phone"), feaDef)); /* Check if context-dependent gv (gv without sil) */ if (htsData.getUseContextDependentGV()) { if (m.getPhoneName().contentEquals("_")) m.setGvSwitch(false); } /* increment number of models in utterance model */ um.setNumModel(um.getNumModel() + 1); /* update number of states */ um.setNumState(um.getNumState() + cart.getNumStates()); // get the duration from the element duration = Integer.parseInt(e.getAttribute("d")) * 0.001f; // in sec. // distribute the duration (in frames) among the five states, here it is done the same // amount for each state durInFrames = (int) (duration / fperiodsec); durStateInFrames = (int) (durInFrames / cart.getNumStates()); m.setTotalDur(0); // reset to set new value according to duration for (s = 0; s < cart.getNumStates(); s++) { m.setDur(s, durStateInFrames); m.setTotalDur(m.getTotalDur() + m.getDur(s)); } um.setTotalFrame(um.getTotalFrame() + m.getTotalDur()); System.out.format( "createUttModel: duration=%.3f sec. durInFrames=%d durStateInFrames=%d m.getTotalDur()=%d\n", duration, durInFrames, durStateInFrames, m.getTotalDur()); /* Find pdf for LF0, this function sets the pdf for each state. * and determines, according to the HMM models, whether the states are voiced or unvoiced, (it can be possible that some states are voiced * and some unvoiced).*/ cart.searchLf0InCartTree(m, fv, feaDef, htsData.getUV()); for (mstate = 0; mstate < cart.getNumStates(); mstate++) { for (frame = 0; frame < m.getDur(mstate); frame++) if (m.getVoiced(mstate)) um.setLf0Frame(um.getLf0Frame() + 1); } } return um; } catch (Exception e) { throw new MaryConfigurationException( "Error searching in tree when creating utterance model. ", e); } } /** * Apply the HMM to a Target to get its predicted value, this method is not used in HMMModel. * * @throws RuntimeException if this method is called. */ @Override protected float evaluate(Target target) { throw new RuntimeException("This method should never be called"); } }
/** * The unit database of a voice * * @author Marc Schröder */ public class UnitDatabase { protected TargetCostFunction targetCostFunction; protected JoinCostFunction joinCostFunction; protected StatisticalCostFunction sCostFunction = null; protected UnitFileReader unitReader; protected int numUnits; protected CART preselectionCART; protected TimelineReader audioTimeline; protected TimelineReader basenameTimeline; protected int backtrace; protected Logger logger = MaryUtils.getLogger("UnitDatabase"); public UnitDatabase() {} public void load( TargetCostFunction aTargetCostFunction, JoinCostFunction aJoinCostFunction, UnitFileReader aUnitReader, CART aPreselectionCART, TimelineReader anAudioTimeline, TimelineReader aBasenameTimeline, int backtraceLeafSize) { this.targetCostFunction = aTargetCostFunction; this.joinCostFunction = aJoinCostFunction; this.unitReader = aUnitReader; this.numUnits = (unitReader != null ? unitReader.getNumberOfUnits() : 0); this.preselectionCART = aPreselectionCART; this.audioTimeline = anAudioTimeline; this.basenameTimeline = aBasenameTimeline; this.backtrace = backtraceLeafSize; } public void load( TargetCostFunction aTargetCostFunction, JoinCostFunction aJoinCostFunction, StatisticalCostFunction asCostFunction, UnitFileReader aUnitReader, CART aPreselectionCART, TimelineReader anAudioTimeline, TimelineReader aBasenameTimeline, int backtraceLeafSize) { this.targetCostFunction = aTargetCostFunction; this.joinCostFunction = aJoinCostFunction; this.sCostFunction = asCostFunction; this.unitReader = aUnitReader; this.numUnits = (unitReader != null ? unitReader.getNumberOfUnits() : 0); this.preselectionCART = aPreselectionCART; this.audioTimeline = anAudioTimeline; this.basenameTimeline = aBasenameTimeline; this.backtrace = backtraceLeafSize; } public TargetCostFunction getTargetCostFunction() { return targetCostFunction; } public JoinCostFunction getJoinCostFunction() { return joinCostFunction; } public UnitFileReader getUnitFileReader() { return unitReader; } public TimelineReader getAudioTimeline() { return audioTimeline; } public StatisticalCostFunction getSCostFunction() { return sCostFunction; } /** * Preselect a set of candidates that could be used to realise the given target. * * @param target a Target object representing an optimal unit * @return an <span style="color:red;">unsorted</span> ArrayList of ViterbiCandidates, each * containing the (same) target and a (different) Unit object */ public List<ViterbiCandidate> getCandidates(Target target) { // BEGIN blacklisting // The point of this is to get the value of the "blacklist" attribute in the first child element // of the MaryXML // and store it in the blacklist String variable. // This code seems rather inelegant; perhaps there is a better way to access the MaryXML from // this method? String blacklist = ""; String unitBasename = "This must never be null or the empty string!"; // otherwise candidate selection fails! Element targetElement = target.getMaryxmlElement(); blacklist = DomUtils.getAttributeFromClosestAncestorOfAnyKind(targetElement, "blacklist"); // END blacklisting // logger.debug("Looking for candidates in cart "+target.getName()); // get the cart tree and extract the candidates int[] clist = (int[]) preselectionCART.interpret(target, backtrace); logger.debug("For target " + target + ", selected " + clist.length + " units"); // Now, clist is an array of unit indexes. List<ViterbiCandidate> candidates = new ArrayList<ViterbiCandidate>(); for (int i = 0; i < clist.length; i++) { // The target is the same for all these candidates in the queue // remember the actual unit: Unit unit = unitReader.getUnit(clist[i]); candidates.add(new ViterbiCandidate(target, unit, targetCostFunction)); } // Blacklisting without crazy performance drop: // just remove candidates again if their basenames are blacklisted java.util.Iterator<ViterbiCandidate> candIt = candidates.iterator(); while (candIt.hasNext()) { ViterbiCandidate candidate = candIt.next(); unitBasename = getFilename(candidate.getUnit()); if (blacklist.contains(unitBasename)) { candIt.remove(); } } return candidates; } /** * For debugging, return the basename of the original audio file from which the unit is coming, as * well as the start time in that file. * * @param unit unit * @return a String containing basename followed by a space and the unit's start time, in seconds, * from the beginning of the file. If no basenameTimeline was specified for this voice, * returns the string "unknown origin". */ public String getFilenameAndTime(Unit unit) { if (basenameTimeline == null) return "unknown origin"; long[] offset = new long[1]; try { Datagram[] datagrams = basenameTimeline.getDatagrams(unit.startTime, 1, unitReader.getSampleRate(), offset); Datagram filenameData = datagrams[0]; float time = (float) offset[0] / basenameTimeline.getSampleRate(); String filename = new String(filenameData.getData(), "UTF-8"); return filename + " " + time; } catch (Exception e) { logger.warn( "Problem getting filename and time for unit " + unit.index + " at time " + unit.startTime, e); return "unknown origin"; } } /** * For debugging, return the basename of the original audio file from which the unit is coming. * * @param unit unit * @return a String containing basename. If no basenameTimeline was specified for this voice, * returns the string "unknown origin". */ public String getFilename(Unit unit) { // if (basenameTimeline == null) return "unknown origin"; try { Datagram filenameData = basenameTimeline.getDatagram(unit.startTime); String filename = new String(filenameData.getData(), "UTF-8"); return filename; } catch (Exception e) { logger.warn("Problem getting filename for unit " + unit.index, e); return "unknown origin"; } } }
public void computeFeaturesFor(String basename) throws IOException, Exception { String text; Locale localVoice; localVoice = MaryUtils.string2locale(locale); // First, test if there is a corresponding .rawmaryxml file in textdir: File rawmaryxmlFile = new File(db.getProp(db.MARYXMLDIR) + basename + db.getProp(db.MARYXMLEXT)); if (rawmaryxmlFile.exists()) { text = FileUtils.getFileAsString(rawmaryxmlFile, "UTF-8"); } else { text = getMaryXMLHeaderWithInitialBoundary(locale) + FileUtils.getFileAsString( new File(db.getProp(db.TEXTDIR) + basename + db.getProp(db.TEXTEXT)), "UTF-8") + "</maryxml>"; } File pfeatFile = new File(unitfeatureDir, basename + featsExt); OutputStream os = new BufferedOutputStream(new FileOutputStream(pfeatFile)); MaryClient maryClient = getMaryClient(); /*Vector voices = maryClient.getVoices(localVoice); MaryClient.Voice defaultVoice = (MaryClient.Voice) voices.firstElement(); String voiceName = defaultVoice.name();*/ // maryClient.process(text, maryInputType, maryOutputType, null, null, os); maryClient.process(text, maryInputType, maryOutputType, locale, null, "slt-arctic", os); // maryClient.process(text, maryInputType, maryOutputType, null, "slt-arctic", os, timeout); // maryClient.getOutputDataTypes().size() // MaryData result = new MaryData(os); os.flush(); os.close(); // System.out.println(" TO STRING: "+new FileReader(pfeatFile).toString()); // BufferedReader bfr = new BufferedReader(new FileReader(pfeatFile)); String line; MaryData d = new MaryData(MaryDataType.get("PHONEMISED_EN"), Locale.US); // d.readFrom(new ByteArrayInputStream(os.toByteArray())); d.readFrom(new FileReader(pfeatFile)); // MaryData d = new MaryData(pfeatFile); Document doc = d.getDocument(); // Document acoustparams = d.getDocument(); // NodeIterator it = ((DocumentTraversal)acoustparams).createNodeIterator(acoustparams, // NodeFilter.SHOW_ELEMENT,new NameNodeFilter(new String[]{MaryXML.TOKEN, // MaryXML.BOUNDARY}),false); NodeIterator it = ((DocumentTraversal) doc) .createNodeIterator( doc, NodeFilter.SHOW_ELEMENT, new NameNodeFilter(MaryXML.TOKEN), false); Element t = null; while ((t = (Element) it.nextNode()) != null) { if (t.hasAttribute("g2p_method")) { String g2p = t.getAttribute("g2p_method"); String nodeText = t.getTextContent().trim(); if (g2p.equals("rules")) { // && nodeText.equals("!")){ System.out.print(basename + " ----> " + nodeText); if (bnl.contains(basename)) bnl.remove(basename); System.out.println(" SO removing basename: " + basename); } // System.out.println("G2P:"+t.getAttribute("g2p_method")); // System.out.println("Text:"+t.getTextContent()); } } /*while((line =bfr.readLine()) != null){ //boolean b = m.matches(); if(Pattern.matches("rules", line)) System.out.println(basename + " LINE ---> " + line); }*/ // System.out.println(" TO STRING: "+line); }
/** * Parameter generation out of trained HMMs. * * <p>Java port and extension of HTS engine API version 1.04 Extension: mixed excitation * * @author Marcela Charfuelan */ public class HTSParameterGeneration { public static final double INFTY = ((double) 1.0e+38); public static final double INFTY2 = ((double) 1.0e+19); public static final double INVINF = ((double) 1.0e-38); public static final double INVINF2 = ((double) 1.0e-19); private HTSPStream mcepPst = null; private HTSPStream strPst = null; private HTSPStream magPst = null; private HTSPStream lf0Pst = null; private boolean voiced[]; private int totalUttFrame; // total number of frames in a mcep, str or mag Pst private int totalLf0Frame; // total number of f0 voiced frames in a lf0 Pst private Logger logger = MaryUtils.getLogger("ParameterGeneration"); public HTSPStream getMcepPst() { return mcepPst; } public void setMcepPst(HTSPStream var) { mcepPst = var; }; public HTSPStream getStrPst() { return strPst; } public void setStrPst(HTSPStream var) { strPst = var; }; public HTSPStream getMagPst() { return magPst; } public void setMagPst(HTSPStream var) { magPst = var; }; public HTSPStream getlf0Pst() { return lf0Pst; } public void setlf0Pst(HTSPStream var) { lf0Pst = var; }; public boolean[] getVoicedArray() { return voiced; } public void setVoicedArray(boolean[] var) { voiced = var; } // only used in HTSEngineTest /* Inverse of a given double */ /* We actually need the inverse of the matrix of covariance, but since this matrix */ /* is a diagonal matrix, then we just need to calculate the inverse of each of the */ /* numbers in the diagonal. */ public static double finv(double x) { if (x >= INFTY2) return 0.0; if (x <= -INFTY2) return 0.0; if (x <= INVINF2 && x >= 0) return INFTY; if (x >= -INVINF2 && x < 0) return -INFTY; return 1.0 / x; } /** * HTS maximum likelihood parameter generation * * @param um : utterance model sequence after processing Mary context features * @param htsData : HMM pdfs model set. * @throws Exception Exception */ public void htsMaximumLikelihoodParameterGeneration(HTSUttModel um, final HMMData htsData) throws Exception { CartTreeSet ms = htsData.getCartTreeSet(); /* Initialisation of PStream objects */ /* Initialise Parameter generation using UttModel um and Modelset ms */ /* initialise PStream objects for all the parameters that are going to be generated: */ /* mceppst, strpst, magpst, lf0pst */ /* Here i should pass the window files to initialise the dynamic windows dw */ /* for the moment the dw are all the same and hard-coded */ if (htsData.getPdfMgcStream() != null) mcepPst = new HTSPStream( ms.getMcepVsize(), um.getTotalFrame(), HMMData.FeatureType.MGC, htsData.getMaxMgcGvIter()); /* for lf0 count just the number of lf0frames that are voiced or non-zero */ if (htsData.getPdfLf0Stream() != null) lf0Pst = new HTSPStream( ms.getLf0Stream(), um.getLf0Frame(), HMMData.FeatureType.LF0, htsData.getMaxLf0GvIter()); /* The following are optional in case of generating mixed excitation */ if (htsData.getPdfStrStream() != null) strPst = new HTSPStream( ms.getStrVsize(), um.getTotalFrame(), HMMData.FeatureType.STR, htsData.getMaxStrGvIter()); if (htsData.getPdfMagStream() != null) magPst = new HTSPStream( ms.getMagVsize(), um.getTotalFrame(), HMMData.FeatureType.MAG, htsData.getMaxMagGvIter()); int lf0Frame = 0; // counts voiced frames int uttFrame = 0; // counts all frames voiced = new boolean[um.getTotalFrame()]; // local variables for faster access int msNumStates = ms.getNumStates(); int totalFrames = um.getTotalFrame(); for (int i = 0; i < um.getNumUttModel(); i++) { HTSModel m = um.getUttModel(i); int numVoicedInModel = 0; for (int state = 0; state < msNumStates; state++) { int dur = m.getDur(state); Arrays.fill(voiced, uttFrame, uttFrame += dur, m.getVoiced(state)); if (m.getVoiced(state)) lf0Frame += dur; } } /* mcepframe and lf0frame are used in the original code to initialise the T field */ /* in each pst, but here the pst are already initialised .... */ logger.debug("utteranceFrame=" + uttFrame + " lf0frame=" + lf0Frame); // Step 1: initialize fields in the parameter streams uttFrame = 0; lf0Frame = 0; /* copy pdfs */ for (int i = 0; i < um.getNumUttModel(); i++) { HTSModel m = um.getUttModel(i); boolean gvSwitch = m.getGvSwitch(); for (int state = 0; state < msNumStates; state++) { for (int frame = 0; frame < m.getDur(state); frame++) { /* copy pdfs for mcep */ if (mcepPst != null) { mcepPst.setMseq(uttFrame, m.getMean(FeatureType.MGC, state)); mcepPst.setVseq(uttFrame, m.getVariance(FeatureType.MGC, state)); if (!gvSwitch) mcepPst.setGvSwitch(uttFrame, false); } /* copy pdf for str */ if (strPst != null) { strPst.setMseq(uttFrame, m.getMean(FeatureType.STR, state)); strPst.setVseq(uttFrame, m.getVariance(FeatureType.STR, state)); if (!gvSwitch) strPst.setGvSwitch(uttFrame, false); } /* copy pdf for mag */ if (magPst != null) { magPst.setMseq(uttFrame, m.getMean(FeatureType.MAG, state)); magPst.setVseq(uttFrame, m.getVariance(FeatureType.MAG, state)); if (!gvSwitch) magPst.setGvSwitch(uttFrame, false); } /* copy pdfs for lf0 */ if (lf0Pst != null && !htsData.getUseAcousticModels()) { for (int k = 0; k < ms.getLf0Stream(); k++) { boolean nobound = true; /* check if current frame is voiced/unvoiced boundary or not */ for (int n = lf0Pst.getDWLeftBoundary(k); n <= lf0Pst.getDWRightBoundary(k); n++) if ((uttFrame + n) <= 0 || totalFrames <= (uttFrame + n)) nobound = false; else nobound = (nobound && voiced[uttFrame + n]); /* copy pdfs */ if (voiced[uttFrame]) { lf0Pst.setMseq(lf0Frame, k, m.getLf0Mean(state, k)); if (nobound || k == 0) lf0Pst.setIvseq(lf0Frame, k, finv(m.getLf0Variance(state, k))); else /* the variances for dynamic features are set to inf on v/uv boundary */ lf0Pst.setIvseq(lf0Frame, k, 0.0); } } } if (voiced[uttFrame]) { if (!gvSwitch) lf0Pst.setGvSwitch(lf0Frame, false); lf0Frame++; } uttFrame++; } /* for each frame in this state */ } /* for each state in this model */ } /* for each model in this utterance */ GVModelSet gvms = htsData.getGVModelSet(); // Step 2: set dynamic features to infinity on the borders for MGC/STR/MAG if (mcepPst != null) mcepPst.fixDynFeatOnBoundaries(); if (strPst != null) strPst.fixDynFeatOnBoundaries(); if (magPst != null) magPst.fixDynFeatOnBoundaries(); // Step 3: optimize individual parameter streams /* parameter generation for mcep */ if (mcepPst != null) { logger.info("Parameter generation for MGC: "); if (htsData.getUseGV() && (htsData.getPdfMgcGVStream() != null)) mcepPst.setGvMeanVar(gvms.getGVmeanMgc(), gvms.getGVcovInvMgc()); mcepPst.mlpg(htsData, htsData.getUseGV()); } // parameter generation for lf0 */ if (htsData.getUseAcousticModels()) loadMaryXmlF0(um, htsData); else if (lf0Pst != null) { logger.info("Parameter generation for LF0: "); if (htsData.getUseGV() && (htsData.getPdfLf0GVStream() != null)) lf0Pst.setGvMeanVar(gvms.getGVmeanLf0(), gvms.getGVcovInvLf0()); lf0Pst.mlpg(htsData, htsData.getUseGV()); // here we need set realisedF0 setRealisedF0(lf0Pst, um, msNumStates); } /* parameter generation for str */ boolean useGV = false; if (strPst != null) { logger.debug("Parameter generation for STR "); if (htsData.getUseGV() && (htsData.getPdfStrGVStream() != null)) { useGV = true; strPst.setGvMeanVar(gvms.getGVmeanStr(), gvms.getGVcovInvStr()); } strPst.mlpg(htsData, useGV); } /* parameter generation for mag */ useGV = false; if (magPst != null) { logger.info("Parameter generation for MAG "); if (htsData.getUseGV() && (htsData.getPdfMagGVStream() != null)) { useGV = true; magPst.setGvMeanVar(gvms.getGVmeanMag(), gvms.getGVcovInvMag()); } magPst.mlpg(htsData, useGV); } } /* method htsMaximumLikelihoodParameterGeneration */ /* Save generated parameters in a binary file */ public void saveParamMaryFormat(String fileName, HTSPStream par, HMMData.FeatureType type) { int t, m, i; double ws = 0.025; /* window size in seconds */ double ss = 0.005; /* skip size in seconds */ int fs = 16000; /* sampling rate */ try { if (type == HMMData.FeatureType.LF0) { fileName += ".ptc"; /* * DataOutputStream data_out = new DataOutputStream (new FileOutputStream (fileName)); * data_out.writeFloat((float)(ws*fs)); data_out.writeFloat((float)(ss*fs)); data_out.writeFloat((float)fs); * data_out.writeFloat(voiced.length); * * i=0; for(t=0; t<voiced.length; t++){ // here par.getT are just the voiced!!! so the actual length of frames can * be taken from the voiced array if( voiced[t] ){ data_out.writeFloat((float)Math.exp(par.getPar(i,0))); i++; * }System.out.println("GEN f0s[" + t + "]=" + Math.exp(lf0Pst.getPar(i,0))); else * data_out.writeFloat((float)0.0); } data_out.close(); */ i = 0; double f0s[] = new double[voiced.length]; // System.out.println("voiced.length=" + voiced.length); for (t = 0; t < voiced.length; t++) { // here par.getT are just the voiced!!! so the actual length of frames can // be taken from the voiced array if (voiced[t]) { f0s[t] = Math.exp(par.getPar(i, 0)); i++; } else f0s[t] = 0.0; System.out.println("GEN f0s[" + t + "]=" + f0s[t]); } /* * i am using this function but it changes the values of sw, and ss *samplingrate+0.5??? for the HTS values * ss=0.005 and sw=0.025 is not a problem though */ PitchReaderWriter.write_pitch_file(fileName, f0s, (float) (ws), (float) (ss), fs); } else if (type == HMMData.FeatureType.MGC) { int numfrm = par.getT(); int dimension = par.getOrder(); Mfccs mgc = new Mfccs(numfrm, dimension); fileName += ".mfc"; for (t = 0; t < par.getT(); t++) for (m = 0; m < par.getOrder(); m++) mgc.mfccs[t][m] = par.getPar(t, m); mgc.params.samplingRate = fs; /* samplingRateInHz */ mgc.params.skipsize = (float) ss; /* skipSizeInSeconds */ mgc.params.winsize = (float) ws; /* windowSizeInSeconds */ mgc.writeMfccFile(fileName); /* * The whole set for header is in the following order: ler.writeInt(numfrm); ler.writeInt(dimension); * ler.writeFloat(winsize); ler.writeFloat(skipsize); ler.writeInt(samplingRate); */ } logger.info("saveParam in file: " + fileName); } catch (IOException e) { logger.info("IO exception = " + e); } } /* Save generated parameters in a binary file */ public void saveParam(String fileName, HTSPStream par, HMMData.FeatureType type) { int t, m, i; try { if (type == HMMData.FeatureType.LF0) { fileName += ".f0"; DataOutputStream data_out = new DataOutputStream(new FileOutputStream(fileName)); i = 0; for (t = 0; t < voiced.length; t++) { /* here par.getT are just the voiced!!! */ if (voiced[t]) { data_out.writeFloat((float) Math.exp(par.getPar(i, 0))); i++; } else data_out.writeFloat((float) 0.0); } data_out.close(); } else if (type == HMMData.FeatureType.MGC) { fileName += ".mgc"; DataOutputStream data_out = new DataOutputStream(new FileOutputStream(fileName)); for (t = 0; t < par.getT(); t++) for (m = 0; m < par.getOrder(); m++) data_out.writeFloat((float) par.getPar(t, m)); data_out.close(); } logger.info("saveParam in file: " + fileName); } catch (IOException e) { logger.info("IO exception = " + e); } } private void loadMaryXmlF0(HTSUttModel um, HMMData htsData) throws Exception { logger.info("Using f0 from maryXML acoustparams"); int i, n, numVoiced; HTSModel m; double[] dval; double lastF0 = 0.0; numVoiced = 0; Vector<Double> f0Vector = new Vector<Double>(); for (i = 0; i < um.getNumUttModel(); i++) { m = um.getUttModel(i); // System.out.format("\nmodel=%s totalDur=%d numVoicedFrames=%d F0=%s\n", m.getPhoneName(), // m.getTotalDur(), // m.getNumVoiced(), m.getMaryXmlF0()); // get contour for this model if voiced frames and maryXml has f0 values dval = getContourSegment(m.getMaryXmlF0(), m.getNumVoiced()); // accumulate the values for (n = 0; n < dval.length; n++) f0Vector.add(dval[n]); } // interpolate values if necessary interpolateSegments(f0Vector); // create a new Lf0Pst with the values from maryXML HTSPStream newLf0Pst = new HTSPStream(3, f0Vector.size(), HMMData.FeatureType.LF0, htsData.getMaxLf0GvIter()); for (n = 0; n < f0Vector.size(); n++) newLf0Pst.setPar(n, 0, Math.log(f0Vector.get(n))); setlf0Pst(newLf0Pst); } private double[] getContourSegment(String maryXmlF0, int numVoiced) throws Exception { int i, t = 0, k = 0, f = 0; // f is number of f0 in xml string // just fill the values in approx. position double[] f0Vector = new double[numVoiced]; int index[] = new int[2]; double value[] = new double[2]; int key, n, interval; double valF0, lastValF0; if (maryXmlF0 != null) { Pattern p = Pattern.compile("(\\d+,\\d+)"); Matcher xml = p.matcher(maryXmlF0); SortedMap<Integer, Double> f0Map = new TreeMap<Integer, Double>(); int numF0s = 0; while (xml.find()) { String[] f0Values = (xml.group().trim()).split(","); f0Map.put(new Integer(f0Values[0]), new Double(f0Values[1])); numF0s++; } Set<Map.Entry<Integer, Double>> s = f0Map.entrySet(); Iterator<Map.Entry<Integer, Double>> if0 = s.iterator(); if (numF0s == numVoiced) { t = 0; while (if0.hasNext() && t < numVoiced) { Map.Entry<Integer, Double> mf0 = if0.next(); key = (Integer) mf0.getKey(); valF0 = (Double) mf0.getValue(); f0Vector[t++] = valF0; } } else { if (numF0s < numVoiced) { for (i = 0; i < numVoiced; i++) // then just some values will be filled, so the other must be 0 f0Vector[i] = 0.0; } while (if0.hasNext() && t < numVoiced) { Map.Entry<Integer, Double> mf0 = if0.next(); key = (Integer) mf0.getKey(); valF0 = (Double) mf0.getValue(); if (key == 0) n = 0; else if (key == 100) n = numVoiced - 1; else n = (int) ((numVoiced * key) / 100.0); if (n >= 0 && n < numVoiced) f0Vector[n] = valF0; } // while(if0.hasNext()) } // numF0s == numVoiced } // if maryXML != null // for(i=0; i<numVoiced; i++) // then just some values will be filled, so the other must be 0 // System.out.format("%.1f ", f0Vector[i]); // System.out.println(); return f0Vector; } private void interpolateSegments(Vector<Double> f0) { int i, n, interval; double slope; // check where there are zeros and interpolate int[] index = new int[2]; double[] value = new double[2]; index[0] = 0; value[0] = 0.0; for (i = 0; i < f0.size(); i++) { if (f0.get(i) > 0.0) { index[1] = i; value[1] = f0.get(i); interval = index[1] - index[0]; if (interval > 1) { // System.out.format("Interval to interpolate index[0]=%d // index[1]=%d\n",index[0],index[1]); slope = ((value[1] - value[0]) / interval); for (n = index[0]; n < index[1]; n++) { double newVal = (slope * (n - index[0])) + value[0]; f0.set(n, newVal); // System.out.format(" n=%d value:%.1f\n",n,newVal); } } index[0] = index[1]; value[0] = value[1]; } } } private void setRealisedF0(HTSPStream lf0Pst, HTSUttModel um, int numStates) { int t = 0; int vt = 0; for (int i = 0; i < um.getNumUttModel(); i++) { HTSModel m = um.getUttModel(i); int numVoicedInModel = m.getNumVoiced(); String formattedF0 = ""; int k = 1; for (int state = 0; state < numStates; state++) { for (int frame = 0; frame < m.getDur(state); frame++) { if (voiced[t++]) { float f0 = (float) Math.exp(lf0Pst.getPar(vt++, 0)); formattedF0 += "(" + Integer.toString((int) ((k * 100.0) / numVoicedInModel)) + "," + Integer.toString((int) f0) + ")"; k++; } } // for unvoiced frame } // for state if (!formattedF0.contentEquals("")) { m.setMaryXmlF0(formattedF0); // m.setUnit_f0ArrayStr(formattedF0); // System.out.println("ph=" + m.getPhoneName() + " " + formattedF0); } } // for model in utterance model list } } /* class ParameterGeneration */
/** * IO functions for directed graphs in Mary format * * @author Marcela Charfuelan, Marc Schröder */ public class DirectedGraphWriter { protected Logger logger = MaryUtils.getLogger(this.getClass().getName()); /** * Dump the graph in Mary format * * @param graph graph * @param destFile the destination file * @throws IOException IOException */ public void saveGraph(DirectedGraph graph, String destFile) throws IOException { if (graph == null) throw new NullPointerException("Cannot dump null graph"); if (destFile == null) throw new NullPointerException("No destination file"); logger.debug("Dumping directed graph in Mary format to " + destFile + " ..."); // Open the destination file and output the header DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(destFile))); // create new CART-header and write it to output file MaryHeader hdr = new MaryHeader(MaryHeader.DIRECTED_GRAPH); hdr.writeTo(out); Properties props = graph.getProperties(); if (props == null) { out.writeShort(0); } else { ByteArrayOutputStream baos = new ByteArrayOutputStream(); props.store(baos, null); byte[] propData = baos.toByteArray(); out.writeShort(propData.length); out.write(propData); } // feature definition graph.getFeatureDefinition().writeBinaryTo(out); // dump graph dumpBinary(graph, out); // finish out.close(); logger.debug(" ... done\n"); } public void toTextOut(DirectedGraph graph, PrintWriter pw) throws IOException { try { int numLeafNodes = setUniqueLeafNodeIds(graph); int numDecNodes = setUniqueDecisionNodeIds(graph); int numGraphNodes = setUniqueDirectedGraphNodeIds(graph); pw.println( "Num decision nodes= " + numDecNodes + " Num leaf nodes= " + numLeafNodes + " Num directed graph nodes= " + numGraphNodes); printDecisionNodes(graph, null, pw); pw.println("\n----------------\n"); printLeafNodes(graph, null, pw); pw.println("\n----------------\n"); printDirectedGraphNodes(graph, null, pw); pw.flush(); pw.close(); } catch (IOException ioe) { IOException newIOE = new IOException("Error dumping graph to standard output"); newIOE.initCause(ioe); throw newIOE; } } /** * Assign unique ids to leaf nodes. * * @param graph * @return the number of different leaf nodes */ private int setUniqueLeafNodeIds(DirectedGraph graph) { int i = 0; for (LeafNode l : graph.getLeafNodes()) { l.setUniqueLeafId(++i); } return i; } /** * Assign unique ids to decision nodes. * * @param graph * @return the number of different decision nodes */ private int setUniqueDecisionNodeIds(DirectedGraph graph) { int i = 0; for (DecisionNode d : graph.getDecisionNodes()) { d.setUniqueDecisionNodeId(++i); } return i; } /** * Assign unique ids to directed graph nodes. * * @param graph * @return the number of different directed graph nodes */ private int setUniqueDirectedGraphNodeIds(DirectedGraph graph) { int i = 0; for (DirectedGraphNode g : graph.getDirectedGraphNodes()) { g.setUniqueGraphNodeID(++i); } return i; } private void dumpBinary(DirectedGraph graph, DataOutput os) throws IOException { try { int numLeafNodes = setUniqueLeafNodeIds(graph); int numDecNodes = setUniqueDecisionNodeIds(graph); int numGraphNodes = setUniqueDirectedGraphNodeIds(graph); int maxNum = 1 << 30; if (numLeafNodes > maxNum || numDecNodes > maxNum || numGraphNodes > maxNum) { throw new UnsupportedOperationException( "Cannot write more than " + maxNum + " nodes of one type in this format"); } // write the number of decision nodes os.writeInt(numDecNodes); printDecisionNodes(graph, os, null); // write the number of leaves. os.writeInt(numLeafNodes); printLeafNodes(graph, os, null); // write the number of directed graph nodes os.writeInt(numGraphNodes); printDirectedGraphNodes(graph, os, null); } catch (IOException ioe) { IOException newIOE = new IOException("Error dumping CART to output stream"); newIOE.initCause(ioe); throw newIOE; } } private void printDecisionNodes(DirectedGraph graph, DataOutput out, PrintWriter pw) throws IOException { for (DecisionNode decNode : graph.getDecisionNodes()) { int id = decNode.getUniqueDecisionNodeId(); String nodeDefinition = decNode.getNodeDefinition(); int featureIndex = decNode.getFeatureIndex(); DecisionNode.Type nodeType = decNode.getDecisionNodeType(); if (out != null) { // dump in binary form to output out.writeInt(featureIndex); out.writeInt(nodeType.ordinal()); // Now, questionValue, which depends on nodeType switch (nodeType) { case BinaryByteDecisionNode: out.writeInt(((BinaryByteDecisionNode) decNode).getCriterionValueAsByte()); assert decNode.getNumberOfDaugthers() == 2; break; case BinaryShortDecisionNode: out.writeInt(((BinaryShortDecisionNode) decNode).getCriterionValueAsShort()); assert decNode.getNumberOfDaugthers() == 2; break; case BinaryFloatDecisionNode: out.writeFloat(((BinaryFloatDecisionNode) decNode).getCriterionValueAsFloat()); assert decNode.getNumberOfDaugthers() == 2; break; case ByteDecisionNode: case ShortDecisionNode: out.writeInt(decNode.getNumberOfDaugthers()); } // The child nodes for (int i = 0, n = decNode.getNumberOfDaugthers(); i < n; i++) { Node daughter = decNode.getDaughter(i); if (daughter == null) { out.writeInt(0); } else if (daughter.isDecisionNode()) { int daughterID = ((DecisionNode) daughter).getUniqueDecisionNodeId(); // Mark as decision node: daughterID |= DirectedGraphReader.DECISIONNODE << 30; out.writeInt(daughterID); } else if (daughter.isLeafNode()) { int daughterID = ((LeafNode) daughter).getUniqueLeafId(); // Mark as leaf node: if (daughterID != 0) daughterID |= DirectedGraphReader.LEAFNODE << 30; out.writeInt(daughterID); } else if (daughter.isDirectedGraphNode()) { int daughterID = ((DirectedGraphNode) daughter).getUniqueGraphNodeID(); // Mark as directed graph node: if (daughterID != 0) daughterID |= DirectedGraphReader.DIRECTEDGRAPHNODE << 30; out.writeInt(daughterID); } } } if (pw != null) { // dump to print writer StringBuilder strNode = new StringBuilder("-" + id + " " + nodeDefinition); for (int i = 0, n = decNode.getNumberOfDaugthers(); i < n; i++) { strNode.append(" "); Node daughter = decNode.getDaughter(i); if (daughter == null) { strNode.append("0"); } else if (daughter.isDecisionNode()) { int daughterID = ((DecisionNode) daughter).getUniqueDecisionNodeId(); strNode.append("-").append(daughterID); out.writeInt(daughterID); } else if (daughter.isLeafNode()) { int daughterID = ((LeafNode) daughter).getUniqueLeafId(); if (daughterID == 0) strNode.append("0"); else strNode.append("id").append(daughterID); } else if (daughter.isDirectedGraphNode()) { int daughterID = ((DirectedGraphNode) daughter).getUniqueGraphNodeID(); if (daughterID == 0) strNode.append("0"); else strNode.append("DGN").append(daughterID); } } pw.println(strNode.toString()); } } } private void printLeafNodes(DirectedGraph graph, DataOutput out, PrintWriter pw) throws IOException { for (LeafNode leaf : graph.getLeafNodes()) { if (leaf.getUniqueLeafId() == 0) // empty leaf, do not write continue; LeafType leafType = leaf.getLeafNodeType(); if (leafType == LeafType.FeatureVectorLeafNode) { leafType = LeafType.IntArrayLeafNode; // save feature vector leaf nodes as int array leaf nodes } if (out != null) { // Leaf node type out.writeInt(leafType.ordinal()); } if (pw != null) { pw.print("id" + leaf.getUniqueLeafId() + " " + leafType); } switch (leaf.getLeafNodeType()) { case IntArrayLeafNode: int data[] = ((IntArrayLeafNode) leaf).getIntData(); // Number of data points following: if (out != null) out.writeInt(data.length); if (pw != null) pw.print(" " + data.length); // for each index, write the index for (int i = 0; i < data.length; i++) { if (out != null) out.writeInt(data[i]); if (pw != null) pw.print(" " + data[i]); } break; case FloatLeafNode: float stddev = ((FloatLeafNode) leaf).getStDeviation(); float mean = ((FloatLeafNode) leaf).getMean(); if (out != null) { out.writeFloat(stddev); out.writeFloat(mean); } if (pw != null) { pw.print(" 1 " + stddev + " " + mean); } break; case IntAndFloatArrayLeafNode: case StringAndFloatLeafNode: int data1[] = ((IntAndFloatArrayLeafNode) leaf).getIntData(); float floats[] = ((IntAndFloatArrayLeafNode) leaf).getFloatData(); // Number of data points following: if (out != null) out.writeInt(data1.length); if (pw != null) pw.print(" " + data1.length); // for each index, write the index and then its float for (int i = 0; i < data1.length; i++) { if (out != null) { out.writeInt(data1[i]); out.writeFloat(floats[i]); } if (pw != null) pw.print(" " + data1[i] + " " + floats[i]); } break; case FeatureVectorLeafNode: FeatureVector fv[] = ((FeatureVectorLeafNode) leaf).getFeatureVectors(); // Number of data points following: if (out != null) out.writeInt(fv.length); if (pw != null) pw.print(" " + fv.length); // for each feature vector, write the index for (int i = 0; i < fv.length; i++) { if (out != null) out.writeInt(fv[i].getUnitIndex()); if (pw != null) pw.print(" " + fv[i].getUnitIndex()); } break; case PdfLeafNode: throw new IllegalArgumentException("Writing of pdf leaf nodes not yet implemented"); } if (pw != null) pw.println(); } } private void printDirectedGraphNodes(DirectedGraph graph, DataOutput out, PrintWriter pw) throws IOException { for (DirectedGraphNode g : graph.getDirectedGraphNodes()) { int id = g.getUniqueGraphNodeID(); if (id == 0) continue; // empty node, do not write Node leaf = g.getLeafNode(); int leafID = 0; int leafNodeType = 0; if (leaf != null) { if (leaf instanceof LeafNode) { leafID = ((LeafNode) leaf).getUniqueLeafId(); leafNodeType = DirectedGraphReader.LEAFNODE; } else if (leaf instanceof DirectedGraphNode) { leafID = ((DirectedGraphNode) leaf).getUniqueGraphNodeID(); leafNodeType = DirectedGraphReader.DIRECTEDGRAPHNODE; } else { throw new IllegalArgumentException("Unexpected leaf type: " + leaf.getClass()); } } DecisionNode d = g.getDecisionNode(); int decID = d != null ? d.getUniqueDecisionNodeId() : 0; if (out != null) { int outLeafId = leafID == 0 ? 0 : leafID | (leafNodeType << 30); out.writeInt(outLeafId); int outDecId = decID == 0 ? 0 : decID | (DirectedGraphReader.DECISIONNODE << 30); out.writeInt(outDecId); } if (pw != null) { pw.print("DGN" + id); if (leafID == 0) { pw.print(" 0"); } else if (leaf.isLeafNode()) { pw.print(" id" + leafID); } else { assert leaf.isDirectedGraphNode(); pw.print(" DGN" + leafID); } if (decID == 0) pw.print(" 0"); else pw.print(" -" + decID); pw.println(); } } } }
public MaryHttpServer() { logger = MaryUtils.getLogger("server"); }
/** @author steigner */ public class PhonemiseDenglish { private static Logger logger = MaryUtils.getLogger("PhonemiseDenglish"); private String[] vowels = {"a", "e", "i", "o", "u"}; private String[] dentalPlosives = {"t", "d"}; private Hashtable<String, String> flectionToPhon = null; private Hashtable<String, String> prefixLexicon = null; private Hashtable<String, String> endingsAndAffixes = null; private Hashtable<String, String> terminalVoicings = null; private HashSet<String> endingSet = null; private int maxEndingLength = 0; private int maxPrefixLength = 0; private Locale locale; private JPhonemiser jphon = null; // building an instance of JPhonemiser for using // lexiconLookup method and transducers from this class private MorphologyReader mr = new MorphologyReader(); public PhonemiseDenglish(JPhonemiser jphon) throws Exception { this.jphon = jphon; String classpathPrefix = "/marytts/language/de/lexicon/denglish/"; this.flectionToPhon = mr.loadInputModel(getClass().getResourceAsStream(classpathPrefix + "flectionsPhonLex.xml")); this.prefixLexicon = mr.loadInputModel(getClass().getResourceAsStream(classpathPrefix + "PrefixLex.xml")); for (Iterator<String> prefixIt = prefixLexicon.keySet().iterator(); prefixIt.hasNext(); ) { String prefix = prefixIt.next(); if (prefix.length() > this.maxPrefixLength) this.maxPrefixLength = prefix.length(); } this.endingsAndAffixes = mr.loadInputModel(getClass().getResourceAsStream(classpathPrefix + "germanEndings.xml")); this.terminalVoicings = mr.loadInputModel( getClass().getResourceAsStream(classpathPrefix + "terminal_voicing_for_german.xml")); String[] endingList = getEndingsAndAffixes("flections"); // list of flection endings of a specific language if (endingList != null) { this.endingSet = new HashSet<String>(50); // for (int j = 0; j < endingList.length; j++) { if (endingList[j].length() > this.maxEndingLength) { this.maxEndingLength = endingList[j].length(); } this.endingSet.add(endingList[j]); } } } /** * Method that is called from JPhonemiser - Managing all the processing * * @param toBePhonemised The input word * @param allowOtherLanguage allowOtherLanguage * @return Transcription of input word if one can be built - null otherwise */ public Result processWord(String toBePhonemised, boolean allowOtherLanguage) { // cleanAllInstanceVariables(); Word currentWord = new Word(toBePhonemised); // Vector result = new Vector(); String transcription = null; Result currentResult = new Result(); long time1 = System.currentTimeMillis(); if (currentWord.getToBePhonemised().equals("")) { logger.debug("Empty String!"); // return null; return currentResult; } // word or item must at least have length 3 to be a denglish item if (currentWord.getToBePhonemised().length() <= 2) { logger.debug("Input to short to be a deng item"); logger.debug("Now using letter to sound rules"); // return null; return currentResult; } // try cutting off inflection ending and/or prefix: transcription = processFlection(currentWord, currentResult, allowOtherLanguage); if (transcription != null) { long time2 = System.currentTimeMillis(); long end = time2 - time1; logger.debug("Processing took: " + end + " ms"); // return transcription; currentResult.setTranscription(transcription); // System.out.println("1) var is "+currentResult.isUsedOtherLanguageToPhonemise()); return currentResult; } // try compound analysis, first without, then with other language: transcription = compoundAnalysis(currentWord, currentResult, false); if (transcription != null) { long time3 = System.currentTimeMillis(); long end = time3 - time1; logger.debug("Processing took: " + end + " ms"); // return transcription; currentResult.setTranscription(transcription); // System.out.println("2) var is "+currentResult.isUsedOtherLanguageToPhonemise()); return currentResult; } transcription = compoundAnalysis(currentWord, currentResult, allowOtherLanguage); if (transcription != null) { long time3 = System.currentTimeMillis(); long end = time3 - time1; logger.debug("Processing took: " + end + " ms"); // return transcription; currentResult.setTranscription(transcription); // System.out.println("2) var is "+currentResult.isUsedOtherLanguageToPhonemise()); return currentResult; } // return null; return currentResult; } /** * Try to process the input word, as it stands, or by cutting off prefixes or inflectional * suffixes. * * @param toBePhonemised the input word * @param allowOtherLanguage allowOtherLanguage * @return the transcription of the word, or null if the word could not be transcribed */ private String processFlection(Word word, Result currentResult, boolean allowOtherLanguage) { String toBePhonemised = word.getToBePhonemised(); logger.debug("processFlection is starting with: " + toBePhonemised); // First of all, make sure there is no userdict/lexicon entry: String transcription = jphon.userdictLookup(toBePhonemised, null); if (transcription != null) { return transcription; } transcription = jphon.lexiconLookup(toBePhonemised, null); if (transcription != null) { return transcription; } // Try to process by cutting off endings only, without cutting off prefix: if (allowOtherLanguage) { transcription = processFlectionEnding(word, currentResult); } if (transcription != null) { return transcription; } // try removing prefix: // Enforce at least 3 characters in the stem (the part of the word that comes after the prefix): int maxPrefLen = Math.min(this.maxPrefixLength, word.getToBePhonemised().length() - 3); for (int i = maxPrefLen; i > 0; i--) { String prefix = word.getToBePhonemised().substring(0, i).toLowerCase(); String prefixPhon = prefixLexiconLookup(prefix); if (prefixPhon != null) { logger.debug("Prefix found: " + prefix + " [" + prefixPhon + "]"); Word partialWord = new Word(word.getToBePhonemised().substring(i)); // recursively call this method, i.e. allow multiple prefixes: String restTranscription = processFlection(partialWord, currentResult, allowOtherLanguage); if (restTranscription != null) { // yes, found valid analysis if (prefixPhon.indexOf("'") != -1) { restTranscription = restTranscription.replaceAll("'", ""); } transcription = prefixPhon + "-" + restTranscription; return transcription; } } } return null; } /** * Try to process the input word as a verbal or adjective flection. * * @param word the input word * @param currentResult currentResult * @return the transcription of the word */ private String processFlectionEnding(Word word, Result currentResult) { String toBePhonemised = word.getToBePhonemised(); logger.debug("processFlectionEnding is starting with: " + toBePhonemised); String wordMinusFlectionEnding = null; String flectionEnding = null; String result = null; // separateFlectionEndings returns null if no valid flection ending can be found // otherwise it returns an array containing the word without flection and the flection String[] wordPlusEnding = separateFlectionEndings(toBePhonemised, this.maxEndingLength); if (wordPlusEnding != null) { wordMinusFlectionEnding = wordPlusEnding[0]; flectionEnding = wordPlusEnding[1]; word = transformWordToEnBaseForm( wordMinusFlectionEnding, flectionEnding, word); // language-dependent if (word.getOtherLanguageBaseForm() != null) { word.setFlectionEnding(flectionEnding); result = transcribeFlection(word, currentResult); // language-dependent } else { // case of upgedatete // start separateFlectionEndings() with a smaller number of ending chars int currentEndingLength = flectionEnding.length(); wordPlusEnding = separateFlectionEndings(toBePhonemised, currentEndingLength - 1); if (wordPlusEnding != null) { wordMinusFlectionEnding = wordPlusEnding[0]; flectionEnding = wordPlusEnding[1]; word = transformWordToEnBaseForm( wordMinusFlectionEnding, flectionEnding, word); // language-dependent if (word.getOtherLanguageBaseForm() != null) { word.setFlectionEnding(flectionEnding); result = transcribeFlection(word, currentResult); // language-dependent } else { currentEndingLength = flectionEnding.length(); wordPlusEnding = separateFlectionEndings(toBePhonemised, currentEndingLength - 1); if (wordPlusEnding != null) { wordMinusFlectionEnding = wordPlusEnding[0]; flectionEnding = wordPlusEnding[1]; word = transformWordToEnBaseForm( wordMinusFlectionEnding, flectionEnding, word); // language-dependent if (word.getOtherLanguageBaseForm() != null) { word.setFlectionEnding(flectionEnding); result = transcribeFlection(word, currentResult); // language-dependent } } } } else { // array is null // we have sth. that is already without flection ending like 'check' word = transformWordToEnBaseForm(toBePhonemised, null, word); // language-dependent if (word.getOtherLanguageBaseForm() != null) { result = transcribeFlection(word, currentResult); // language-dependent } else { logger.debug("Unable to transcribe flection. Returning null."); } } } } // System.out.println("var is in processFlection: // "+currentResult.isUsedOtherLanguageToPhonemise()); logger.debug("processFlection: " + result); return result; } /** * Analyses parts of input word for affixes, compounds etc. * * @param word the input word * @param currentResult currentResult * @param allowOtherLanguage whether to allow component words from other language in compound * analysis * @return If a transcription for the input can be found, then it is returned. Otherwise returns * null. */ private String compoundAnalysis(Word word, Result currentResult, boolean allowOtherLanguage) { // Chop off longest possible prefixes and try to look them up // in the lexicon. Any part must have a minimum length of 3 -> 2!! characters. logger.debug("compoundAnalysis is starting with: " + word.getToBePhonemised()); for (int i = word.getToBePhonemised().length() - 3; i >= 3; i--) { // -3!!! >= 3!!! String firstPhon = null; String fugePhon = null; String restPhon = null; String[] genitiveAccusativeAndPluralEndings = getEndingsAndAffixes("noun_genitive_accusative_and_plural_endings"); // should // be // 's' // and // 'n' // for // german String prefix = word.getToBePhonemised().substring(0, i); logger.debug("Pre: " + prefix); firstPhon = jphon.userdictLookup(prefix, null); if (firstPhon == null) { firstPhon = jphon.lexiconLookup(prefix, null); } if (firstPhon == null && allowOtherLanguage) { firstPhon = jphon.phonemiseEn(prefix); if (firstPhon != null) { currentResult.setUsedOtherLanguageToPhonemise(true); } } if (firstPhon != null) { // found a valid prefix // TODO: shouldn't this call processFlection()? String rest = word.getToBePhonemised().substring(i); logger.debug("Rest is: " + rest); // Is the rest a simple lexical entry? // restPhon = germanLexiconLookup(rest); restPhon = prefixLexiconLookup(rest); logger.debug("RestPhon: " + restPhon); if (restPhon == null) { restPhon = jphon.userdictLookup(rest, null); } if (restPhon == null) { restPhon = jphon.lexiconLookup(rest, null); } if (restPhon == null && allowOtherLanguage) { restPhon = jphon.phonemiseEn(rest); if (restPhon != null) { currentResult.setUsedOtherLanguageToPhonemise(true); } } if (restPhon == null) { for (int j = 0; j < genitiveAccusativeAndPluralEndings.length; j++) { if (rest.endsWith(genitiveAccusativeAndPluralEndings[j])) { logger.debug("rest ends with: " + genitiveAccusativeAndPluralEndings[j]); String restWithoutLast = rest.substring(0, rest.length() - 1); String restPhonDe = jphon.userdictLookup(restWithoutLast, null); if (restPhonDe == null) restPhonDe = jphon.lexiconLookup(restWithoutLast, null); String genitiveAndPluralEndingTrans = endingTranscriptionLookup(genitiveAccusativeAndPluralEndings[j]); if (restPhonDe != null) { restPhon = restPhonDe + genitiveAndPluralEndingTrans; } else if (allowOtherLanguage) { String restPhonEn = jphon.phonemiseEn(rest.substring(0, rest.length() - 1)); if (restPhonEn != null) { currentResult.setUsedOtherLanguageToPhonemise(true); restPhon = restPhonEn + genitiveAndPluralEndingTrans; } } } if (restPhon != null) break; } } // Or does it help if we cut off a Fuge? if (restPhon == null) { String[] helper = fugeSearch(rest); if (helper != null && helper.length == 2) { fugePhon = helper[0]; String rest2 = helper[1]; restPhon = jphon.userdictLookup(rest2, null); if (restPhon == null) { restPhon = jphon.lexiconLookup(rest2, null); } if (restPhon == null && allowOtherLanguage) { restPhon = jphon.phonemiseEn(rest2); if (restPhon != null) { currentResult.setUsedOtherLanguageToPhonemise(true); } } if (restPhon == null) restPhon = compoundAnalysis(new Word(rest2), currentResult, allowOtherLanguage); } } // Maybe rest is a flection if (restPhon == null) { // System.out.println("1) new word is : "+rest+". processFlection is called from here. var // is : "+currentResult.isUsedOtherLanguageToPhonemise()); restPhon = processFlection(new Word(rest), currentResult, allowOtherLanguage); // System.out.println("2) new word was : "+rest+". processFlection is called from here. // var is : "+currentResult.isUsedOtherLanguageToPhonemise()); } // Or can the rest be analysed as a compound? if (restPhon == null) restPhon = compoundAnalysis(new Word(rest), currentResult, allowOtherLanguage); if (restPhon != null) { // In restPhon, delete stress signs: restPhon = restPhon.replaceAll("'", ""); return firstPhon + (fugePhon != null ? fugePhon : "") + "-" + restPhon; } } } return null; } /** * Try to cut off a Fuge morpheme at the beginning of suffix. * * @param suffix a part of a word with a prefix already removed. * @return a two-item String array. First string is the trannscnscription of the Fuge found, * second is the suffix after the Fuge was removed. Returns null if no Fuge was found. */ private String[] fugeSearch(String suffix) { String fugePhon = null; int fugeLength = 0; String[] validFuges = getEndingsAndAffixes("compound_fuge"); for (int j = 0; j < validFuges.length; j++) { if (suffix.startsWith(validFuges[j])) { fugePhon = endingTranscriptionLookup(validFuges[j]); fugeLength = validFuges[j].length(); break; } } if (fugePhon != null) { // found a Fuge String[] returnValue = new String[2]; returnValue[0] = fugePhon; returnValue[1] = suffix.substring(fugeLength); return returnValue; } else { return null; } } /** * Separates flection ending from input word. * * @param toBePhonemised the input word * @param endingLength endings from language specific ending list * @return when valid flection ending is found, returns a string array of two elements (first is * stem, second is ending); else, returns null. */ private String[] separateFlectionEndings(String toBePhonemised, int endingLength) { String wordMinusFlectionEnding = null; String flectionEnding = knowEnding(toBePhonemised, endingLength); if (flectionEnding != null) { String[] wordPlusEnding = new String[2]; wordMinusFlectionEnding = toBePhonemised.substring(0, toBePhonemised.length() - flectionEnding.length()); wordPlusEnding[0] = wordMinusFlectionEnding; wordPlusEnding[1] = flectionEnding; return wordPlusEnding; } else { return null; } } /** * Try to find baseform of otherLanguageWord (i.e. english infinitive in denglish word) * * @param wordMinusFlectionEnding wordMinusFlectionEnding * @param flectionEnding flectionEnding * @param word word * @return word */ private Word transformWordToEnBaseForm( String wordMinusFlectionEnding, String flectionEnding, Word word) { logger.debug("getEnBaseForm is starting with...: " + wordMinusFlectionEnding); String[] participleBaseLong = getEndingsAndAffixes("participle_base_long"); // 'et' for german String[] participleBaseShort = getEndingsAndAffixes("participle_base_short"); // 't' for german // String[] flectionFuge = getEndingsAndAffixes("flection_fuge");//should be 'e' for german String wordMinusFlectionEndingPenultimateChar = null; String wordMinusFlectionEndingUltimateChar = null; if (wordMinusFlectionEnding.length() > 2) { wordMinusFlectionEndingPenultimateChar = wordMinusFlectionEnding.substring( wordMinusFlectionEnding.length() - 2, wordMinusFlectionEnding.length() - 1); } if (wordMinusFlectionEnding.length() > 1) { wordMinusFlectionEndingUltimateChar = wordMinusFlectionEnding.substring( wordMinusFlectionEnding.length() - 1, wordMinusFlectionEnding.length()); } String wordMinusFlectionEndingLastTwo = wordMinusFlectionEndingPenultimateChar + wordMinusFlectionEndingUltimateChar; if (wordMinusFlectionEnding.length() > 3) { if (knowEnBaseForm( wordMinusFlectionEnding)) { // item without ending is already en base form like >boot< if (flectionEnding != null) { if (isLongParticipleBaseEnding(flectionEnding, participleBaseLong) || isShortParticipleBaseEnding( flectionEnding, participleBaseShort)) { // 'boot >et< or 'scroll>t<' word.setOtherLanguageBaseForm(wordMinusFlectionEnding); word.setCouldBeParticiple(true); word.setCouldBeParticipleInBaseForm(true); // next is special case for words like dat>e<>te< } else if (endsWithVowel(wordMinusFlectionEnding)) { // 'te' word.setOtherLanguageBaseForm(wordMinusFlectionEnding); word.setWordMinusFlectionEndsWithVowel(true); word.setCouldBeParticiple(true); } else { // downloaden word.setOtherLanguageBaseForm(wordMinusFlectionEnding); } } else { // scroll word.setOtherLanguageBaseForm(wordMinusFlectionEnding); logger.debug("wordMinusFlectionEnding is already enBaseForm"); } } // (up | ge) | date >t< (em) (j = 1) // (ge) | boot >et< (em) (j =2) // scroll >t< (en) (j=1) // scan >nt< (en) (j=2) if (word.getOtherLanguageBaseForm() == null) { for (int j = 1; j < 3; j++) { // chop off 1-3 chars from end of word logger.debug( "new(2a): " + wordMinusFlectionEnding.substring(0, wordMinusFlectionEnding.length() - j)); if (knowEnBaseForm( wordMinusFlectionEnding.substring(0, wordMinusFlectionEnding.length() - j))) { if (isLongParticipleBaseEnding( wordMinusFlectionEndingLastTwo, participleBaseLong) // 'et' || isShortParticipleBaseEnding( wordMinusFlectionEndingUltimateChar, participleBaseShort) && !(isShortParticipleBaseEnding( wordMinusFlectionEndingUltimateChar, participleBaseShort) && // to // force // that // sth // like // 'cha>tt<en' // is // correctly isShortParticipleBaseEnding( wordMinusFlectionEndingPenultimateChar, participleBaseShort))) { // processed // in // geminate // clause word.setOtherLanguageBaseForm( wordMinusFlectionEnding.substring(0, wordMinusFlectionEnding.length() - j)); word.setCouldBeParticiple(true); word.setCutOffCharacter(true); logger.debug("new(2a)"); } } if (word.getOtherLanguageBaseForm() != null) break; } } if (word.getOtherLanguageBaseForm() == null) { // check for geminates -> scannen logger.debug("in geminate clause: " + wordMinusFlectionEnding); if (wordMinusFlectionEndingUltimateChar.equals( wordMinusFlectionEndingPenultimateChar)) { // sca>nn< if (knowEnBaseForm( wordMinusFlectionEnding.substring(0, wordMinusFlectionEnding.length() - 1))) { word.setOtherLanguageBaseForm( wordMinusFlectionEnding.substring(0, wordMinusFlectionEnding.length() - 1)); logger.debug("geminate......."); } } } if (word.getOtherLanguageBaseForm() == null) { // try to test if it is a gerund -> updatend word.setIsVerbalGerund(checkIfGerund(wordMinusFlectionEnding)); if (word.getIsVerbalGerund()) { // we have a gerund word.setOtherLanguageBaseForm(transformWordToEnBaseFormGerund(wordMinusFlectionEnding)); } } } logger.debug("finally enBaseForm: " + word.getOtherLanguageBaseForm()); return word; } /** * @param word word * @return enBaseForm */ private String transformWordToEnBaseFormGerund(String word) { logger.debug("getBaseFormGerund called with: " + word); String enBaseForm = null; String[] flectionFuge = getEndingsAndAffixes("flection_fuge"); // logger.debug("found gerund.........."); for (int j = 0; j < flectionFuge.length; j++) { if (knowEnBaseForm(word.substring(0, word.length() - 3) + flectionFuge[j])) { // updat>e<nd enBaseForm = word.substring(0, word.length() - 3) + flectionFuge[j]; // like updat >e< nd logger.debug("gerund case 3"); } if (enBaseForm != null) break; } if (enBaseForm == null) { if (knowEnBaseForm(word.substring(0, word.length() - 3))) { // download>end< enBaseForm = word.substring(0, word.length() - 3); // item without 'end' is base logger.debug("gerund case 1"); } else if (knowEnBaseForm(word.substring(0, word.length() - 4)) && word.charAt(word.length() - 4) == word.charAt(word.length() - 5)) { // scan>n< end enBaseForm = word.substring(0, word.length() - 4); logger.debug("gerund case 2"); } } return enBaseForm; } /** * Building the transcription and syllabification of a flection * * @param currentResult currentResult * @param word : the English infinitive as found in English lexicon * @return transcription of complete input word */ private String transcribeFlection(Word word, Result currentResult) { String result = null; String otherLanguageTranscription = null; String endingTranscription = null; String gerundEndingTrans = null; String participleBaseShortEndingTrans = null; String flectionFugeTrans = null; otherLanguageTranscription = jphon.phonemiseEn(word.getOtherLanguageBaseForm()); if (otherLanguageTranscription != null) { // System.out.println("var should be true"); currentResult.setUsedOtherLanguageToPhonemise(true); for (int j = 0; j < this.dentalPlosives.length; j++) { if (otherLanguageTranscription.endsWith(this.dentalPlosives[j])) { word.setExtraSyll(true); logger.debug("extraSyll true"); } } // System.out.println("var is in transcribeFlection: // "+currentResult.isUsedOtherLanguageToPhonemise()); // for cases like 'scrollet' where 'et' is flection ending and NOT ending of // participleBaseForm; otherwise 'scrollet' would sound like 'scrollt' String[] participleBaseLongEndings = getEndingsAndAffixes("participle_base_long"); for (int j = 0; j < participleBaseLongEndings.length; j++) { if (word.getFlectionEnding() != null && word.getFlectionEnding().equals(participleBaseLongEndings[j]) && !(word.getCutOffCharacter())) { // 'et' word.setExtraSyll(true); } } String[] gerundEndings = getEndingsAndAffixes("gerund_ending"); // should be 'end' -> bootend // String gerundEndingTrans = endingTranscriptionLookup(gerundEnding);//should be '@nt' for (int j = 0; j < gerundEndings.length; j++) { if (endingTranscriptionLookup(gerundEndings[j]) != null) { gerundEndingTrans = endingTranscriptionLookup(gerundEndings[j]); } } String[] participleBaseShortEndings = getEndingsAndAffixes("participle_base_short"); // If the participle ends with 'ed' or 'et' doesn't matter -> you get the same transcription // String participleBaseEndingTrans = endingTranscriptionLookup(participleBaseEnding);//gives // you 't' for (int j = 0; j < participleBaseShortEndings.length; j++) { if (endingTranscriptionLookup(participleBaseShortEndings[j]) != null) { participleBaseShortEndingTrans = endingTranscriptionLookup(participleBaseShortEndings[j]); // gives you 't' } } String[] flectionFuge = getEndingsAndAffixes("flection_fuge"); // gives you 'e' for (int j = 0; j < flectionFuge.length; j++) { if (endingTranscriptionLookup(flectionFuge[j]) != null) { flectionFugeTrans = endingTranscriptionLookup(flectionFuge[j]); } } endingTranscription = endingTranscriptionLookup(word.getFlectionEnding()); String newEnTranscription = rebuildTrans(otherLanguageTranscription); String newGerundEndingTrans = rebuildTrans(gerundEndingTrans); // should then be '@n-t' String voicedNewGerundEndingTrans = voiceFinal(newGerundEndingTrans); // should be '@n-d' // String voicedGerundEndingTrans = voiceFinal(gerundEndingTrans); //should be '@nd' logger.debug("enTrans: " + otherLanguageTranscription); if (word.getFlectionEnding() != null) { if (endingTranscriptionLookup(word.getFlectionEnding()) != null) { // special rule in case of enBaseForm's last char equals valid flection ending i.e. 't' // in this case give us back the enBaseForm aka enInfinitive // testing for participle because of date>te< enBaseForm ends with found ending if (otherLanguageTranscription.endsWith(word.getFlectionEnding()) && !(word.getIsVerbalGerund()) && !(word.getCouldBeParticiple())) { result = otherLanguageTranscription; logger.debug("(0)"); } else { if (word.getCouldBeParticiple() && isShortSuperlative(word.getFlectionEnding()) && word.getExtraSyll()) { // i.e. // downgeloadetsten result = newEnTranscription + flectionFugeTrans + participleBaseShortEndingTrans + endingTranscription; logger.debug("(1)"); } else if (word.getCouldBeParticiple() && word.getCouldBeParticipleInBaseForm() && word.getExtraSyll()) { // scrollet // or // downloadet result = newEnTranscription + flectionFugeTrans + participleBaseShortEndingTrans; logger.debug("(2)"); } else if (word.getCouldBeParticiple() && word.getExtraSyll() && word.getWordMinusFlectionEndsWithVowel()) { result = newEnTranscription + flectionFugeTrans + "-" + endingTranscription; logger.debug("(3)"); } else if (word.getCouldBeParticiple() && word.getExtraSyll()) { // i.e. downgeloadetere result = newEnTranscription + flectionFugeTrans + "-" + participleBaseShortEndingTrans + endingTranscription; logger.debug("(4)"); } else if (word.getCouldBeParticiple() && isShortSuperlative(word.getFlectionEnding())) { // i.e. // gescrolltstem result = otherLanguageTranscription + participleBaseShortEndingTrans + endingTranscription; logger.debug("(5)"); } else if (word.getCouldBeParticiple() && word.getCouldBeParticipleInBaseForm()) { result = otherLanguageTranscription + participleBaseShortEndingTrans; logger.debug("(6)"); } else if (word.getCouldBeParticiple()) { // i.e. gescrolltestem result = otherLanguageTranscription + "-" + participleBaseShortEndingTrans + endingTranscription; logger.debug("(7)"); } else { if (word.getIsVerbalGerund()) { logger.debug("isVerbalGerund"); if (isShortSuperlative(word.getFlectionEnding())) { result = newEnTranscription + gerundEndingTrans + endingTranscription; } else { result = newEnTranscription + voicedNewGerundEndingTrans + endingTranscription; } } else { if (isShortSuperlative(word.getFlectionEnding())) { result = otherLanguageTranscription + endingTranscription; } else { // no Gerund, no superlative but maybe something like 'scannst' if (word.getExtraSyll()) { // means: word ends on 't' or 'd' logger.debug("extraSyll is true here..."); result = newEnTranscription + endingTranscription; } else { // means: word ends on something else if (endingContainsVowel(word.getFlectionEnding()) && (!(endingBeginsWithVowel(word.getFlectionEnding())))) { result = otherLanguageTranscription + "-" + endingTranscription; } else { if (endingContainsVowel(word.getFlectionEnding()) && endingBeginsWithVowel(word.getFlectionEnding())) { result = newEnTranscription + endingTranscription; } else { result = otherLanguageTranscription + endingTranscription; } } } } } } } } } else { // flection ending is null: two possibilities: en-Word like boot or ger gerund like // bootend if (word.getIsVerbalGerund()) { result = newEnTranscription + gerundEndingTrans; logger.debug("(((1)))"); } else { // scann, date result = otherLanguageTranscription; logger.debug("(((2)))"); } } } return result; } /** * Checks if input word has valid ending from ending list. * * @param toBePhonemised The input word * @param endingLength endingLength * @return Flection ending if one can be found, null otherwise */ private String knowEnding(String toBePhonemised, int endingLength) { logger.debug("in knowEnding: " + toBePhonemised); String currentEnding = null; String foundEnding = null; int wordLength = toBePhonemised.length(); for (int j = endingLength; j > 0; j--) { if (j < wordLength) { currentEnding = toBePhonemised.substring(wordLength - j, wordLength); logger.debug("currentEnding: " + currentEnding); if (this.endingSet.contains(currentEnding)) { foundEnding = currentEnding; logger.debug("foundEnding....: " + foundEnding); } } else { continue; } if (foundEnding != null) break; } return foundEnding; } /** * Voices the final consonant of an ending. * * @param ending the input ending * @return voiced ending of ending can be voiced, input ending otherwise */ private String voiceFinal(String ending) { String finalPhoneme = null; String voicedFinalPhoneme = null; String voicedEnding = null; if (ending.length() > 0) { finalPhoneme = ending.substring(ending.length() - 1, ending.length()); } if (getVoicedFinal(finalPhoneme) != null) { voicedFinalPhoneme = getVoicedFinal(finalPhoneme); voicedEnding = ending.substring(0, ending.length() - 1) + voicedFinalPhoneme; // return new ending voiced } else { // if there is no voiced value for the last phone, return ending as it came in voicedEnding = ending; } return voicedEnding; } /** * If the given string ends with a consonant, insert a syllable boundary before that consonant. * Otherwise, append a syllable boundary. * * @param s input syllable * @return syllable with boundaries reset */ private String rebuildTrans(String s) { AllophoneSet set = jphon.getAllophoneSet(); if (set != null) { Allophone[] allophones = set.splitIntoAllophones(s); if (allophones != null && allophones.length > 0) { Allophone last = allophones[allophones.length - 1]; if (last.isConsonant()) { // insert a syllable boundary before final consonant String lastPh = last.name(); return s.substring(0, s.length() - lastPh.length()) + "-" + lastPh; } } } return s + "-"; } /** * Checks if input string is a gerund - means: input ends with 'end' Sets global var * isVerbalGerund which is important for building transcription later on * * @param s input string * @return true if s.substring(s.length -3, s.length).equals(gerundEndings[j]), false otherwise */ private boolean checkIfGerund(String s) { String[] gerundEndings = getEndingsAndAffixes("gerund_ending"); // should be 'end' for german for (int j = 0; j < gerundEndings.length; j++) { if (s.length() > gerundEndings[j].length()) { if (s.substring(s.length() - 3, s.length()).equals(gerundEndings[j])) { // we have an gerund return true; } } } return false; } /** * Checks if flection ending is a short superlative ending * * @param flectionEnding flection ending * @return true if ending is short superlative false otherwise */ private boolean isShortSuperlative(String flectionEnding) { String[] shortSuperlativeEndings = getEndingsAndAffixes("superlative_short"); for (int i = 0; i < shortSuperlativeEndings.length; i++) { if (flectionEnding != null) { if (flectionEnding.equals(shortSuperlativeEndings[i])) { return true; } } } return false; } /** * Checks if flection ending begins with a vowel * * @param ending flection ending * @return true if ending begins with a vowel, false otherwise */ private boolean endingBeginsWithVowel(String ending) { for (int i = 0; i < this.vowels.length; i++) { if (this.vowels[i].equals(ending.substring(0, 1))) { return true; } } return false; } /** * Checks if flection ending contains a vowel * * @param ending flection ending * @return true if ending contains a vowel, false otherwise */ private boolean endingContainsVowel(String ending) { for (int i = 0; i < this.vowels.length; i++) { for (int j = 0; j < ending.length(); j++) { if (this.vowels[i].equals(ending.substring(j, j + 1))) { return true; } } } return false; } private boolean endsWithVowel(String s) { for (int i = 0; i < this.vowels.length; i++) { for (int j = 0; j < s.length(); j++) { if (this.vowels[i].equals(s.substring(s.length() - 1, s.length()))) { return true; } } } return false; } private boolean isLongParticipleBaseEnding(String s, String[] participleBaseLong) { for (int j = 0; j < participleBaseLong.length; j++) { if (s.equals(participleBaseLong[j])) { // 'et' return true; } } return false; } private boolean isShortParticipleBaseEnding(String s, String[] participleBaseShort) { for (int j = 0; j < participleBaseShort.length; j++) { if (s.equals(participleBaseShort[j])) { // 't' return true; } } return false; } public Locale getLocale() { return this.locale; } /** * Checks if item is in english lexicon. * * @param s english base form * @return true if item is in english lexicon, false if not */ private boolean knowEnBaseForm(String s) { if (jphon.phonemiseEn(s) != null) { return true; } return false; } // public boolean usedOtherLanguageToPhonemise(boolean usedOtherLanguageToPhonemise) { // return usedOtherLanguageToPhonemise; // } /** * ***************************************************************************** Hashtable lookup * methods for morphology & phonology information * ***************************************************************************** */ /** * Looking for item in german prefix lexicon * * @param s item to be found * @return Transcription of item */ private String prefixLexiconLookup(String s) { String prefixTranscription = null; try { if (this.prefixLexicon.get(s) != null) { prefixTranscription = (String) this.prefixLexicon.get(s); } } catch (Exception e) { logger.debug("prefixLexLookup: " + e.toString()); } return prefixTranscription; } /** * Looks up in terminal voicing hash if there is a match for a unvoiced consonant * * @param phon The unvoiced consonant * @return Voiced consonant if one can be found, null otherwise */ private String getVoicedFinal(String phon) { String voicedPhon = null; try { if (this.terminalVoicings.get(phon) != null) { voicedPhon = (String) this.terminalVoicings.get(phon); } } catch (Exception e) { logger.debug("getVoicedFinal: " + e.toString()); } return voicedPhon; } /** * Looks up list of all endings and affixes for specific language * * @param key key of the affixes/endings you want to get, i.e. superlative_short * @return list of endings or affixes if key is valid, null otherwise */ private String[] getEndingsAndAffixes(String key) { String[] endingList = null; try { if (this.endingsAndAffixes.get(key) != null) { String value = (String) this.endingsAndAffixes.get(key); endingList = value.split("/"); } } catch (Exception e) { logger.debug("getEndingsAndAffixes: " + e.toString()); } return endingList; } /** * Try to get transcription for ending * * @param s The ending to be phonemised * @return Transcription of ending */ private String endingTranscriptionLookup(String s) { String affixPhon = null; try { if (this.flectionToPhon.get(s) != null) { affixPhon = (String) this.flectionToPhon.get(s); } } catch (Exception e) { logger.debug("endingTranscriptionLookup: " + e.toString()); } return affixPhon; } }
/** * A representation of any type of mary data, be it input, intermediate or output data. The * "technical" representation of the read data is hidden from the caller, but can be accessed on * request. Internally, the data is appropriately represented according to this data's type, i.e. as * a String containing plain text, an XML DOM tree, or an input stream containing audio data. * * @author Marc Schröder */ public class MaryData { private MaryDataType type; private Locale locale; private String outputParams = null; // Only one of the following data representations should be non-null // for a given instance; which one depends on our type. private Document xmlDocument = null; private String plainText = null; private AudioInputStream audio = null; private AudioFileFormat audioFileFormat = null; private Logger logger = MaryUtils.getLogger("IO"); // for plainText, allow additional information: private Voice defaultVoice = null; private String defaultStyle = ""; private String defaultEffects = ""; // The following XML I/O helpers are only initialised // if actually needed. private MaryNormalisedWriter writer = null; private boolean doValidate; private boolean doWarnClient = false; public MaryData(MaryDataType type, Locale locale) { this(type, locale, false); } public MaryData(MaryDataType type, Locale locale, boolean createStubDocument) { if (type == null) throw new NullPointerException("Received null type for MaryData"); this.type = type; this.locale = locale; // The following is the default setting for module output (we suppose // that for the input data, setValidating() is called as appropriate): doValidate = MaryProperties.getBoolean("maryxml.validate.modules", false); if (createStubDocument && type.isMaryXML()) { xmlDocument = MaryXML.newDocument(); } } public boolean getValidating() { return doValidate; } public void setValidating(boolean doValidate) throws ParserConfigurationException { this.doValidate = doValidate; } @Deprecated public boolean getWarnClient() { return doWarnClient; } @Deprecated public void setWarnClient(boolean doWarnClient) {} public MaryDataType getType() { return type; } public Locale getLocale() { return locale; } /** * Read data from input stream <code>is</code>, in the appropriate way as determined by our <code> * type</code>. * * @param is is * @throws ParserConfigurationException ParserConfigurationException * @throws SAXException SAXException * @throws IOException IOException * @throws TransformerConfigurationException TransformerConfigurationException * @throws TransformerException TransformerException */ public void readFrom(InputStream is) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException { readFrom(is, null); } /** * Read data from input stream <code>is</code>, in the appropriate way as determined by our <code> * type</code>. * * @param is the InputStream from which to read. * @param endMarker a string marking end of file. If this is null, read until end-of-file; if it * is non-null, read up to (and including) the first line containing the end marker string. * This will be ignored for audio data. * @throws ParserConfigurationException ParserConfigurationException * @throws SAXException SAXException * @throws IOException IOException * @throws TransformerConfigurationException TransformerConfigurationException * @throws TransformerException TransformerException */ public void readFrom(InputStream is, String endMarker) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException { if (type.isXMLType() || type.isTextType()) readFrom(new InputStreamReader(is, "UTF-8"), endMarker); else { // audio // ignore endMarker setAudio((AudioInputStream) is); } } /** * Read data from reader <code>r</code> in the appropriate way as determined by our <code>type * </code>. Only XML and Text data can be read from a reader, audio data cannot. * * @param from from * @throws ParserConfigurationException ParserConfigurationException * @throws SAXException SAXException * @throws IOException IOException */ public void readFrom(Reader from) throws ParserConfigurationException, SAXException, IOException { String inputData = FileUtils.getReaderAsString(from); setData(inputData); } /** * Read data from reader <code>r</code> in the appropriate way as determined by our <code>type * </code>. Only XML and Text data can be read from a reader, audio data cannot. "Helpers" needed * to read the data, such as XML parser objects, are created when they are needed. If doWarnClient * is set to true, warning and error messages related to XML parsing are logged to the log * category connected to the client from which this request originated. * * @param from the Reader from which to read. * @param endMarker a string marking end of file. If this is null, read until end-of-file; if it * is non-null, read up to (and including) the first line containing the end marker string. * @throws ParserConfigurationException ParserConfigurationException * @throws SAXException SAXException * @throws IOException IOException */ public void readFrom(Reader from, String endMarker) throws ParserConfigurationException, SAXException, IOException { // For the case that the data to be read it is not // followed by end-of-file, we use a ReaderSplitter which // provides a reader artificially "inserting" an end-of-file // after a line containing the pattern given in endMarker. Reader r = from; if (endMarker != null) { ReaderSplitter fromSplitter = new ReaderSplitter(from, endMarker); r = fromSplitter.nextReader(); } readFrom(r); } /** * Set the content data of this MaryData object from the given String. For XML data ({@link * MaryDataType#isXMLType()}), parse the String representation of the data into a DOM tree. * * @param dataString string representation of the input data. * @throws ParserConfigurationException ParserConfigurationException * @throws IOException IOException * @throws SAXException SAXException * @throws IllegalArgumentException if this method is called for MaryDataTypes that are neither * text nor XML. */ public void setData(String dataString) throws ParserConfigurationException, SAXException, IOException { // First, some data cleanup: dataString = StringUtils.purgeNonBreakingSpaces(dataString); // Now, deal with it. if (type.isXMLType()) { logger.debug( "Parsing XML input (" + (doValidate ? "" : "non-") + "validating): " + dataString); xmlDocument = DomUtils.parseDocument(dataString, doValidate); } else if (type.isTextType()) { logger.debug("Setting text input: " + dataString); plainText = dataString; } else { throw new IllegalArgumentException("Cannot set data of type " + type + " from a string"); } } /** * Write our internal representation to output stream <code>os</code>, in the appropriate way as * determined by our <code>type</code>. * * @param os os * @throws TransformerConfigurationException TransformerConfigurationException * @throws FileNotFoundException FileNotFoundException * @throws TransformerException TransformerException * @throws IOException IOException * @throws Exception Exception */ public void writeTo(OutputStream os) throws TransformerConfigurationException, FileNotFoundException, TransformerException, IOException, Exception { if (type.isXMLType()) { if (writer == null) writer = new MaryNormalisedWriter(); if (logger.getEffectiveLevel().equals(Level.DEBUG)) { ByteArrayOutputStream debugOut = new ByteArrayOutputStream(); writer.output(xmlDocument, debugOut); logger.debug(debugOut.toString()); } writer.output(xmlDocument, new BufferedOutputStream(os)); } else if (type.isTextType()) { // caution: XML types are text types! writeTo(new OutputStreamWriter(os, "UTF-8")); } else { // audio logger.debug("Writing audio output, frame length " + audio.getFrameLength()); AudioSystem.write(audio, audioFileFormat.getType(), os); os.flush(); os.close(); } } /* * public void writeTo(HttpResponse response) throws TransformerConfigurationException, FileNotFoundException, * TransformerException, IOException, Exception { if (type.isUtterances()) throw new * IOException("Cannot write out utterance-based data type!"); * * if (type.isXMLType()) { if (writer == null) writer = new MaryNormalisedWriter(); if * (logger.getEffectiveLevel().equals(Level.DEBUG)) { ByteArrayOutputStream debugOut = new ByteArrayOutputStream(); * writer.output(xmlDocument, debugOut); logger.debug(debugOut.toString()); } * * //writer.output(xmlDocument, new BufferedOutputStream(os)); * * ByteArrayOutputStream os = new ByteArrayOutputStream(); writer.output(xmlDocument, new BufferedOutputStream(os)); * NByteArrayEntity body = new NByteArrayEntity(os.toByteArray()); body.setContentType("text/html; charset=UTF-8"); * response.setEntity(body); } else if (type.isTextType()) // caution: XML types are text types! { //writeTo(new * OutputStreamWriter(os, "UTF-8")); * * ByteArrayOutputStream os = new ByteArrayOutputStream(); writeTo(new OutputStreamWriter(os, "UTF-8")); NByteArrayEntity body * = new NByteArrayEntity(os.toByteArray()); body.setContentType("text/html; charset=UTF-8"); response.setEntity(body); } else * // audio { logger.debug("Writing audio output, frame length "+audio.getFrameLength()); //AudioSystem.write(audio, * audioFileFormat.getType(), os); //os.flush(); * * ByteArrayOutputStream os = new ByteArrayOutputStream(); AudioSystem.write(audio, audioFileFormat.getType(), os); * os.flush(); * * MaryHttpServerUtils.toHttpResponse(os.toByteArray(), response); } } */ /** * Write our internal representation to writer <code>w</code>, in the appropriate way as * determined by our <code>type</code>. Only XML and Text data can be written to a writer, audio * data cannot. "Helpers" needed to read the data, such as XML parser objects, are created when * they are needed. * * @param w w * @throws TransformerConfigurationException TransformerConfigurationException * @throws FileNotFoundException FileNotFoundException * @throws TransformerException TransformerException * @throws IOException IOException * @throws Exception Exception */ public void writeTo(Writer w) throws TransformerConfigurationException, FileNotFoundException, TransformerException, IOException, Exception { if (type.isXMLType()) { throw new IOException("Better write XML data to an OutputStream, not to a Writer"); } else if (type.isTextType()) { // caution: XML types are text types! w.write(plainText); w.flush(); logger.debug("Writing Text output:\n" + plainText); } else { // audio - cannot write this to a writer throw new Exception("Illegal attempt to write audio data to a character Writer"); } } public Object getData() { if (type.isXMLType()) { return xmlDocument; } else if (type.isTextType()) { return plainText; } else { // audio return audio; } } public String getPlainText() { return plainText; } public void setPlainText(String plainText) { this.plainText = plainText; } public Document getDocument() { return xmlDocument; } public void setDocument(Document xmlDocument) { this.xmlDocument = xmlDocument; } public AudioInputStream getAudio() { return audio; } /** * Set the audio data. This will discard any previously set audio data. If audio data is to be * appended, consider appendAudio(). * * @param audio audio */ public void setAudio(AudioInputStream audio) { this.audio = audio; } public void setDefaultVoice(Voice voice) { if (voice == null) { return; } // check that voice locale fits before accepting the voice: Locale voiceLocale = null; voiceLocale = voice.getLocale(); Locale docLocale = getLocale(); if (docLocale == null && getType().isXMLType() && getDocument() != null && getDocument().getDocumentElement().hasAttribute("xml:lang")) { docLocale = MaryUtils.string2locale(getDocument().getDocumentElement().getAttribute("xml:lang")); } if (docLocale != null && voiceLocale != null && !(MaryUtils.subsumes(docLocale, voiceLocale) || MaryUtils.subsumes(voiceLocale, docLocale))) { logger.warn( "Voice `" + voice.getName() + "' does not match document locale `" + docLocale + "' -- ignoring!"); } this.defaultVoice = voice; } public Voice getDefaultVoice() { return defaultVoice; } public void setDefaultStyle(String style) { defaultStyle = style; } public String getDefaultStyle() { return defaultStyle; } public void setDefaultEffects(String effects) { defaultEffects = effects; } public String getDefaultEffects() { return defaultEffects; } /** * The audio file format is required only for data types serving as input to modules producing * AUDIO data (e.g., MBROLA data), as well as for the AUDIO data itself. It should be set by the * calling code before passing the data to the module producing AUDIO data. * * @param audioFileFormat audioFileFormat */ public void setAudioFileFormat(AudioFileFormat audioFileFormat) { this.audioFileFormat = audioFileFormat; } public AudioFileFormat getAudioFileFormat() { return audioFileFormat; } public void append(MaryData md) { if (md == null) throw new NullPointerException("Received null marydata"); if (!md.getType().equals(this.getType())) throw new IllegalArgumentException( "Cannot append mary data of type `" + md.getType().name() + "' to mary data of type `" + this.getType().name() + "'"); if (getType().isXMLType()) { NodeList kids = md.getDocument().getDocumentElement().getChildNodes(); logger.debug("Appending " + kids.getLength() + " nodes to MaryXML structure"); Element docEl = this.getDocument().getDocumentElement(); for (int i = 0; i < kids.getLength(); i++) { docEl.appendChild(this.getDocument().importNode(kids.item(i), true)); } } else if (getType().isTextType()) { // Attention: XML type is a text type! if (this.plainText == null) { this.plainText = md.getPlainText(); } else { this.plainText = this.plainText + "\n\n" + md.getPlainText(); } } else if (getType().equals(MaryDataType.get("AUDIO"))) { appendAudio(md.getAudio()); } else { throw new UnsupportedOperationException( "Cannot append two mary data items of type `" + getType() + "'"); } } /** * For audio data, append more audio data to the one currently present. If no audio data is set * yet, this call is equivalent to setAudio(). * * @param audioToAppend the new audio data to append */ public void appendAudio(AudioInputStream audioToAppend) { if (this.audio == null) setAudio(audioToAppend); else if (this.audio instanceof AppendableSequenceAudioInputStream) ((AppendableSequenceAudioInputStream) this.audio).append(audioToAppend); else this.audio = new SequenceAudioInputStream( this.audio.getFormat(), Arrays.asList(new AudioInputStream[] {this.audio, audioToAppend})); } public void setOutputParams(String params) { this.outputParams = params; } public String getOutputParams() { return outputParams; } public String toString() { return Objects.toStringHelper(this) .add("type", getType()) .add("locale", getLocale()) .add("output parameters", getOutputParams()) .add("data", getData()) .add("document", DomUtils.serializeToString(getDocument())) .add("validating", getValidating()) .add("plain text", getPlainText()) .add("audio", getAudio()) .add("audio file format", getAudioFileFormat()) .toString(); } }
/** * An expansion pattern implementation for date patterns. * * <p>Modified from the German version of Marc Schröder with - removed all word abbreviations, * month names are always numerical - moved up the YYYY MM DD pattern as it's most common - added * expansion for day name */ public class DateEP extends ExpansionPattern { private final String[] _knownTypes = { "date", "date:dmy", "date:ymd", // standard Korean way is YY MM DD "date:dm", "date:my", "date:y", "date:m", "date:d", "date:mdy", "date:md" }; /** * Every subclass has its own list knownTypes, an internal string representation of known types. * These are possible values of the <code>type</code> attribute to the <code>say-as</code> * element, as defined in MaryXML.dtd. If there is more than one known type, the first type ( * <code>knownTypes[0]</code>) is expected to be the most general one, of which the others are * specialisations. */ private final List<String> knownTypes = Arrays.asList(_knownTypes); public List<String> knownTypes() { return knownTypes; } // Domain-specific primitives: protected final String sDay = "(?:0?[1-9]|[12][0-9]|3[01])"; protected final String sMonth = "(?:0[1-9]|1[0-2])"; protected final String sYear = "(?:([0-9][0-9])?[0-9][0-9])"; protected final String sAbbrevDayPattern = "(?:[윌화수목금토일])"; protected final String sDot = "[\\.]"; protected final String sSlash = "[/]"; protected final String sMinus = "[\\-]"; protected final String sMatchingChars = "[0-9/\\.\\w\\(\\)윌화수목금토일]"; protected final String sSeparators = (sDot + "|" + sSlash + "|" + sMinus); protected static Map<String, String> dayOfWeekExpansion = new HashMap<String, String>(); static { dayOfWeekExpansion.put("윌", "윌요일"); dayOfWeekExpansion.put("화", "화요일"); dayOfWeekExpansion.put("수", "수요일"); dayOfWeekExpansion.put("목", "목요일"); dayOfWeekExpansion.put("금", "금요일"); dayOfWeekExpansion.put("토", "토요일"); dayOfWeekExpansion.put("일", "일요일"); } // Now the actual match patterns: protected final Pattern reDOWYearMonthDay = Pattern.compile( "(?:\\s|\\()*" + "(" + sAbbrevDayPattern + ")" + "(?:" + sDot + "|" + sSlash + "|\\s|\\))*" + "(" + sYear + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sMonth + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sDay + ")"); protected final Pattern reYearMonthDayDOW = Pattern.compile( "(" + sYear + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sMonth + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sDay + ")" + "(?:" + sDot + "|" + sSlash + "|\\s|\\()*" + "(" + sAbbrevDayPattern + ")" + "(?:\\s|\\))*"); // the list of allowed characters should prevent already expanded // days of week tokens from being expanded again protected final Pattern reYearMonthDay = Pattern.compile( "(" + sYear + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sMonth + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sDay + ")"); protected final Pattern reDayMonthYear = Pattern.compile( "(" + sDay + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sMonth + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sYear + ")"); protected final Pattern reMonthDayYear = Pattern.compile( "(" + sMonth + ")" + "(?:" + sSeparators + ")" + "(" + sDay + ")" + "(?:" + sSeparators + ")" + "(" + sYear + ")"); protected final Pattern reDayMonth = Pattern.compile( "(" + sDay + ")" + "(?:" + sDot + "|" + sSlash + ")" + "(" + sMonth + ")" + "(?:" + sDot + "|" + sSlash + ")"); protected final Pattern reMonthDay = Pattern.compile( "(" + sMonth + ")" + "(?:" + sSeparators + ")" + "(" + sDay + ")" + "(?:" + sSeparators + ")"); protected final Pattern reMonthYear = Pattern.compile("(" + sMonth + ")(" + sSlash + "|" + sDot + ")(" + sYear + ")"); protected final Pattern reYear = Pattern.compile("(?:([0-9][0-9])?[0-9][0-9])"); protected final Pattern reMonth = Pattern.compile("(" + sMonth + ")" + "(?:" + sDot + ")"); protected final Pattern reDay = Pattern.compile("(" + sDay + ")" + "?:" + sDot); private final Pattern reMatchingChars = Pattern.compile(sMatchingChars); public Pattern reMatchingChars() { return reMatchingChars; } /** * Every subclass has its own logger. The important point is that if several threads are accessing * the variable at the same time, the logger needs to be thread-safe or it will produce rubbish. */ private Logger logger = MaryUtils.getLogger("DateEP"); public DateEP() { super(); } protected int canDealWith(String s, int type) { return match(s, type); } protected int match(String s, int type) { switch (type) { case 0: if (matchDateDMY(s)) return 1; if (matchDateYMD(s)) return 2; if (matchDateDM(s)) return 3; // if (matchDateMY(s)) return 4; // if (matchDateMDY(s)) return 8; // if (matchDateMD(s)) return 9; // if (matchDateM(s)) return 6; // if (matchDateD(s)) return 7; // if (matchDateY(s)) return 5; break; case 1: if (matchDateDMY(s)) return 1; break; case 2: if (matchDateYMD(s)) return 2; break; case 3: if (matchDateDM(s)) return 3; break; case 4: if (matchDateMY(s)) return 4; break; case 5: if (matchDateY(s)) return 5; break; case 6: if (matchDateM(s)) return 6; break; case 7: if (matchDateD(s)) return 7; break; case 8: if (matchDateMDY(s)) return 8; break; case 9: if (matchDateMD(s)) return 9; break; } return -1; } protected List<Element> expand(List<Element> tokens, String s, int type) { if (tokens == null) throw new NullPointerException("Received null argument"); if (tokens.isEmpty()) throw new IllegalArgumentException("Received empty list"); Document doc = ((Element) tokens.get(0)).getOwnerDocument(); // we expect type to be one of the return values of match(): List<Element> expanded = null; switch (type) { case 1: expanded = expandDateDMY(doc, s); break; case 2: expanded = expandDateYMD(doc, s); break; case 3: expanded = expandDateDM(doc, s); break; case 4: expanded = expandDateMY(doc, s); break; case 5: expanded = expandDateYear(doc, s); break; case 6: expanded = expandDateMonth(doc, s); break; case 7: expanded = expandDateDay(doc, s); break; case 8: expanded = expandDateMDY(doc, s); break; case 9: expanded = expandDateMD(doc, s); break; } replaceTokens(tokens, expanded); return expanded; } protected boolean matchDateDMY(String s) { return reDayMonthYear.matcher(s).matches(); } protected boolean matchDateYMD(String s) { return reYearMonthDay.matcher(s).matches() || reDOWYearMonthDay.matcher(s).matches() || reYearMonthDayDOW.matcher(s).matches(); } protected boolean matchDateDM(String s) { return reDayMonth.matcher(s).matches(); } protected boolean matchDateMY(String s) { return reMonthYear.matcher(s).matches(); } protected boolean matchDateY(String s) { return reYear.matcher(s).matches(); } protected boolean matchDateM(String s) { return reMonth.matcher(s).matches(); } protected boolean matchDateD(String s) { return reDay.matcher(s).matches(); } protected boolean matchDateMDY(String s) { return reMonthDayYear.matcher(s).matches(); } protected boolean matchDateMD(String s) { return reMonthDay.matcher(s).matches(); } protected List<Element> expandDateDMY(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reDayMonthYear.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String day = reMatcher.group(1); exp.addAll(number.expandCounted(doc, day + " 일", false)); String month = reMatcher.group(2); exp.addAll(number.expandCounted(doc, month + " 월", false)); String year = reMatcher.group(3); exp.addAll(number.expandCounted(doc, year + " 년", false)); return exp; } protected List<Element> expandDateYMD(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reDOWYearMonthDay.matcher(s); boolean found = reMatcher.find(); int dayOfWeekType = 1; if (!found) { reMatcher = reYearMonthDayDOW.matcher(s); found = reMatcher.find(); dayOfWeekType = 2; // month == Januar, Februar, ... } if (!found) { reMatcher = reYearMonthDay.matcher(s); found = reMatcher.find(); dayOfWeekType = 3; // month == Jan, Feb, ... } if (!found) { return null; } String day = null, month = null, year = null, dow = null; switch (dayOfWeekType) { case 1: dow = reMatcher.group(1); year = reMatcher.group(2); month = reMatcher.group(4); day = reMatcher.group(5); break; case 2: year = reMatcher.group(1); month = reMatcher.group(3); day = reMatcher.group(4); dow = reMatcher.group(5); break; case 3: year = reMatcher.group(1); month = reMatcher.group(3); day = reMatcher.group(4); dow = null; break; } if (dayOfWeekType == 1) { exp.addAll(makeNewTokens(doc, dayOfWeekExpansion.get(dow))); } exp.addAll(number.expandCounted(doc, year + " 년", false)); exp.addAll(number.expandCounted(doc, month + " 월", false)); exp.addAll(number.expandCounted(doc, day + " 일", false)); if (dayOfWeekType == 2) { exp.addAll(makeNewTokens(doc, dayOfWeekExpansion.get(dow))); } return exp; } protected List<Element> expandDateDM(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reDayMonth.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String day = reMatcher.group(1); exp.addAll(number.expandCounted(doc, day + " 일", false)); String month = reMatcher.group(2); exp.addAll(number.expandCounted(doc, month + " 월", false)); return exp; } protected List<Element> expandDateMY(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reMonthYear.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String year = reMatcher.group(3); exp.addAll(number.expandCounted(doc, year + " 년", false)); String month = reMatcher.group(1); if (month.charAt(0) == '0') month = month.substring(1); exp.addAll(number.expandCounted(doc, month + " 월", false)); // alternatively: keep the number: // exp.addAll(number.expandInteger(doc, month, false)); return exp; } protected List<Element> expandDateYear(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); exp.addAll(makeNewTokens(doc, number.expandInteger(s), true, s)); return exp; } protected List<Element> expandDateMonth(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reMonth.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String month = reMatcher.group(1); // Leading Zero must be deleted to get month from monthabbr. if (month.charAt(0) == '0') month = month.substring(1); exp.addAll(number.expandCounted(doc, month + " 월", false)); return exp; } protected List<Element> expandDateDay(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reDay.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String day = reMatcher.group(1); // Leading Zero must be deleted to get month from monthabbr. if (day.charAt(0) == '0') day = day.substring(1); exp.addAll(number.expandCounted(doc, day + " 일", false)); return exp; } protected List<Element> expandDateMD(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reMonthDay.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String year = reMatcher.group(3); exp.addAll(number.expandCounted(doc, year + " 년", false)); String month = reMatcher.group(1); if (month.charAt(0) == '0') { month = month.substring(1); } exp.addAll(number.expandCounted(doc, month + " 월", false)); // alternatively: keep the number: // exp.addAll(number.expandInteger(doc, month, false)); String day; day = reMatcher.group(2); exp.addAll(number.expandCounted(doc, day + " 일", false)); return exp; } protected List<Element> expandDateMDY(Document doc, String s) { ArrayList<Element> exp = new ArrayList<Element>(); Matcher reMatcher = reMonthDayYear.matcher(s); boolean found = reMatcher.find(); if (!found) { return null; } String day; String year; day = reMatcher.group(2); year = reMatcher.group(3); String month = reMatcher.group(1); if (month.charAt(0) == '0') month = month.substring(1); exp.addAll(number.expandCounted(doc, year + " 년", false)); exp.addAll(number.expandCounted(doc, month + " 월", false)); exp.addAll(number.expandCounted(doc, day + " 일", false)); return exp; } }