Forums

Need help - Pink noise analyzer

Started by FreeGameDev 3 years ago21 replieslatest reply 3 years ago113 views

Hello, I am making a 31 band pink noise analyzer. 

I send the pink noise file through a windowed FFT. I then take the frequency data and add the values in each 1/3 octave together. If I understand Pink noise correctly I should have equal values in each octave (or a flat graph). But what I am getting is a slope with larger values towards the higher frequencies. 

Here is an image of the 31 band's graphed.

 Lowest value is:0.05139772 Highest value is 2.575877

pink_noise_31band_spectrogram_9469.jpg

Is there something I am missing or that I do not understand correctly?

Thanks

ANSWER: Below you will find the path to the current answer which is:

energy = (fftResults[i] - 1/f) ^ 2               

OneThirdOctaveBin[Index] += energy;

Thanks to oliviert & SteveSmith

Also thanks to Rick Lyons for the explanation on calculating Decibels.

dB = 20*log(magnitude) 

Also thanks to everyone else who added to the discussion!

[ - ]
Reply by SteveSmithJune 4, 2018

You aren't calculating the energy correctly.  

You want to square each FFT value (to convert from amplitude to energy), and then take the average of the points in each 1/3 octave.  I think you will find that this is flat for 1/f noise.    

[ - ]
Reply by FreeGameDevJune 4, 2018

That is getting me a flat response! Thank you...

I switched the code to this: (and divide the bin once full by the number of values I put in the bin.

float energy = Mathf.Pow(fftResultBins[i], 2);
bin[binIndex] += energy;

changed from:
bin[binIndex] += fftResultBins[i] - (1f / currentFrequency);

Pink noise graph: Black is un-smoothed, pink is smoothed.

squareaveragepinknoise_28444.jpg

440hz graph:

squareaverage440hz_22685.jpg

White noise Graph:

squareaveragewhitenoise_29409.jpg

80hz, 440hz, 20k (looks like it is giving me the right results!)

80-440-20k_20399.jpg

Do the results look right? Did I understand correctly?

Thanks!

[ - ]
Reply by SteveSmithJune 4, 2018

I misspoke... Taking the mean of each 1/3 octave should make the white noise flat. Taking the sum of each 1/3 octave should make the 1/f noise flat. In other words, white noise has the same energy in each Hz, while 1/f noise has the same energy in each octave.  

But in your graphs the 1/f noise and the white noise look the same... that can't be right.    

[ - ]
Reply by FreeGameDevJune 4, 2018

Here are the 2 noise types zoomed in with the mean. I see what you mean.

Pink is not flat but white is. I will try the other next.

white noise

white_zoom_95073.jpg

Pink noise

pink_zoom_91834.jpg

[ - ]
Reply by FreeGameDevJune 4, 2018
                float energy = Mathf.Pow(fftResultBins[i], 2);
                b[binIndex] += energy;


Without dividing by the number of values in the bin results in this.

pink_zoom_53862.jpg

[ - ]
Reply by FreeGameDevJune 4, 2018

Think I got it now. 

Using your answer and Oliviert's I think I might have it. 


float energy = Mathf.Pow(Mathf.Abs(fftResultBins[i] - (1f / currentFrequency)), 2);
               

b[binIndex] += energy;


Pink

pink_zoom_60556.jpg

White

white_zoom_18703.jpg

[ - ]
Reply by oliviertJune 4, 2018

Pink Noise has a spectral density that decreases in 1/f.

so -10dB per octave

[ - ]
Reply by FreeGameDevJune 4, 2018

Thanks for the quick reply! Did I understand correctly in the code below? I subtract 1/currentFrequency from each frequency that I am adding to the bins? I have no idea how to convert it to db to see if it is correctly subtracting 10db per octave.

    void Make31OctaveBand()
    {
        float currentFrequency = 0;
        int binIndex = 0;
        bins[binIndex].value = 0;
        hzPerBin = SamplingRate / (float)fftResultBins.Length;

        for (int i = 0; i < fftResultBins.Length; i++)
        {
            currentFrequency = i * hzPerBin;
            //if larger than bin's max frequency move to next bin
            if (currentFrequency > bins[binIndex].maxFrequency)        
            {
                binIndex++;
                bins[binIndex].value = 0;
            }
            //Add result minus 1/f
            bins[binIndex].value += fftResultBins[i] - (1 / currentFrequency);
        }
    }


With this new code I get this. (red is the fftResults, magenta is my 31 band values)

31band_spectrogram_99138.jpg

[ - ]
Reply by lamabrewJune 4, 2018

If the red in your plot is the FFT results then that's not the FFT of typical pink noise.  BTW for any given FFT there is going to be significant variation (it's noise after all...) in each bin's level but in my experience averaging somewhere in the range of 8 to 128 frames will get you something smooth-ish.

