content: /**
   * Portions Copyright 2006 DFKI GmbH.
   * Portions Copyright 2001 Sun Microsystems, Inc.
   * Portions Copyright 1999-2001 Language Technologies Institute,
   * Carnegie Mellon University.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * Permission is hereby granted, free of charge, to use and distribute
   * this software and its documentation without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of this work, and to
   * permit persons to whom this work is furnished to do so, subject to
   * the following conditions:
   * <p/>
   * 1. The code must retain the above copyright notice, this list of
   * conditions and the following disclaimer.
   * 2. Any modifications must be clearly marked as such.
   * 3. Original authors' names are not deleted.
   * 4. The authors' names are not used to endorse or promote products
   * derived from this software without specific prior written
   * permission.
   * <p/>
   * DFKI GMBH AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL WARRANTIES WITH
   * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DFKI GMBH NOR THE
   * CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
   * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
   * THIS SOFTWARE.
   */
  package marytts.util.data;
  
  import java.io.DataInputStream;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  
  import marytts.util.io.General;
  
  /**
   * EST Track file reader
   * <p/>
   * This class interpretes a DataInputStream as an EST_Track file:
   * it loads the whole track data in memory, and provides access methods
   * to reach each individual frame or each individual track value.
   */
  public class ESTTrackReader {
  
      private int numFrames;
      private int numChannels;
      private float[] times;
      private float[][] frames;
      private boolean isBigEndian = false;
      private boolean isBinary = false;
  
      /****************/
      /* CONSTRUCTORS */
      /****************/
      /**
       * Internal time pointer for the getClosestTime(double) method.
       */
      private int timeIdx = 0;
  
      /**
       * Constructor from an already open DataInputStream
       *
       * @param dis DataInputStream to read the EST_Track from
       */
      public ESTTrackReader(DataInputStream dis) {
          loadHeaderAndData(dis);
      }
  
  
      /*****************/
      /* OTHER METHODS */
      /*****************/
  
      /**
       * Constructor from a file name
       *
       * @param fileName the name of the file to read the EST_Track from
       */
      public ESTTrackReader(String fileName) {
          try {
              /* Open the file */
              FileInputStream fis = new FileInputStream(fileName);
              /* Stick the file to a DataInputStream to allow easy reading of primitive classes (numbers) */
              DataInputStream dis = new DataInputStream(fis);
              /* Parse the header and load the data */
              loadHeaderAndData(dis);
              /* Close the file */
              fis.close();
          } catch (FileNotFoundException e) {
              throw new Error("EST track file [" + fileName + "] was not found.");
          } catch (SecurityException e) {
              throw new Error("You do not have read access to the file [" + fileName + "].");
          } catch (IOException e) {
              throw new Error("IO Exception caught when closing file [" + fileName + "]: " + e.getMessage());
          }
      }
  
      /**
       * Parse the EST header and load the track data
       *
       * @param dis DataInputStream to read from
       * @throws IOException on ill-formatted input
       */
      private void loadHeaderAndData(DataInputStream dis) {
          try {
              if (!General.readWord(dis).equals("EST_File") ||
                      !General.readWord(dis).equals("Track")) {
                  throw new Error("The given data input stream is not an EST Track file.");
              }
  
              // Read Header
              String token = General.readWord(dis);
              while (!token.equals("EST_Header_End")) {
                  if (token.equals("DataType")) {
                      isBinary = General.readWord(dis).equals("binary");
                  } else if (token.equals("ByteOrder")) {
                      isBigEndian = General.readWord(dis).equals("10");
                  } else if (token.equals("NumFrames")) {
                      numFrames = Integer.parseInt(General.readWord(dis));
                  } else if (token.equals("NumChannels")) {
                      numChannels = Integer.parseInt(General.readWord(dis));
                  }
                  // Ignore all other content in header
  
                  token = General.readWord(dis);
              }
  
               /* Prepare the storage for the data... */
              times = new float[numFrames];
              frames = new float[numFrames][numChannels];
               /* ... then load it. */
              if (isBinary) {
                  loadBinaryData(dis);
              } else {
                  loadTextData(dis);
              }
          }
  
           /* Verify if everything went OK during the reading of the DataInputStream */ catch (IOException ioe) {
              throw new Error("IO Exception while parsing EST Track file: " + ioe.getMessage());
          }
  
      } /* End of loadHeaderAndData() */
  
      /**
       * load the data section of the file as ascii text
       *
       * @param dis DataInputStream to read from
       * @throws IOException on ill-formatted input
       */
      private void loadTextData(DataInputStream dis) throws IOException {
          for (int f = 0; f < numFrames; f++) {
              times[f] = Float.parseFloat(General.readWord(dis));
              General.readWord(dis);  // can be only 1
              for (int c = 0; c < numChannels; c++) {
                  frames[f][c] = Float.parseFloat(General.readWord(dis));
              }
          }
      }
  
      /**
       * load the data section of the file as ascii text
       *
       * @param dis DataInputStream to read from
       * @throws IOException on ill-formatted input
       */
      private void loadBinaryData(DataInputStream dis)
              throws IOException {
          for (int f = 0; f < numFrames; f++) {
              times[f] = General.readFloat(dis, isBigEndian);
  
              // Ignore the 'breaks' field
              General.readFloat(dis, isBigEndian);
  
              for (int c = 0; c < numChannels; c++) {
                  frames[f][c] = General.readFloat(dis, isBigEndian);
              }
          }
      }
  
      /**
       * Get the number of frames in this track
       *
       * @return number of frames in this track
       */
      public int getNumFrames() {
          return numFrames;
      }
  
      /**
       * Get the number of channels in this track
       *
       * @return number of channels in this track
       */
      public int getNumChannels() {
          return numChannels;
      }
  
      /**
       * Get the times associated with this track
       *
       * @return an array of times associated with this track
       */
      public float[] getTimes() {
          return times;
      }
  
      /**
       * Get the frames associated with this track
       *
       * @return an array of frames associated with this track
       */
      public float[][] getFrames() {
          return frames;
      }
  
      /**
       * Get an individual time associated with this track
       *
       * @param index index of time to get
       * @return time value at given index
       */
      public float getTime(int index) {
          return times[index];
      }
  
      /**
       * Returns the endianness of the file
       *
       * @return false if little endian (Intel), true if big endian (PowerPC, SPARC)
       */
      public boolean isBigEndian() {
          return isBigEndian;
      }
  
      /**
       * Returns the mode of the file (ascii or binary)
       *
       * @return false if ascii text, true if binary
       */
      public boolean isBinary() {
          return isBinary;
      }
  
      /**
       * Get the frame time which is closest to a certain time specification.
       * Time specifications falling after the last frame return the position of the last frame.
       *
       * @param seconds A time point, in seconds.
       * @return
       */
      public float getClosestTime(double seconds) {
          /* Obvious conditions */
          if (seconds < 0.0) return (0.0f);
          if (seconds > getTimeSpan()) return (getTimeSpan());
          /* If the internal pointer is after the requested time, rewind */
          if (getTime(timeIdx) > seconds) timeIdx = 0;
          float t1 = 0.0f;
          float t2 = getTime(timeIdx);
          /* Advance the internal time pointer until the requested time is crossed */
          while ((t2 < seconds) && (timeIdx < numFrames)) {
              timeIdx++;
              t1 = t2;
              t2 = getTime(timeIdx);
          }
          /* Find and return the closest time */
          return ((seconds - t1) < (t2 - seconds) ? t1 : t2);
      }
  
      /**
       * Get the time associated with the last frame
       *
       * @return time value of the last frame
       */
      public float getTimeSpan() {
          return times[numFrames - 1];
      }
  
      /**
       * Get an individual frame
       *
       * @param i index of frame
       * @return the frame
       */
      public float[] getFrame(int i) {
          return frames[i];
      }
  
      /**
       * Get an individual frame entry
       *
       * @param i index of frame
       * @param j index into frame
       * @return the frame entry in frame <code>i</code> at index
       * <code>j</code>
       */
      public float getFrameEntry(int i, int j) {
          return frames[i][j];
      }
  
      /**
       * Get the max and the min over the whole file
       *
       * @return a vector of 2 float values in the order [min,max].
       * @author Sacha K.
       */
      public float[] getMinMax() {
          /* Initialize */
          float min = getFrameEntry(0, 0);
          float max = min;
          float val;
          /* Browse the values */
          for (int f = 0; f < numFrames; f++) {
              for (int c = 0; c < numChannels; c++) {
                  val = frames[f][c];
                  if (val < min) {
                      min = val;
                  }
                  if (val > max) {
                      max = val;
                  }
              }
          }
          /* Return the result in a float vector */
          float[] result = new float[2]; // Resulting vector, returned as [min,max,range]
          result[0] = min;
          result[1] = max;
          return result;
      }
  
      /**
       * Get the max and the min over the whole file,
       * excluding the first column (which can be the energy in the EST LPCs)
       *
       * @return a vector of 2 float values in the order [min,max].
       * @author Sacha K.
       */
      public float[] getMinMaxNo1st() {
          /* Initialize */
          float min = getFrameEntry(0, 1);
          float max = min;
          float val;
          /* Browse the values */
          for (int f = 0; f < numFrames; f++) {
              for (int c = 1; c < numChannels; c++) {
                  val = frames[f][c];
                  if (val < min) {
                      min = val;
                  }
                  if (val > max) {
                      max = val;
                  }
              }
          }
          /* Return the result in a float vector */
          float[] result = new float[2]; // Resulting vector, returned as [min,max,range]
          result[0] = min;
          result[1] = max;
          return result;
      }
  }
  
  
  