content: /**
   * Copyright 2006 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.unitselection.data;
  
  
  import android.util.Log;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import marytts.cart.CART;
  import marytts.server.Mary;
  import marytts.unitselection.select.JoinCostFunction;
  import marytts.unitselection.select.StatisticalCostFunction;
  import marytts.unitselection.select.Target;
  import marytts.unitselection.select.TargetCostFunction;
  import marytts.unitselection.select.viterbi.ViterbiCandidate;
  import marytts.util.data.Datagram;
  import marytts.util.dom.DomUtils;
  import mf.org.w3c.dom.Element;
  
  //import org.apache.log4j.Logger;
  
  
  /**
   * 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);
          Log.d(Mary.LOG, "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
       * @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) {
              Log.w(Mary.LOG, "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
       * @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) {
              Log.w(Mary.LOG, "Problem getting filename for unit " + unit.index, e);
              return "unknown origin";
          }
      }
  
  }
  
  