public float DetectPitch(float[] buffer, int inFrames) {
    if (prevBuffer == null) {
      prevBuffer = new float[inFrames];
    }

    // double frames since we are combining present and previous buffers
    int frames = inFrames * 2;
    if (fftBuffer == null) {
      fftBuffer = new float[frames * 2]; // times 2 because it is complex input
    }

    for (int n = 0; n < frames; n++) {
      if (n < inFrames) {
        fftBuffer[n * 2] = prevBuffer[n] * HammingWindow(n, frames);
        fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
      } else {
        fftBuffer[n * 2] = buffer[n - inFrames] * HammingWindow(n, frames);
        fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
      }
    }

    // assuming frames is a power of 2
    shifter.smbFft(fftBuffer, frames, -1);

    float binSize = sampleRate / frames;
    int minBin = (int) (85 / binSize);
    int maxBin = (int) (300 / binSize);
    float maxIntensity = 0f;
    int maxBinIndex = 0;
    for (int bin = minBin; bin <= maxBin; bin++) {
      float real = fftBuffer[bin * 2];
      float imaginary = fftBuffer[bin * 2 + 1];
      float intensity = real * real + imaginary * imaginary;
      if (intensity > maxIntensity) {
        maxIntensity = intensity;
        maxBinIndex = bin;
      }
    }

    return binSize * maxBinIndex;
  }