6

I have a sound file (.3gp) and its about ~1 min. I would like to get the frequency of this sound file in every 1/4 seconds. My idea is to receive samples in every 1/4 seconds from the audio file and using FFT I might get the frequency values. Is there any way to do this?

Actually I would split the sound file into 1/4sec samples sound files (alwyas overwriting the preveious one), then using FFT algorithm and detect the frequency where the magintude is the bigggest. But there might be easier solutions however I dont have a clue how to do this either.

***UPDATE 2 - new code

I use this code so far:

public class RecordAudio extends AsyncTask<Void, double[], Void> {

    @Override
    protected Void doInBackground(Void... arg0) {

        try {
             int bufferSize = AudioRecord.getMinBufferSize(frequency,
             AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);


            //int bufferSize = AudioRecord.getMinBufferSize(frequency, 
                  //  channelConfiguration, audioEncoding); 

            AudioRecord audioRecord = new AudioRecord( 
                    MediaRecorder.AudioSource.MIC, frequency, 
                    channelConfiguration, audioEncoding, bufferSize); 

            short[] buffer = new short[blockSize];
            //double[] toTransform = new double[blockSize];


            audioRecord.startRecording();


            // started = true; hopes this should true before calling
            // following while loop

            while (started) {
               sampling++;

               double[] re = new double[blockSize];
               double[] im = new double[blockSize];

               double[] newArray = new double[blockSize*2];
               double[] magns = new double[blockSize];

               double MaxMagn=0;
               double pitch = 0;

               int bufferReadResult = audioRecord.read(buffer, 0,
                        blockSize);


               for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
                   re[i] = (double) buffer[i] / 32768.0; // signed   16bit
                   im[i] = 0;
               }    

               newArray = FFTbase.fft(re, im,true);

               for (int i = 0; i < newArray.length; i+=2) {

                   re[i/2]=newArray[i];
                   im[i/2]=newArray[i+1];
                   magns[i/2] = Math.sqrt(re[i/2]*re[i/2]+im[i/2]*im[i/2]);
               }

              // I only need the first half      

              for (int i = 0; i < (magns.length)/2; i++) {
                   if (magns[i]>MaxMagn)
                   {
                       MaxMagn = magns[i];
                       pitch=i;
                   }
               }                                           
                 if (sampling > 50) {
                   Log.i("pitch and magnitude", "" + MaxMagn + "   " + pitch*15.625f);
                   sampling=0;
                   MaxMagn=0;pitch=0;
                   }                   


            }

            audioRecord.stop();

        } catch (Throwable t) {
            t.printStackTrace();
            Log.e("AudioRecord", "Recording Failed");
        }
        return null;
    }

I use this: http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

Guitar strings seem correct, but my own sound is not good because of this:

enter image description here

The magnitude of the two peaks change most of the time and I always find the biggest to get the fundamental frequency.

Erwan Douaille
  • 553
  • 1
  • 10
  • 31
Jani Bela
  • 1,660
  • 4
  • 27
  • 50
  • Hi, I have the same problem, I need to record voice real time and calculate frequency in every 4ms, how did you achieve this? Any sample code with you? – Yasitha Waduge Mar 13 '13 at 04:57
  • Hi, I did not succeed to overcome the problem however my guitar sounds were appropriate 9 out of 10, but my voice was maybe 7 out of 10.. – Jani Bela Apr 10 '13 at 19:41

2 Answers2

7

Pitch tracking with the FFT is asked so often on Stack Overflow I wrote a blog entry with sample code. The code is in C, but with the explanation and links you should be able to do what you want.

As to dividing it up into 1/4 second increments, you could simply take FFTs of 1/4 second segments as you suggested, instead of the default (which I think is about 1 second). If this doesn't give you the frequency resolution you want, you may have to use a different pitch recognition method. Another thing you could do is use overlapping segments that are longer than 1/4 second, but start at intervals that are 1/4 second apart. This method is alluded to the blog entry, but it may not meet your design spec.

