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

2

7

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

Perquisite answered 14/8, 2011 at 6:4 Comment(4)
Are you clearing your canvas before each time you redraw the spectrum? I'm just (wildly) guessing the broken pieces are left over from previous draws...Selhorst
I am calling invalidate() in updateVisualizer() method. It is doing that task.Perquisite
I think your dB and magnitude formulas are not correct. First magnitude is calculate by multyplying with SQR(2). Also in dB you need to divide by a limit, as dB is always a measure related to something. You are not diving by your limit, so your limit is 1 dB, thats why your graph is so weird. Check out formulas in here zone.ni.com/devzone/cda/tut/p/id/4278Average
You do not want to sweep quickly, because to debug the FFT analyzer you want it to be looking at an input with only one frequency present. If that frequency changes during operation, you will get a more complicated result making it harder to understand the problems. - if you must use files and not a live generator, you'd probably be better with ten files each having one frequency. Also, this latest graph is simply not a valid output - perhaps your screenshot caught things in the process of redrawing between two different plots showing the sweep at two different frequencies.Yeaton
A
2

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.

Average answered 14/8, 2011 at 6:4 Comment(1)
Testing single frequency inputs across the range will likely be key to debugging, but automatic sweeping is probably going to make it hard to analyze unless the sweep is slow enough to be able to understand what is being seen at each frequency. Problematic cases would then need to be tested with steady inputs.Yeaton
Y
2

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.

Yeaton answered 16/8, 2011 at 19:31 Comment(3)
For you information, I need spectrum for my media player and i will try the suggestion "known magnitude sinusoids".Perquisite
I suggest not using square waves. Do you know what the frequency spectrum of a square wave looks like? That said, you can see some interesting things - the third peak has the division of energy between adjacent bins problem I mentioned. It looks like you have a reasonable response up to a bit above 15 KHZ, then some rolloff probably from a filter somewhere. The "hole" around 10 KHz is interesting. Would suggest you explore this in more detail by setting up something where you can adjust the frequency of a sine wave, so you'll only have one frequency at a time of constant input magnitude.Yeaton
I see now you are using an ogg file - don't do that. Even though the file seems to have a decent spectrum when decoded and plotted by audacity, the decoder you are using may well be the source of the high frequency rolloff and that hole in the noise floor. To evaluate your FFT and display implementation, should be testing not only with sine waves rather than square, but with linear PCM samples - if you must use sound files rather than raw data they should be .wav files.Yeaton

© 2022 - 2024 — McMap. All rights reserved.