Esempio n. 1
0
 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()
           + ")");
 }
Esempio n. 3
0
  /**
   * 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;
  }
Esempio n. 4
0
 /**
  * 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;
 }
Esempio n. 5
0
 /**
  * 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;
 }
Esempio n. 6
0
  /**
   * 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;
  }
Esempio n. 7
0
 /**
  * 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();
 }
Esempio n. 8
0
 /**
  * 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;
 }
Esempio n. 9
0
  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;
  }
Esempio n. 10
0
/**
 * 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");
  }
}
Esempio n. 11
0
/**
 * The unit database of a voice
 *
 * @author Marc Schr&ouml;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 */
Esempio n. 14
0
/**
 * 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();
      }
    }
  }
}
Esempio n. 15
0
 public MaryHttpServer() {
   logger = MaryUtils.getLogger("server");
 }
Esempio n. 16
0
/** @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;
  }
}
Esempio n. 17
0
/**
 * 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&ouml;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();
  }
}
Esempio n. 18
0
/**
 * An expansion pattern implementation for date patterns.
 *
 * <p>Modified from the German version of Marc Schr&ouml;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;
  }
}