Bjorn Roche
  • 11,279
  • 6
  • 36
  • 58
  • Thanks for the answer, updated my questiion with my code. I solved the 1/4 seconds with starting a sampling counter and when it reaches a given value it starts again. But the pitch detection is not so good at higher frequencies. If I make a loud high sound, the upper harmonics makes the whole thing wrong and I get around 13khz instead if 3khz. However for example I get 600hz insted of 1kz so I dont know whats the problem. – Jani Bela Aug 08 '12 at 08:18
  • The problem is that if you have a sound that has harmonics (ie any musical instrument, or any noise that isn't a pure sine wave) then simply finding the peak of the FFT won't tell you the pitch. The frequency corresponding to the pitch may be a lower amplitude than the harmonics. You need to read up on [Pitch Estimation algorithms](http://en.wikipedia.org/wiki/Pitch_detection_algorithm) – the_mandrill Aug 08 '12 at 09:00
  • It's true what the_mandrill said, but it's clear you have other problems because the frequencies you are getting are not multiples and therefore are not harmonics. If I have a chance later, I'll look more closely at your code, but on first skim it looks like you are making a few mistakes: 1. looking at the entire transformed data, rather than the lower half, 2. not windowing your data. All this and more is covered in my blog entry tutorial. – Bjorn Roche Aug 08 '12 at 14:05
  • Bjorn: I am just starting to understand your blog entry. This little project would be quite important for me so I am planning to work a lot about this :) – Jani Bela Aug 08 '12 at 17:01
  • I checked your blog (anyway I am learning EE). I edited my code with an applied LPF. This will give me a frequency lower than 4KHz, but it will be wrong. On may guitar A2 is the second string with 110Hz. My app gives me 129Hz if I make the sound smoothly. A little more loud sound and it gives me 129*2= 258Hz. So I somehow have to find the lowest harmonic. Maybe I can set a limit: chose the frequencies where the magnitude is over the limit and select the lowest frequency. But I think its a workaround and there might be correct algorithms to do it.. – Jani Bela Aug 08 '12 at 18:47
  • When I used Audacity and plotted the spectrum of the sound of my 110hz guitar string, the biggest magintude was about at 110hz, so maybe another fft should do the trick... However it would be easier not to change.:) – Jani Bela Aug 08 '12 at 20:55
  • I may not be reading your code correctly (it's hard to follow), but it's not clear to me that you are looking at the right part of the fft (lower half only) or that you are combining the real and imaginary parts correctly to get the magnitude (sqrt(r^2+i^2)). Again, this is all covered in the tutorial. – Bjorn Roche Aug 08 '12 at 21:16
  • No. If you have 512 samples, you will put 1024 data points into your FFT: 512 real (those are your samples), and 512 imaginary (set these to zero). Then you'll get 512 real and 512 imaginary data points out. Only use 256 of each of these, because the other half are dupes. You will then compute 256 magnitude values with the equation I gave above. How you specify the real vs imaginary data depends on your FFT algorithm, so read those docs. – Bjorn Roche Aug 08 '12 at 22:28
  • http://netscale.cse.nd.edu/twiki/pub/Main/Projects/Analyze_the_frequency_and_strength_of_sound_in_Android.pdf I used this. I found that int downy = (int) (100 - (toTransform[0][i] * 10)) means that the first part of the toTransform array is the imaginary part, and its always 0. So I use only the real part. Now the problem is still the upper harmonics, I dont know why they disturb the signal so strongly.:( – Jani Bela Aug 08 '12 at 23:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/15101/discussion-between-bjorn-roche-and-jani-bela) – Bjorn Roche Aug 09 '12 at 01:09
1

Try AsyncTask:

class GetFrequency extends AsyncTask<String, Void, Void> {
   public Void doInBackground(String... params) {
          while (true) {

             // Apply Logic Here

           try {
                Thread.sleep(250);
               } catch (Exception ie) {
                  // TODO Auto-generated catch block
                e.printStackTrace();
               }
       }
   }  
}

Call this in your MainActivity by,

frequencyButtonListener.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

        new GetFrequency.execute(params);

        }
    });
Name is Nilay
  • 2,743
  • 4
  • 35
  • 77
  • Hi, Thanks for the answer. I got some error I cannot fix. onPostExecute, onPreExecute and onProgress update gives me sytax errors. – Jani Bela Aug 07 '12 at 12:09
  • If you do not want them, then just remove them !! – Name is Nilay Aug 07 '12 at 12:14
  • I removed them. Frankly, I do not understand how it should work. I have a .3gp file in /sdcard/music.3gp and would like to analyse that. So I made a button with new GetFrequency.execute(params); but it gives me an error GetFrequency.execute cannot be resolved to a type. – Jani Bela Aug 07 '12 at 12:21
  • My other problem is that I would be courious about the logic I need to apply. – Jani Bela Aug 07 '12 at 12:25
  • @JaniBela-Try the Edited Code...n sorry I cant help with u about logic as I havnt work with frequencies in Android! – Name is Nilay Aug 07 '12 at 12:27