content: /**
   * Copyright 2007 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.signalproc.sinusoidal.test;
  
  import java.io.IOException;
  import java.util.Arrays;
  
  import marytts.signalproc.sinusoidal.Sinusoid;
  import marytts.util.math.MathUtils;
  import marytts.util.signal.SignalProcUtils;
  
  /**
   * This class can be used to generate various sinusoid signals and writing them to wav and ptc files
   * to be used in testing the analysis/synthesis algorithms
   *
   * @author Oytun T&uumlrk
   */
  public class SinusoidsTester extends BaseTester {
      public static float DEFAULT_PHASE = 0.0f;
      public static int DEFAULT_FRAME_INDEX = 0;
  
      public SinusoidsTester() {
  
      }
  
      public SinusoidsTester(float freqInHz) {
          this(freqInHz, DEFAULT_AMP);
      }
  
      public SinusoidsTester(float freqInHz, float amp) {
          this(freqInHz, amp, DEFAULT_PHASE);
      }
  
      public SinusoidsTester(float freqInHz, float amp, float phaseInDegrees) {
          this(freqInHz, amp, phaseInDegrees, DEFAULT_FRAME_INDEX);
      }
  
      public SinusoidsTester(float freqInHz, float amp, float phaseInDegrees, int frameIndex) {
          this(freqInHz, amp, phaseInDegrees, frameIndex, DEFAULT_DUR);
      }
  
      public SinusoidsTester(float freqInHz, float amp, float phaseInDegrees, int frameIndex, float durationInSeconds) {
          this(freqInHz, amp, phaseInDegrees, frameIndex, durationInSeconds, DEFAULT_FS);
      }
  
      public SinusoidsTester(Sinusoid sin) {
          this(sin, DEFAULT_DUR);
      }
  
      public SinusoidsTester(Sinusoid sin, float durationInSeconds) {
          this(sin, durationInSeconds, DEFAULT_FS);
      }
  
      public SinusoidsTester(float freqInHz, float amp, float phaseInDegrees, int frameIndex, float durationInSeconds, int samplingRateInHz) {
          super();
  
          Sinusoid[] tmpSins = new Sinusoid[1];
          tmpSins[0] = new Sinusoid(amp, freqInHz, phaseInDegrees, frameIndex);
          init(tmpSins, durationInSeconds, samplingRateInHz);
      }
  
      public SinusoidsTester(Sinusoid sin, float durationInSeconds, int samplingRateInHz) {
          super();
  
          Sinusoid[] tmpSins = new Sinusoid[1];
          tmpSins[0] = new Sinusoid(sin);
          init(tmpSins, durationInSeconds, samplingRateInHz);
      }
  
      public SinusoidsTester(Sinusoid[] sinsIn) {
          this(sinsIn, DEFAULT_DUR);
      }
  
      public SinusoidsTester(Sinusoid[] sinsIn, float durationInSeconds) {
          this(sinsIn, durationInSeconds, DEFAULT_FS);
      }
  
      public SinusoidsTester(Sinusoid[] sinsIn, float durationInSeconds, int samplingRateInHz) {
          super();
  
          init(sinsIn, durationInSeconds, samplingRateInHz);
      }
  
      //These constructors can be used to create tracks starting and terminating at desired time instants    
      public SinusoidsTester(Sinusoid[] sinsIn, float[] startTimesInSeconds, float[] endTimesInSeconds) {
          this(sinsIn, startTimesInSeconds, endTimesInSeconds, DEFAULT_FS);
      }
  
      public SinusoidsTester(Sinusoid[] sinsIn, float[] startTimesInSeconds, float[] endTimesInSeconds, int samplingRateInHz) {
          super();
  
          init(sinsIn, startTimesInSeconds, endTimesInSeconds, samplingRateInHz);
      }
      //
  
      public static void main(String[] args) throws IOException {
          int i, numSins;
          float[] tStarts, tEnds;
          SinusoidsTester s = null;
  
          //Single sinusoid, time-invariant
          s = new SinusoidsTester(200.0f);
          //
  
          //Several sinusoids, time-invariant
          numSins = 1;
          Sinusoid[] sins = new Sinusoid[numSins];
          tStarts = new float[numSins];
          tEnds = new float[numSins];
          sins[0] = new Sinusoid(100.0f, 400.0f, 0.0f);
          tStarts[0] = 0.0f;
          tEnds[0] = 1.5f;
          //sins[1] = new Sinusoid(25.0f, 211.0f, 0.0f); tStarts[1] = 0.0f; tEnds[1] = 1.5f;
          //sins[2] = new Sinusoid(125.0f, 555.0f, 0.0f); tStarts[2] = 0.0f; tEnds[2] = 1.5f;
          //sins[3] = new Sinusoid(110.0f, 917.0f, 0.0f); tStarts[3] = 0.0f; tEnds[3] = 1.5f;
          //sins[4] = new Sinusoid(100.0f, 175.0f, 0.0f); tStarts[4] = 1.3f; tEnds[4] = 2.5f;
          //sins[5] = new Sinusoid(25.0f, 346.0f, 0.0f); tStarts[5] = 1.3f; tEnds[5] = 2.5f;
          //sins[6] = new Sinusoid(125.0f, 981.0f, 0.0f); tStarts[6] = 2.0f; tEnds[6] = 3.0f;
          //sins[7] = new Sinusoid(70.0f, 1317.0f, 0.0f); tStarts[7] = 2.0f; tEnds[7] = 3.5f;
          s = new SinusoidsTester(sins, tStarts, tEnds);
          //
  
          /*
          //Sinus part
          numSins = 10;
          float [] sinFreqs = new float[numSins];
          sinFreqs[0] = 180.0f;
          sinFreqs[1] = 380.0f;
          sinFreqs[2] = 580.0f;
          sinFreqs[3] = 780.0f;
  
          Sinusoid [] sins = new Sinusoid[numSins];
          for (i=0; i<numSins; i++)
              sins[i] = new Sinusoid(100.0f, i*300+100, 0.0f);
  
          s = new SinusoidsTester(sins);
          //
          */
  
  
          /*
          //Fixed sinusoidal track with a gap
          numSins = 4;
          Sinusoid [] sins = new Sinusoid[numSins];
          tStarts = new float[numSins];
          tEnds = new float[numSins];
  
          sins[0] = new Sinusoid(0.0f, 0.0f, 0.0f);
          tStarts[0] = 0.0f;
          tEnds[0] = 0.1f;
          sins[1] = new Sinusoid(100.0f, 200.0f, 0.0f);
          tStarts[1] = 0.1f;
          tEnds[1] = 0.2f;
          sins[2] = new Sinusoid(100.0f, 300.0f, 0.0f);
          tStarts[2] = 0.3f;
          tEnds[2] = 0.4f;
          sins[3] = new Sinusoid(0.0f, 0.0f, 0.0f);
          tStarts[3] = 0.4f;
          tEnds[3] = 0.5f;
          s = new SinusoidsTester(sins, tStarts, tEnds);
          //
           */
  
          if (args.length > 1)
              s.write(args[0], args[1]);
          else
              s.write(args[0]);
      }
  
      public void init(Sinusoid[] sinsIn, float durationInSeconds, int samplingRateInHz) {
          if (sinsIn.length > 0) {
              float[] startTimesInSeconds = new float[sinsIn.length];
              float[] endTimesInSeconds = new float[sinsIn.length];
              for (int i = 0; i < sinsIn.length; i++) {
                  startTimesInSeconds[i] = 0.0f;
                  endTimesInSeconds[i] = durationInSeconds;
              }
  
              init(sinsIn, startTimesInSeconds, endTimesInSeconds, samplingRateInHz);
          }
      }
  
      public void init(Sinusoid[] sinsIn, float startTimeInSeconds, float endTimeInSeconds, int samplingRateInHz) {
          float[] startTimesInSeconds = new float[sinsIn.length];
          float[] endTimesInSeconds = new float[sinsIn.length];
          Arrays.fill(startTimesInSeconds, startTimeInSeconds);
          Arrays.fill(endTimesInSeconds, endTimeInSeconds);
  
          init(sinsIn, startTimesInSeconds, endTimesInSeconds, samplingRateInHz);
      }
  
      public void init(Sinusoid[] sinsIn, float[] startTimesInSeconds, float[] endTimesInSeconds, int samplingRateInHz) {
          fs = samplingRateInHz;
          signal = null;
          pitchMarks = null;
          int i, j;
  
          if (sinsIn != null) {
              assert startTimesInSeconds.length == endTimesInSeconds.length;
              assert sinsIn.length == endTimesInSeconds.length;
  
              float minFreq = 2 * fs;
              int minFreqInd = -1;
              for (i = 0; i < sinsIn.length; i++) {
                  if (sinsIn[i].freq > 0.0f && sinsIn[i].freq < minFreq) {
                      minFreq = sinsIn[i].freq;
                      minFreqInd = i;
                  }
              }
  
              int[] startSampleIndices = new int[sinsIn.length];
              int[] endSampleIndices = new int[sinsIn.length];
  
              for (i = 0; i < startTimesInSeconds.length; i++) {
                  if (startTimesInSeconds[i] < 0.0f)
                      startTimesInSeconds[i] = 0.0f;
                  if (endTimesInSeconds[i] < 0.0f)
                      endTimesInSeconds[i] = 0.0f;
                  if (startTimesInSeconds[i] > endTimesInSeconds[i])
                      startTimesInSeconds[i] = endTimesInSeconds[i];
  
                  startSampleIndices[i] = (int) (Math.floor(startTimesInSeconds[i] * fs + 0.5));
                  endSampleIndices[i] = (int) (Math.floor(endTimesInSeconds[i] * fs + 0.5)) - 1;
              }
  
              //int minStartSampleIndex = MathUtils.getMin(startSampleIndices);
              int minStartSampleIndex = 0; //To ensure pitch marks being generated starting from 0th sample
              int maxEndSampleIndex = MathUtils.getMax(endSampleIndices);
  
              //Create pitch marks by finding the longest period
              int maxT0;
  
              if (minFreqInd >= 0)
                  maxT0 = (int) (Math.floor(fs / minFreq + 0.5));
              else //No non-zero Hz sinusoids found, therefore set maxT0 to a fixed number
                  maxT0 = (int) Math.floor(0.010f * fs + 0.5);
  
              int numPitchMarks = (int) (Math.floor(((double) (maxEndSampleIndex - minStartSampleIndex + 1)) / maxT0 + 0.5)) + 1;
              pitchMarks = new int[numPitchMarks];
              for (i = 0; i < numPitchMarks; i++)
                  pitchMarks[i] = Math.min(i * maxT0 + minStartSampleIndex, maxEndSampleIndex);
              //
  
              float lastTime = SignalProcUtils.sample2time(pitchMarks[pitchMarks.length - 1], fs);
              int numfrm = (int) Math.floor((lastTime - 0.5 * ws) / ss + 0.5);
              f0s = new double[numfrm];
              Arrays.fill(f0s, minFreq);
  
              if (maxEndSampleIndex > 0) {
                  signal = new double[maxEndSampleIndex + 1];
                  Arrays.fill(signal, 0.0);
  
                  //Synthesize sinusoids
                  for (i = 0; i < sinsIn.length; i++) {
                      for (j = startSampleIndices[i]; j < endSampleIndices[i]; j++)
                          signal[j] += sinsIn[i].amp * Math.sin(MathUtils.TWOPI * ((j - startSampleIndices[i]) * (sinsIn[i].freq / fs) + sinsIn[i].phase / 360.0));
                  }
              }
          }
      }
  }
  
  