[ - ]
Reply by FreeGameDevJune 4, 2018

Does it not look right because it is linear? Here it is in log. Green is fft results and blue is 1/f. (The results are better but as seen above, the graph is still sloping up.) I'll try your suggestion with smoothing next. Thanks again!

Ps: can someone tell me how I convert the fftResults to DB?


log_spectrogram_74590.jpg

[ - ]
Reply by StenzJune 4, 2018
It looks like your approach is wrong - you sum up FFT results for each 1/3 octave and expect the output to resemble 1/f scaling. However, the number of FFT bins per octave or 1/3 octave is not constant, so better divide each sum by the number of summands.
[ - ]
Reply by Rick LyonsJune 4, 2018

Hi. Regarding your question: "how I convert the fftResults to DB?", the answer is: For each complex-valued FFT output sample you must compute that sample's magnitude value (recall Pythagoras' Theorem). Now you have a sequence of real-valued magnitude samples. Next, compute 20 times the log-base-ten of each of those magnitude sample values. That last step yields your spectral samples measured in dB.

[ - ]
Reply by FreeGameDevJune 5, 2018

Thanks, I'll use some different code that allows me access to the imaginary and real parts. With the function I use now I only have access to the frequency bins.

[ - ]
Reply by Rick LyonsJune 5, 2018

Hi FreeGameDev.

I went to the following web site:

https://docs.unity3d.com/ScriptReference/AudioSour...

I can't read the software gibberish on that page but I had the distinct impression that the 'GetSpectrumData' function generates spectral data that is in terms of dB.

FFT software produces output data in one of the following forms (formats):

* complex-valued data,

* rel-valued magnitude data,

* real-valued power data (magnitude squared),

* decibels (dB)

FreeGameDev, it's your job to determine which one of the above types of FFT output data is produced by the 'Get SpectrumData' function. Do need help in that determination?

[ - ]
Reply by FreeGameDevJune 5, 2018

From this post here it says "each element shows the relative amplitude (0..1) of a the frequency equal to **N 24000 / Q* Hertz"

It looks like the values represented are amplitude. So, I guess the thing is to get DB from amplitude.

dB = 20*log( amplitude)  <- this right?

[ - ]
Reply by Rick LyonsJune 6, 2018
Hi FreeGameDev

I believe what that post calls "amplitude" is what I call "magnitude." If that is true then you are correct, and you should use:

dB = 20*log(amplitude)



[ - ]
Reply by FreeGameDevJune 6, 2018

Thanks

Now how do I give beers in here. :)

[ - ]
Reply by Rick LyonsJune 6, 2018

Hi FreeGameDev.

Great. Please send a case of Sierra Nevada IPA to:

Richard Lyons

Cell# 1638, Cell Block D

Folsom State Prison

Folsom, California  95603

[ - ]
Reply by FreeGameDevJune 6, 2018

lol

[ - ]
Reply by lamabrewJune 4, 2018

I think you meant -3dB/octave and -10 dB/decade?

[ - ]
Reply by lamabrewJune 4, 2018

Darn let me correct myself before I confuse things further...pink noise has equal energy per octave, which means the power drops off at -3dB/octave.