text: /**
   * Copyright 2004-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.concat;
  
  import marytts.signalproc.window.DynamicTwoHalvesWindow;
  import marytts.signalproc.window.Window;
  import marytts.util.data.BufferedDoubleDataSource;
  import marytts.util.data.Datagram;
  import marytts.util.data.DoubleDataSource;
  
  
  public class DatagramOverlapDoubleDataSource extends BufferedDoubleDataSource {
      protected Datagram[][] datagrams;
      protected Datagram[] rightContexts;
      protected int p; // point to current datagrams/rightContext
      protected int q; // point to current datagram within datagrams[p]
      protected int totalRead; // count samples read from datagrams
  
      /**
       * Construct an double data source from the given array of datagram arrays and right contexts.
       *
       * @param datagrams
       */
      public DatagramOverlapDoubleDataSource(Datagram[][] datagrams, Datagram[] rightContexts) {
          super((DoubleDataSource) null);
          this.datagrams = datagrams;
          this.rightContexts = rightContexts;
          dataLength = 0;
          for (int i = 0; i < datagrams.length; i++) {
              for (int j = 0; j < datagrams[i].length; j++) {
                  dataLength += datagrams[i][j].getDuration();
              }
          }
          p = 0;
          q = 0;
      }
  
      /**
       * Whether or not any more data can be read from this data source.
       *
       * @return true if another call to getData() will return data, false otherwise.
       */
      @Override
      public boolean hasMoreData() {
          return currentlyInBuffer() > 0 || totalRead < dataLength;
      }
  
      /**
       * The number of doubles that can currently be read from this
       * double data source without blocking. This number can change over time.
       *
       * @return the number of doubles that can currently be read without blocking
       */
      @Override
      public int available() {
          int available = (int) (currentlyInBuffer() + dataLength - totalRead);
          return available;
      }
  
      /**
       * Attempt to get more data from the input source. If less than this can be read,
       * the possible amount will be read, but canReadMore() will return false afterwards.
       *
       * @param minLength the amount of data to get from the input source
       * @return true if the requested amount could be read, false if none or less data could be read.
       */
      @Override
      protected boolean readIntoBuffer(int minLength) {
          if (bufferSpaceLeft() < minLength) {
              // current buffer cannot hold the data requested;
              // need to make it larger
              increaseBufferSize(minLength + currentlyInBuffer());
          } else if (buf.length - writePos < minLength) {
              compact(); // create a contiguous space for the new data
          }
          // Now we have a buffer that can hold at least minLength new data points
          int readSum = 0;
          // read blocks:
  
          while (readSum < minLength && p < datagrams.length) {
              if (q >= datagrams[p].length) {
                  p++;
                  q = 0;
              } else {
                  Datagram next = datagrams[p][q];
                  int length = (int) next.getDuration();
                  //System.out.println("Unit duration = " + String.valueOf(length));
                  if (buf.length < writePos + length) {
                      increaseBufferSize(writePos + length);
                  }
                  int read = readDatagram(next, buf, writePos);
                  if (q == 0 && p > 0 && rightContexts[p - 1] != null) {
                      // overlap-add situation
                      // window the data that we have just read with the left half of a HANN window:
                      new DynamicTwoHalvesWindow(Window.HANNING).applyInlineLeftHalf(buf, writePos, read);
                      // and overlap-add the previous right context, windowed with the right half of a HANN window:
                      double[] context = new double[(int) rightContexts[p - 1].getDuration()];
                      readDatagram(rightContexts[p - 1], context, 0);
                      new DynamicTwoHalvesWindow(Window.HANNING).applyInlineRightHalf(context, 0, context.length);
                      for (int i = 0, iMax = Math.min(read, context.length); i < iMax; i++) {
                          buf[writePos + i] += context[i];
                      }
                  }
                  writePos += read;
                  readSum += read;
                  totalRead += read;
                  q++;
              }
          }
          if (dataProcessor != null) {
              dataProcessor.applyInline(buf, writePos - readSum, readSum);
          }
          return readSum >= minLength;
      }
  
      protected int readDatagram(Datagram d, double[] target, int pos) {
          int dur = (int) d.getDuration();
          byte[] frameAudio = d.getData();
          assert frameAudio.length / 2 == dur : "expected datagram data length to be " + (dur * 2) + ", found " + frameAudio.length;
          for (int i = 0; i < frameAudio.length; i += 2, pos++) {
              int sample;
              byte lobyte;
              byte hibyte;
              // big endian:
              lobyte = frameAudio[i + 1];
              hibyte = frameAudio[i];
              sample = hibyte << 8 | lobyte & 0xFF;
              target[pos] = sample / 32768.0;// normalise to range [-1, 1];
          }
          return dur;
      }
  
  }
  
  