gt:               double curFreq = i * this.rate;
context: package org.herac.tuxguitar.gui.tools.custom.tuner;
  
  import javax.sound.sampled.LineUnavailableException;
  import javax.sound.sampled.TargetDataLine;
  
  import org.apache.log4j.Logger;
  
  import craigl.spectrumanalyzer.FFT;
  
  /**
   * @author Nikola Kolarovic <nikola.kolarovic at gmail.com>
   * 
   * 
   *         Having great help analizyng project:
   * 
   *         GuitarTuner developed by OpenStudio www.openstudio.fr Copyright
   *         (2005) Arnault Pachot. Project URL:
   *         http://sourceforge.net/projects/guitartuner
   * 
   */
  public class TGTuner extends Thread {
    static class TGTunerException extends Exception {
  
      private static final long serialVersionUID = -1860514279319403845L;
  
      public TGTunerException(String string) {
        super(string);
      }
  
    }
  
    public static final transient Logger LOG = Logger.getLogger(TGTuner.class);
  
    public static void computeFFT(int sign, int n, double[] ar, double[] ai) {
      double scale = 2.0 / n;
      int i, j;
      for (i = j = 0; i < n; ++i) {
        if (j >= i) {
          double tempr = ar[j] * scale;
          double tempi = ai[j] * scale;
          ar[j] = ar[i] * scale;
          ai[j] = ai[i] * scale;
          ar[i] = tempr;
          ai[i] = tempi;
        }
        int m = n / 2;
        while ((m >= 1) && (j >= m)) {
          j -= m;
          m /= 2;
        }
        j += m;
      }
      int mmax, istep;
      for (mmax = 1, istep = 2 * mmax; mmax < n; mmax = istep, istep = 2 * mmax) {
        double delta = sign * Math.PI / mmax;
        for (int m = 0; m < mmax; ++m) {
          double w = m * delta;
          double wr = Math.cos(w);
          double wi = Math.sin(w);
          for (i = m; i < n; i += istep) {
            j = i + mmax;
            double tr = wr * ar[j] - wi * ai[j];
            double ti = wr * ai[j] + wi * ar[j];
            ar[j] = ar[i] - tr;
            ai[j] = ai[i] - ti;
            ar[i] += tr;
            ai[i] += ti;
          }
        }
        mmax = istep;
      }
    }
  
    protected double[] ai;
    protected double[] ar;
    protected boolean canceled;
  
    protected byte[] data;
    protected TargetDataLine dataLine;
    // static final int LOG2_FFTSIZE = 14;
    // protected int FFT_SIZE;
    protected FFT fft;
    protected TGTunerListener mainWindow = null;
    protected double maximumFrequency;
  
    protected double minimumFrequency;
    protected boolean paused;
  
    protected TGTunerQueue queue;
  
    protected double rate;
  
    protected TGTunerSettings settings;
  
    TGTuner(TGTunerListener mainWindow) {
      this.mainWindow = mainWindow;
      this.canceled = false;
      this.paused = false;
      this.queue = new TGTunerQueue();
      try {
        this.settings = TGTunerSettings.loadTuxGuitarSettings();
        this.dataLine = TGTunerSettings.getDataLine(this.settings);
      } catch (Exception ex) {
        this.mainWindow.fireException(ex);
      }
  
      this.setWantedRange();
  
    }
  
    public void closeDataLine() {
      synchronized (this) {
        if (this.dataLine != null) {
          this.dataLine.stop();
          this.dataLine.close();
        }
      }
    }
  
    protected void computeFFTParams() {
      if (this.settings != null) {
  
        int number = this.settings.getFFTSize(), log = 0;
        for (int i = 0; i < 20; i++) { // count log2
          number /= 2;
          if (number < 1) {
            this.fft = new FFT(log); // instantiate proper FFT alrgorithm
            break;
          }
          log++;
        }
        this.data = new byte[this.settings.getBufferSize()]; // data buffer
        this.ar = new double[this.settings.getFFTSize()]; // create real array for
                                                          // FFT
        this.ai = new double[this.settings.getFFTSize()]; // create imaginary
                                                          // array for FFT
  
        // TODO: rate is maybe not the best /2 ?
        this.rate = ((double) this.settings.getSampleRate())
            / this.settings.getFFTSize();
      }
    }
  
    public double getNoteFrequency(int midiNote) {
      // A = 110Hz = MidiNote(45)
      return 110 * Math.pow(2.0, (midiNote - 45) / 12.0);
    }
  
    public TGTunerSettings getSettings() {
      return this.settings;
    }
  
    public void openDataLine() {
      if (this.dataLine != null) {
        synchronized (this) {
          this.computeFFTParams();
          try {
            this.dataLine.open();
            this.dataLine.start();
            // this.dataLine.open(this.settings.getAudioFormat(),
            // this.settings.getBufferSize());
          } catch (LineUnavailableException e) {
            LOG.error("------- TGTuner: openDataLine() exception -------", e);
          }
        }
      }
    }
  
    public void pause() {
      this.paused = true;
  
    }
  
    public void resumeFromPause() {
      this.paused = false;
      this.queue.clear();
    }
  
    public void run() {
  
      // TODO: remove this benchmark
      int cycles = 0;
      long timePassed = 0;
  
      if (this.dataLine != null) {
        this.openDataLine();
      }
  
      while (!this.canceled) {
        if (this.dataLine != null)
          if (!this.paused && this.dataLine.isOpen()) {
            synchronized (this) {
  
              // read from the input
              this.dataLine.read(this.data, 0, this.settings.getBufferSize());
  
              // preapare the arrays for FFT
              for (int i = 0; i < this.settings.getBufferSize(); i++) {
                this.ar[i] = this.data[i]; // implicit cast to double
                this.ai[i] = 0.0f;
              }
  
              // when buffer size is smaller than FFT_SIZE
              for (int i = this.settings.getBufferSize(); i < this.settings
                  .getFFTSize(); i++) {
                this.ar[i] = 0.0f;
                this.ai[i] = 0.0f;
              }
  
              // ///////////////////////////////////////////////////////////
              // TODO: look at the benchmark, uncoment the other one and comment
              // the first one
              cycles++;
              long startTime = System.currentTimeMillis();
  
              if (this.fft != null)
                this.fft.doFFT(this.ar, this.ai, false);
              // TGTuner.computeFFT(1, this.settings.getFFTSize(), this.ar,
              // this.ai);
  
              timePassed += System.currentTimeMillis() - startTime;
              // ///////////////////////////////////////////////////////////
  
              // ------ determine the dominant frequency -------
              double frequency = -1;
              double maxAmplitude = this.settings.getTreshold(); // noise gate
  
              // TODO: maybe to analyze only the data around the area of interest
              // start has to be at least 1, because we want to skip the DC
              // component!
  
              // for (int i=0; i<this.settings.getFFTSize(); i++) {
              for (int i = (int) Math.round(this.minimumFrequency / this.rate); i < Math
                  .round(this.maximumFrequency / this.rate); i++) {
  