package sound;

public class Oscillator {
  private double audioData[];
  private double waveTable[];

  private int sampleRate = 8000;

  private double frequency;
  private double lambda;
  private double samplesPerCycle;

  private double delta_freq;

  private final static double twopi = Math.PI * 2;

  private double frequencyOfModulation;
  private double modulationIndex;

  public Oscillator(double frequency_, int length) {
    frequency = frequency_;
    audioData = new double[length];

    //the period of the wave form is
    lambda = 1 / frequency;

    //The number of samples per period is
    samplesPerCycle = sampleRate * lambda;

    delta_freq = 1 / samplesPerCycle;
    waveTable =
        new double[(int) Math.round(samplesPerCycle)];

  }

  public double actualFrequency() {
    return (double) sampleRate / (double) waveTable.length;
  }

  private double[] AudioDataFromTable() {
    int k = 0;
    for (int i = 0; i < audioData.length; i++) {
      audioData[i] = waveTable[k];
      k++;
      if (k >= waveTable.length)
        k = 0;
    }
    System.out.println("\nlambda=" + lambda +
                       "\nfrequency = " + frequency +
                       "\nwaveTable.length = " + waveTable.length +
                       "\nsampleRate = " + sampleRate +
                       "\naudioData.length = " + audioData.length +
                       "\nactual frequency = " + actualFrequency());
    return audioData;

  }

  public double[] getSineWave() {

    for (int i = 0; i < waveTable.length; i++)
      waveTable[i] =
          0.98 * Math.sin(twopi * i / waveTable.length);
    return AudioDataFromTable();
  }

  public double[] getSquareWave() {
    getSawWave();
    for (int i = 0; i < waveTable.length; i++)
      if (waveTable[i] > 0)
        waveTable[i] = .98;
      else
        waveTable[i] = -.98;
    return AudioDataFromTable();
  }

  public double[] getSawWave() {
    double v = -0.99;
    double dv = 2.0 / (double) waveTable.length;
    for (int i = 0; i < waveTable.length; i++) {
      waveTable[i] = v;
      v += dv;
    }
    System.out.println("Sawwave ends at:" + (double) (v - dv));
    return AudioDataFromTable();

  }

  public double[] getTriangleWave() {
    int sign;
    double v = 0;
    double dv = 3.d / (double) waveTable.length;
    for (int i = 0; i < waveTable.length; i++) {
      waveTable[i] = v;
      sign =
          sign(
              Math.cos(twopi * i / waveTable.length));
      v = v + sign * dv;
    }
    return AudioDataFromTable();
  }

  public int sign(double d) {
    if (d < 0) return -1;
    return 1;
  }

  public double getDuration() {
    return
        ((double) audioData.length /
        (double) sampleRate);
  }

  public int getSampleRate() {
    return sampleRate;
  }


  public double getFrequency() {
    return frequency;
  }

  public void setModulationIndex(double I) {
    modulationIndex = I;
  }

  public void setModulationFrequency(double fm) {
    frequencyOfModulation = fm;
  }

  public double[] getFM() {
    double r1 = twopi * frequency / sampleRate;
    double r2 = twopi * frequencyOfModulation / sampleRate;

    for (int n = 0; n < audioData.length; n++)
      audioData[n] =
          Math.sin(r1 * n +
                   +modulationIndex * Math.sin(r2 * n));
    return audioData;
  }

  public double[] getAM() {
    double r1 = twopi * frequency / sampleRate;
    double r2 = twopi * frequencyOfModulation / sampleRate;

    for (int n = 0; n < audioData.length; n++)
      audioData[n] =
          Math.cos(r1 * n) * Math.cos(r2 * n);
    return audioData;
  }

}