content: /**
   * Copyright 2000-2009 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.util.data.audio;
  
  import java.io.IOException;
  
  import lib.sound.sampled.AudioFormat;
  import lib.sound.sampled.AudioInputStream;
  
  public class MonoAudioInputStream extends AudioInputStream {
      protected int inputChannels;
      protected int inputMode;
      protected AudioFormat newFormat;
  
      public MonoAudioInputStream(AudioInputStream input) {
          this(input, AudioPlayer.STEREO);
      }
  
      /**
       * @param input
       * @param inputMode if AudioPlayer.STEREO, average both input streams; if AudioPlayer.LEFT_ONLY, use only the left channel;
       *                  if AudioPlayer.RIGHT_ONLY, use only the right channel.
       */
      public MonoAudioInputStream(AudioInputStream input, int inputMode) {
          super(input, input.getFormat(), input.getFrameLength());
          this.newFormat = new AudioFormat(input.getFormat().getEncoding(), input.getFormat().getSampleRate(),
                  input.getFormat().getSampleSizeInBits(), 1, input.getFormat().getFrameSize() / input.getFormat().getChannels(),
                  input.getFormat().getFrameRate(), input.getFormat().isBigEndian());
          this.inputChannels = input.getFormat().getChannels();
          if (inputChannels < 2)
              throw new IllegalArgumentException("expected more than one input channel!");
          this.inputMode = inputMode;
          if (inputMode == AudioPlayer.MONO)
              throw new IllegalArgumentException("expected non-mono input mode");
      }
  
      /**
       * Reads up to a specified maximum number of bytes of data from the audio
       * stream, putting them into the given byte array.
       * <p>This method will always read an integral number of frames.
       * If <code>len</code> does not specify an integral number
       * of frames, a maximum of <code>len - (len % frameSize)
       * </code> bytes will be read.
       *
       * @param b   the buffer into which the data is read
       * @param off the offset, from the beginning of array <code>b</code>, at which
       *            the data will be written
       * @param len the maximum number of bytes to read
       * @return the total number of bytes read into the buffer, or -1 if there
       * is no more data because the end of the stream has been reached
       * @throws IOException if an input or output error occurs
       * @see #read(byte[])
       * @see #read()
       * @see #skip
       * @see #available
       */
      @Override
      public int read(byte[] b, int off, int len) throws IOException {
          int sampleSizeInBytes = frameSize / inputChannels;
          int outputFrameSize = sampleSizeInBytes; // mono output
          int nFrames = len / outputFrameSize;
          boolean bigEndian = getFormat().isBigEndian();
          byte[] inputBytes = new byte[nFrames * frameSize];
          int nInputBytes = super.read(inputBytes, 0, inputBytes.length);
          if (nInputBytes <= 0) return nInputBytes;
  
          if (inputMode == AudioPlayer.STEREO) {
              for (int i = 0, j = off; i < nInputBytes; i += frameSize, j += outputFrameSize) {
                  int sample = 0;
                  for (int c = 0; c < inputChannels; c++) {
                      if (sampleSizeInBytes == 1) {
                          sample += inputBytes[i] << 8;
                      } else if (sampleSizeInBytes == 2) { // 16 bit
                          byte lobyte;
                          byte hibyte;
                          if (!bigEndian) {
                              lobyte = inputBytes[i];
                              hibyte = inputBytes[i + 1];
                          } else {
                              lobyte = inputBytes[i + 1];
                              hibyte = inputBytes[i];
                          }
                          sample += hibyte << 8 | lobyte & 0xFF;
                      } else { // bytesPerSample == 3, i.e. 24 bit
                          assert sampleSizeInBytes == 3 : "Unsupported sample size in bytes: " + sampleSizeInBytes;
                          byte lobyte;
                          byte midbyte;
                          byte hibyte;
                          if (!bigEndian) {
                              lobyte = inputBytes[i];
                              midbyte = inputBytes[i + 1];
                              hibyte = inputBytes[i + 2];
                          } else {
                              lobyte = inputBytes[i + 2];
                              midbyte = inputBytes[i + 1];
                              hibyte = inputBytes[i];
                          }
                          sample += hibyte << 16 | (midbyte & 0xFF) << 8 | lobyte & 0xFF;
                      }
                  }
                  sample /= inputChannels; // here is where we average the three samples
                  if (sampleSizeInBytes == 1) {
                      b[j] = (byte) ((sample >> 8) & 0xFF);
                  } else if (sampleSizeInBytes == 2) { // 16 bit
                      byte lobyte = (byte) (sample & 0xFF);
                      byte hibyte = (byte) (sample >> 8);
                      if (!bigEndian) {
                          b[j] = lobyte;
                          b[j + 1] = hibyte;
                      } else {
                          b[j] = hibyte;
                          b[j + 1] = lobyte;
                      }
                  } else { // bytesPerSample == 3, i.e. 24 bit
                      assert sampleSizeInBytes == 3 : "Unsupported sample size in bytes: " + sampleSizeInBytes;
                      byte lobyte = (byte) (sample & 0xFF);
                      byte midbyte = (byte) ((sample >> 8) & 0xFF);
                      byte hibyte = (byte) (sample >> 16);
                      if (!bigEndian) {
                          b[j] = lobyte;
                          b[j + 1] = midbyte;
                          b[j + 2] = hibyte;
                      } else {
                          b[j] = hibyte;
                          b[j + 1] = midbyte;
                          b[j + 2] = lobyte;
                      }
                  }
              } // for all frames
          } else if (inputMode == AudioPlayer.LEFT_ONLY) {
              for (int i = 0, j = off; i < nInputBytes; i += frameSize, j += outputFrameSize) {
                  for (int k = 0; k < sampleSizeInBytes; k++) {
                      b[j + k] = inputBytes[i + k];
                  }
              }
          } else {
              assert inputMode == AudioPlayer.RIGHT_ONLY : "unexpected input mode: " + inputMode;
              for (int i = 0, j = off; i < nInputBytes; i += frameSize, j += outputFrameSize) {
                  for (int k = 0; k < sampleSizeInBytes; k++) {
                      b[j + k] = inputBytes[i + k + sampleSizeInBytes];
                  }
              }
  
          }
  
          return nInputBytes / inputChannels;
      }
  
      /**
       * Skips over and discards a specified number of bytes from this
       * audio input stream.
       *
       * @param n the requested number of bytes to be skipped
       * @return the actual number of bytes skipped
       * @throws IOException if an input or output error occurs
       * @see #read
       * @see #available
       */
      @Override
      public long skip(long n) throws IOException {
          return super.skip(n * inputChannels) / inputChannels;
      }
  
      /**
       * Returns the maximum number of bytes that can be read (or skipped over) from this
       * audio input stream without blocking.  This limit applies only to the next invocation of
       * a <code>read</code> or <code>skip</code> method for this audio input stream; the limit
       * can vary each time these methods are invoked.
       * Depending on the underlying stream,an IOException may be thrown if this
       * stream is closed.
       *
       * @return the number of bytes that can be read from this audio input stream without blocking
       * @throws IOException if an input or output error occurs
       * @see #read(byte[], int, int)
       * @see #read(byte[])
       * @see #read()
       * @see #skip
       */
      @Override
      public int available() throws IOException {
          int av = super.available();
          if (av <= 0) return av;
          return av / inputChannels;
      }
  
  
      @Override
      public AudioFormat getFormat() {
          return newFormat;
      }
  }
  
  