4

I want to get ultrasound from any Android device from example an ultrasound with a frequency between 18KHz and 19KHz.

I use the code below to calculate frequency but it doesn't seems to get me the correct frequency. The frequency i get stay between 11 KHz and 13KHz.

private void        calculateFrequency()
{
    // 1 - Initialize audio
    int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    int format = AudioFormat.ENCODING_PCM_16BIT;
    int sampleRate = 8000;
    int bufferSize = 2048;

    if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channel_config, format))
        bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel_config, format);
    AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleRate, channel_config, format, bufferSize);

    // 2 - Get sound
    byte[] audioBuffer = new byte[bufferSize];
    audioInput.startRecording();
    int nbRead = audioInput.read(audioBuffer, 0, bufferSize);
    audioInput.stop();
    audioInput.release();

    // 3 - Transform to double array
    double[] micBufferData = new double[bufferSize];
    final int bytesPerSample = 2; // As it is 16bit PCM
    final double amplification = 100.0; // choose a number as you like
    for (int index = 0, floatIndex = 0; index < nbRead - 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;
    }

    // 4 - Create complex array
    Complex[] fftTempArray = new Complex[bufferSize];
    for (int i=0; i<bufferSize; i++)
    {
        fftTempArray[i] = new Complex(micBufferData[i], 0);
    }

    // 5 - Calculate FFT
    Complex[] fftArray = FFT.fft(fftTempArray);

    // 6 - Calculate magnitude
    double[] magnitude = new double[bufferSize / 2];
    for (int i = 0; i < (bufferSize / 2); i++)
    {
        magnitude[i] = Math.sqrt(fftArray[i*2].re() * fftArray[i*2].re() + fftArray[i*2].im() * fftArray[i*2].im());
    }

    // 7 - Get maximum magnitude
    double max_magnitude = -1;
    for (int i = 0; i < bufferSize / 2; i++)
    {
        if (magnitude[i] > max_magnitude)
        {
            max_magnitude = magnitude[i];
        }
    }

    // 8 - Calculate frequency
    int freq = (int)(max_magnitude * sampleRate / bufferSize);

    ((TextView) findViewById(R.id.textView1)).setText("FREQUENCY = " + freq + "Hz");
}

I'm using two phones : one the send ultrasound with this app and the other one to get this ultrasound. I used this question as start point where I Took FFT and Complex classes.

What's wrong with my code ?

Community
  • 1
  • 1
Drakkin
  • 828
  • 12
  • 26
  • Nice idea. Could you please explain this for me: `int freq = (int)(max_magnitude * sampleRate / bufferSize);`. – FelixMarcus Oct 31 '14 at 15:59
  • I used this answer [link](http://stackoverflow.com/a/7675171/1770833) : see the last line of code – Drakkin Oct 31 '14 at 16:02
  • The mistakes are in steps 7 and 8 - you need the *index* of the max magnitude to determine the frequency, not the magnitude itself. Look at the pseudo code in the linked answer a little more carefully. – Paul R Nov 01 '14 at 08:37
  • Your sample rate must be in excess of twice the frequency of interest, per nyquist. – Chris Stratton Nov 02 '14 at 02:47
  • man how u fix wrong values in magnitude index? its sometime like i = max_length_of_magnitude and values in freq = 46000+ – Peter Aug 22 '16 at 12:34

2 Answers2

4

In order to get a correct non-aliased frequency estimate, one has to use a sample rate more (perhaps 10% to 20% greater to avoid filter roll-off) than twice the highest frequency in the audio input, thus more than twice the highest frequency you want to find.

This is due to the required Nyquist rate of the sampling theorem.

So if you want to find a 19 kHz signal, a sampling rate closer to 48000 is required.

hotpaw2
  • 68,014
  • 12
  • 81
  • 143
3

Steps 7 and 8 are not quite right - you need to use the index of the FFT bin with the largest magnitude in order to determine the frequency:

    // 7 - Get maximum magnitude
    double max_magnitude = -1;
    int max_magnitude_index = -1;
    for (int i = 0; i < bufferSize / 2; i++)
    {
        if (magnitude[i] > max_magnitude)
        {
            max_magnitude = magnitude[i];
            max_magnitude_index = i;
        }
    }

    // 8 - Calculate frequency
    int freq = (int)(max_magnitude_imdex * sampleRate / bufferSize);

And as noted by @hotpaw2, your sample rate is way too low at 8 kHz - it would need to be at least 44.1 kHz, preferably 48 kHz.

Paul R
  • 195,989
  • 32
  • 353
  • 519
  • Thank you for your answer. I set my sample rate to 48kHz and I have change my steps 7 and 8 as you said. When I try it without producing sound the frequencies sound OK 40 - 200 Hz. But when I produce ultrasound at 19kHz from my second phone I haven't got the good values and sometimes I get 23kHz. Do you know if a phone can really produce ultrasound ? Do you think in will be better to produce ultrasound from my compute's speaker ? – Drakkin Nov 03 '14 at 08:33
  • 1
    Most consumer grade transducers (microphones and loudspeakers) in phones, computers, etc, struggle at frequencies above ~15 kHz, since they are not really designed for "hi fi" use. You will probably find it difficult to do anything reliable in this frequency range. – Paul R Nov 03 '14 at 08:35
  • @PaulR hey buddy, do u know why sometimes max_magnitude_index return very big value? frequency then not right like if i have 500 hertc , app sometime calculated it as 45000/49000 and other big numbers ? – Peter Aug 22 '16 at 12:08
  • @Peter: sounds like a bug - maybe post a new question with your code and as much background info as possible ? – Paul R Aug 22 '16 at 12:43