3

I am trying to implement an app where I can detect a whistle of a particular frequency (1000Hz - 1500Hz) even if there is environmental background noise and after a doing a lot of research on the net, I've used FFT methods to try and detect to see if the max amplitude that's captured from the mic corresponds with the whistle tone frequency.

public void run() {
    if (ar == null) {
        bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
        ar = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,bufferSize);
        audioBuffer = new short[bufferSize];
        ar.startRecording();
        ar.read(audioBuffer, 0, bufferSize);

        //Conversion from short to double
        double[] micBufferData = new double[bufferSize];//size may need to change
        final int bytesPerSample = 2; // As it is 16bit PCM
        final double amplification = 1.0; // choose a number as you like
        for (int index = 0, floatIndex = 0; index < (byte) bufferSize - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
            double sample = 0;
            for (int b = 0; b < bytesPerSample; b++) {
                int v = audioBuffer[index + b];
                if (b < bytesPerSample - 1 || bytesPerSample == 1) {
                    v &= 0xFF;
                }
                sample += v << (b * 8);
            }
            double sample32 = amplification * (sample / 32768.0);
            micBufferData[floatIndex] = sample32;
        }

        //Create Complex array for use in FFT
        Complex[] fftTempArray = new Complex[bufferSize];
        for (int i=0; i< (byte) bufferSize; i++)
        {
            fftTempArray[i] = new Complex(micBufferData[i], 0);
        }

        //Obtain array of FFT data
        final Complex[] fftArray = FFT.fft(fftTempArray);
        //final Complex[] fftInverse = FFT.ifft(fftTempArray);

        //Create an array of magnitude of fftArray
        double[] magnitude = new double[fftArray.length];
        for (int i=0; i<fftArray.length; i++){
            magnitude[i]= fftArray[i].abs();
        }


        double maxVal = -1.0;
        int maxIndex = 1;
        for( int j=0; j < fftArray.length / 2; ++j ) {
            double v = magnitude[2*j] * magnitude[2*j] + magnitude[2*j+1] * magnitude[2*j+1];
            if( v > maxVal ) {
                maxVal = v;
                maxIndex = j;
            }
        }

        maxFrequency =  ((1.0 * 44100) / (1.0 * bufferSize)) * maxIndex;

    }
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (isRunning) {
                tv2.setText("Frequency Detected: " + maxFrequency);
            }
        }
    });

}

I've set up a mic recorder already and so forth but I can't understand what the code is doing and I'm getting some errors saying that my fftarray is negative. Could someone help me point in the right direction? Or is there a better way to implement a whistle detection? I'm using code from here. I'm getting a N is not to the power of 2 exception being thrown.

Community
  • 1
  • 1
Guy Lee
  • 163
  • 1
  • 9
  • What do you mean you don't understand what the code is doing? It's not yours? – stackoverflowuser2010 May 01 '16 at 05:40
  • If you're trying to determine if particular frequencies are present in a sound, then FFT is the right way to go. What exactly is your question? – stackoverflowuser2010 May 01 '16 at 05:42
  • I'm getting some errors when I try to run the code. I get java.lang.RuntimeException: N is not a power of 2 for final Complex[] fftArray = FFT.fft(fftTempArray); – Guy Lee May 01 '16 at 05:51
  • See this: http://stackoverflow.com/questions/4364823/how-do-i-obtain-the-frequencies-of-each-value-in-a-fft – stackoverflowuser2010 May 01 '16 at 06:42
  • This is the kind of thing which is better to prototype in a higher level language first (mathematica, matlab, python-numpy) to sort out the math. For a bandpass in numpy see e.g. http://stackoverflow.com/questions/36968418/python-designing-a-time-series-filter-after-fourier-analysis/36975979#36975979 – roadrunner66 May 02 '16 at 06:20

1 Answers1

0

The following line doesn't work reliably:

for (int i=0; i< (byte) bufferSize; i++)

bufferSize can be much greater than a byte and the cast then can produce even negative numbers so that your loop isn't executed a single time. fftTempArray isn't initialized then.

Removing (byte) will correct this error.

But there is at least a 2nd error: Your "Conversion from short to double" is wrong. It combines two successive 16-bit-samples to one double sample in micBufferData while each 16-bit-sample should correspond to its own double sample.

EDIT

The error from your comment shows, that your array additionally needs to have a 2^N size. So, find the 2^N size which is next lower than bufferSize.

Hartmut Pfitzinger
  • 2,237
  • 3
  • 25
  • 47