Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my FFT gives a different visualizer output than Windows Media Player?

I am trying to implement an audio spectrum analyzer in android using the Visualizer class.

I am getting FFT data in the onFftDataCapture() method of OnDataCaptureListener() event and I'm drawing that on the canvas using drawLines().

But the spectrum display is not showing properly. I can see changes on left side of graph only. But in Window Media Player, the output of the same song is different. What I am missing?

Can anyone help me on this with an example or a link?

CODE

mVisualizer.setDataCaptureListener(
            new Visualizer.OnDataCaptureListener() {

                public void onWaveFormDataCapture(Visualizer visualizer,
                        byte[] bytes, int samplingRate) {}

                public void onFftDataCapture(Visualizer visualizer,
                        byte[] bytes, int samplingRate) {
                    mVisualizerView.updateVisualizer(bytes, samplingRate);
                }
            }, Visualizer.getMaxCaptureRate() / 2, false, true);

onPaint()

    for (int i = 0; i < mBytes.length / 2; i++) {
        mPoints[i * 4] = i * 8;
        mPoints[i * 4 + 1] = 0;
        mPoints[i * 4 + 2] = i * 8;
        byte rfk = mBytes[2 * i];
        byte ifk = mBytes[2 * i + 1];
        magnitude = (float) (rfk * rfk + ifk * ifk);
        int dbValue = (int) (10 * Math.log10(magnitude));
        mPoints[i * 4 + 3] = (float) (dbValue * 7);
    }       
    canvas.drawLines(mPoints, mForePaint);

Where mVisualizer is Visualizer class object, and mBytes is FFT Data got from onFftDataCapture event.

You can read more about FFT data returned by event here.

This is what values I get onFftDataCapture() :

[90, -1, -27, 102, 13, -18, 40, 33, -7, 16, -23, -23, -2, -8, -11, -9, -8, -33, -29, 44, 4, -9, -15, -1, -2, -17, -7, 1, 1, 0, 3, -11, -5, 10, -24, -6, -23, 1, -9, -21, -2, 4, 9, -10, -14, -5, -16, 8, 6, -16, 14, 3, 7, 15, 10, -2, -15, -14, -5, 10, 8, 23, -1, -16, -2, -6, 4, 9, -1, 0, 0, 9, 1, 4, -2, 6, -6, -6, 8, -4, 6, 6, -4, -5, -5, -2, 3, 0, -1, 0, -7, 0, 2, 1, 0, 1, -1, 0, -1, 1, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -1]

Any idea, link would be helpful.

Partial value drawing

full value drawing

Update for @Chris Stratton

Now i am playing square wave at 1000 Hz file and took screen shot of that. What you suggest now?

1 KHz spectrum

Updated after @ruhalde suggestion

Now I am playing Frequency sweep (20-20000 Hz) file and this file generated following output.

Frequency sweep (20-20000 Hz) output

like image 910
Raj Avatar asked Aug 14 '11 06:08

Raj


2 Answers

It might be useful to figure out exactly how the behavior differs from expected (in the hope that would lead to an understanding of why) by playing known magnitude sinusoids on both the windows reference and the android app under development. Test one sythesized frequency at at time, and see the position, magnitude, and specificity of how it is plotted on each screen and the apparent magnitude.

You might, for example, discover differences in the covered frequency range, or perhaps one version is plotting frequency on a logarithmic axis (decades or octaves) rather than a linear one.

If your data source is a microphone, you could also have rolloff in the input circuitry or settings.

The linked document doesn't explain what window function is being used. Additionally, with raw FFT output you can have the energy distributed between adjacent bins, so it can produce a more consistent result to display each point as the average of two or three adjacent ones.

like image 89
Chris Stratton Avatar answered Sep 28 '22 13:09

Chris Stratton


I'll see some flaws in your code, mainly here >>

Visualizer.getMaxCaptureRate() / 2

No need of using maximum capture rate/2, just put a value somewhere between 10 and 30 times per second (millihertz according to documention, that is between 10000 and 30000 though), this would be good enough for not flickering and not putting too much pressure on resources inside Visualizer. Also, draw only with magnitudes between 20 and 20Khz, that is the audible spectrum, in your code you are drawing every frequency between 0 and your capture rate /2, which is maximum rate/2, who knows which frequency is the higher one...

Besides that, you'll need a pure sine wave, constantly sweeping from 0 to 20Khz to see how it looks like, better if its a RAW file with no compression. I would not use any OGG, MP3 or PCM files, I'll try an uncompressed WAV, neither use a square wave which generates lots of spikes in meter because of harmonycs.

Get the sweep files from here if you want Have you tried with another thread running, polling getFft() instead of doing it with OnDataCaptureListener??. I'll try this approach inside a Runnable, updating UI with a runOnUtiThread() method.

like image 42
ruhalde Avatar answered Sep 28 '22 13:09

ruhalde