Android FFT to display the main frequency

I have been working on an Android project for some time that displays the main frequency of the input signal (to act as a tuner). I have successfully implemented the AudioRecord class and get data from it. However, I hardly perform FFT on this data to get the main frequency of the input signal. I watched the post here , and use FFT in Java and the Complex class to go with it.

I have successfully used the FFT function found in FFT in Java, but I'm not sure that I am getting the correct results. For the FFT value (sqrt [rere + imim]), I get values ​​that start high, about 15000 Hz, and then slowly decrease to about 300 Hz. Doesn't seem right.

In addition, with regard to the raw microphone data, the data seems accurate, except that the first 50 values ​​or so are always number 3, unless I hit the setup button again while still in the application, and then I only get about 15. Is this normal?

Here is some of my code.

First of all, I convert the short data (received from the microphone) into double using the following code, which is from the message that I was considering . I don’t quite understand this piece of code, but I think it works.

//Conversion from short to double double[] micBufferData = new double[bufferSizeInBytes];//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 < bufferSizeInBytes - bytesPerSample + 1; index += bytesPerSample, floatIndex++) { double sample = 0; for (int b = 0; b < bytesPerSample; b++) { int v = audioData[index + b]; if (b < bytesPerSample - 1 || bytesPerSample == 1) { v &= 0xFF; } sample += v << (b * 8); } double sample32 = amplification * (sample / 32768.0); micBufferData[floatIndex] = sample32; } 

Then the code continues as follows:

 //Create Complex array for use in FFT Complex[] fftTempArray = new Complex[bufferSizeInBytes]; for (int i=0; i<bufferSizeInBytes; 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(); } fft.setTextColor(Color.GREEN); fft.setText("fftArray is "+ fftArray[500] +" and fftTempArray is "+fftTempArray[500] + " and fftInverse is "+fftInverse[500]+" and audioData is "+audioData[500]+ " and magnitude is "+ magnitude[1] + ", "+magnitude[500]+", "+magnitude[1000]+" Good job!"); for(int i = 2; i < samples; i++){ fft.append(" " + magnitude[i] + " Hz"); } 

This last bit is just to check what values ​​I get (and keep me sane!). The above article talks about the need for a sampling rate and gives this code:

 private double ComputeFrequency(int arrayIndex) { return ((1.0 * sampleRate) / (1.0 * fftOutWindowSize)) * arrayIndex; } 

How to implement this code? I do not understand where fftOutWindowSize and arrayIndex come from?

Any help is much appreciated!

Dustin

+4
source share
3 answers

Recently, I am working on a project that requires almost the same thing. You probably don't need any more help, but I will give my thoughts anyway. Perhaps someone will need it in the future.

  • I'm not sure if the short to double function works, I don't understand this piece of code. A double conversion is written for the byte.
  • In the code: "double[] micBufferData = new double[bufferSizeInBytes];" I think that the size of micBufferData should be " bufferSizeInBytes / 2 ", since each sample takes two bytes, and the size of micBufferData should be the number of the sample.
  • FFT algorithms require a FFT window size, and it must be a number, which is a power of 2. However, many algorithms can take an arbitrary number as input, and that will do the rest. The document of these algorithms must have input requirements. In your case, the size of the Complex array may be the input of the FFT algorithms. And I do not know the details of the FFT algorithm, but I think that the reverse is not needed.
  • To use the code you gave, finally, you must first find the peak index in the pattern array. I used a double array as an input instead of Complex, so in my case it is something like: double maxVal = -1;int maxIndex = -1;

     for( int j=0; j < mFftSize / 2; ++j ) { double v = fftResult[2*j] * fftResult[2*j] + fftResult[2*j+1] * fftResult[2*j+1]; if( v > maxVal ) { maxVal = v; maxIndex = j; } } 

    2 * j is the real part, and 2 * j + 1 is the imaginary part. maxIndex is the index of the required peak value (more info here ) and use it as an input to the ComputeFrequency function. The return value represents the frequency of the required pattern array.

Hope this can help someone.

+3
source

You should choose the size of the FFT window depending on the time and frequency requirements, and not just use the size of the audio buffer when creating the temp FFT array.

The array index is your int i used in the print [i] statement.

The fundamental tone frequency for music is often different from the peak FFT, so you can explore some pitch estimation algorithms.

+2
source

I suspect that the strange results you get are because you might need to unpack the FFT. This will depend on the library used (see here for documents on how it is packaged in GSL, for example). Packaging may mean that the real and imaginary components are not in positions in the array you are expecting.

For other questions about the size and resolution of the window, if you are creating a tuner, I would suggest trying a window size of about 20 ms (for example, 1024 samples at 44.1 kHz). For the tuner, you need a fairly high resolution, so you can try zero fill 8 or 16 times, which will give you a resolution of 3-6 Hz.

+2
source

Source: https://habr.com/ru/post/1383857/


All Articles