public MaryData process(MaryData d) throws Exception { // prevUnitIndex; // numberOfConsecutiveUnits; // basenameDuration; // phoneTier; // PraatIntervalTier unitTier; // PraatIntervalTier sourceTier; // sourceInterval; Document doc = d.getDocument(); // initialize various variables: Double duration = 0.0; String phone = null; // initialize some class variables: PraatIntervalTier phoneTier = new PraatIntervalTier("phones"); Double basenameDuration = 0.0; int prevUnitIndex = Integer.MIN_VALUE; int numberOfConsecutiveUnits = 0; // counter to track consecutive units PraatInterval sourceInterval = new PraatInterval(basenameDuration); // until we have a robust way of checking the voice type, just initialize unit and source tiers // anyway: PraatIntervalTier unitTier = new PraatIntervalTier("units"); PraatIntervalTier sourceTier = new PraatIntervalTier("sources"); // prepare to iterate only over the PHONE, SENTENCE, and BOUNDARY nodes in the MaryXML: NodeIterator ni = DomUtils.createNodeIterator(doc, PHONE, BOUNDARY); Element element; // now iterate over these nodes: while ((element = (Element) ni.nextNode()) != null) { switch (element.getTagName()) { // <s>, <ph>, or <boundary> as specified above case PHONE: phone = element.getAttribute("p"); duration = Integer.parseInt(element.getAttribute("d")) / 1000.0; // duration is always in ms break; case BOUNDARY: phone = "_"; // TODO: perhaps we should access TargetFeatureComputer.getPauseSymbol() instead if (element.hasAttribute("duration")) { duration = Double.parseDouble(element.getAttribute("duration")) / 1000.0; // duration is always in ms } else { duration = 0.0; // HMM voices can have duration-less <boundary/> tags } break; default: logger.error( "NodeIterator should not find an element of type " + element.getTagName() + " here!"); break; } PraatInterval phoneInterval = new PraatInterval(duration, phone); // TODO: crude way of checking for unit selection voice; also, name of attribute could change! if (element.hasAttribute("units")) { // unitselectionProcessing(element, unitTier, prevUnitIndex, numberOfConsecutiveUnits, // basenameDuration, // sourceInterval, sourceTier); String units = element.getAttribute("units"); String[] unitStrings = units.split("; "); // boundaries have only one unit string boolean differentSource = false; String basename = null; String unitRange = null; for (String unitString : unitStrings) { // TODO verify that unit string matches "UNITNAME BASENAME UNITINDEX UNITDURATION" String[] unitFields = unitString.split(" "); String unitName = unitFields[0]; basename = unitFields[1]; int unitIndex = Integer.parseInt(unitFields[2]); Double unitDuration = Double.parseDouble(unitFields[3]); // units are straightforward, just like phones: unitTier.appendInterval(new PraatInterval(unitDuration, unitString)); // unit source processing is a little more elaborate: /* * Note: the following assumes that consecutive selected units are ALWAYS from the same basename! That could * change if basename boundaries are no longer marked by null units in the timeline. */ differentSource = unitIndex != prevUnitIndex + 1; // is source unit from a different part of the timeline?; if (differentSource) { // reset primary variables: numberOfConsecutiveUnits = 0; basenameDuration = 0.0; } // increment/increase primary variables: numberOfConsecutiveUnits++; basenameDuration += unitDuration; // construct unit index range string: unitRange = Integer.toString(unitIndex - numberOfConsecutiveUnits + 1); if (numberOfConsecutiveUnits > 1) { unitRange = unitRange + "-" + unitIndex; } // append source intervals to source tier: if (differentSource) { sourceInterval = new PraatInterval(basenameDuration, basename + ": " + unitRange); sourceTier.appendInterval(sourceInterval); } else { sourceInterval.setDuration(basenameDuration); sourceInterval.setText(basename + ": " + unitRange); } prevUnitIndex = unitIndex; } // HACK: arbitrary threshold to detect end points in ms (in the case of diphone voice or // boundary segment) } else if (duration > 10) { // TODO: there is still a bug somewhere regarding boundary durations with mbrola... phoneInterval.setDuration(duration / 1000.0); } phoneTier.appendInterval(phoneInterval); } PraatTextGrid textGrid = new PraatTextGrid(); phoneTier.updateBoundaries(); // force full specification of timings textGrid.appendTier(phoneTier); // fragile way of checking whether this is a unit selection voice: if (unitTier.getNumberOfIntervals() > 0) { // complete and append unit and source tiers: unitTier.updateBoundaries(); textGrid.appendTier(unitTier); sourceTier.updateBoundaries(); textGrid.appendTier(sourceTier); } // return raw TextGrid as result: MaryData result = new MaryData(getOutputType(), d.getLocale()); result.setPlainText(textGrid.toString()); return result; }