title: Android: Get more precise frequencies through fft
body: <p>I'm using <a href="https://github.com/sommukhopadhyay/FFTBasedSpectrumAnalyzer" rel="nofollow">this FFTBasedSpectrumAnalyzer</a> to analyze sound gathered by the mic. However, the FFTBasedSpectrumAnalyzer created a graph whereas I want a single frequency I could place in a label, so I am trying to get the frequency of the peak by this formula: <code>mFreq = (((1.0 * frequency) / (1.0 * blockSize)) * mPeakPos)/2</code>. I'm also getting the magnitude (and therefore the peak and peak frequency) through this formula: </p>
  
  <pre><code>int mPeakPos = 0;
                  double mMaxFFTSample = 150.0;
                  for (int i = 0; i &lt; progress[0].length; i++) {
                      int x = i;
                      int downy = (int) (150 - (progress[0][i] * 10));
                      int upy = 150;
                      //Log.i("SETTT", "X: " + i + " downy: " + downy + " upy: " + upy);
  
                      if(downy &lt; mMaxFFTSample)
                      {
                          mMaxFFTSample = downy;
                          //mMag = mMaxFFTSample;
                          mPeakPos = i;
                      }
                  }
  </code></pre>
  
  <p>However, I have two problems. First, the max frequency is off by 10-40 Hz and varies even as I play a constant tone. Second, I can only analyze audio up to 4000 Hz. Is there a way to make this more accurate and/or analyze audio up to 22 kHz? Perhaps by editing block size to be something other than 256 or frequency other than 8000 (even though when I try this, mFreq drops to 0 and mMaxFFTSample becomes -2, typically). 
   Thank you.</p>
  
  <p>Here is the complete code:</p>
  
  <pre><code>public class FrequencyListener extends AppCompatActivity {
      private double mFreq;
      private double mMag;
      private boolean mDidHitTargetFreq;
      private View mBackgroundView;
  
      int frequency = 8000;
      int channelConfiguration = AudioFormat.CHANNEL_IN_MONO;
      int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
  
      AudioRecord audioRecord;
      private RealDoubleFFT transformer;
      int blockSize;
      boolean started = false;
      boolean CANCELLED_FLAG = false;
  
  
      RecordAudio recordTask;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          blockSize = 256;
          transformer = new RealDoubleFFT(blockSize);
  
          started = true;
          CANCELLED_FLAG = false;
          recordTask = new RecordAudio();
          recordTask.execute();
      }
  
      private class RecordAudio extends AsyncTask&lt;Void, double[], Boolean&gt; {
  
          @Override
          protected Boolean doInBackground(Void... params) {
  
              int bufferSize = AudioRecord.getMinBufferSize(frequency,
                      channelConfiguration, audioEncoding);
              audioRecord = new AudioRecord(
                      MediaRecorder.AudioSource.DEFAULT, frequency,
                      channelConfiguration, audioEncoding, bufferSize);
              int bufferReadResult;
              short[] buffer = new short[blockSize];
              double[] toTransform = new double[blockSize];
              try {
                  audioRecord.startRecording();
              } catch (IllegalStateException e) {
                  Log.e("Recording failed", e.toString());
  
              }
              while (started) {
                  if (isCancelled() || (CANCELLED_FLAG == true)) {
  
                      started = false;
                      //publishProgress(cancelledResult);
                      Log.d("doInBackground", "Cancelling the RecordTask");
                      break;
                  } else {
                      bufferReadResult = audioRecord.read(buffer, 0, blockSize);
  
                      for (int i = 0; i &lt; blockSize &amp;&amp; i &lt; bufferReadResult; i++) {
                          toTransform[i] = (double) buffer[i] / 32768.0; // signed 16 bit
                      }
  
                      transformer.ft(toTransform);
  
                      publishProgress(toTransform);
  
                  }
  
              }
              return true;
          }
          @Override
          protected void onProgressUpdate(double[]...progress) {
  
              int mPeakPos = 0;
              double mMaxFFTSample = 150.0;
              for (int i = 0; i &lt; progress[0].length; i++) {
                  int x = i;
                  int downy = (int) (150 - (progress[0][i] * 10));
                  int upy = 150;
                  //Log.i("SETTT", "X: " + i + " downy: " + downy + " upy: " + upy);
  
                  if(downy &lt; mMaxFFTSample)
                  {
                      mMaxFFTSample = downy;
                      //mMag = mMaxFFTSample;
                      mPeakPos = i;
                  }
              }
  
              mFreq = (((1.0 * frequency) / (1.0 * blockSize)) * mPeakPos)/2;
              Log.i("SETTT", "FREQ: " + mFreq + " MAG: " + mMaxFFTSample);
  
          }
          @Override
          protected void onPostExecute(Boolean result) {
              super.onPostExecute(result);
              try{
                  audioRecord.stop();
              }
              catch(IllegalStateException e){
                  Log.e("Stop failed", e.toString());
  
              }
          }
      }
  
      @Override
      protected void onPause() {
          super.onPause();
          started = false;
      }
  
      @Override
      protected void onResume() {
          super.onResume();
          started = true;
      }
  }
  </code></pre>